add js/py bridge, jquery

This commit is contained in:
Damien Elmes 2011-03-15 03:40:22 +09:00
parent d948b00c54
commit 0a4efd018f
5 changed files with 156 additions and 63 deletions

View file

@ -9,15 +9,48 @@ from anki import Deck
from anki.utils import fmtTimeSpan
from anki.hooks import addHook
_css = """
body { background-color: #ddd; }
td { border-bottom: 1px solid #000;}
#outer { margin-top: 1em; }
.sub { color: #555; }
"""
_body = """
<center>
<div id="outer">
<h1>%s</h1>
<table cellspacing=0 width=90%%>
%s
</table>
<br>
<div id="today">
</div>
<a href="#" onClick="py.run('full');">show today's stats</a>
</div>
"""
class DeckBrowser(object):
def __init__(self, mw):
self.mw = mw
self.web = mw.web
self._browserLastRefreshed = 0
self._decks = []
addHook("deckClosing", self.onClose)
def _bridge(self, str):
if str == "refresh":
pass
elif str == "full":
self.onFull()
def _link(self, url):
pass
def show(self):
self.web.setBridge(self._bridge)
self.web.setLinkHandler(self._link)
if (time.time() - self._browserLastRefreshed >
self.mw.config['deckBrowserRefreshPeriod']):
t = time.time()
@ -26,14 +59,10 @@ class DeckBrowser(object):
else:
self._reorderDecks()
if self._decks:
buf = self._header()
buf += "<center><h1>Decks</h1><table cellspacing=0 width=90%>"
t = time.time
buf = ""
for c, deck in enumerate(self._decks):
buf += self._deckRow(c, deck)
buf += "</table>"
buf += self._buttons()
buf += self._summary()
self.web.stdHtml(_body%(_("Decks"), buf), _css)
else:
buf = ("""\
<br>
@ -44,7 +73,7 @@ later by using File>Close.
<br>
""")
# FIXME: ensure deck open button is focused
self.mw.web.setHtml(buf)
def onClose(self, deck):
print "onClose"
@ -61,12 +90,6 @@ later by using File>Close.
d['time'] = self.deck._dailyStats.reviewTime
d['reps'] = self.deck._dailyStats.reps
def _header(self):
return "<html><head><style>td { border-bottom: 1px solid #000; margin:0px; padding:0px;} </style></head><body>"
def _footer(self):
return "</body></html>"
def _deckRow(self, c, deck):
buf = "<tr>"
# name and status
@ -81,7 +104,7 @@ later by using File>Close.
elif deck['state'] == 'in use':
sub = _("(already open)")
sub = "<font size=-1>%s</font>" % sub
buf += "<td>%s<br>%s</td>" % (deck['name'], sub)
buf += "<td><b>%s</b><br><span class=sub>%s</span></td>" % (deck['name'], sub)
if ok:
# due
col = '<td><b><font color=#0000ff>%s</font></b></td>'
@ -154,24 +177,24 @@ later by using File>Close.
# self.moreMenus.append(moreMenu)
return ""
def _summary(self):
return ""
def onFull(self):
# summarize
reps = 0
mins = 0
revC = 0
newC = 0
for d in self._decks:
reps += d['reps']
mins += d['time']
revC += d['due']
newC += d['new']
if d['state']=='ok':
reps += d['reps']
mins += d['time']
revC += d['due']
newC += d['new']
line1 = ngettext(
"Studied <b>%(reps)d card</b> in <b>%(time)s</b> today.",
"Studied <b>%(reps)d cards</b> in <b>%(time)s</b> today.",
reps) % {
'reps': reps,
'time': anki.utils.fmtTimeSpan(mins, point=2),
'time': fmtTimeSpan(mins, point=2),
}
rev = ngettext(
"<b><font color=#0000ff>%d</font></b> review",
@ -180,7 +203,7 @@ later by using File>Close.
new = ngettext("<b>%d</b> new card", "<b>%d</b> new cards", newC) % newC
line2 = _("Due: %(rev)s, %(new)s") % {
'rev': rev, 'new': new}
return ""
return self.web.eval("$('#today').html('%s');" % (line1+"<br>"+line2))
def _checkDecks(self, forget=False):
self._decks = []
@ -203,6 +226,8 @@ later by using File>Close.
t = time.time()
deck = Deck(d)
counts = deck.sched.counts()
dtime = deck.sched.timeToday()
dreps = deck.sched.repsToday()
self._decks.append({
'path': d,
'state': 'ok',
@ -210,9 +235,9 @@ later by using File>Close.
'due': counts[0],
'new': counts[1],
'mod': deck.mod,
# these multiple deck check time by a factor of 6
'time': 0, #deck.sched.timeToday(),
'reps': 0, #deck.sched.repsToday()
# these multiply deck check time by a factor of 6
'time': dtime,
'reps': dreps
})
deck.close()
# reset modification time for the sake of backup systems

View file

@ -8,7 +8,6 @@ from operator import itemgetter
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import QWebPage, QWebView
from PyQt4 import pyqtconfig
QtConfig = pyqtconfig.Configuration()
@ -19,7 +18,7 @@ from anki.hooks import runHook, addHook, removeHook
import anki.consts
import aqt, aqt.utils, aqt.view, aqt.help, aqt.status, aqt.facteditor, \
aqt.progress
aqt.progress, aqt.webview
from aqt.utils import saveGeom, restoreGeom, showInfo, showWarning, \
saveState, restoreState
config = aqt.config
@ -328,7 +327,7 @@ Please do not file a bug report with Anki.<br>""")
diag.setMinimumHeight(400)
diag.setMinimumWidth(500)
diag.exec_()
self.clearProgress()
self.progress.clear()
# Main window setup
##########################################################################
@ -337,7 +336,7 @@ Please do not file a bug report with Anki.<br>""")
# main window
self.form = aqt.forms.main.Ui_MainWindow()
self.form.setupUi(self)
self.web = AnkiWebView(self.form.centralwidget)
self.web = aqt.webview.AnkiWebView(self.form.centralwidget)
self.web.setObjectName("mainText")
self.web.setFocusPolicy(Qt.ClickFocus)
self.mainLayout = QVBoxLayout()
@ -345,9 +344,9 @@ Please do not file a bug report with Anki.<br>""")
self.mainLayout.setContentsMargins(0,0,0,0)
self.form.centralwidget.setLayout(self.mainLayout)
#self.help = aqt.help.HelpArea(self.form.helpFrame, self.config, self)
self.connect(self.web.pageAction(QWebPage.Reload),
SIGNAL("triggered()"),
self.onReload)
#self.connect(self.web.pageAction(QWebPage.Reload),
# SIGNAL("triggered()"),
# self.onReload)
# congrats
# self.connect(self.mainWin.learnMoreButton,
# SIGNAL("clicked()"),
@ -2710,34 +2709,3 @@ It can take a long time. Proceed?""")):
self.form.decksLabel.hide()
self.form.decksLine.hide()
self.form.studyOptsLabel.hide()
# Main web view
##########################################################################
class AnkiWebView(QWebView):
def __init__(self, *args):
QWebView.__init__(self, *args)
self.setObjectName("mainText")
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.setLinkHandler()
self.connect(self, SIGNAL("linkClicked(QUrl)"), self._linkHandler)
def keyPressEvent(self, evt):
if evt.matches(QKeySequence.Copy):
self.triggerPageAction(QWebPage.Copy)
evt.accept()
QWebView.keyPressEvent(self, evt)
def contextMenuEvent(self, evt):
QWebView.contextMenuEvent(self, evt)
def dropEvent(self, evt):
pass
def _linkHandler(self, url):
self.linkHandler(url)
def setLinkHandler(self, handler=None):
if handler:
self.linkHandler = handler
else:
self.linkHandler = self._openLinksExternally
def _openLinksExternally(self, url):
QDesktopServices.openUrl(QUrl(url))

83
aqt/webview.py Normal file
View file

@ -0,0 +1,83 @@
# Copyright: Damien Elmes <anki@ichi2.net>
# -*- coding: utf-8 -*-
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import QWebPage, QWebView
from PyQt4 import pyqtconfig
QtConfig = pyqtconfig.Configuration()
# Bridge for Qt<->JS
##########################################################################
class Bridge(QObject):
@pyqtSlot(str, result=str)
def run(self, str):
return unicode(self._bridge(unicode(str)))
def setBridge(self, func):
self._bridge = func
# Page for debug messages
##########################################################################
class AnkiWebPage(QWebPage):
def __init__(self, parent, jsErr):
QWebPage.__init__(self, parent)
self._jsErr = jsErr
def javaScriptConsoleMessage(self, msg, line, srcID):
self._jsErr(msg, line, srcID)
# Main web view
##########################################################################
class AnkiWebView(QWebView):
def __init__(self, parent):
QWebView.__init__(self, parent)
self.setObjectName("mainText")
self._bridge = Bridge()
self._page = AnkiWebPage(parent, self._jsErr)
self._loadFinishedCB = None
self.setPage(self._page)
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.page().mainFrame().addToJavaScriptWindowObject("py", self._bridge)
self.setLinkHandler()
self.connect(self, SIGNAL("linkClicked(QUrl)"), self._linkHandler)
self.connect(self, SIGNAL("loadFinished(bool)"), self._loadFinished)
def keyPressEvent(self, evt):
if evt.matches(QKeySequence.Copy):
self.triggerPageAction(QWebPage.Copy)
evt.accept()
QWebView.keyPressEvent(self, evt)
def contextMenuEvent(self, evt):
QWebView.contextMenuEvent(self, evt)
def dropEvent(self, evt):
pass
def setLinkHandler(self, handler=None):
if handler:
self.linkHandler = handler
else:
self.linkHandler = self._openLinksExternally
def setHtml(self, html, loadCB=None):
if loadCB:
self._loadFinishedCB = loadCB
QWebView.setHtml(self, html)
def stdHtml(self, body, css="", loadCB=None):
self.setHtml("""
<html><head><style>%s</style><script src="qrc:/jquery.min.js"></script></head>
<body>%s</body></html>""" % (css, body), loadCB)
def setBridge(self, bridge):
self._bridge.setBridge(bridge)
def eval(self, js):
self.page().mainFrame().evaluateJavaScript(js)
def _openLinksExternally(self, url):
QDesktopServices.openUrl(QUrl(url))
def _jsErr(self, msg, line, srcID):
sys.stderr.write(_("JS error on line %d: %s") % (line, msg+"\n"))
def _linkHandler(self, url):
self.linkHandler(unicode(url.toString()))
def _loadFinished(self):
if self._loadFinishedCB:
self._loadFinishedCB(self)

View file

@ -94,5 +94,6 @@
<file>icons/text_under.png</file>
<file>icons/view-pim-news.png</file>
<file>icons/view_text.png</file>
<file>jquery.min.js</file>
</qresource>
</RCC>

16
designer/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long