More work on the new viewer

This commit is contained in:
Kovid Goyal 2018-08-28 10:13:11 +05:30
parent 559adc51a2
commit 7b75c67e15
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 111 additions and 26 deletions

View File

@ -96,14 +96,14 @@ def ensure_single_instance(args, open_at):
def option_parser():
from gui2.main_window import option_parser
from calibre.gui2.main_window import option_parser
parser = option_parser(_('''\
%prog [options] file
View an e-book.
'''))
a = parser.add_option
a('--raise-window', default=False, action='strore_true',
a('--raise-window', default=False, action='store_true',
help=_('If specified, viewer window will try to come to the '
'front when started.'))
a('--full-screen', '--fullscreen', '-f', default=False, action='store_true',
@ -168,6 +168,10 @@ def main(args=sys.argv):
t.daemon = True
t.start()
QTimer.singleShot(0, acc.flush)
if opts.raise_window:
main.raise_()
if opts.full_screen:
main.showFullScreen()
app.exec_()
if listener is not None:

View File

@ -5,18 +5,34 @@
from __future__ import absolute_import, division, print_function, unicode_literals
import os
from threading import Thread
from PyQt5.Qt import pyqtSignal
from PyQt5.Qt import QDockWidget, Qt, pyqtSignal
from calibre.gui2 import error_dialog
from calibre.gui2.main_window import MainWindow
from calibre.gui2.viewer2.convert_book import prepare_book
from calibre.gui2.viewer2.web_view import WebView, set_book_path
from calibre.utils.ipc.simple_worker import WorkerError
class EbookViewer(MainWindow):
msg_from_anotherinstance = pyqtSignal(object)
book_prepared = pyqtSignal(object, object)
def __init__(self):
MainWindow.__init__(self)
MainWindow.__init__(self, None)
self.book_prepared.connect(self.load_finished, type=Qt.QueuedConnection)
self.web_view = WebView(self)
self.setCentralWidget(self.web_view)
def create_dock(title, name, areas=Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea):
ans = QDockWidget(title, self)
ans.setObjectName(name)
ans.close()
self.toc_dock = create_dock(_('Table of Contents'), 'toc-dock')
def handle_commandline_arg(self, arg):
if arg and os.path.isfile(arg) and os.access(arg, os.R_OK):
@ -29,3 +45,28 @@ class EbookViewer(MainWindow):
return
self.load_ebook(path, open_at=open_at)
self.raise_()
def load_ebook(self, pathtoebook, open_at=None):
# TODO: Implement open_at
t = Thread(name='LoadBook', target=self._load_ebook_worker, args=(pathtoebook, open_at))
t.daemon = True
t.start()
def _load_ebook_worker(self, pathtoebook, open_at):
try:
ans = prepare_book(pathtoebook)
except WorkerError as e:
self.book_prepared.emit(False, {'exception': e, 'tb': e.orig_tb, 'pathtoebook': pathtoebook})
except Exception as e:
import traceback
self.book_prepared.emit(False, {'exception': e, 'tb': traceback.format_exc(), 'pathtoebook': pathtoebook})
else:
self.book_prepared.emit(True, {'base': ans, 'pathtoebook': pathtoebook, 'open_at': open_at})
def load_finished(self, ok, data):
if not ok:
error_dialog(self, _('Loading book failed'), _(
'Failed to open the book at {0}. Click "Show details" for more info.').format(data['pathtoebook']),
det_msg=data['tb'], show=True)
return
set_book_path(data['base'])

View File

@ -4,17 +4,20 @@
from __future__ import absolute_import, division, print_function, unicode_literals
from PyQt5.Qt import QApplication, QBuffer, QByteArray
import os
from PyQt5.Qt import QApplication, QBuffer, QByteArray, QSize
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
from PyQt5.QtWebEngineWidgets import (
QWebEnginePage, QWebEngineProfile, QWebEngineScript
)
from calibre import prints
from calibre import as_unicode, prints
from calibre.constants import (
FAKE_HOST, FAKE_PROTOCOL, __version__, is_running_from_develop
)
from calibre.gui2 import open_url
from calibre.ebooks.oeb.polish.utils import guess_type
from calibre.gui2 import error_dialog, open_url
from calibre.gui2.webengine import (
Bridge, RestartingWebEngineView, create_script, from_js, insert_scripts,
secure_webengine, to_js
@ -28,8 +31,22 @@ except ImportError:
# Override network access to load data from the book {{{
def set_book_path(path=None):
set_book_path.path = os.path.abspath(path)
def get_data(name):
raise NotImplementedError('TODO: implement this')
bdir = getattr(set_book_path, 'path', None)
if bdir is None:
return None, None
path = os.path.abspath(os.path.join(bdir, name))
if not path.startswith(bdir):
return None, None
try:
with lopen(path, 'rb') as f:
return f.read(), guess_type(name)
except EnvironmentError as err:
prints('Failed to read from book file: {} with error: {}'.format(name, as_unicode(err)))
class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
@ -46,24 +63,26 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
rq.fail(rq.UrlNotFound)
return
name = url.path()[1:]
try:
data, mime_type = get_data(name)
if data is None:
rq.fail(rq.UrlNotFound)
return
if isinstance(data, type('')):
data = data.encode('utf-8')
mime_type = {
# Prevent warning in console about mimetype of fonts
'application/vnd.ms-opentype':'application/x-font-ttf',
'application/x-font-truetype':'application/x-font-ttf',
'application/font-sfnt': 'application/x-font-ttf',
}.get(mime_type, mime_type)
self.send_reply(rq, mime_type, data)
except Exception:
import traceback
traceback.print_exc()
rq.fail(rq.RequestFailed)
if name.startswith('book/'):
name = name.partition('/')[2]
try:
data, mime_type = get_data(name)
if data is None:
rq.fail(rq.UrlNotFound)
return
if isinstance(data, type('')):
data = data.encode('utf-8')
mime_type = {
# Prevent warning in console about mimetype of fonts
'application/vnd.ms-opentype':'application/x-font-ttf',
'application/x-font-truetype':'application/x-font-ttf',
'application/font-sfnt': 'application/x-font-ttf',
}.get(mime_type, mime_type)
self.send_reply(rq, mime_type, data)
except Exception:
import traceback
traceback.print_exc()
rq.fail(rq.RequestFailed)
def send_reply(self, rq, mime_type, data):
if sip.isdeleted(rq):
@ -151,3 +170,24 @@ class WebView(RestartingWebEngineView):
def __init__(self, parent=None):
RestartingWebEngineView.__init__(self, parent)
self.dead_renderer_error_shown = False
self.render_process_failed.connect(self.render_process_died)
w = QApplication.instance().desktop().availableGeometry(self).width()
self._size_hint = QSize(int(w/3), int(w/2))
self._page = WebPage(self)
self.setPage(self._page)
self.setAcceptDrops(False)
def render_process_died(self):
if self.dead_renderer_error_shown:
return
self.dead_renderer_error_shown = True
error_dialog(self, _('Render process crashed'), _(
'The Qt WebEngine Render process has crashed.'
' You should try restarting the viewer.') , show=True)
def sizeHint(self):
return self._size_hint
def refresh(self):
self.pageAction(QWebEnginePage.Reload).trigger()