"
- t = time.time
+ buf = ""
for c, deck in enumerate(self._decks):
buf += self._deckRow(c, deck)
- buf += "
"
- buf += self._buttons()
- buf += self._summary()
+ self.web.stdHtml(_body%(_("Decks"), buf), _css)
else:
buf = ("""\
@@ -44,7 +73,7 @@ later by using File>Close.
""")
# 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 ""
-
- def _footer(self):
- return ""
-
def _deckRow(self, c, deck):
buf = "
"
# name and status
@@ -81,7 +104,7 @@ later by using File>Close.
elif deck['state'] == 'in use':
sub = _("(already open)")
sub = "%s" % sub
- buf += "
%s %s
" % (deck['name'], sub)
+ buf += "
%s %s
" % (deck['name'], sub)
if ok:
# due
col = '
%s
'
@@ -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 %(reps)d card in %(time)s today.",
"Studied %(reps)d cards in %(time)s today.",
reps) % {
'reps': reps,
- 'time': anki.utils.fmtTimeSpan(mins, point=2),
+ 'time': fmtTimeSpan(mins, point=2),
}
rev = ngettext(
"%d review",
@@ -180,7 +203,7 @@ later by using File>Close.
new = ngettext("%d new card", "%d new cards", newC) % newC
line2 = _("Due: %(rev)s, %(new)s") % {
'rev': rev, 'new': new}
- return ""
+ return self.web.eval("$('#today').html('%s');" % (line1+" "+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
diff --git a/aqt/main.py b/aqt/main.py
index 87be3044c..25f3ebcfb 100755
--- a/aqt/main.py
+++ b/aqt/main.py
@@ -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. """)
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. """)
# 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. """)
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))
-
diff --git a/aqt/webview.py b/aqt/webview.py
new file mode 100644
index 000000000..e77a2562b
--- /dev/null
+++ b/aqt/webview.py
@@ -0,0 +1,83 @@
+# Copyright: Damien Elmes
+# -*- 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("""
+
+%s""" % (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)
diff --git a/designer/icons.qrc b/designer/icons.qrc
index 230974765..34def02b1 100644
--- a/designer/icons.qrc
+++ b/designer/icons.qrc
@@ -94,5 +94,6 @@
icons/text_under.pngicons/view-pim-news.pngicons/view_text.png
+ jquery.min.js
diff --git a/designer/jquery.min.js b/designer/jquery.min.js
new file mode 100644
index 000000000..9144b8ae9
--- /dev/null
+++ b/designer/jquery.min.js
@@ -0,0 +1,16 @@
+/*!
+ * jQuery JavaScript Library v1.5
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Mon Jan 31 08:31:29 2011 -0500
+ */
+(function(a,b){function b$(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function bX(a){if(!bR[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bR[a]=c}return bR[a]}function bW(a,b){var c={};d.each(bV.concat.apply([],bV.slice(0,b)),function(){c[this]=a});return c}function bJ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f=a.converters,g,h=e.length,i,j=e[0],k,l,m,n,o;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(q,"`").replace(r,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,p,q=[],r=[],s=d._data(this,u);typeof s==="function"&&(s=s.events);if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,p=f.handleObj.origHandler.apply(f.elem,arguments);if(p===!1||a.isPropagationStopped()){c=f.level,p===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,b,c){c[0].type=a;return d.event.handle.apply(b,c)}function w(){return!0}function v(){return!1}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");e.type="text/javascript",d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1?(g=Array(c),d.each(b,function(a,b){d.when(b).then(function(b){g[a]=arguments.length>1?E.call(arguments,0):b,--c||e.resolveWith(f,g)},e.reject)})):e!==a&&e.resolve(a);return f},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return a.jQuery=a.$=d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="