Loading documents in preview panel works

This commit is contained in:
Kovid Goyal 2018-07-27 17:26:22 +05:30
parent 8877445c78
commit 84a884e49e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 85 additions and 88 deletions

View File

@ -39,7 +39,7 @@ is64bit = sys.maxsize > (1 << 32)
isworker = hasenv('CALIBRE_WORKER') or hasenv('CALIBRE_SIMPLE_WORKER') isworker = hasenv('CALIBRE_WORKER') or hasenv('CALIBRE_SIMPLE_WORKER')
if isworker: if isworker:
os.environ.pop(environ_item('CALIBRE_FORCE_ANSI'), None) os.environ.pop(environ_item('CALIBRE_FORCE_ANSI'), None)
FAKE_PROTOCOL, FAKE_HOST = 'https', 'calibre-internal.invalid' FAKE_PROTOCOL, FAKE_HOST = 'calibre', 'internal.invalid'
VIEWER_APP_UID = 'com.calibre-ebook.viewer' VIEWER_APP_UID = 'com.calibre-ebook.viewer'
EDITOR_APP_UID = 'com.calibre-ebook.edit-book' EDITOR_APP_UID = 'com.calibre-ebook.edit-book'
MAIN_APP_UID = 'com.calibre-ebook.main-gui' MAIN_APP_UID = 'com.calibre-ebook.main-gui'

View File

@ -9,36 +9,45 @@ from __future__ import absolute_import, division, print_function, unicode_litera
# check that clicking on both internal and external links works # check that clicking on both internal and external links works
# check if you can remove the restriction that prevents inspector dock from being undocked # check if you can remove the restriction that prevents inspector dock from being undocked
# check the context menu # check the context menu
# check syncing of position back and forth
# check all butotns and search functionality in preview panel
# rewrite JS from coffeescript to rapydscript # rewrite JS from coffeescript to rapydscript
# pass user stylesheet with css for split
import json import json
import textwrap import textwrap
import time import time
from collections import defaultdict
from functools import partial from functools import partial
from threading import Thread from threading import Thread
from PyQt5.Qt import ( from PyQt5.Qt import (
QApplication, QIcon, QMenu, QNetworkAccessManager, QNetworkReply, QApplication, QBuffer, QByteArray, QIcon, QMenu, QSize, QTimer, QToolBar, QUrl,
QNetworkRequest, QSize, QTimer, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal, QVBoxLayout, QWidget, pyqtSignal, pyqtSlot
pyqtSlot
) )
from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler
from PyQt5.QtWebEngineWidgets import ( from PyQt5.QtWebEngineWidgets import (
QWebEnginePage, QWebEngineProfile, QWebEngineScript, QWebEngineView QWebEnginePage, QWebEngineProfile, QWebEngineScript, QWebEngineView
) )
from calibre import prints from calibre import prints
from calibre.constants import FAKE_HOST, FAKE_PROTOCOL, __version__ from calibre.constants import FAKE_HOST, FAKE_PROTOCOL, __version__
from calibre.ebooks.oeb.base import OEB_DOCS, serialize from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.polish.parsing import parse
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, open_url, secure_webengine from calibre.gui2 import NO_URL_FORMATTING, error_dialog, open_url, secure_webengine
from calibre.gui2.tweak_book import TOP, actions, current_container, editors, tprefs from calibre.gui2.tweak_book import TOP, actions, current_container, editors, tprefs
from calibre.gui2.widgets2 import HistoryLineEdit2 from calibre.gui2.widgets2 import HistoryLineEdit2
from calibre.utils.ipc.simple_worker import offload_worker from calibre.utils.ipc.simple_worker import offload_worker
from polyglot.binary import as_base64_unicode
from polyglot.builtins import native_string_type, unicode_type from polyglot.builtins import native_string_type, unicode_type
from polyglot.queue import Empty, Queue from polyglot.queue import Empty, Queue
from polyglot.urllib import urlparse from polyglot.urllib import urlparse
try:
from PyQt5 import sip
except ImportError:
import sip
shutdown = object() shutdown = object()
@ -161,87 +170,76 @@ parse_worker = ParseWorker()
# Override network access to load data "live" from the editors {{{ # Override network access to load data "live" from the editors {{{
class NetworkReply(QNetworkReply): class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, parent, request, mime_type, name): def __init__(self, parent=None):
QNetworkReply.__init__(self, parent) QWebEngineUrlSchemeHandler.__init__(self, parent)
self.setOpenMode(QNetworkReply.ReadOnly | QNetworkReply.Unbuffered) self.requests = defaultdict(list)
self.setRequest(request)
self.setUrl(request.url()) def requestStarted(self, rq):
self._aborted = False if bytes(rq.requestMethod()) != b'GET':
rq.fail(rq.RequestDenied)
return
url = rq.requestUrl()
if url.host() != FAKE_HOST:
rq.fail(rq.UrlNotFound)
return
name = url.path()[1:]
try:
c = current_container()
if not c.has_name(name):
rq.fail(rq.UrlNotFound)
return
mime_type = c.mime_map.get(name, 'application/octet-stream')
if mime_type in OEB_DOCS: if mime_type in OEB_DOCS:
self.resource_name = name mime_type = XHTML_MIME
self.requests[name].append((mime_type, rq))
QTimer.singleShot(0, self.check_for_parse) QTimer.singleShot(0, self.check_for_parse)
else: else:
data = get_data(name) data = get_data(name)
if isinstance(data, unicode_type): if isinstance(data, type('')):
data = data.encode('utf-8') data = data.encode('utf-8')
mime_type += '; charset=utf-8'
self.__data = data
mime_type = { mime_type = {
# Prevent warning in console about mimetype of fonts # Prevent warning in console about mimetype of fonts
'application/vnd.ms-opentype':'application/x-font-ttf', 'application/vnd.ms-opentype':'application/x-font-ttf',
'application/x-font-truetype':'application/x-font-ttf', 'application/x-font-truetype':'application/x-font-ttf',
'application/font-sfnt': 'application/x-font-ttf', 'application/font-sfnt': 'application/x-font-ttf',
}.get(mime_type, mime_type) }.get(mime_type, mime_type)
self.setHeader(QNetworkRequest.ContentTypeHeader, mime_type) self.send_reply(rq, mime_type, data)
self.setHeader(QNetworkRequest.ContentLengthHeader, len(self.__data))
QTimer.singleShot(0, self.finalize_reply)
def check_for_parse(self):
if self._aborted:
return
data = parse_worker.get_data(self.resource_name)
if data is None:
return QTimer.singleShot(10, self.check_for_parse)
self.__data = data
self.setHeader(QNetworkRequest.ContentTypeHeader, 'application/xhtml+xml; charset=utf-8')
self.setHeader(QNetworkRequest.ContentLengthHeader, len(self.__data))
self.finalize_reply()
def bytesAvailable(self):
try:
return len(self.__data)
except AttributeError:
return 0
def isSequential(self):
return True
def abort(self):
self._aborted = True
def readData(self, maxlen):
ans, self.__data = self.__data[:maxlen], self.__data[maxlen:]
return ans
read = readData
def finalize_reply(self):
if self._aborted:
return
self.setFinished(True)
self.setAttribute(QNetworkRequest.HttpStatusCodeAttribute, 200)
self.setAttribute(QNetworkRequest.HttpReasonPhraseAttribute, "Ok")
self.metaDataChanged.emit()
self.downloadProgress.emit(len(self.__data), len(self.__data))
self.readyRead.emit()
self.finished.emit()
class NetworkAccessManager(QNetworkAccessManager):
def createRequest(self, operation, request, data):
qurl = request.url()
if operation == self.GetOperation and qurl.host() == FAKE_HOST:
name = qurl.path()[1:]
c = current_container()
if c.has_name(name):
try:
return NetworkReply(self, request, c.mime_map.get(name, 'application/octet-stream'), name)
except Exception: except Exception:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
return QNetworkAccessManager.createRequest(self, operation, request, data) rq.fail(rq.RequestFailed)
def send_reply(self, rq, mime_type, data):
if sip.isdeleted(rq):
return
buf = QBuffer(parent=rq)
buf.open(QBuffer.WriteOnly)
# we have to copy data into buf as it will be garbage
# collected by python
buf.write(data)
buf.seek(0)
buf.close()
buf.aboutToClose.connect(buf.deleteLater)
rq.reply(mime_type.encode('ascii'), buf)
def check_for_parse(self):
remove = []
for name, requests in self.requests.iteritems():
data = parse_worker.get_data(name)
if data is not None:
if not isinstance(data, bytes):
data = data.encode('utf-8')
for mime_type, rq in requests:
self.send_reply(rq, mime_type, data)
remove.append(name)
for name in remove:
del self.requests[name]
if self.requests:
return QTimer.singleShot(10, self.check_for_parse)
# }}} # }}}
@ -284,8 +282,8 @@ def create_profile():
js += P('csscolorparser.js', data=True, allow_user_override=False) js += P('csscolorparser.js', data=True, allow_user_override=False)
js += compiled_coffeescript('ebooks.oeb.polish.preview', dynamic=False) js += compiled_coffeescript('ebooks.oeb.polish.preview', dynamic=False)
insert_scripts(ans, create_script('editor-preview.js', js)) insert_scripts(ans, create_script('editor-preview.js', js))
# ans.url_handler = UrlSchemeHandler(ans) url_handler = UrlSchemeHandler(ans)
# ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), ans.url_handler) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
s = ans.settings() s = ans.settings()
s.setDefaultTextEncoding('utf-8') s.setDefaultTextEncoding('utf-8')
s.setAttribute(s.FullScreenSupportEnabled, False) s.setAttribute(s.FullScreenSupportEnabled, False)
@ -302,9 +300,8 @@ class WebPage(QWebEnginePage):
def __init__(self, parent): def __init__(self, parent):
QWebEnginePage.__init__(self, create_profile(), parent) QWebEnginePage.__init__(self, create_profile(), parent)
secure_webengine(self, for_viewer=True) secure_webengine(self, for_viewer=True)
data = 'data:text/css;charset=utf-8;base64,' # TOD: Implement this
css = '[data-in-split-mode="1"] [data-is-block="1"]:hover { cursor: pointer !important; border-top: solid 5px green !important }' # css = '[data-in-split-mode="1"] [data-is-block="1"]:hover { cursor: pointer !important; border-top: solid 5px green !important }'
data += as_base64_unicode(css)
def javaScriptConsoleMessage(self, level, msg, linenumber, source_id): def javaScriptConsoleMessage(self, level, msg, linenumber, source_id):
prints('%s:%s: %s' % (source_id, linenumber, msg)) prints('%s:%s: %s' % (source_id, linenumber, msg))
@ -312,7 +309,7 @@ class WebPage(QWebEnginePage):
def acceptNavigationRequest(self, url, req_type, is_main_frame): def acceptNavigationRequest(self, url, req_type, is_main_frame):
if req_type == self.NavigationTypeReload: if req_type == self.NavigationTypeReload:
return True return True
if url.scheme() == FAKE_PROTOCOL: if url.scheme() in (FAKE_PROTOCOL, 'data'):
return True return True
open_url(url) open_url(url)
return False return False