def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-editor-preview ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_editor compile_editor() js = P('editor.js', data=True, allow_user_override=False) cparser = P('csscolorparser.js', data=True, allow_user_override=False) insert_scripts( ans, create_script('csscolorparser.js', cparser), create_script('editor.js', js), ) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.FullScreenSupportEnabled, False) s.setAttribute(s.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) osname = 'windows' if iswindows else ('macos' if ismacos else 'linux') # DO NOT change the user agent as it is used to workaround # Qt bugs see workaround_qt_bug() in ajax.pyj ua = 'calibre-viewer {} {}'.format(__version__, osname) ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_viewer prints('Compiling viewer code...') compile_viewer() js = P('viewer.js', data=True, allow_user_override=False) translations_json = get_translations_data() or b'null' js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1) if in_develop_mode: js = js.replace(b'__IN_DEVELOP_MODE__', b'1') insert_scripts(ans, create_script('viewer.js', js)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-editor-preview ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_editor compile_editor() js = P('editor.js', data=True, allow_user_override=False) cparser = P('csscolorparser.js', data=True, allow_user_override=False) dark_mode_css = P('dark_mode.css', data=True, allow_user_override=False).decode('utf-8') insert_scripts( ans, create_script('csscolorparser.js', cparser), create_script('editor.js', js), create_script('dark-mode.js', ''' (function() { var settings = JSON.parse(navigator.userAgent.split('|')[1]); var dark_css = CSS; function apply_body_colors(event) { if (document.documentElement) { if (settings.bg) document.documentElement.style.backgroundColor = settings.bg; if (settings.fg) document.documentElement.style.color = settings.fg; } if (document.body) { if (settings.bg) document.body.style.backgroundColor = settings.bg; if (settings.fg) document.body.style.color = settings.fg; } } function apply_css() { var css = ''; if (settings.link) css += 'html > body :link, html > body :link * { color: ' + settings.link + ' !important; }'; if (settings.is_dark_theme) { css += dark_css; } var style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); apply_body_colors(); } apply_body_colors(); document.addEventListener("DOMContentLoaded", apply_css); })(); '''.replace('CSS', json.dumps(dark_mode_css), 1), injection_point=QWebEngineScript.InjectionPoint. DocumentCreation)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.FullScreenSupportEnabled, False) s.setAttribute(s.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
class WebEngineView(QWebEngineView): def __init__(self, *args, **kwargs): super(WebEngineView, self).__init__(*args, **kwargs) self.interceptor = Interceptor(self) self.cid_handler = CidSchemeHandler(self) self.profile = QWebEngineProfile(self) self.profile.setRequestInterceptor(self.interceptor) self.profile.installUrlSchemeHandler(b'cid', self.cid_handler) self.setPage(WebEnginePage(self.profile, self)) self.page().contentsSizeChanged.connect(self._pageSizeChanged) self._restrict() # warning: the signal is emitted with an empty string if the link isn't hovered anymore self.page().linkHovered.connect(self.window().statusBar().showMessage) def _restrict(self): self.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, False) @Slot(QSizeF) def _pageSizeChanged(self, size): self.updateGeometry() def minimumSizeHint(self): return self.page().contentsSize().toSize() def setMessage(self, pymessage): self.cid_handler.setMessage(pymessage)
def create_profile(): ans = getattr(create_profile, 'ans', None) if ans is None: ans = QWebEngineProfile(QApplication.instance()) ua = 'calibre-viewer ' + __version__ ans.setHttpUserAgent(ua) if is_running_from_develop: from calibre.utils.rapydscript import compile_viewer print('Compiling viewer code...') compile_viewer() js = P('viewer.js', data=True, allow_user_override=False) translations_json = get_translations_data() or b'null' js = js.replace(b'__TRANSLATIONS_DATA__', translations_json, 1) insert_scripts(ans, create_script('viewer.js', js)) url_handler = UrlSchemeHandler(ans) ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler) s = ans.settings() s.setDefaultTextEncoding('utf-8') s.setAttribute(s.LinksIncludedInFocusChain, False) create_profile.ans = ans return ans
class ViewQt5WebEngine(View, QObject): _emit_js_signal = pyqtSignal(str, QVariant); def __init__(self, name="MyLensApp", width=640, height=480, inspector=False, start_maximized=False, *args, **kwargs): View.__init__(self, name=name, width=width,height=height, *args, **kwargs) QObject.__init__(self) self._app = QApplication([]) # prepare Qt DBus mainloop try: DBusQtMainLoop(set_as_default=True) except NameError: # TODO: validate DBus failed to import (windows) pass self._app_loaded = False self._manager = ThreadManagerQt5(app=self._app) self._inspector = inspector self._start_maximized = start_maximized self._build_app() def _build_app(self): if self._inspector: os.environ.setdefault('QTWEBENGINE_REMOTE_DEBUGGING', '8001') # build webengine container self._lensview = lv = _QWebView(inspector=self._inspector) self._page = p = _QWebPage() lv.setPage(self._page) # connect to Qt signals lv.loadFinished.connect(self._loaded_cb) self._app.lastWindowClosed.connect(self._last_window_closed_cb) # build webchannel script and inject qwebchannel_js = QFile(':/qtwebchannel/qwebchannel.js') if not qwebchannel_js.open(QIODevice.ReadOnly): raise SystemExit('Failed to load qwebchannel.js with error: %s' % qwebchannel_js.errorString()) qwebchannel_js = bytes(qwebchannel_js.readAll()).decode('utf-8') script = QWebEngineScript() script.setSourceCode(qwebchannel_js + ''' window.lens = window.lens || {}; window.lens._channel = new QWebChannel(qt.webChannelTransport, function(channel) { window.lens.emit = function() { var args = Array.prototype.slice.call(arguments); if (args.length > 0) { channel.objects.bridge._from_bridge(args); } }; channel.objects.bridge._emit_js_signal.connect(function(name, args) { window.lens.__broadcast(name, args); }); }); ''') script.setName('lens-bridge') script.setWorldId(QWebEngineScript.MainWorld) script.setInjectionPoint(QWebEngineScript.DocumentCreation) script.setRunsOnSubFrames(True) p.profile().scripts().insert(script) self._channel = QWebChannel(p) p.setWebChannel(self._channel) self._channel.registerObject('bridge', self) # set up scheme handlers for app:// and lens:// self._app_scheme_handler = AppSchemeHandler() self._lens_scheme_handler = LensSchemeHandler() self._interceptor = RequestInterceptor() self._profile = QWebEngineProfile().defaultProfile() self._profile.installUrlSchemeHandler('app'.encode(), self._app_scheme_handler) self._profile.installUrlSchemeHandler('lens'.encode(), self._lens_scheme_handler) self._profile.setRequestInterceptor(self._interceptor) # connect to Lens signals self.on('__close_app', self._close_cb) # center on screen _frame_geometry = lv.frameGeometry() _active_screen = self._app.desktop().screenNumber(self._app.desktop().cursor().pos()) _center = self._app.desktop().screenGeometry(_active_screen).center() _frame_geometry.moveCenter(_center) lv.move(_frame_geometry.topLeft()) self.set_title(self._app_name) self.set_size(self._app_width, self._app_height) def _close_cb(self): self.emit('app.close') self._app.exit() @pyqtSlot(QVariant) def _from_bridge(self, name_args): # emit our python/js bridge signal self.emit(name_args[0], *name_args[1:]) def _last_window_closed_cb(self, *args): self.emit('__close_app', *args) def _loaded_cb(self, success): # show window once some page has loaded self._lensview.show() if self._start_maximized: self.toggle_window_maximize() if not self._app_loaded: self._app_loaded = True self.emit('app.loaded') def _run(self): signal.signal(signal.SIGINT, signal.SIG_DFL) self._app.exec_() def emit_js(self, name, *args): self._emit_js_signal.emit(name, list(args)) def load_string(self, data): index_uri = pathlib.Path(self._app_scheme_handler._uri_app_base).as_uri() self._lensview.setHtml(data, QUrl(index_uri)) def load_uri(self, uri): uri_base = os.path.dirname(uri) + '/' self.set_uri_app_base(uri_base) path = uri_base + 'app.html' stream = QFile(path) if stream.open(QFile.ReadOnly): data = str(stream.readAll(), 'utf-8') index_uri = pathlib.Path(uri_base).as_uri() + os.sep logger.debug('Using {0} as index URI'.format(index_uri)) self._lensview.setHtml(data, QUrl(index_uri)) def set_inspector(self, state): self._lensview.set_inspector(state) def set_size(self, width, height): logger.debug('Setting app size: {0}x{1}'.format(width, height)) self._lensview.setMinimumSize(width, height) self._lensview.resize(width, height) def set_title(self, title): self._lensview.setWindowTitle(QString(title)) def set_uri_app_base(self, uri): self._app_scheme_handler._uri_app_base = pathlib.Path(uri).as_uri() + "/" def set_uri_lens_base(self, uri): self._lens_scheme_handler._uri_lens_base = pathlib.Path(uri).as_uri() + "/" def timer(self, interval, callback, once=False): q = QTimer(parent=self._lensview) q.timeout.connect(callback) q.start(interval) return q.timerId def toggle_window_maximize(self): if self._lensview.windowState() & Qt.WindowMaximized: self._lensview.setWindowState(self._lensview.windowState() ^ Qt.WindowMaximized) self.emit_js('window-unmaximized') else: self._lensview.setWindowState(self._lensview.windowState() | Qt.WindowMaximized) self.emit_js('window-maximized') def toggle_window_fullscreen(self): if self._lensview.windowState() & Qt.WindowFullScreen: self._lensview.setWindowState(self._lensview.windowState() ^ Qt.WindowFullScreen) self.emit_js('window-unfullscreen') else: self._lensview.setWindowState(self._lensview.windowState() | Qt.WindowFullScreen) self.emit_js('window-fullscreen')
class ViewQt5WebEngine(View, QObject): _emit_js_signal = pyqtSignal(str, QVariant) def __init__(self, name="MyLensApp", width=640, height=480, inspector=False, start_maximized=False, *args, **kwargs): View.__init__(self, name=name, width=width, height=height, *args, **kwargs) QObject.__init__(self) self._app = QApplication([]) # prepare Qt DBus mainloop try: DBusQtMainLoop(set_as_default=True) except NameError: # TODO: validate DBus failed to import (windows) pass self._app_loaded = False self._manager = ThreadManagerQt5(app=self._app) self._inspector = inspector self._start_maximized = start_maximized self._build_app() def _build_app(self): if self._inspector: os.environ.setdefault('QTWEBENGINE_REMOTE_DEBUGGING', '8001') # build webengine container self._lensview = lv = _QWebView(inspector=self._inspector) self._page = p = _QWebPage() lv.setPage(self._page) # connect to Qt signals lv.loadFinished.connect(self._loaded_cb) self._app.lastWindowClosed.connect(self._last_window_closed_cb) # build webchannel script and inject qwebchannel_js = QFile(':/qtwebchannel/qwebchannel.js') if not qwebchannel_js.open(QIODevice.ReadOnly): raise SystemExit('Failed to load qwebchannel.js with error: %s' % qwebchannel_js.errorString()) qwebchannel_js = bytes(qwebchannel_js.readAll()).decode('utf-8') script = QWebEngineScript() script.setSourceCode(qwebchannel_js + ''' window.lens = window.lens || {}; window.lens._channel = new QWebChannel(qt.webChannelTransport, function(channel) { window.lens.emit = function() { var args = Array.prototype.slice.call(arguments); if (args.length > 0) { channel.objects.bridge._from_bridge(args); } }; channel.objects.bridge._emit_js_signal.connect(function(name, args) { window.lens.__broadcast(name, args); }); }); ''') script.setName('lens-bridge') script.setWorldId(QWebEngineScript.MainWorld) script.setInjectionPoint(QWebEngineScript.DocumentCreation) script.setRunsOnSubFrames(True) p.profile().scripts().insert(script) self._channel = QWebChannel(p) p.setWebChannel(self._channel) self._channel.registerObject('bridge', self) # set up scheme handlers for app:// and lens:// self._app_scheme_handler = AppSchemeHandler() self._lens_scheme_handler = LensSchemeHandler() self._profile = QWebEngineProfile().defaultProfile() self._profile.installUrlSchemeHandler('app'.encode(), self._app_scheme_handler) self._profile.installUrlSchemeHandler('lens'.encode(), self._lens_scheme_handler) # connect to Lens signals self.on('__close_app', self._close_cb) # center on screen _frame_geometry = lv.frameGeometry() _active_screen = self._app.desktop().screenNumber( self._app.desktop().cursor().pos()) _center = self._app.desktop().screenGeometry(_active_screen).center() _frame_geometry.moveCenter(_center) lv.move(_frame_geometry.topLeft()) self.set_title(self._app_name) self.set_size(self._app_width, self._app_height) def _close_cb(self): self.emit('app.close') self._app.exit() @pyqtSlot(QVariant) def _from_bridge(self, name_args): # emit our python/js bridge signal self.emit(name_args[0], *name_args[1:]) def _last_window_closed_cb(self, *args): self.emit('__close_app', *args) def _loaded_cb(self, success): # show window once some page has loaded self._lensview.show() if self._start_maximized: self.toggle_window_maximize() if not self._app_loaded: self._app_loaded = True self.emit('app.loaded') def _run(self): signal.signal(signal.SIGINT, signal.SIG_DFL) self._app.exec_() def emit_js(self, name, *args): self._emit_js_signal.emit(name, list(args)) def load_string(self, data): index_uri = pathlib.Path( self._app_scheme_handler._uri_app_base).as_uri() self._lensview.setHtml(data, QUrl(index_uri)) def load_uri(self, uri): uri_base = os.path.dirname(uri) + '/' self.set_uri_app_base(uri_base) path = uri_base + 'app.html' stream = QFile(path) if stream.open(QFile.ReadOnly): data = str(stream.readAll(), 'utf-8') index_uri = pathlib.Path(uri_base).as_uri() logger.debug('Using {0} as index URI'.format(index_uri)) self._lensview.setHtml(data, QUrl(index_uri)) def set_inspector(self, state): self._lensview.set_inspector(state) def set_size(self, width, height): logger.debug('Setting app size: {0}x{1}'.format(width, height)) self._lensview.setMinimumSize(width, height) self._lensview.resize(width, height) def set_title(self, title): self._lensview.setWindowTitle(QString(title)) def set_uri_app_base(self, uri): self._app_scheme_handler._uri_app_base = pathlib.Path( uri).as_uri() + "/" def set_uri_lens_base(self, uri): self._lens_scheme_handler._uri_lens_base = pathlib.Path( uri).as_uri() + "/" def toggle_window_maximize(self): if self._lensview.windowState() & Qt.WindowMaximized: self._lensview.setWindowState(self._lensview.windowState() ^ Qt.WindowMaximized) self.emit_js('window-unmaximized') else: self._lensview.setWindowState(self._lensview.windowState() | Qt.WindowMaximized) self.emit_js('window-maximized') def toggle_window_fullscreen(self): if self._lensview.windowState() & Qt.WindowFullScreen: self._lensview.setWindowState(self._lensview.windowState() ^ Qt.WindowFullScreen) self.emit_js('window-unfullscreen') else: self._lensview.setWindowState(self._lensview.windowState() | Qt.WindowFullScreen) self.emit_js('window-fullscreen')