Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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)
Beispiel #5
0
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')
Beispiel #7
0
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')