From 9abc9fde9ffedf264660d686ee2e9b7feb83bb79 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 6 Jun 2016 15:50:03 +1000 Subject: [PATCH] use a channel for bridge acceptNavRequest was not being called for rapid updates, so it's not usable --- aqt/browser.py | 8 ++--- aqt/deckbrowser.py | 18 +++++----- aqt/main.py | 4 +-- aqt/overview.py | 6 ++-- aqt/qt.py | 7 +--- aqt/reviewer.py | 16 ++++----- aqt/toolbar.py | 6 ++-- aqt/webview.py | 89 +++++++++++++++++++++++++++------------------- 8 files changed, 82 insertions(+), 72 deletions(-) diff --git a/aqt/browser.py b/aqt/browser.py index 685a8a0de..07db165b7 100644 --- a/aqt/browser.py +++ b/aqt/browser.py @@ -1383,7 +1383,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids, frm.fields.addItems(fields) self._dupesButton = None # links - frm.webView.onAnkiLink = self.dupeLinkClicked + frm.webView.onBridgeCmd = self.dupeLinkClicked def onFin(code): saveGeom(d, "findDupes") d.finished.connect(onFin) @@ -1410,7 +1410,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids, t += _("Found %(a)s across %(b)s.") % dict(a=part1, b=part2) t += "

    " for val, nids in res: - t += '''
  1. %s: %s''' % ( + t += '''
  2. %s: %s''' % ( "nid:" + ",".join(str(id) for id in nids), ngettext("%d note", "%d notes", len(nids)) % len(nids), cgi.escape(val)) @@ -1693,11 +1693,11 @@ class BrowserToolbar(Toolbar): def borderImg(link, icon, on, title, tooltip=None): if on: fmt = '''\ -\ +\ %s''' else: fmt = '''\ - %s''' + %s''' return fmt % (tooltip or title, link, icon, title) right = "
    " right += borderImg("add", "add16", False, _("Add"), diff --git a/aqt/deckbrowser.py b/aqt/deckbrowser.py index f895f9f50..7f000f411 100644 --- a/aqt/deckbrowser.py +++ b/aqt/deckbrowser.py @@ -22,7 +22,7 @@ class DeckBrowser(object): def show(self): clearAudioQueue() self.web.resetHandlers() - self.web.onAnkiLink = self._linkHandler + self.web.onBridgeCmd = self._linkHandler self.mw.keyHandler = self._keyHandler self._renderPage() @@ -132,7 +132,7 @@ body { margin: 1em; -webkit-user-select: none; } var draggedDeckId = ui.draggable.attr('id'); var ontoDeckId = $(this).attr('id'); - openAnkiLink("drag:" + draggedDeckId + "," + ontoDeckId); + pycmd("drag:" + draggedDeckId + "," + ontoDeckId); } """ @@ -172,9 +172,9 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000) return "" return "
    "+( _("You have a lot of decks. Please see %(a)s. %(b)s") % dict( - a=("%s" % _( + a=("%s" % _( "this page")), - b=("
    (" + b=("
    (" "%s)" % (_("hide"))+ " ?""", (self.mw.col.sched.dayCutoff-86400)*1000) buf = "" % (klass, did) # deck link if children: - collapse = "%s" % (did, prefix) + collapse = "%s" % (did, prefix) else: collapse = "" if deck['dyn']: @@ -230,7 +230,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000) buf += """ %s%s%s"""% ( + href=# onclick="pycmd('open:%d')">%s"""% ( indent(), collapse, extraclass, did, name) # due counts def nonzeroColour(cnt, colour): @@ -243,7 +243,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000) nonzeroColour(due, "#007700"), nonzeroColour(new, "#000099")) # options - buf += ("" + buf += ("" "" % did) # children buf += self._renderDeckTree(children, depth+1) @@ -347,7 +347,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000) if b[0]: b[0] = _("Shortcut key: %s") % shortcut(b[0]) buf += """ -""" % tuple(b) +""" % tuple(b) self.bottom.draw(buf) if isMac: size = 28 @@ -355,7 +355,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000) size = 36 + self.mw.fontHeightDelta*3 self.bottom.web.setFixedHeight(size) self.bottom.web.resetHandlers() - self.bottom.web.onAnkiLink = self._linkHandler + self.bottom.web.onBridgeCmd = self._linkHandler def _onShared(self): openLink(aqt.appShared+"decks/") diff --git a/aqt/main.py b/aqt/main.py index 2c96d1047..603b1b6fe 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -456,7 +456,7 @@ the manual for information on how to restore from an automatic backup.")) # we don't have to change the webview, as we have a covering window return self.web.resetHandlers() - self.web.onAnkiLink = lambda url: self.delayedMaybeReset() + self.web.onBridgeCmd = lambda url: self.delayedMaybeReset() i = _("Waiting for editing to finish.") b = self.button("refresh", _("Resume Now"), id="resume") self.web.stdHtml(""" @@ -487,7 +487,7 @@ h1 { margin-bottom: 0.2em; } else: key = "" return ''' -''' % ( id, class_, link, key, extra, name) diff --git a/aqt/overview.py b/aqt/overview.py index 88f18951e..1f839ee62 100644 --- a/aqt/overview.py +++ b/aqt/overview.py @@ -18,7 +18,7 @@ class Overview(object): def show(self): clearAudioQueue() self.web.resetHandlers() - self.web.onAnkiLink = self._linkHandler + self.web.onBridgeCmd = self._linkHandler self.mw.keyHandler = self._keyHandler self.refresh() @@ -199,14 +199,14 @@ text-align: center; if b[0]: b[0] = _("Shortcut key: %s") % shortcut(b[0]) buf += """ -""" % tuple(b) +""" % tuple(b) self.bottom.draw(buf) if isMac: size = 28 else: size = 36 + self.mw.fontHeightDelta*3 self.bottom.web.setFixedHeight(size) - self.bottom.web.onAnkiLink = self._linkHandler + self.bottom.web.onBridgeCmd = self._linkHandler # Studying more ###################################################################### diff --git a/aqt/qt.py b/aqt/qt.py index 8965bb26e..b8f7d2696 100644 --- a/aqt/qt.py +++ b/aqt/qt.py @@ -11,12 +11,7 @@ os.environ["LIBOVERLAY_SCROLLBAR"] = "0" from anki.utils import isWin, isMac -from PyQt5.QtCore import * -from PyQt5.QtGui import * -from PyQt5.QtWidgets import * -from PyQt5.QtWebEngineWidgets import * - -from PyQt5.QtNetwork import QLocalServer, QLocalSocket +from PyQt5.Qt import * def debug(): from PyQt5.QtCore import pyqtRemoveInputHook diff --git a/aqt/reviewer.py b/aqt/reviewer.py index 6c0feaf1b..1acba1eb6 100644 --- a/aqt/reviewer.py +++ b/aqt/reviewer.py @@ -43,14 +43,14 @@ class Reviewer(object): self.mw.col.reset() self.web.resetHandlers() self.mw.keyHandler = self._keyHandler - self.web.onAnkiLink = self._linkHandler + self.web.onBridgeCmd = self._linkHandler self.web.setKeyHandler(self._catchEsc) if isMac: self.bottom.web.setFixedHeight(46) else: self.bottom.web.setFixedHeight(52+self.mw.fontHeightDelta*4) self.bottom.web.resetHandlers() - self.bottom.web.onAnkiLink = self._linkHandler + self.bottom.web.onBridgeCmd = self._linkHandler self._reps = None self.nextCard() @@ -163,12 +163,12 @@ function _toggleStar (show) { function _getTypedText () { if (typeans) { - openAnkiLink("typeans:"+typeans.value); + pycmd("typeans:"+typeans.value); } }; function _typeAnsPress() { if (window.event.keyCode === 13) { - openAnkiLink("ans"); + pycmd("ans"); } } @@ -533,12 +533,12 @@ min-width: 60px; white-space: nowrap;
    - +
    - + @@ -596,7 +596,7 @@ function showAnswer(txt) { self.bottom.web.setFocus() middle = ''' %s
    -''' % ( +''' % ( self._remaining(), _("Shortcut key: %s") % _("Space"), _("Show Answer")) # wrap it in a table so it has the same top margin as the ease buttons middle = "
    %s
    " % middle @@ -654,7 +654,7 @@ function showAnswer(txt) { extra = "" due = self._buttonTime(i) return ''' -%s''' % (due, extra, _("Shortcut key: %s") % i, i, label) buf = "
    " for ease, label in self._answerButtonList(): diff --git a/aqt/toolbar.py b/aqt/toolbar.py index d5f8a67b4..069c2ec88 100644 --- a/aqt/toolbar.py +++ b/aqt/toolbar.py @@ -10,7 +10,7 @@ class Toolbar(object): self.mw = mw self.web = web self.web.resetHandlers() - self.web.onAnkiLink = self._linkHandler + self.web.onBridgeCmd = self._linkHandler self.link_handlers = { "decks": self._deckLinkHandler, "study": self._studyLinkHandler, @@ -51,7 +51,7 @@ class Toolbar(object): buf = "" for ln, name, title in links: buf += ''' - %s''' % ( + %s''' % ( title, ln, name) buf += " "*3 return buf @@ -60,7 +60,7 @@ class Toolbar(object): buf = "" for ln, icon, title in self._rightIconsList(): buf += ''' - ''' % ( + ''' % ( title, ln, icon) return buf diff --git a/aqt/webview.py b/aqt/webview.py index 9ee31df29..c29931a66 100644 --- a/aqt/webview.py +++ b/aqt/webview.py @@ -14,16 +14,53 @@ import anki.js class AnkiWebPage(QWebEnginePage): - def __init__(self, jsErr, acceptNavReq): + def __init__(self, onBridgeCmd): QWebEnginePage.__init__(self) - self._jsErr = jsErr - self._acceptNavReq = acceptNavReq + self._onBridgeCmd = onBridgeCmd + self._setupBridge() + + def _setupBridge(self): + class Bridge(QObject): + @pyqtSlot(str) + def cmd(self, str): + self.onCmd(str) + + self._bridge = Bridge() + self._bridge.onCmd = self._onCmd + + self._channel = QWebChannel(self) + self._channel.registerObject("py", self._bridge) + self.setWebChannel(self._channel) + + js = QFile(':/qtwebchannel/qwebchannel.js') + assert js.open(QIODevice.ReadOnly) + js = bytes(js.readAll()).decode('utf-8') + + script = QWebEngineScript() + script.setSourceCode(js + ''' + var pycmd; + new QWebChannel(qt.webChannelTransport, function(channel) { + pycmd = channel.objects.py.cmd; + pycmd("domDone"); + }); + ''') + script.setWorldId(QWebEngineScript.MainWorld) + script.setInjectionPoint(QWebEngineScript.DocumentReady) + script.setRunsOnSubFrames(False) + self.profile().scripts().insert(script) def javaScriptConsoleMessage(self, lvl, msg, line, srcID): - self._jsErr(lvl, msg, line, srcID) + sys.stdout.write( + (_("JS error on line %(a)d: %(b)s") % + dict(a=line, b=msg+"\n"))) def acceptNavigationRequest(self, url, navType, isMainFrame): - return self._acceptNavReq(url, navType, isMainFrame) + # load all other links in browser + openLink(url) + return False + + def _onCmd(self, str): + self._onBridgeCmd(str) # Main web view ########################################################################## @@ -33,7 +70,8 @@ class AnkiWebView(QWebEngineView): def __init__(self, canFocus=True): QWebEngineView.__init__(self) self.setObjectName("mainText") - self._page = AnkiWebPage(self._jsErr, self._acceptNavReq) + self._page = AnkiWebPage(self._onBridgeCmd) + self._loadFinishedCB = None self.setPage(self._page) self.resetHandlers() @@ -104,14 +142,6 @@ button { %s %s @@ -133,34 +163,19 @@ document.addEventListener("DOMContentLoaded", function(event) { def _openLinksExternally(self, url): openLink(url) - def _jsErr(self, lvl, msg, line, srcID): - sys.stdout.write( - (_("JS error on line %(a)d: %(b)s") % - dict(a=line, b=msg+"\n"))) + def _onBridgeCmd(self, cmd): + if cmd == "domDone": + self.onLoadFinished() + else: + self.onBridgeCmd(cmd) - def _acceptNavReq(self, url, navType, isMainFrame): - # is it an anki link? - urlstr = url.toString() - #print("got url",urlstr) - prefix = "http://anki/" - if urlstr.startswith(prefix): - urlstr = urlstr[len(prefix):] - if urlstr == "domDone": - self.onLoadFinished() - else: - self.onAnkiLink(urlstr) - return False - # load all other links in browser - openLink(url) - return False - - def defaultOnAnkiLink(self, link): - print("unhandled anki link:", link) + def defaultOnBridgeCmd(self, cmd): + print("unhandled bridge cmd:", cmd) def defaultOnLoadFinished(self): pass def resetHandlers(self): self.setKeyHandler(None) - self.onAnkiLink = self.defaultOnAnkiLink + self.onBridgeCmd = self.defaultOnBridgeCmd self.onLoadFinished = self.defaultOnLoadFinished