port majority of code to qt5.5+
- a few issues to work out still, and editor changes not done yet - for communication between webengine and python code, we set window .location to 'http://anki/<something>' - the leading http is necessary for qt to call the link handler, which was introduced in qt5.5 - the designer files now use a promoted qobject to create instances of AnkiWebView - we use the css zoom property to alter webengine font size based on system dpi - prefs and addons folder stored in new location (at least for now)
This commit is contained in:
parent
4d88b62fbf
commit
de7e40537d
@ -1,7 +1,6 @@
|
|||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import getpass
|
import getpass
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import optparse
|
import optparse
|
||||||
import tempfile
|
import tempfile
|
||||||
@ -118,6 +117,8 @@ class AnkiApp(QApplication):
|
|||||||
# Single instance support on Win32/Linux
|
# Single instance support on Win32/Linux
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
|
appMsg = pyqtSignal(str)
|
||||||
|
|
||||||
KEY = "anki"+checksum(getpass.getuser())
|
KEY = "anki"+checksum(getpass.getuser())
|
||||||
TMOUT = 5000
|
TMOUT = 5000
|
||||||
|
|
||||||
@ -140,7 +141,7 @@ class AnkiApp(QApplication):
|
|||||||
# previous instance died
|
# previous instance died
|
||||||
QLocalServer.removeServer(self.KEY)
|
QLocalServer.removeServer(self.KEY)
|
||||||
self._srv = QLocalServer(self)
|
self._srv = QLocalServer(self)
|
||||||
self.connect(self._srv, SIGNAL("newConnection()"), self.onRecv)
|
self._srv.newConnection.connect(self.onRecv)
|
||||||
self._srv.listen(self.KEY)
|
self._srv.listen(self.KEY)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ class AnkiApp(QApplication):
|
|||||||
if not sock.waitForConnected(self.TMOUT):
|
if not sock.waitForConnected(self.TMOUT):
|
||||||
# first instance or previous instance dead
|
# first instance or previous instance dead
|
||||||
return False
|
return False
|
||||||
sock.write(txt)
|
sock.write(txt.encode("utf8"))
|
||||||
if not sock.waitForBytesWritten(self.TMOUT):
|
if not sock.waitForBytesWritten(self.TMOUT):
|
||||||
# existing instance running but hung
|
# existing instance running but hung
|
||||||
return False
|
return False
|
||||||
@ -162,9 +163,8 @@ class AnkiApp(QApplication):
|
|||||||
if not sock.waitForReadyRead(self.TMOUT):
|
if not sock.waitForReadyRead(self.TMOUT):
|
||||||
sys.stderr.write(sock.errorString())
|
sys.stderr.write(sock.errorString())
|
||||||
return
|
return
|
||||||
buf = sock.readAll()
|
path = bytes(sock.readAll()).decode("utf8")
|
||||||
buf = str(buf, sys.getfilesystemencoding(), "ignore")
|
self.appMsg.emit(path)
|
||||||
self.emit(SIGNAL("appMsg"), buf)
|
|
||||||
sock.disconnectFromServer()
|
sock.disconnectFromServer()
|
||||||
|
|
||||||
# OS X file/url handler
|
# OS X file/url handler
|
||||||
@ -172,7 +172,7 @@ class AnkiApp(QApplication):
|
|||||||
|
|
||||||
def event(self, evt):
|
def event(self, evt):
|
||||||
if evt.type() == QEvent.FileOpen:
|
if evt.type() == QEvent.FileOpen:
|
||||||
self.emit(SIGNAL("appMsg"), evt.file() or "raise")
|
self.appMsg.emit(evt.file() or "raise")
|
||||||
return True
|
return True
|
||||||
return QApplication.event(self, evt)
|
return QApplication.event(self, evt)
|
||||||
|
|
||||||
@ -207,12 +207,9 @@ def _run():
|
|||||||
|
|
||||||
# on osx we'll need to add the qt plugins to the search path
|
# on osx we'll need to add the qt plugins to the search path
|
||||||
if isMac and getattr(sys, 'frozen', None):
|
if isMac and getattr(sys, 'frozen', None):
|
||||||
rd = os.path.abspath(moduleDir + "/../../..")
|
rd = os.path.abspath(moduleDir + "/../../../plugins")
|
||||||
QCoreApplication.setLibraryPaths([rd])
|
QCoreApplication.setLibraryPaths([rd])
|
||||||
|
|
||||||
if isMac:
|
|
||||||
QFont.insertSubstitution(".Lucida Grande UI", "Lucida Grande")
|
|
||||||
|
|
||||||
# create the app
|
# create the app
|
||||||
app = AnkiApp(sys.argv)
|
app = AnkiApp(sys.argv)
|
||||||
QCoreApplication.setApplicationName("Anki")
|
QCoreApplication.setApplicationName("Anki")
|
||||||
@ -234,13 +231,6 @@ No usable temporary folder found. Make sure C:\\temp exists or TEMP in your \
|
|||||||
environment points to a valid, writable folder.""")
|
environment points to a valid, writable folder.""")
|
||||||
return
|
return
|
||||||
|
|
||||||
# qt version must be up to date
|
|
||||||
if qtmajor <= 4 and qtminor <= 6:
|
|
||||||
QMessageBox.warning(
|
|
||||||
None, "Error", "Your Qt version is known to be buggy. Until you "
|
|
||||||
"upgrade to a newer Qt, you may experience issues such as images "
|
|
||||||
"failing to show up during review.")
|
|
||||||
|
|
||||||
# profile manager
|
# profile manager
|
||||||
from aqt.profiles import ProfileManager
|
from aqt.profiles import ProfileManager
|
||||||
pm = ProfileManager(opts.base, opts.profile)
|
pm = ProfileManager(opts.base, opts.profile)
|
||||||
|
@ -11,12 +11,6 @@ def show(parent):
|
|||||||
dialog = QDialog(parent)
|
dialog = QDialog(parent)
|
||||||
abt = aqt.forms.about.Ui_About()
|
abt = aqt.forms.about.Ui_About()
|
||||||
abt.setupUi(dialog)
|
abt.setupUi(dialog)
|
||||||
abt.label.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
|
|
||||||
def onLink(url):
|
|
||||||
openLink(url.toString())
|
|
||||||
parent.connect(abt.label,
|
|
||||||
SIGNAL("linkClicked(QUrl)"),
|
|
||||||
onLink)
|
|
||||||
abouttext = "<center><img src='qrc:/icons/anki-logo-thin.png'></center>"
|
abouttext = "<center><img src='qrc:/icons/anki-logo-thin.png'></center>"
|
||||||
abouttext += '<p>' + _("Anki is a friendly, intelligent spaced learning \
|
abouttext += '<p>' + _("Anki is a friendly, intelligent spaced learning \
|
||||||
system. It's free and open source.")
|
system. It's free and open source.")
|
||||||
|
@ -53,20 +53,18 @@ class AddCards(QDialog):
|
|||||||
ar = QDialogButtonBox.ActionRole
|
ar = QDialogButtonBox.ActionRole
|
||||||
# add
|
# add
|
||||||
self.addButton = bb.addButton(_("Add"), ar)
|
self.addButton = bb.addButton(_("Add"), ar)
|
||||||
|
self.addButton.clicked.connect(self.addCards)
|
||||||
self.addButton.setShortcut(QKeySequence("Ctrl+Return"))
|
self.addButton.setShortcut(QKeySequence("Ctrl+Return"))
|
||||||
self.addButton.setToolTip(shortcut(_("Add (shortcut: ctrl+enter)")))
|
self.addButton.setToolTip(shortcut(_("Add (shortcut: ctrl+enter)")))
|
||||||
self.connect(self.addButton, SIGNAL("clicked()"), self.addCards)
|
|
||||||
# close
|
# close
|
||||||
self.closeButton = QPushButton(_("Close"))
|
self.closeButton = QPushButton(_("Close"))
|
||||||
self.closeButton.setAutoDefault(False)
|
self.closeButton.setAutoDefault(False)
|
||||||
bb.addButton(self.closeButton,
|
bb.addButton(self.closeButton, QDialogButtonBox.RejectRole)
|
||||||
QDialogButtonBox.RejectRole)
|
|
||||||
# help
|
# help
|
||||||
self.helpButton = QPushButton(_("Help"))
|
self.helpButton = QPushButton(_("Help"), clicked=self.helpRequested)
|
||||||
self.helpButton.setAutoDefault(False)
|
self.helpButton.setAutoDefault(False)
|
||||||
bb.addButton(self.helpButton,
|
bb.addButton(self.helpButton,
|
||||||
QDialogButtonBox.HelpRole)
|
QDialogButtonBox.HelpRole)
|
||||||
self.connect(self.helpButton, SIGNAL("clicked()"), self.helpRequested)
|
|
||||||
# history
|
# history
|
||||||
b = bb.addButton(
|
b = bb.addButton(
|
||||||
_("History")+ " "+downArrow(), ar)
|
_("History")+ " "+downArrow(), ar)
|
||||||
@ -76,7 +74,7 @@ class AddCards(QDialog):
|
|||||||
sc = "Ctrl+H"
|
sc = "Ctrl+H"
|
||||||
b.setShortcut(QKeySequence(sc))
|
b.setShortcut(QKeySequence(sc))
|
||||||
b.setToolTip(_("Shortcut: %s") % shortcut(sc))
|
b.setToolTip(_("Shortcut: %s") % shortcut(sc))
|
||||||
self.connect(b, SIGNAL("clicked()"), self.onHistory)
|
b.clicked.connect(self.onHistory)
|
||||||
b.setEnabled(False)
|
b.setEnabled(False)
|
||||||
self.historyButton = b
|
self.historyButton = b
|
||||||
|
|
||||||
@ -145,8 +143,7 @@ class AddCards(QDialog):
|
|||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
for nid, txt in self.history:
|
for nid, txt in self.history:
|
||||||
a = m.addAction(_("Edit %s") % txt)
|
a = m.addAction(_("Edit %s") % txt)
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(lambda b, nid=nid: self.editHistory(nid))
|
||||||
lambda nid=nid: self.editHistory(nid))
|
|
||||||
runHook("AddCards.onHistory", self, m)
|
runHook("AddCards.onHistory", self, m)
|
||||||
m.exec_(self.historyButton.mapToGlobal(QPoint(0,0)))
|
m.exec_(self.historyButton.mapToGlobal(QPoint(0,0)))
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ class AddonManager(object):
|
|||||||
|
|
||||||
def __init__(self, mw):
|
def __init__(self, mw):
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
f = self.mw.form; s = SIGNAL("triggered()")
|
f = self.mw.form
|
||||||
self.mw.connect(f.actionOpenPluginFolder, s, self.onOpenAddonFolder)
|
f.actionOpenPluginFolder.triggered.connect(self.onOpenAddonFolder)
|
||||||
self.mw.connect(f.actionDownloadSharedPlugin, s, self.onGetAddons)
|
f.actionDownloadSharedPlugin.triggered.connect(self.onGetAddons)
|
||||||
self._menus = []
|
self._menus = []
|
||||||
if isWin:
|
if isWin:
|
||||||
self.clearAddonCache()
|
self.clearAddonCache()
|
||||||
@ -46,7 +46,7 @@ class AddonManager(object):
|
|||||||
# Menus
|
# Menus
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def onOpenAddonFolder(self, path=None):
|
def onOpenAddonFolder(self, checked, path=None):
|
||||||
if path is None:
|
if path is None:
|
||||||
path = self.addonsFolder()
|
path = self.addonsFolder()
|
||||||
openFolder(path)
|
openFolder(path)
|
||||||
@ -58,14 +58,11 @@ class AddonManager(object):
|
|||||||
m = self.mw.form.menuPlugins.addMenu(
|
m = self.mw.form.menuPlugins.addMenu(
|
||||||
os.path.splitext(file)[0])
|
os.path.splitext(file)[0])
|
||||||
self._menus.append(m)
|
self._menus.append(m)
|
||||||
a = QAction(_("Edit..."), self.mw)
|
a = QAction(_("Edit..."), self.mw, triggered=self.onEdit)
|
||||||
p = os.path.join(self.addonsFolder(), file)
|
p = os.path.join(self.addonsFolder(), file)
|
||||||
self.mw.connect(a, SIGNAL("triggered()"),
|
|
||||||
lambda p=p: self.onEdit(p))
|
|
||||||
m.addAction(a)
|
m.addAction(a)
|
||||||
a = QAction(_("Delete..."), self.mw)
|
a = QAction(_("Delete..."), self.mw, triggered=self.onRem)
|
||||||
self.mw.connect(a, SIGNAL("triggered()"),
|
|
||||||
lambda p=p: self.onRem(p))
|
|
||||||
m.addAction(a)
|
m.addAction(a)
|
||||||
|
|
||||||
def onEdit(self, path):
|
def onEdit(self, path):
|
||||||
@ -74,8 +71,7 @@ class AddonManager(object):
|
|||||||
frm.setupUi(d)
|
frm.setupUi(d)
|
||||||
d.setWindowTitle(os.path.basename(path))
|
d.setWindowTitle(os.path.basename(path))
|
||||||
frm.text.setPlainText(open(path).read())
|
frm.text.setPlainText(open(path).read())
|
||||||
d.connect(frm.buttonBox, SIGNAL("accepted()"),
|
frm.buttonBox.accepted.connect(lambda: self.onAcceptEdit(path, frm))
|
||||||
lambda: self.onAcceptEdit(path, frm))
|
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
def onAcceptEdit(self, path, frm):
|
def onAcceptEdit(self, path, frm):
|
||||||
@ -94,8 +90,6 @@ class AddonManager(object):
|
|||||||
|
|
||||||
def addonsFolder(self):
|
def addonsFolder(self):
|
||||||
dir = self.mw.pm.addonFolder()
|
dir = self.mw.pm.addonFolder()
|
||||||
if isWin:
|
|
||||||
dir = dir.encode(sys.getfilesystemencoding())
|
|
||||||
return dir
|
return dir
|
||||||
|
|
||||||
def clearAddonCache(self):
|
def clearAddonCache(self):
|
||||||
@ -115,7 +109,9 @@ class AddonManager(object):
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def onGetAddons(self):
|
def onGetAddons(self):
|
||||||
GetAddons(self.mw)
|
showInfo("Currently disabled, as add-ons built for 2.0.x will need updating")
|
||||||
|
|
||||||
|
# GetAddons(self.mw)
|
||||||
|
|
||||||
def install(self, data, fname):
|
def install(self, data, fname):
|
||||||
if fname.endswith(".py"):
|
if fname.endswith(".py"):
|
||||||
@ -146,7 +142,7 @@ class GetAddons(QDialog):
|
|||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
b = self.form.buttonBox.addButton(
|
b = self.form.buttonBox.addButton(
|
||||||
_("Browse"), QDialogButtonBox.ActionRole)
|
_("Browse"), QDialogButtonBox.ActionRole)
|
||||||
self.connect(b, SIGNAL("clicked()"), self.onBrowse)
|
b.clicked.connect(self.onBrowse)
|
||||||
restoreGeom(self, "getaddons", adjustSize=True)
|
restoreGeom(self, "getaddons", adjustSize=True)
|
||||||
self.exec_()
|
self.exec_()
|
||||||
saveGeom(self, "getaddons")
|
saveGeom(self, "getaddons")
|
||||||
|
164
aqt/browser.py
164
aqt/browser.py
@ -55,7 +55,7 @@ class DataModel(QAbstractTableModel):
|
|||||||
del self.cardObjs[c.id]
|
del self.cardObjs[c.id]
|
||||||
refresh = True
|
refresh = True
|
||||||
if refresh:
|
if refresh:
|
||||||
self.emit(SIGNAL("layoutChanged()"))
|
self.layoutChanged.emit()
|
||||||
|
|
||||||
# Model interface
|
# Model interface
|
||||||
######################################################################
|
######################################################################
|
||||||
@ -382,61 +382,61 @@ class Browser(QMainWindow):
|
|||||||
|
|
||||||
def setupMenus(self):
|
def setupMenus(self):
|
||||||
# actions
|
# actions
|
||||||
c = self.connect; f = self.form; s = SIGNAL("triggered()")
|
f = self.form
|
||||||
if not isMac:
|
if not isMac:
|
||||||
f.actionClose.setVisible(False)
|
f.actionClose.setVisible(False)
|
||||||
c(f.actionReposition, s, self.reposition)
|
f.actionReposition.triggered.connect(self.reposition)
|
||||||
c(f.actionReschedule, s, self.reschedule)
|
f.actionReschedule.triggered.connect(self.reschedule)
|
||||||
c(f.actionCram, s, self.cram)
|
f.actionCram.triggered.connect(self.cram)
|
||||||
c(f.actionChangeModel, s, self.onChangeModel)
|
f.actionChangeModel.triggered.connect(self.onChangeModel)
|
||||||
# edit
|
# edit
|
||||||
c(f.actionUndo, s, self.mw.onUndo)
|
f.actionUndo.triggered.connect(self.mw.onUndo)
|
||||||
c(f.previewButton, SIGNAL("clicked()"), self.onTogglePreview)
|
f.previewButton.clicked.connect(self.onTogglePreview)
|
||||||
f.previewButton.setToolTip(_("Preview Selected Card (%s)") %
|
f.previewButton.setToolTip(_("Preview Selected Card (%s)") %
|
||||||
shortcut(_("Ctrl+Shift+P")))
|
shortcut(_("Ctrl+Shift+P")))
|
||||||
c(f.actionInvertSelection, s, self.invertSelection)
|
f.actionInvertSelection.triggered.connect(self.invertSelection)
|
||||||
c(f.actionSelectNotes, s, self.selectNotes)
|
f.actionSelectNotes.triggered.connect(self.selectNotes)
|
||||||
c(f.actionFindReplace, s, self.onFindReplace)
|
f.actionFindReplace.triggered.connect(self.onFindReplace)
|
||||||
c(f.actionFindDuplicates, s, self.onFindDupes)
|
f.actionFindDuplicates.triggered.connect(self.onFindDupes)
|
||||||
# jumps
|
# jumps
|
||||||
c(f.actionPreviousCard, s, self.onPreviousCard)
|
f.actionPreviousCard.triggered.connect(self.onPreviousCard)
|
||||||
c(f.actionNextCard, s, self.onNextCard)
|
f.actionNextCard.triggered.connect(self.onNextCard)
|
||||||
c(f.actionFirstCard, s, self.onFirstCard)
|
f.actionFirstCard.triggered.connect(self.onFirstCard)
|
||||||
c(f.actionLastCard, s, self.onLastCard)
|
f.actionLastCard.triggered.connect(self.onLastCard)
|
||||||
c(f.actionFind, s, self.onFind)
|
f.actionFind.triggered.connect(self.onFind)
|
||||||
c(f.actionNote, s, self.onNote)
|
f.actionNote.triggered.connect(self.onNote)
|
||||||
c(f.actionTags, s, self.onTags)
|
f.actionTags.triggered.connect(self.onTags)
|
||||||
c(f.actionCardList, s, self.onCardList)
|
f.actionCardList.triggered.connect(self.onCardList)
|
||||||
# help
|
# help
|
||||||
c(f.actionGuide, s, self.onHelp)
|
f.actionGuide.triggered.connect(self.onHelp)
|
||||||
# keyboard shortcut for shift+home/end
|
# keyboard shortcut for shift+home/end
|
||||||
self.pgUpCut = QShortcut(QKeySequence("Shift+Home"), self)
|
self.pgUpCut = QShortcut(QKeySequence("Shift+Home"), self)
|
||||||
c(self.pgUpCut, SIGNAL("activated()"), self.onFirstCard)
|
self.pgUpCut.activated.connect(self.onFirstCard)
|
||||||
self.pgDownCut = QShortcut(QKeySequence("Shift+End"), self)
|
self.pgDownCut = QShortcut(QKeySequence("Shift+End"), self)
|
||||||
c(self.pgDownCut, SIGNAL("activated()"), self.onLastCard)
|
self.pgDownCut.activated.connect(self.onLastCard)
|
||||||
# add note
|
# add note
|
||||||
self.addCut = QShortcut(QKeySequence("Ctrl+E"), self)
|
self.addCut = QShortcut(QKeySequence("Ctrl+E"), self)
|
||||||
c(self.addCut, SIGNAL("activated()"), self.mw.onAddCard)
|
self.addCut.activated.connect(self.mw.onAddCard)
|
||||||
# card info
|
# card info
|
||||||
self.infoCut = QShortcut(QKeySequence("Ctrl+Shift+I"), self)
|
self.infoCut = QShortcut(QKeySequence("Ctrl+Shift+I"), self)
|
||||||
c(self.infoCut, SIGNAL("activated()"), self.showCardInfo)
|
self.infoCut.activated.connect(self.showCardInfo)
|
||||||
# set deck
|
# set deck
|
||||||
self.changeDeckCut = QShortcut(QKeySequence("Ctrl+D"), self)
|
self.changeDeckCut = QShortcut(QKeySequence("Ctrl+D"), self)
|
||||||
c(self.changeDeckCut, SIGNAL("activated()"), self.setDeck)
|
self.changeDeckCut.activated.connect(self.setDeck)
|
||||||
# add/remove tags
|
# add/remove tags
|
||||||
self.tagCut1 = QShortcut(QKeySequence("Ctrl+Shift+T"), self)
|
self.tagCut1 = QShortcut(QKeySequence("Ctrl+Shift+T"), self)
|
||||||
c(self.tagCut1, SIGNAL("activated()"), self.addTags)
|
self.tagCut1.activated.connect(self.addTags)
|
||||||
self.tagCut2 = QShortcut(QKeySequence("Ctrl+Alt+T"), self)
|
self.tagCut2 = QShortcut(QKeySequence("Ctrl+Alt+T"), self)
|
||||||
c(self.tagCut2, SIGNAL("activated()"), self.deleteTags)
|
self.tagCut2.activated.connect(self.deleteTags)
|
||||||
self.tagCut3 = QShortcut(QKeySequence("Ctrl+K"), self)
|
self.tagCut3 = QShortcut(QKeySequence("Ctrl+K"), self)
|
||||||
c(self.tagCut3, SIGNAL("activated()"), self.onMark)
|
self.tagCut3.activated.connect(self.onMark)
|
||||||
# suspending
|
# suspending
|
||||||
self.susCut1 = QShortcut(QKeySequence("Ctrl+J"), self)
|
self.susCut1 = QShortcut(QKeySequence("Ctrl+J"), self)
|
||||||
c(self.susCut1, SIGNAL("activated()"), self.onSuspend)
|
self.susCut1.activated.connect(self.onSuspend)
|
||||||
# deletion
|
# deletion
|
||||||
self.delCut1 = QShortcut(QKeySequence("Delete"), self)
|
self.delCut1 = QShortcut(QKeySequence("Delete"), self)
|
||||||
self.delCut1.setAutoRepeat(False)
|
self.delCut1.setAutoRepeat(False)
|
||||||
c(self.delCut1, SIGNAL("activated()"), self.deleteNotes)
|
self.delCut1.activated.connect(self.deleteNotes)
|
||||||
# add-on hook
|
# add-on hook
|
||||||
runHook('browser.setupMenus', self)
|
runHook('browser.setupMenus', self)
|
||||||
self.mw.maybeHideAccelerators(self)
|
self.mw.maybeHideAccelerators(self)
|
||||||
@ -507,12 +507,8 @@ class Browser(QMainWindow):
|
|||||||
def setupSearch(self):
|
def setupSearch(self):
|
||||||
self.filterTimer = None
|
self.filterTimer = None
|
||||||
self.form.searchEdit.setLineEdit(FavouritesLineEdit(self.mw, self))
|
self.form.searchEdit.setLineEdit(FavouritesLineEdit(self.mw, self))
|
||||||
self.connect(self.form.searchButton,
|
self.form.searchButton.clicked.connect(self.onSearch)
|
||||||
SIGNAL("clicked()"),
|
self.form.searchEdit.lineEdit().returnPressed.connect(self.onSearch)
|
||||||
self.onSearch)
|
|
||||||
self.connect(self.form.searchEdit.lineEdit(),
|
|
||||||
SIGNAL("returnPressed()"),
|
|
||||||
self.onSearch)
|
|
||||||
self.form.searchEdit.setCompleter(None)
|
self.form.searchEdit.setCompleter(None)
|
||||||
self.form.searchEdit.addItems(self.mw.pm.profile['searchHistory'])
|
self.form.searchEdit.addItems(self.mw.pm.profile['searchHistory'])
|
||||||
|
|
||||||
@ -578,9 +574,7 @@ class Browser(QMainWindow):
|
|||||||
self.form.tableView.setModel(self.model)
|
self.form.tableView.setModel(self.model)
|
||||||
self.form.tableView.selectionModel()
|
self.form.tableView.selectionModel()
|
||||||
self.form.tableView.setItemDelegate(StatusDelegate(self, self.model))
|
self.form.tableView.setItemDelegate(StatusDelegate(self, self.model))
|
||||||
self.connect(self.form.tableView.selectionModel(),
|
self.form.tableView.selectionModel().selectionChanged.connect(self.onRowChanged)
|
||||||
SIGNAL("selectionChanged(QItemSelection,QItemSelection)"),
|
|
||||||
self.onRowChanged)
|
|
||||||
|
|
||||||
def setupEditor(self):
|
def setupEditor(self):
|
||||||
self.editor = aqt.editor.Editor(
|
self.editor = aqt.editor.Editor(
|
||||||
@ -628,16 +622,13 @@ class Browser(QMainWindow):
|
|||||||
restoreHeader(hh, "editor")
|
restoreHeader(hh, "editor")
|
||||||
hh.setHighlightSections(False)
|
hh.setHighlightSections(False)
|
||||||
hh.setMinimumSectionSize(50)
|
hh.setMinimumSectionSize(50)
|
||||||
hh.setMovable(True)
|
hh.setSectionsMovable(True)
|
||||||
self.setColumnSizes()
|
self.setColumnSizes()
|
||||||
hh.setContextMenuPolicy(Qt.CustomContextMenu)
|
hh.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
hh.connect(hh, SIGNAL("customContextMenuRequested(QPoint)"),
|
hh.customContextMenuRequested.connect(self.onHeaderContext)
|
||||||
self.onHeaderContext)
|
|
||||||
self.setSortIndicator()
|
self.setSortIndicator()
|
||||||
hh.connect(hh, SIGNAL("sortIndicatorChanged(int, Qt::SortOrder)"),
|
hh.sortIndicatorChanged.connect(self.onSortChanged)
|
||||||
self.onSortChanged)
|
hh.sectionMoved.connect(self.onColumnMoved)
|
||||||
hh.connect(hh, SIGNAL("sectionMoved(int,int,int)"),
|
|
||||||
self.onColumnMoved)
|
|
||||||
|
|
||||||
def onSortChanged(self, idx, ord):
|
def onSortChanged(self, idx, ord):
|
||||||
type = self.model.activeCols[idx]
|
type = self.model.activeCols[idx]
|
||||||
@ -692,8 +683,7 @@ by clicking on one on the left."""))
|
|||||||
a = m.addAction(name)
|
a = m.addAction(name)
|
||||||
a.setCheckable(True)
|
a.setCheckable(True)
|
||||||
a.setChecked(type in self.model.activeCols)
|
a.setChecked(type in self.model.activeCols)
|
||||||
a.connect(a, SIGNAL("toggled(bool)"),
|
a.toggled.connect(lambda b, t=type: self.toggleField(t))
|
||||||
lambda b, t=type: self.toggleField(t))
|
|
||||||
m.exec_(gpos)
|
m.exec_(gpos)
|
||||||
|
|
||||||
def toggleField(self, type):
|
def toggleField(self, type):
|
||||||
@ -718,8 +708,8 @@ by clicking on one on the left."""))
|
|||||||
|
|
||||||
def setColumnSizes(self):
|
def setColumnSizes(self):
|
||||||
hh = self.form.tableView.horizontalHeader()
|
hh = self.form.tableView.horizontalHeader()
|
||||||
hh.setResizeMode(QHeaderView.Interactive)
|
hh.setSectionResizeMode(QHeaderView.Interactive)
|
||||||
hh.setResizeMode(hh.logicalIndex(len(self.model.activeCols)-1),
|
hh.setSectionResizeMode(hh.logicalIndex(len(self.model.activeCols)-1),
|
||||||
QHeaderView.Stretch)
|
QHeaderView.Stretch)
|
||||||
# this must be set post-resize or it doesn't work
|
# this must be set post-resize or it doesn't work
|
||||||
hh.setCascadingSectionResizes(False)
|
hh.setCascadingSectionResizes(False)
|
||||||
@ -737,19 +727,13 @@ by clicking on one on the left."""))
|
|||||||
self.oncollapse = oncollapse
|
self.oncollapse = oncollapse
|
||||||
|
|
||||||
def setupTree(self):
|
def setupTree(self):
|
||||||
self.connect(
|
self.form.tree.itemClicked.connect(self.onTreeClick)
|
||||||
self.form.tree, SIGNAL("itemClicked(QTreeWidgetItem*,int)"),
|
|
||||||
self.onTreeClick)
|
|
||||||
p = QPalette()
|
p = QPalette()
|
||||||
p.setColor(QPalette.Base, QColor("#d6dde0"))
|
p.setColor(QPalette.Base, QColor("#d6dde0"))
|
||||||
self.form.tree.setPalette(p)
|
self.form.tree.setPalette(p)
|
||||||
self.buildTree()
|
self.buildTree()
|
||||||
self.connect(
|
self.form.tree.itemExpanded.connect(lambda item: self.onTreeCollapse(item))
|
||||||
self.form.tree, SIGNAL("itemExpanded(QTreeWidgetItem*)"),
|
self.form.tree.itemCollapsed.connect(lambda item: self.onTreeCollapse(item))
|
||||||
lambda item: self.onTreeCollapse(item))
|
|
||||||
self.connect(
|
|
||||||
self.form.tree, SIGNAL("itemCollapsed(QTreeWidgetItem*)"),
|
|
||||||
lambda item: self.onTreeCollapse(item))
|
|
||||||
|
|
||||||
def buildTree(self):
|
def buildTree(self):
|
||||||
self.form.tree.clear()
|
self.form.tree.clear()
|
||||||
@ -876,18 +860,18 @@ by clicking on one on the left."""))
|
|||||||
reps = self._revlogData(cs)
|
reps = self._revlogData(cs)
|
||||||
d = QDialog(self)
|
d = QDialog(self)
|
||||||
l = QVBoxLayout()
|
l = QVBoxLayout()
|
||||||
l.setMargin(0)
|
l.setContentsMargins(0,0,0,0)
|
||||||
w = AnkiWebView()
|
w = AnkiWebView()
|
||||||
l.addWidget(w)
|
l.addWidget(w)
|
||||||
w.stdHtml(info + "<p>" + reps)
|
w.stdHtml(info + "<p>" + reps)
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Close)
|
bb = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
l.addWidget(bb)
|
l.addWidget(bb)
|
||||||
bb.connect(bb, SIGNAL("rejected()"), d, SLOT("reject()"))
|
bb.rejected.connect(d.reject)
|
||||||
d.setLayout(l)
|
d.setLayout(l)
|
||||||
d.setWindowModality(Qt.WindowModal)
|
d.setWindowModality(Qt.WindowModal)
|
||||||
d.resize(500, 400)
|
d.resize(500, 400)
|
||||||
restoreGeom(d, "revlog")
|
restoreGeom(d, "revlog")
|
||||||
d.exec_()
|
d.show()
|
||||||
saveGeom(d, "revlog")
|
saveGeom(d, "revlog")
|
||||||
|
|
||||||
def _cardInfoData(self):
|
def _cardInfoData(self):
|
||||||
@ -1005,14 +989,13 @@ where id in %s""" % ids2str(sf))
|
|||||||
self._openPreview()
|
self._openPreview()
|
||||||
|
|
||||||
def _openPreview(self):
|
def _openPreview(self):
|
||||||
c = self.connect
|
|
||||||
self._previewState = "question"
|
self._previewState = "question"
|
||||||
self._previewWindow = QDialog(None, Qt.Window)
|
self._previewWindow = QDialog(None, Qt.Window)
|
||||||
self._previewWindow.setWindowTitle(_("Preview"))
|
self._previewWindow.setWindowTitle(_("Preview"))
|
||||||
|
|
||||||
c(self._previewWindow, SIGNAL("finished(int)"), self._onPreviewFinished)
|
self._previewWindow.finished.connect(self._onPreviewFinished)
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
vbox.setMargin(0)
|
vbox.setContentsMargins(0,0,0,0)
|
||||||
self._previewWeb = AnkiWebView()
|
self._previewWeb = AnkiWebView()
|
||||||
vbox.addWidget(self._previewWeb)
|
vbox.addWidget(self._previewWeb)
|
||||||
bbox = QDialogButtonBox()
|
bbox = QDialogButtonBox()
|
||||||
@ -1032,9 +1015,9 @@ where id in %s""" % ids2str(sf))
|
|||||||
self._previewNext.setShortcut(QKeySequence("Right"))
|
self._previewNext.setShortcut(QKeySequence("Right"))
|
||||||
self._previewNext.setToolTip(_("Shortcut key: Right arrow or Enter"))
|
self._previewNext.setToolTip(_("Shortcut key: Right arrow or Enter"))
|
||||||
|
|
||||||
c(self._previewPrev, SIGNAL("clicked()"), self._onPreviewPrev)
|
self._previewPrev.clicked.connect(self._onPreviewPrev)
|
||||||
c(self._previewNext, SIGNAL("clicked()"), self._onPreviewNext)
|
self._previewNext.clicked.connect(self._onPreviewNext)
|
||||||
c(self._previewReplay, SIGNAL("clicked()"), self._onReplayAudio)
|
self._previewReplay.clicked.connect(self._onReplayAudio)
|
||||||
|
|
||||||
vbox.addWidget(bbox)
|
vbox.addWidget(bbox)
|
||||||
self._previewWindow.setLayout(vbox)
|
self._previewWindow.setLayout(vbox)
|
||||||
@ -1349,8 +1332,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
|||||||
frm.setupUi(d)
|
frm.setupUi(d)
|
||||||
d.setWindowModality(Qt.WindowModal)
|
d.setWindowModality(Qt.WindowModal)
|
||||||
frm.field.addItems([_("All Fields")] + fields)
|
frm.field.addItems([_("All Fields")] + fields)
|
||||||
self.connect(frm.buttonBox, SIGNAL("helpRequested()"),
|
frm.buttonBox.helpRequested.connect(self.onFindReplaceHelp)
|
||||||
self.onFindReplaceHelp)
|
|
||||||
restoreGeom(d, "findreplace")
|
restoreGeom(d, "findreplace")
|
||||||
r = d.exec_()
|
r = d.exec_()
|
||||||
saveGeom(d, "findreplace")
|
saveGeom(d, "findreplace")
|
||||||
@ -1401,20 +1383,16 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
|||||||
frm.fields.addItems(fields)
|
frm.fields.addItems(fields)
|
||||||
self._dupesButton = None
|
self._dupesButton = None
|
||||||
# links
|
# links
|
||||||
frm.webView.page().setLinkDelegationPolicy(
|
frm.webView.onAnkiLink = self.dupeLinkClicked
|
||||||
QWebPage.DelegateAllLinks)
|
|
||||||
self.connect(frm.webView,
|
|
||||||
SIGNAL("linkClicked(QUrl)"),
|
|
||||||
self.dupeLinkClicked)
|
|
||||||
def onFin(code):
|
def onFin(code):
|
||||||
saveGeom(d, "findDupes")
|
saveGeom(d, "findDupes")
|
||||||
self.connect(d, SIGNAL("finished(int)"), onFin)
|
d.finished.connect(onFin)
|
||||||
def onClick():
|
def onClick():
|
||||||
field = fields[frm.fields.currentIndex()]
|
field = fields[frm.fields.currentIndex()]
|
||||||
self.duplicatesReport(frm.webView, field, frm.search.text(), frm)
|
self.duplicatesReport(frm.webView, field, frm.search.text(), frm)
|
||||||
search = frm.buttonBox.addButton(
|
search = frm.buttonBox.addButton(
|
||||||
_("Search"), QDialogButtonBox.ActionRole)
|
_("Search"), QDialogButtonBox.ActionRole)
|
||||||
self.connect(search, SIGNAL("clicked()"), onClick)
|
search.clicked.connect(onClick)
|
||||||
d.show()
|
d.show()
|
||||||
|
|
||||||
def duplicatesReport(self, web, fname, search, frm):
|
def duplicatesReport(self, web, fname, search, frm):
|
||||||
@ -1423,7 +1401,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
|||||||
if not self._dupesButton:
|
if not self._dupesButton:
|
||||||
self._dupesButton = b = frm.buttonBox.addButton(
|
self._dupesButton = b = frm.buttonBox.addButton(
|
||||||
_("Tag Duplicates"), QDialogButtonBox.ActionRole)
|
_("Tag Duplicates"), QDialogButtonBox.ActionRole)
|
||||||
self.connect(b, SIGNAL("clicked()"), lambda: self._onTagDupes(res))
|
b.clicked.connect(lambda: self._onTagDupes(res))
|
||||||
t = "<html><body>"
|
t = "<html><body>"
|
||||||
groups = len(res)
|
groups = len(res)
|
||||||
notes = sum(len(r[1]) for r in res)
|
notes = sum(len(r[1]) for r in res)
|
||||||
@ -1432,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 += _("Found %(a)s across %(b)s.") % dict(a=part1, b=part2)
|
||||||
t += "<p><ol>"
|
t += "<p><ol>"
|
||||||
for val, nids in res:
|
for val, nids in res:
|
||||||
t += '<li><a href="%s">%s</a>: %s</a>' % (
|
t += '''<li><a href=# onclick="openAnkiLink('%s')">%s</a>: %s</a>''' % (
|
||||||
"nid:" + ",".join(str(id) for id in nids),
|
"nid:" + ",".join(str(id) for id in nids),
|
||||||
ngettext("%d note", "%d notes", len(nids)) % len(nids),
|
ngettext("%d note", "%d notes", len(nids)) % len(nids),
|
||||||
cgi.escape(val))
|
cgi.escape(val))
|
||||||
@ -1456,7 +1434,7 @@ update cards set usn=?, mod=?, did=? where id in """ + scids,
|
|||||||
tooltip(_("Notes tagged."))
|
tooltip(_("Notes tagged."))
|
||||||
|
|
||||||
def dupeLinkClicked(self, link):
|
def dupeLinkClicked(self, link):
|
||||||
self.form.searchEdit.lineEdit().setText(link.toString())
|
self.form.searchEdit.lineEdit().setText(link)
|
||||||
self.onSearch()
|
self.onSearch()
|
||||||
self.onNote()
|
self.onNote()
|
||||||
|
|
||||||
@ -1552,11 +1530,11 @@ class ChangeModel(QDialog):
|
|||||||
def setup(self):
|
def setup(self):
|
||||||
# maps
|
# maps
|
||||||
self.flayout = QHBoxLayout()
|
self.flayout = QHBoxLayout()
|
||||||
self.flayout.setMargin(0)
|
self.flayout.setContentsMargins(0,0,0,0)
|
||||||
self.fwidg = None
|
self.fwidg = None
|
||||||
self.form.fieldMap.setLayout(self.flayout)
|
self.form.fieldMap.setLayout(self.flayout)
|
||||||
self.tlayout = QHBoxLayout()
|
self.tlayout = QHBoxLayout()
|
||||||
self.tlayout.setMargin(0)
|
self.tlayout.setContentsMargins(0,0,0,0)
|
||||||
self.twidg = None
|
self.twidg = None
|
||||||
self.form.templateMap.setLayout(self.tlayout)
|
self.form.templateMap.setLayout(self.tlayout)
|
||||||
if self.style().objectName() == "gtk+":
|
if self.style().objectName() == "gtk+":
|
||||||
@ -1572,8 +1550,7 @@ class ChangeModel(QDialog):
|
|||||||
self.modelChooser = aqt.modelchooser.ModelChooser(
|
self.modelChooser = aqt.modelchooser.ModelChooser(
|
||||||
self.browser.mw, self.form.modelChooserWidget, label=False)
|
self.browser.mw, self.form.modelChooserWidget, label=False)
|
||||||
self.modelChooser.models.setFocus()
|
self.modelChooser.models.setFocus()
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.form.buttonBox.helpRequested.connect(self.onHelp)
|
||||||
self.onHelp)
|
|
||||||
self.modelChanged(self.browser.mw.col.models.current())
|
self.modelChanged(self.browser.mw.col.models.current())
|
||||||
self.pauseUpdate = False
|
self.pauseUpdate = False
|
||||||
|
|
||||||
@ -1609,8 +1586,8 @@ class ChangeModel(QDialog):
|
|||||||
idx = min(i, len(targets)-1)
|
idx = min(i, len(targets)-1)
|
||||||
cb.setCurrentIndex(idx)
|
cb.setCurrentIndex(idx)
|
||||||
indices[cb] = idx
|
indices[cb] = idx
|
||||||
self.connect(cb, SIGNAL("currentIndexChanged(int)"),
|
cb.currentIndexChanged.connect(
|
||||||
lambda i, cb=cb, key=key: self.onComboChanged(i, cb, key))
|
lambda i, cb=cb, key=key: self.onComboChanged(i, cb, key))
|
||||||
combos.append(cb)
|
combos.append(cb)
|
||||||
l.addWidget(cb, i, 1)
|
l.addWidget(cb, i, 1)
|
||||||
map.setLayout(l)
|
map.setLayout(l)
|
||||||
@ -1716,11 +1693,11 @@ class BrowserToolbar(Toolbar):
|
|||||||
def borderImg(link, icon, on, title, tooltip=None):
|
def borderImg(link, icon, on, title, tooltip=None):
|
||||||
if on:
|
if on:
|
||||||
fmt = '''\
|
fmt = '''\
|
||||||
<a class=hitem title="%s" href="%s">\
|
<a class=hitem title="%s" href=# onclick="openAnkiLink('%s')">\
|
||||||
<img valign=bottom style='border: 1px solid #aaa;' src="qrc:/icons/%s.png"> %s</a>'''
|
<img valign=bottom style='border: 1px solid #aaa;' src="qrc:/icons/%s.png"> %s</a>'''
|
||||||
else:
|
else:
|
||||||
fmt = '''\
|
fmt = '''\
|
||||||
<a class=hitem title="%s" href="%s"><img style="padding: 1px;" valign=bottom src="qrc:/icons/%s.png"> %s</a>'''
|
<a class=hitem title="%s" href=# onclick="openAnkiLink('%s')"><img style="padding: 1px;" valign=bottom src="qrc:/icons/%s.png"> %s</a>'''
|
||||||
return fmt % (tooltip or title, link, icon, title)
|
return fmt % (tooltip or title, link, icon, title)
|
||||||
right = "<div>"
|
right = "<div>"
|
||||||
right += borderImg("add", "add16", False, _("Add"),
|
right += borderImg("add", "add16", False, _("Add"),
|
||||||
@ -1740,8 +1717,9 @@ class BrowserToolbar(Toolbar):
|
|||||||
"Bulk Remove Tags (Ctrl+Alt+T)")))
|
"Bulk Remove Tags (Ctrl+Alt+T)")))
|
||||||
right += borderImg("delete", "delete16", False, _("Delete"))
|
right += borderImg("delete", "delete16", False, _("Delete"))
|
||||||
right += "</div>"
|
right += "</div>"
|
||||||
self.web.page().currentFrame().setScrollBarPolicy(
|
# fixme
|
||||||
Qt.Horizontal, Qt.ScrollBarAlwaysOff)
|
#self.web.page().currentFrame().setScrollBarPolicy(
|
||||||
|
# Qt.Horizontal, Qt.ScrollBarAlwaysOff)
|
||||||
self.web.stdHtml(self._body % (
|
self.web.stdHtml(self._body % (
|
||||||
"", #<span style='display:inline-block; width: 100px;'></span>",
|
"", #<span style='display:inline-block; width: 100px;'></span>",
|
||||||
#self._centerLinks(),
|
#self._centerLinks(),
|
||||||
@ -1798,8 +1776,8 @@ class FavouritesLineEdit(QLineEdit):
|
|||||||
# name of current saved filter (if query matches)
|
# name of current saved filter (if query matches)
|
||||||
self.name = None
|
self.name = None
|
||||||
self.buttonClicked.connect(self.onClicked)
|
self.buttonClicked.connect(self.onClicked)
|
||||||
self.connect(self, SIGNAL("textChanged(QString)"), self.updateButton)
|
self.textChanged.connect(self.updateButton)
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
buttonSize = self.button.sizeHint()
|
buttonSize = self.button.sizeHint()
|
||||||
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
|
frameWidth = self.style().pixelMetric(QStyle.PM_DefaultFrameWidth)
|
||||||
|
@ -60,7 +60,6 @@ class CardLayout(QDialog):
|
|||||||
self.selectCard(idx)
|
self.selectCard(idx)
|
||||||
|
|
||||||
def setupTabs(self):
|
def setupTabs(self):
|
||||||
c = self.connect
|
|
||||||
cloze = self.model['type'] == MODEL_CLOZE
|
cloze = self.model['type'] == MODEL_CLOZE
|
||||||
self.tabs = QTabWidget()
|
self.tabs = QTabWidget()
|
||||||
self.tabs.setTabsClosable(not cloze)
|
self.tabs.setTabsClosable(not cloze)
|
||||||
@ -69,10 +68,10 @@ class CardLayout(QDialog):
|
|||||||
add = QPushButton("+")
|
add = QPushButton("+")
|
||||||
add.setFixedWidth(30)
|
add.setFixedWidth(30)
|
||||||
add.setToolTip(_("Add new card"))
|
add.setToolTip(_("Add new card"))
|
||||||
c(add, SIGNAL("clicked()"), self.onAddCard)
|
add.clicked.connect(self.onAddCard)
|
||||||
self.tabs.setCornerWidget(add)
|
self.tabs.setCornerWidget(add)
|
||||||
c(self.tabs, SIGNAL("currentChanged(int)"), self.onCardSelected)
|
self.tabs.currentChanged.connect(self.onCardSelected)
|
||||||
c(self.tabs, SIGNAL("tabCloseRequested(int)"), self.onRemoveTab)
|
self.tabs.tabCloseRequested.connect(self.onRemoveTab)
|
||||||
|
|
||||||
def updateTabs(self):
|
def updateTabs(self):
|
||||||
self.forms = []
|
self.forms = []
|
||||||
@ -81,10 +80,9 @@ class CardLayout(QDialog):
|
|||||||
self.addTab(t)
|
self.addTab(t)
|
||||||
|
|
||||||
def addTab(self, t):
|
def addTab(self, t):
|
||||||
c = self.connect
|
|
||||||
w = QWidget()
|
w = QWidget()
|
||||||
l = QHBoxLayout()
|
l = QHBoxLayout()
|
||||||
l.setMargin(0)
|
l.setContentsMargins(0,0,0,0)
|
||||||
l.setSpacing(3)
|
l.setSpacing(3)
|
||||||
left = QWidget()
|
left = QWidget()
|
||||||
# template area
|
# template area
|
||||||
@ -102,9 +100,9 @@ class CardLayout(QDialog):
|
|||||||
if len(self.cards) > 1:
|
if len(self.cards) > 1:
|
||||||
tform.groupBox_3.setTitle(_(
|
tform.groupBox_3.setTitle(_(
|
||||||
"Styling (shared between cards)"))
|
"Styling (shared between cards)"))
|
||||||
c(tform.front, SIGNAL("textChanged()"), self.saveCard)
|
tform.front.textChanged.connect(self.saveCard)
|
||||||
c(tform.css, SIGNAL("textChanged()"), self.saveCard)
|
tform.css.textChanged.connect(self.saveCard)
|
||||||
c(tform.back, SIGNAL("textChanged()"), self.saveCard)
|
tform.back.textChanged.connect(self.saveCard)
|
||||||
l.addWidget(left, 5)
|
l.addWidget(left, 5)
|
||||||
# preview area
|
# preview area
|
||||||
right = QWidget()
|
right = QWidget()
|
||||||
@ -124,9 +122,6 @@ class CardLayout(QDialog):
|
|||||||
pform.frontPrevBox.addWidget(pform.frontWeb)
|
pform.frontPrevBox.addWidget(pform.frontWeb)
|
||||||
pform.backWeb = AnkiWebView()
|
pform.backWeb = AnkiWebView()
|
||||||
pform.backPrevBox.addWidget(pform.backWeb)
|
pform.backPrevBox.addWidget(pform.backWeb)
|
||||||
for wig in pform.frontWeb, pform.backWeb:
|
|
||||||
wig.page().setLinkDelegationPolicy(
|
|
||||||
QWebPage.DelegateExternalLinks)
|
|
||||||
l.addWidget(right, 5)
|
l.addWidget(right, 5)
|
||||||
w.setLayout(l)
|
w.setLayout(l)
|
||||||
self.forms.append({'tform': tform, 'pform': pform})
|
self.forms.append({'tform': tform, 'pform': pform})
|
||||||
@ -151,31 +146,30 @@ Please create a new card type first."""))
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def setupButtons(self):
|
def setupButtons(self):
|
||||||
c = self.connect
|
|
||||||
l = self.buttons = QHBoxLayout()
|
l = self.buttons = QHBoxLayout()
|
||||||
help = QPushButton(_("Help"))
|
help = QPushButton(_("Help"))
|
||||||
help.setAutoDefault(False)
|
help.setAutoDefault(False)
|
||||||
l.addWidget(help)
|
l.addWidget(help)
|
||||||
c(help, SIGNAL("clicked()"), self.onHelp)
|
help.clicked.connect(self.onHelp)
|
||||||
l.addStretch()
|
l.addStretch()
|
||||||
addField = QPushButton(_("Add Field"))
|
addField = QPushButton(_("Add Field"))
|
||||||
addField.setAutoDefault(False)
|
addField.setAutoDefault(False)
|
||||||
l.addWidget(addField)
|
l.addWidget(addField)
|
||||||
c(addField, SIGNAL("clicked()"), self.onAddField)
|
addField.clicked.connect(self.onAddField)
|
||||||
if self.model['type'] != MODEL_CLOZE:
|
if self.model['type'] != MODEL_CLOZE:
|
||||||
flip = QPushButton(_("Flip"))
|
flip = QPushButton(_("Flip"))
|
||||||
flip.setAutoDefault(False)
|
flip.setAutoDefault(False)
|
||||||
l.addWidget(flip)
|
l.addWidget(flip)
|
||||||
c(flip, SIGNAL("clicked()"), self.onFlip)
|
flip.clicked.connect(self.onFlip)
|
||||||
more = QPushButton(_("More") + " "+downArrow())
|
more = QPushButton(_("More") + " "+downArrow())
|
||||||
more.setAutoDefault(False)
|
more.setAutoDefault(False)
|
||||||
l.addWidget(more)
|
l.addWidget(more)
|
||||||
c(more, SIGNAL("clicked()"), lambda: self.onMore(more))
|
more.clicked.connect(lambda: self.onMore(more))
|
||||||
l.addStretch()
|
l.addStretch()
|
||||||
close = QPushButton(_("Close"))
|
close = QPushButton(_("Close"))
|
||||||
close.setAutoDefault(False)
|
close.setAutoDefault(False)
|
||||||
l.addWidget(close)
|
l.addWidget(close)
|
||||||
c(close, SIGNAL("clicked()"), self.accept)
|
close.clicked.connect(self.accept)
|
||||||
|
|
||||||
# Cards
|
# Cards
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -335,23 +329,19 @@ adjust the template manually to switch the question and answer."""))
|
|||||||
def onMore(self, button):
|
def onMore(self, button):
|
||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
a = m.addAction(_("Rename"))
|
a = m.addAction(_("Rename"))
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(self.onRename)
|
||||||
self.onRename)
|
|
||||||
if self.model['type'] != MODEL_CLOZE:
|
if self.model['type'] != MODEL_CLOZE:
|
||||||
a = m.addAction(_("Reposition"))
|
a = m.addAction(_("Reposition"))
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(self.onReorder)
|
||||||
self.onReorder)
|
|
||||||
t = self.card.template()
|
t = self.card.template()
|
||||||
if t['did']:
|
if t['did']:
|
||||||
s = _(" (on)")
|
s = _(" (on)")
|
||||||
else:
|
else:
|
||||||
s = _(" (off)")
|
s = _(" (off)")
|
||||||
a = m.addAction(_("Deck Override") + s)
|
a = m.addAction(_("Deck Override") + s)
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(self.onTargetDeck)
|
||||||
self.onTargetDeck)
|
|
||||||
a = m.addAction(_("Browser Appearance"))
|
a = m.addAction(_("Browser Appearance"))
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(self.onBrowserDisplay)
|
||||||
self.onBrowserDisplay)
|
|
||||||
m.exec_(button.mapToGlobal(QPoint(0,0)))
|
m.exec_(button.mapToGlobal(QPoint(0,0)))
|
||||||
|
|
||||||
def onBrowserDisplay(self):
|
def onBrowserDisplay(self):
|
||||||
@ -363,8 +353,7 @@ adjust the template manually to switch the question and answer."""))
|
|||||||
f.afmt.setText(t.get('bafmt', ""))
|
f.afmt.setText(t.get('bafmt', ""))
|
||||||
f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
|
f.font.setCurrentFont(QFont(t.get('bfont', "Arial")))
|
||||||
f.fontSize.setValue(t.get('bsize', 12))
|
f.fontSize.setValue(t.get('bsize', 12))
|
||||||
d.connect(f.buttonBox, SIGNAL("accepted()"),
|
f.buttonBox.accepted.connect(lambda: self.onBrowserDisplayOk(f))
|
||||||
lambda: self.onBrowserDisplayOk(f))
|
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
def onBrowserDisplayOk(self, f):
|
def onBrowserDisplayOk(self, f):
|
||||||
@ -393,7 +382,7 @@ Enter deck to place new %s cards in, or leave blank:""") %
|
|||||||
te.setText(self.col.decks.get(t['did'])['name'])
|
te.setText(self.col.decks.get(t['did'])['name'])
|
||||||
te.selectAll()
|
te.selectAll()
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Close)
|
bb = QDialogButtonBox(QDialogButtonBox.Close)
|
||||||
self.connect(bb, SIGNAL("rejected()"), d, SLOT("close()"))
|
bb.rejected.connect(d.close)
|
||||||
l.addWidget(bb)
|
l.addWidget(bb)
|
||||||
d.setLayout(l)
|
d.setLayout(l)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
@ -31,13 +31,13 @@ class CustomStudy(QDialog):
|
|||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
def setupSignals(self):
|
def setupSignals(self):
|
||||||
f = self.form; c = self.connect; s = SIGNAL("clicked()")
|
f = self.form
|
||||||
c(f.radio1, s, lambda: self.onRadioChange(1))
|
f.radio1.clicked.connect(lambda: self.onRadioChange(1))
|
||||||
c(f.radio2, s, lambda: self.onRadioChange(2))
|
f.radio2.clicked.connect(lambda: self.onRadioChange(2))
|
||||||
c(f.radio3, s, lambda: self.onRadioChange(3))
|
f.radio3.clicked.connect(lambda: self.onRadioChange(3))
|
||||||
c(f.radio4, s, lambda: self.onRadioChange(4))
|
f.radio4.clicked.connect(lambda: self.onRadioChange(4))
|
||||||
c(f.radio5, s, lambda: self.onRadioChange(5))
|
f.radio5.clicked.connect(lambda: self.onRadioChange(5))
|
||||||
c(f.radio6, s, lambda: self.onRadioChange(6))
|
f.radio6.clicked.connect(lambda: self.onRadioChange(6))
|
||||||
|
|
||||||
def onRadioChange(self, idx):
|
def onRadioChange(self, idx):
|
||||||
f = self.form; sp = f.spin
|
f = self.form; sp = f.spin
|
||||||
|
@ -21,8 +21,8 @@ class DeckBrowser(object):
|
|||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
clearAudioQueue()
|
clearAudioQueue()
|
||||||
self.web.setLinkHandler(self._linkHandler)
|
self.web.resetHandlers()
|
||||||
self.web.setKeyHandler(None)
|
self.web.onAnkiLink = self._linkHandler
|
||||||
self.mw.keyHandler = self._keyHandler
|
self.mw.keyHandler = self._keyHandler
|
||||||
self._renderPage()
|
self._renderPage()
|
||||||
|
|
||||||
@ -60,13 +60,13 @@ class DeckBrowser(object):
|
|||||||
self._dragDeckOnto(draggedDeckDid, ontoDeckDid)
|
self._dragDeckOnto(draggedDeckDid, ontoDeckDid)
|
||||||
elif cmd == "collapse":
|
elif cmd == "collapse":
|
||||||
self._collapse(arg)
|
self._collapse(arg)
|
||||||
|
return False
|
||||||
|
|
||||||
def _keyHandler(self, evt):
|
def _keyHandler(self, evt):
|
||||||
# currently does nothing
|
# currently does nothing
|
||||||
key = str(evt.text())
|
key = str(evt.text())
|
||||||
|
|
||||||
def _selDeck(self, did):
|
def _selDeck(self, did):
|
||||||
self.scrollPos = self.web.page().mainFrame().scrollPosition()
|
|
||||||
self.mw.col.decks.select(did)
|
self.mw.col.decks.select(did)
|
||||||
self.mw.onOverview()
|
self.mw.onOverview()
|
||||||
|
|
||||||
@ -86,7 +86,8 @@ tr.drag-hover td { border-bottom: %(width)s solid #aaa; }
|
|||||||
body { margin: 1em; -webkit-user-select: none; }
|
body { margin: 1em; -webkit-user-select: none; }
|
||||||
.current { background-color: #e7e7e7; }
|
.current { background-color: #e7e7e7; }
|
||||||
.decktd { min-width: 15em; }
|
.decktd { min-width: 15em; }
|
||||||
.count { width: 6em; text-align: right; }
|
.count { min-width: 4em; text-align: right; }
|
||||||
|
.optscol { width: 2em; }
|
||||||
.collapse { color: #000; text-decoration:none; display:inline-block;
|
.collapse { color: #000; text-decoration:none; display:inline-block;
|
||||||
width: 1em; }
|
width: 1em; }
|
||||||
.filtered { color: #00a !important; }
|
.filtered { color: #00a !important; }
|
||||||
@ -131,7 +132,7 @@ body { margin: 1em; -webkit-user-select: none; }
|
|||||||
var draggedDeckId = ui.draggable.attr('id');
|
var draggedDeckId = ui.draggable.attr('id');
|
||||||
var ontoDeckId = $(this).attr('id');
|
var ontoDeckId = $(this).attr('id');
|
||||||
|
|
||||||
py.link("drag:" + draggedDeckId + "," + ontoDeckId);
|
openAnkiLink("drag:" + draggedDeckId + "," + ontoDeckId);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
"""
|
"""
|
||||||
@ -142,11 +143,9 @@ body { margin: 1em; -webkit-user-select: none; }
|
|||||||
self._dueTree = self.mw.col.sched.deckDueTree()
|
self._dueTree = self.mw.col.sched.deckDueTree()
|
||||||
tree = self._renderDeckTree(self._dueTree)
|
tree = self._renderDeckTree(self._dueTree)
|
||||||
stats = self._renderStats()
|
stats = self._renderStats()
|
||||||
op = self._oldPos()
|
|
||||||
self.web.stdHtml(self._body%dict(
|
self.web.stdHtml(self._body%dict(
|
||||||
tree=tree, stats=stats, countwarn=self._countWarn()), css=css,
|
tree=tree, stats=stats, countwarn=self._countWarn()), css=css,
|
||||||
js=anki.js.jquery+anki.js.ui, loadCB=lambda ok:\
|
js=anki.js.jquery+anki.js.ui)
|
||||||
self.web.page().mainFrame().setScrollPosition(op))
|
|
||||||
self.web.key = "deckBrowser"
|
self.web.key = "deckBrowser"
|
||||||
self._drawButtons()
|
self._drawButtons()
|
||||||
|
|
||||||
@ -173,8 +172,10 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
return ""
|
return ""
|
||||||
return "<br><div style='width:50%;border: 1px solid #000;padding:5px;'>"+(
|
return "<br><div style='width:50%;border: 1px solid #000;padding:5px;'>"+(
|
||||||
_("You have a lot of decks. Please see %(a)s. %(b)s") % dict(
|
_("You have a lot of decks. Please see %(a)s. %(b)s") % dict(
|
||||||
a=("<a href=lots>%s</a>" % _("this page")),
|
a=("<a href=# onclick='openAnkiLink('lots')>%s</a>" % _(
|
||||||
b=("<br><small><a href=hidelots>(%s)</a></small>" % (_("hide"))+
|
"this page")),
|
||||||
|
b=("<br><small><a href=# onclick='openAnkiLink(\"hidelots\")'>("
|
||||||
|
"%s)</a></small>" % (_("hide"))+
|
||||||
"</div")))
|
"</div")))
|
||||||
|
|
||||||
def _renderDeckTree(self, nodes, depth=0):
|
def _renderDeckTree(self, nodes, depth=0):
|
||||||
@ -183,7 +184,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
if depth == 0:
|
if depth == 0:
|
||||||
buf = """
|
buf = """
|
||||||
<tr><th colspan=5 align=left>%s</th><th class=count>%s</th>
|
<tr><th colspan=5 align=left>%s</th><th class=count>%s</th>
|
||||||
<th class=count>%s</th><th class=count></th></tr>""" % (
|
<th class=count>%s</th><th class=optscol></th></tr>""" % (
|
||||||
_("Deck"), _("Due"), _("New"))
|
_("Deck"), _("Due"), _("New"))
|
||||||
buf += self._topLevelDragRow()
|
buf += self._topLevelDragRow()
|
||||||
else:
|
else:
|
||||||
@ -219,7 +220,7 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
buf = "<tr class='%s' id='%d'>" % (klass, did)
|
buf = "<tr class='%s' id='%d'>" % (klass, did)
|
||||||
# deck link
|
# deck link
|
||||||
if children:
|
if children:
|
||||||
collapse = "<a class=collapse href='collapse:%d'>%s</a>" % (did, prefix)
|
collapse = "<a class=collapse href=# onclick='openAnkiLink(\"collapse:%d\")'>%s</a>" % (did, prefix)
|
||||||
else:
|
else:
|
||||||
collapse = "<span class=collapse></span>"
|
collapse = "<span class=collapse></span>"
|
||||||
if deck['dyn']:
|
if deck['dyn']:
|
||||||
@ -228,7 +229,8 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
extraclass = ""
|
extraclass = ""
|
||||||
buf += """
|
buf += """
|
||||||
|
|
||||||
<td class=decktd colspan=5>%s%s<a class="deck %s" href='open:%d'>%s</a></td>"""% (
|
<td class=decktd colspan=5>%s%s<a class="deck %s"
|
||||||
|
href=# onclick="openAnkiLink('open:%d')">%s</a></td>"""% (
|
||||||
indent(), collapse, extraclass, did, name)
|
indent(), collapse, extraclass, did, name)
|
||||||
# due counts
|
# due counts
|
||||||
def nonzeroColour(cnt, colour):
|
def nonzeroColour(cnt, colour):
|
||||||
@ -241,8 +243,8 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
nonzeroColour(due, "#007700"),
|
nonzeroColour(due, "#007700"),
|
||||||
nonzeroColour(new, "#000099"))
|
nonzeroColour(new, "#000099"))
|
||||||
# options
|
# options
|
||||||
buf += "<td align=right class=opts>%s</td></tr>" % self.mw.button(
|
buf += ("<td align=center class=opts><a onclick='openAnkiLink(\"opts:%d\");'>"
|
||||||
link="opts:%d"%did, name="<img valign=bottom src='qrc:/icons/gears.png'>"+downArrow())
|
"<img valign=right src='qrc:/icons/gears.png'></a></td></tr>" % did)
|
||||||
# children
|
# children
|
||||||
buf += self._renderDeckTree(children, depth+1)
|
buf += self._renderDeckTree(children, depth+1)
|
||||||
return buf
|
return buf
|
||||||
@ -265,13 +267,13 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
def _showOptions(self, did):
|
def _showOptions(self, did):
|
||||||
m = QMenu(self.mw)
|
m = QMenu(self.mw)
|
||||||
a = m.addAction(_("Rename"))
|
a = m.addAction(_("Rename"))
|
||||||
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._rename(did))
|
a.triggered.connect(lambda b, did=did: self._rename(did))
|
||||||
a = m.addAction(_("Options"))
|
a = m.addAction(_("Options"))
|
||||||
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._options(did))
|
a.triggered.connect(lambda b, did=did: self._options(did))
|
||||||
a = m.addAction(_("Export"))
|
a = m.addAction(_("Export"))
|
||||||
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._export(did))
|
a.triggered.connect(lambda b, did=did: self._export(did))
|
||||||
a = m.addAction(_("Delete"))
|
a = m.addAction(_("Delete"))
|
||||||
a.connect(a, SIGNAL("triggered()"), lambda did=did: self._delete(did))
|
a.triggered.connect(lambda b, did=did: self._delete(did))
|
||||||
m.exec_(QCursor.pos())
|
m.exec_(QCursor.pos())
|
||||||
|
|
||||||
def _export(self, did):
|
def _export(self, did):
|
||||||
@ -345,14 +347,15 @@ where id > ?""", (self.mw.col.sched.dayCutoff-86400)*1000)
|
|||||||
if b[0]:
|
if b[0]:
|
||||||
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
||||||
buf += """
|
buf += """
|
||||||
<button title='%s' onclick='py.link(\"%s\");'>%s</button>""" % tuple(b)
|
<button title='%s' onclick='openAnkiLink(\"%s\");'>%s</button>""" % tuple(b)
|
||||||
self.bottom.draw(buf)
|
self.bottom.draw(buf)
|
||||||
if isMac:
|
if isMac:
|
||||||
size = 28
|
size = 28
|
||||||
else:
|
else:
|
||||||
size = 36 + self.mw.fontHeightDelta*3
|
size = 36 + self.mw.fontHeightDelta*3
|
||||||
self.bottom.web.setFixedHeight(size)
|
self.bottom.web.setFixedHeight(size)
|
||||||
self.bottom.web.setLinkHandler(self._linkHandler)
|
self.bottom.web.resetHandlers()
|
||||||
|
self.bottom.web.onAnkiLink = self._linkHandler
|
||||||
|
|
||||||
def _onShared(self):
|
def _onShared(self):
|
||||||
openLink(aqt.appShared+"decks/")
|
openLink(aqt.appShared+"decks/")
|
||||||
|
@ -14,7 +14,7 @@ class DeckChooser(QHBoxLayout):
|
|||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.deck = mw.col
|
self.deck = mw.col
|
||||||
self.label = label
|
self.label = label
|
||||||
self.setMargin(0)
|
self.setContentsMargins(0,0,0,0)
|
||||||
self.setSpacing(8)
|
self.setSpacing(8)
|
||||||
self.setupDecks()
|
self.setupDecks()
|
||||||
self.widget.setLayout(self)
|
self.widget.setLayout(self)
|
||||||
@ -25,12 +25,10 @@ class DeckChooser(QHBoxLayout):
|
|||||||
self.deckLabel = QLabel(_("Deck"))
|
self.deckLabel = QLabel(_("Deck"))
|
||||||
self.addWidget(self.deckLabel)
|
self.addWidget(self.deckLabel)
|
||||||
# decks box
|
# decks box
|
||||||
self.deck = QPushButton()
|
self.deck = QPushButton(clicked=self.onDeckChange)
|
||||||
self.deck.setToolTip(shortcut(_("Target Deck (Ctrl+D)")))
|
self.deck.setToolTip(shortcut(_("Target Deck (Ctrl+D)")))
|
||||||
s = QShortcut(QKeySequence(_("Ctrl+D")), self.widget)
|
s = QShortcut(QKeySequence(_("Ctrl+D")), self.widget, activated=self.onDeckChange)
|
||||||
s.connect(s, SIGNAL("activated()"), self.onDeckChange)
|
|
||||||
self.addWidget(self.deck)
|
self.addWidget(self.deck)
|
||||||
self.connect(self.deck, SIGNAL("clicked()"), self.onDeckChange)
|
|
||||||
# starting label
|
# starting label
|
||||||
if self.mw.col.conf.get("addToCur", True):
|
if self.mw.col.conf.get("addToCur", True):
|
||||||
col = self.mw.col
|
col = self.mw.col
|
||||||
|
@ -24,14 +24,10 @@ class DeckConf(QDialog):
|
|||||||
self.setupCombos()
|
self.setupCombos()
|
||||||
self.setupConfs()
|
self.setupConfs()
|
||||||
self.setWindowModality(Qt.WindowModal)
|
self.setWindowModality(Qt.WindowModal)
|
||||||
self.connect(self.form.buttonBox,
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp("deckoptions"))
|
||||||
SIGNAL("helpRequested()"),
|
self.form.confOpts.clicked.connect(self.confOpts)
|
||||||
lambda: openHelp("deckoptions"))
|
|
||||||
self.connect(self.form.confOpts, SIGNAL("clicked()"), self.confOpts)
|
|
||||||
self.form.confOpts.setText(downArrow())
|
self.form.confOpts.setText(downArrow())
|
||||||
self.connect(self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults),
|
self.form.buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked.connect(self.onRestore)
|
||||||
SIGNAL("clicked()"),
|
|
||||||
self.onRestore)
|
|
||||||
self.setWindowTitle(_("Options for %s") % self.deck['name'])
|
self.setWindowTitle(_("Options for %s") % self.deck['name'])
|
||||||
# qt doesn't size properly with altered fonts otherwise
|
# qt doesn't size properly with altered fonts otherwise
|
||||||
restoreGeom(self, "deckconf", adjustSize=True)
|
restoreGeom(self, "deckconf", adjustSize=True)
|
||||||
@ -43,15 +39,13 @@ class DeckConf(QDialog):
|
|||||||
import anki.consts as cs
|
import anki.consts as cs
|
||||||
f = self.form
|
f = self.form
|
||||||
f.newOrder.addItems(list(cs.newCardOrderLabels().values()))
|
f.newOrder.addItems(list(cs.newCardOrderLabels().values()))
|
||||||
self.connect(f.newOrder, SIGNAL("currentIndexChanged(int)"),
|
f.newOrder.currentIndexChanged.connect(self.onNewOrderChanged)
|
||||||
self.onNewOrderChanged)
|
|
||||||
|
|
||||||
# Conf list
|
# Conf list
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def setupConfs(self):
|
def setupConfs(self):
|
||||||
self.connect(self.form.dconf, SIGNAL("currentIndexChanged(int)"),
|
self.form.dconf.currentIndexChanged.connect(self.onConfChange)
|
||||||
self.onConfChange)
|
|
||||||
self.conf = None
|
self.conf = None
|
||||||
self.loadConfs()
|
self.loadConfs()
|
||||||
|
|
||||||
@ -75,13 +69,13 @@ class DeckConf(QDialog):
|
|||||||
def confOpts(self):
|
def confOpts(self):
|
||||||
m = QMenu(self.mw)
|
m = QMenu(self.mw)
|
||||||
a = m.addAction(_("Add"))
|
a = m.addAction(_("Add"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.addGroup)
|
a.triggered.connect(self.addGroup)
|
||||||
a = m.addAction(_("Delete"))
|
a = m.addAction(_("Delete"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.remGroup)
|
a.triggered.connect(self.remGroup)
|
||||||
a = m.addAction(_("Rename"))
|
a = m.addAction(_("Rename"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.renameGroup)
|
a.triggered.connect(self.renameGroup)
|
||||||
a = m.addAction(_("Set for all subdecks"))
|
a = m.addAction(_("Set for all subdecks"))
|
||||||
a.connect(a, SIGNAL("triggered()"), self.setChildren)
|
a.triggered.connect(self.setChildren)
|
||||||
if not self.childDids:
|
if not self.childDids:
|
||||||
a.setEnabled(False)
|
a.setEnabled(False)
|
||||||
m.exec_(QCursor.pos())
|
m.exec_(QCursor.pos())
|
||||||
|
@ -28,7 +28,7 @@ def download(mw, code):
|
|||||||
# unsure why this is happening, but guard against throwing the
|
# unsure why this is happening, but guard against throwing the
|
||||||
# error
|
# error
|
||||||
pass
|
pass
|
||||||
mw.connect(thread, SIGNAL("recv"), onRecv)
|
thread.recv.connect(onRecv)
|
||||||
thread.start()
|
thread.start()
|
||||||
mw.progress.start(immediate=True)
|
mw.progress.start(immediate=True)
|
||||||
while not thread.isFinished():
|
while not thread.isFinished():
|
||||||
@ -43,6 +43,8 @@ def download(mw, code):
|
|||||||
|
|
||||||
class Downloader(QThread):
|
class Downloader(QThread):
|
||||||
|
|
||||||
|
recv = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, code):
|
def __init__(self, code):
|
||||||
QThread.__init__(self)
|
QThread.__init__(self)
|
||||||
self.code = code
|
self.code = code
|
||||||
@ -59,7 +61,7 @@ class Downloader(QThread):
|
|||||||
def recvEvent(bytes):
|
def recvEvent(bytes):
|
||||||
self.recvTotal += bytes
|
self.recvTotal += bytes
|
||||||
if canPost():
|
if canPost():
|
||||||
self.emit(SIGNAL("recv"))
|
self.recv.emit()
|
||||||
addHook("httpRecv", recvEvent)
|
addHook("httpRecv", recvEvent)
|
||||||
con = httpCon()
|
con = httpCon()
|
||||||
try:
|
try:
|
||||||
|
@ -22,9 +22,7 @@ class DeckConf(QDialog):
|
|||||||
label, QDialogButtonBox.AcceptRole)
|
label, QDialogButtonBox.AcceptRole)
|
||||||
self.mw.checkpoint(_("Options"))
|
self.mw.checkpoint(_("Options"))
|
||||||
self.setWindowModality(Qt.WindowModal)
|
self.setWindowModality(Qt.WindowModal)
|
||||||
self.connect(self.form.buttonBox,
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp("filtered"))
|
||||||
SIGNAL("helpRequested()"),
|
|
||||||
lambda: openHelp("filtered"))
|
|
||||||
self.setWindowTitle(_("Options for %s") % self.deck['name'])
|
self.setWindowTitle(_("Options for %s") % self.deck['name'])
|
||||||
restoreGeom(self, "dyndeckconf")
|
restoreGeom(self, "dyndeckconf")
|
||||||
self.setupOrder()
|
self.setupOrder()
|
||||||
|
@ -23,9 +23,7 @@ class EditCurrent(QDialog):
|
|||||||
self.setWindowTitle(_("Edit Current"))
|
self.setWindowTitle(_("Edit Current"))
|
||||||
self.setMinimumHeight(400)
|
self.setMinimumHeight(400)
|
||||||
self.setMinimumWidth(500)
|
self.setMinimumWidth(500)
|
||||||
self.connect(self,
|
self.rejected.connect(self.onSave)
|
||||||
SIGNAL("rejected()"),
|
|
||||||
self.onSave)
|
|
||||||
self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut(
|
self.form.buttonBox.button(QDialogButtonBox.Close).setShortcut(
|
||||||
QKeySequence("Ctrl+Return"))
|
QKeySequence("Ctrl+Return"))
|
||||||
self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self)
|
self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea, self)
|
||||||
|
@ -1,22 +1,30 @@
|
|||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import sys
|
import sys, traceback
|
||||||
import cgi
|
import cgi
|
||||||
|
|
||||||
from anki.lang import _
|
from anki.lang import _
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.utils import showText, showWarning
|
from aqt.utils import showText, showWarning
|
||||||
|
|
||||||
|
def excepthook(etype,val,tb):
|
||||||
|
sys.stderr.write("Caught exception:\n%s%s\n" % (
|
||||||
|
''.join(traceback.format_tb(tb)),
|
||||||
|
'{0}: {1}'.format(etype, val)))
|
||||||
|
sys.excepthook = excepthook
|
||||||
|
|
||||||
class ErrorHandler(QObject):
|
class ErrorHandler(QObject):
|
||||||
"Catch stderr and write into buffer."
|
"Catch stderr and write into buffer."
|
||||||
ivl = 100
|
ivl = 100
|
||||||
|
|
||||||
|
errorTimer = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, mw):
|
def __init__(self, mw):
|
||||||
QObject.__init__(self, mw)
|
QObject.__init__(self, mw)
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.timer = None
|
self.timer = None
|
||||||
self.connect(self, SIGNAL("errorTimer"), self._setTimer)
|
self.errorTimer.connect(self._setTimer)
|
||||||
self.pool = ""
|
self.pool = ""
|
||||||
sys.stderr = self
|
sys.stderr = self
|
||||||
|
|
||||||
@ -31,12 +39,12 @@ class ErrorHandler(QObject):
|
|||||||
def setTimer(self):
|
def setTimer(self):
|
||||||
# we can't create a timer from a different thread, so we post a
|
# we can't create a timer from a different thread, so we post a
|
||||||
# message to the object on the main thread
|
# message to the object on the main thread
|
||||||
self.emit(SIGNAL("errorTimer"))
|
self.errorTimer.emit()
|
||||||
|
|
||||||
def _setTimer(self):
|
def _setTimer(self):
|
||||||
if not self.timer:
|
if not self.timer:
|
||||||
self.timer = QTimer(self.mw)
|
self.timer = QTimer(self.mw)
|
||||||
self.mw.connect(self.timer, SIGNAL("timeout()"), self.onTimeout)
|
self.timer.timeout.connect(self.onTimeout)
|
||||||
self.timer.setInterval(self.ivl)
|
self.timer.setInterval(self.ivl)
|
||||||
self.timer.setSingleShot(True)
|
self.timer.setSingleShot(True)
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
@ -57,7 +65,7 @@ Anki manual for more information.""")
|
|||||||
return showWarning(_("Please install PyAudio"))
|
return showWarning(_("Please install PyAudio"))
|
||||||
if "install mplayer" in error:
|
if "install mplayer" in error:
|
||||||
return showWarning(_("Please install mplayer"))
|
return showWarning(_("Please install mplayer"))
|
||||||
if "no default output" in error:
|
if "no default input" in error.lower():
|
||||||
return showWarning(_("Please connect a microphone, and ensure "
|
return showWarning(_("Please connect a microphone, and ensure "
|
||||||
"other programs are not using the audio device."))
|
"other programs are not using the audio device."))
|
||||||
if "invalidTempFolder" in error:
|
if "invalidTempFolder" in error:
|
||||||
|
@ -26,9 +26,8 @@ class ExportDialog(QDialog):
|
|||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
def setup(self, did):
|
def setup(self, did):
|
||||||
self.frm.format.insertItems(0, list(zip(*exporters())[0]))
|
self.frm.format.insertItems(0, list(zip(*exporters()))[0])
|
||||||
self.connect(self.frm.format, SIGNAL("activated(int)"),
|
self.frm.format.activated.connect(self.exporterChanged)
|
||||||
self.exporterChanged)
|
|
||||||
self.exporterChanged(0)
|
self.exporterChanged(0)
|
||||||
self.decks = [_("All Decks")] + sorted(self.col.decks.allNames())
|
self.decks = [_("All Decks")] + sorted(self.col.decks.allNames())
|
||||||
self.frm.deck.addItems(self.decks)
|
self.frm.deck.addItems(self.decks)
|
||||||
|
@ -38,16 +38,14 @@ class FieldDialog(QDialog):
|
|||||||
self.form.fieldList.addItem(f['name'])
|
self.form.fieldList.addItem(f['name'])
|
||||||
|
|
||||||
def setupSignals(self):
|
def setupSignals(self):
|
||||||
c = self.connect
|
|
||||||
s = SIGNAL
|
|
||||||
f = self.form
|
f = self.form
|
||||||
c(f.fieldList, s("currentRowChanged(int)"), self.onRowChange)
|
f.fieldList.currentRowChanged.connect(self.onRowChange)
|
||||||
c(f.fieldAdd, s("clicked()"), self.onAdd)
|
f.fieldAdd.clicked.connect(self.onAdd)
|
||||||
c(f.fieldDelete, s("clicked()"), self.onDelete)
|
f.fieldDelete.clicked.connect(self.onDelete)
|
||||||
c(f.fieldRename, s("clicked()"), self.onRename)
|
f.fieldRename.clicked.connect(self.onRename)
|
||||||
c(f.fieldPosition, s("clicked()"), self.onPosition)
|
f.fieldPosition.clicked.connect(self.onPosition)
|
||||||
c(f.sortField, s("clicked()"), self.onSortField)
|
f.sortField.clicked.connect(self.onSortField)
|
||||||
c(f.buttonBox, s("helpRequested()"), self.onHelp)
|
f.buttonBox.helpRequested.connect(self.onHelp)
|
||||||
|
|
||||||
def onRowChange(self, idx):
|
def onRowChange(self, idx):
|
||||||
if idx == -1:
|
if idx == -1:
|
||||||
|
@ -69,15 +69,14 @@ class ImportDialog(QDialog):
|
|||||||
self.importer = importer
|
self.importer = importer
|
||||||
self.frm = aqt.forms.importing.Ui_ImportDialog()
|
self.frm = aqt.forms.importing.Ui_ImportDialog()
|
||||||
self.frm.setupUi(self)
|
self.frm.setupUi(self)
|
||||||
self.connect(self.frm.buttonBox.button(QDialogButtonBox.Help),
|
self.frm.buttonBox.button(QDialogButtonBox.Help).clicked.connect(
|
||||||
SIGNAL("clicked()"), self.helpRequested)
|
self.helpRequested)
|
||||||
self.setupMappingFrame()
|
self.setupMappingFrame()
|
||||||
self.setupOptions()
|
self.setupOptions()
|
||||||
self.modelChanged()
|
self.modelChanged()
|
||||||
self.frm.autoDetect.setVisible(self.importer.needDelimiter)
|
self.frm.autoDetect.setVisible(self.importer.needDelimiter)
|
||||||
addHook("currentModelChanged", self.modelChanged)
|
addHook("currentModelChanged", self.modelChanged)
|
||||||
self.connect(self.frm.autoDetect, SIGNAL("clicked()"),
|
self.frm.autoDetect.clicked.connect(self.onDelimiter)
|
||||||
self.onDelimiter)
|
|
||||||
self.updateDelimiterButtonText()
|
self.updateDelimiterButtonText()
|
||||||
self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML', True))
|
self.frm.allowHTML.setChecked(self.mw.pm.profile.get('allowHTML', True))
|
||||||
self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode', 1))
|
self.frm.importMode.setCurrentIndex(self.mw.pm.profile.get('importMode', 1))
|
||||||
@ -211,7 +210,7 @@ you can enter it here. Use \\t to represent tab."""),
|
|||||||
self.mapbox.addWidget(self.mapwidget)
|
self.mapbox.addWidget(self.mapwidget)
|
||||||
self.grid = QGridLayout(self.mapwidget)
|
self.grid = QGridLayout(self.mapwidget)
|
||||||
self.mapwidget.setLayout(self.grid)
|
self.mapwidget.setLayout(self.grid)
|
||||||
self.grid.setMargin(3)
|
self.grid.setContentsMargins(3,3,3,3)
|
||||||
self.grid.setSpacing(6)
|
self.grid.setSpacing(6)
|
||||||
fields = self.importer.fields()
|
fields = self.importer.fields()
|
||||||
for num in range(len(self.mapping)):
|
for num in range(len(self.mapping)):
|
||||||
@ -226,8 +225,7 @@ you can enter it here. Use \\t to represent tab."""),
|
|||||||
self.grid.addWidget(QLabel(text), num, 1)
|
self.grid.addWidget(QLabel(text), num, 1)
|
||||||
button = QPushButton(_("Change"))
|
button = QPushButton(_("Change"))
|
||||||
self.grid.addWidget(button, num, 2)
|
self.grid.addWidget(button, num, 2)
|
||||||
self.connect(button, SIGNAL("clicked()"),
|
button.clicked.connect(lambda s=self,n=num: s.changeMappingNum(n))
|
||||||
lambda s=self,n=num: s.changeMappingNum(n))
|
|
||||||
|
|
||||||
def changeMappingNum(self, n):
|
def changeMappingNum(self, n):
|
||||||
f = ChangeMap(self.mw, self.importer.model, self.mapping[n]).getField()
|
f = ChangeMap(self.mw, self.importer.model, self.mapping[n]).getField()
|
||||||
|
99
aqt/main.py
99
aqt/main.py
@ -59,8 +59,6 @@ class AnkiQt(QMainWindow):
|
|||||||
self.onAppMsg(args[0])
|
self.onAppMsg(args[0])
|
||||||
# Load profile in a timer so we can let the window finish init and not
|
# Load profile in a timer so we can let the window finish init and not
|
||||||
# close on profile load error.
|
# close on profile load error.
|
||||||
if isMac and qtmajor >= 5:
|
|
||||||
self.show()
|
|
||||||
self.progress.timer(10, self.setupProfile, False)
|
self.progress.timer(10, self.setupProfile, False)
|
||||||
|
|
||||||
def setupUI(self):
|
def setupUI(self):
|
||||||
@ -110,16 +108,14 @@ class AnkiQt(QMainWindow):
|
|||||||
d = self.profileDiag = QDialog()
|
d = self.profileDiag = QDialog()
|
||||||
f = self.profileForm = aqt.forms.profiles.Ui_Dialog()
|
f = self.profileForm = aqt.forms.profiles.Ui_Dialog()
|
||||||
f.setupUi(d)
|
f.setupUi(d)
|
||||||
d.connect(f.login, SIGNAL("clicked()"), self.onOpenProfile)
|
f.login.clicked.connect(self.onOpenProfile)
|
||||||
d.connect(f.profiles, SIGNAL("itemDoubleClicked(QListWidgetItem*)"),
|
f.profiles.itemDoubleClicked.connect(self.onOpenProfile)
|
||||||
self.onOpenProfile)
|
f.quit.clicked.connect(lambda: sys.exit(0))
|
||||||
d.connect(f.quit, SIGNAL("clicked()"), lambda: sys.exit(0))
|
f.add.clicked.connect(self.onAddProfile)
|
||||||
d.connect(f.add, SIGNAL("clicked()"), self.onAddProfile)
|
f.rename.clicked.connect(self.onRenameProfile)
|
||||||
d.connect(f.rename, SIGNAL("clicked()"), self.onRenameProfile)
|
f.delete_2.clicked.connect(self.onRemProfile)
|
||||||
d.connect(f.delete_2, SIGNAL("clicked()"), self.onRemProfile)
|
d.rejected.connect(d.close)
|
||||||
d.connect(d, SIGNAL("rejected()"), lambda: d.close())
|
f.profiles.currentRowChanged.connect(self.onProfileRowChange)
|
||||||
d.connect(f.profiles, SIGNAL("currentRowChanged(int)"),
|
|
||||||
self.onProfileRowChange)
|
|
||||||
self.refreshProfilesList()
|
self.refreshProfilesList()
|
||||||
# raise first, for osx testing
|
# raise first, for osx testing
|
||||||
d.show()
|
d.show()
|
||||||
@ -459,7 +455,8 @@ the manual for information on how to restore from an automatic backup."))
|
|||||||
if self.resetModal:
|
if self.resetModal:
|
||||||
# we don't have to change the webview, as we have a covering window
|
# we don't have to change the webview, as we have a covering window
|
||||||
return
|
return
|
||||||
self.web.setLinkHandler(lambda url: self.delayedMaybeReset())
|
self.web.resetHandlers()
|
||||||
|
self.web.onAnkiLink = lambda url: self.delayedMaybeReset()
|
||||||
i = _("Waiting for editing to finish.")
|
i = _("Waiting for editing to finish.")
|
||||||
b = self.button("refresh", _("Resume Now"), id="resume")
|
b = self.button("refresh", _("Resume Now"), id="resume")
|
||||||
self.web.stdHtml("""
|
self.web.stdHtml("""
|
||||||
@ -483,16 +480,16 @@ margin: 2em;
|
|||||||
h1 { margin-bottom: 0.2em; }
|
h1 { margin-bottom: 0.2em; }
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def button(self, link, name, key=None, class_="", id=""):
|
def button(self, link, name, key=None, class_="", id="", extra=""):
|
||||||
class_ = "but "+ class_
|
class_ = "but "+ class_
|
||||||
if key:
|
if key:
|
||||||
key = _("Shortcut key: %s") % key
|
key = _("Shortcut key: %s") % key
|
||||||
else:
|
else:
|
||||||
key = ""
|
key = ""
|
||||||
return '''
|
return '''
|
||||||
<button id="%s" class="%s" onclick="py.link('%s');return false;"
|
<button id="%s" class="%s" onclick="openAnkiLink('%s');return false;"
|
||||||
title="%s">%s</button>''' % (
|
title="%s" %s>%s</button>''' % (
|
||||||
id, class_, link, key, name)
|
id, class_, link, key, extra, name)
|
||||||
|
|
||||||
# Main window setup
|
# Main window setup
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -615,8 +612,7 @@ title="%s">%s</button>''' % (
|
|||||||
self.keyHandler = None
|
self.keyHandler = None
|
||||||
# debug shortcut
|
# debug shortcut
|
||||||
self.debugShortcut = QShortcut(QKeySequence("Ctrl+:"), self)
|
self.debugShortcut = QShortcut(QKeySequence("Ctrl+:"), self)
|
||||||
self.connect(
|
self.debugShortcut.activated.connect(self.onDebug)
|
||||||
self.debugShortcut, SIGNAL("activated()"), self.onDebug)
|
|
||||||
|
|
||||||
def keyPressEvent(self, evt):
|
def keyPressEvent(self, evt):
|
||||||
# do we have a delegate?
|
# do we have a delegate?
|
||||||
@ -791,23 +787,21 @@ title="%s">%s</button>''' % (
|
|||||||
|
|
||||||
def setupMenus(self):
|
def setupMenus(self):
|
||||||
m = self.form
|
m = self.form
|
||||||
s = SIGNAL("triggered()")
|
m.actionSwitchProfile.triggered.connect(lambda b: self.unloadProfile())
|
||||||
#self.connect(m.actionDownloadSharedPlugin, s, self.onGetSharedPlugin)
|
m.actionImport.triggered.connect(self.onImport)
|
||||||
self.connect(m.actionSwitchProfile, s, self.unloadProfile)
|
m.actionExport.triggered.connect(self.onExport)
|
||||||
self.connect(m.actionImport, s, self.onImport)
|
m.actionExit.triggered.connect(self.close)
|
||||||
self.connect(m.actionExport, s, self.onExport)
|
m.actionPreferences.triggered.connect(self.onPrefs)
|
||||||
self.connect(m.actionExit, s, self, SLOT("close()"))
|
m.actionAbout.triggered.connect(self.onAbout)
|
||||||
self.connect(m.actionPreferences, s, self.onPrefs)
|
m.actionUndo.triggered.connect(self.onUndo)
|
||||||
self.connect(m.actionAbout, s, self.onAbout)
|
m.actionFullDatabaseCheck.triggered.connect(self.onCheckDB)
|
||||||
self.connect(m.actionUndo, s, self.onUndo)
|
m.actionCheckMediaDatabase.triggered.connect(self.onCheckMediaDB)
|
||||||
self.connect(m.actionFullDatabaseCheck, s, self.onCheckDB)
|
m.actionDocumentation.triggered.connect(self.onDocumentation)
|
||||||
self.connect(m.actionCheckMediaDatabase, s, self.onCheckMediaDB)
|
m.actionDonate.triggered.connect(self.onDonate)
|
||||||
self.connect(m.actionDocumentation, s, self.onDocumentation)
|
m.actionStudyDeck.triggered.connect(self.onStudyDeck)
|
||||||
self.connect(m.actionDonate, s, self.onDonate)
|
m.actionCreateFiltered.triggered.connect(self.onCram)
|
||||||
self.connect(m.actionStudyDeck, s, self.onStudyDeck)
|
m.actionEmptyCards.triggered.connect(self.onEmptyCards)
|
||||||
self.connect(m.actionCreateFiltered, s, self.onCram)
|
m.actionNoteTypes.triggered.connect(self.onNoteTypes)
|
||||||
self.connect(m.actionEmptyCards, s, self.onEmptyCards)
|
|
||||||
self.connect(m.actionNoteTypes, s, self.onNoteTypes)
|
|
||||||
|
|
||||||
def updateTitleBar(self):
|
def updateTitleBar(self):
|
||||||
self.setWindowTitle("Anki")
|
self.setWindowTitle("Anki")
|
||||||
@ -818,9 +812,9 @@ title="%s">%s</button>''' % (
|
|||||||
def setupAutoUpdate(self):
|
def setupAutoUpdate(self):
|
||||||
import aqt.update
|
import aqt.update
|
||||||
self.autoUpdate = aqt.update.LatestVersionFinder(self)
|
self.autoUpdate = aqt.update.LatestVersionFinder(self)
|
||||||
self.connect(self.autoUpdate, SIGNAL("newVerAvail"), self.newVerAvail)
|
self.autoUpdate.newVerAvail.connect(self.newVerAvail)
|
||||||
self.connect(self.autoUpdate, SIGNAL("newMsg"), self.newMsg)
|
self.autoUpdate.newMsg.connect(self.newMsg)
|
||||||
self.connect(self.autoUpdate, SIGNAL("clockIsOff"), self.clockIsOff)
|
self.autoUpdate.clockIsOff.connect(self.clockIsOff)
|
||||||
self.autoUpdate.start()
|
self.autoUpdate.start()
|
||||||
|
|
||||||
def newVerAvail(self, ver):
|
def newVerAvail(self, ver):
|
||||||
@ -889,7 +883,7 @@ and if the problem comes up again, please ask on the support site."""))
|
|||||||
"select id, mid, flds from notes where id in %s" %
|
"select id, mid, flds from notes where id in %s" %
|
||||||
ids2str(nids)):
|
ids2str(nids)):
|
||||||
fields = splitFields(flds)
|
fields = splitFields(flds)
|
||||||
f.write(("\t".join([str(id), str(mid)] + fields)).encode("utf8"))
|
f.write(("\t".join([str(id), str(mid)] + fields)))
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
# Schema modifications
|
# Schema modifications
|
||||||
@ -955,9 +949,9 @@ will be lost. Continue?"""))
|
|||||||
b = QPushButton(_("Delete Unused"))
|
b = QPushButton(_("Delete Unused"))
|
||||||
b.setAutoDefault(False)
|
b.setAutoDefault(False)
|
||||||
box.addButton(b, QDialogButtonBox.ActionRole)
|
box.addButton(b, QDialogButtonBox.ActionRole)
|
||||||
b.connect(
|
b.clicked.connect(
|
||||||
b, SIGNAL("clicked()"), lambda u=unused, d=diag: self.deleteUnused(u, d))
|
lambda c, u=unused, d=diag: self.deleteUnused(u, d))
|
||||||
diag.connect(box, SIGNAL("rejected()"), diag, SLOT("reject()"))
|
box.rejected.connect(diag.reject)
|
||||||
diag.setMinimumHeight(400)
|
diag.setMinimumHeight(400)
|
||||||
diag.setMinimumWidth(500)
|
diag.setMinimumWidth(500)
|
||||||
restoreGeom(diag, "checkmediadb")
|
restoreGeom(diag, "checkmediadb")
|
||||||
@ -1006,7 +1000,7 @@ will be lost. Continue?"""))
|
|||||||
self.col.remCards(cids)
|
self.col.remCards(cids)
|
||||||
tooltip(ngettext("%d card deleted.", "%d cards deleted.", len(cids)) % len(cids))
|
tooltip(ngettext("%d card deleted.", "%d cards deleted.", len(cids)) % len(cids))
|
||||||
self.reset()
|
self.reset()
|
||||||
diag.connect(box, SIGNAL("accepted()"), onDelete)
|
box.accepted.connect(onDelete)
|
||||||
diag.show()
|
diag.show()
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
@ -1017,12 +1011,10 @@ will be lost. Continue?"""))
|
|||||||
frm = aqt.forms.debug.Ui_Dialog()
|
frm = aqt.forms.debug.Ui_Dialog()
|
||||||
frm.setupUi(d)
|
frm.setupUi(d)
|
||||||
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+return"), d)
|
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+return"), d)
|
||||||
self.connect(s, SIGNAL("activated()"),
|
s.activated.connect(lambda: self.onDebugRet(frm))
|
||||||
lambda: self.onDebugRet(frm))
|
|
||||||
s = self.debugDiagShort = QShortcut(
|
s = self.debugDiagShort = QShortcut(
|
||||||
QKeySequence("ctrl+shift+return"), d)
|
QKeySequence("ctrl+shift+return"), d)
|
||||||
self.connect(s, SIGNAL("activated()"),
|
s.activated.connect(lambda: self.onDebugPrint(frm))
|
||||||
lambda: self.onDebugPrint(frm))
|
|
||||||
d.show()
|
d.show()
|
||||||
|
|
||||||
def _captureOutput(self, on):
|
def _captureOutput(self, on):
|
||||||
@ -1080,6 +1072,12 @@ will be lost. Continue?"""))
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def setupFonts(self):
|
def setupFonts(self):
|
||||||
|
print("fixme: setupFonts()")
|
||||||
|
self.fontHeight = 12
|
||||||
|
self.fontFamily = "arial"
|
||||||
|
self.fontHeightDelta = 0
|
||||||
|
|
||||||
|
return
|
||||||
f = QFontInfo(self.font())
|
f = QFontInfo(self.font())
|
||||||
ws = QWebSettings.globalSettings()
|
ws = QWebSettings.globalSettings()
|
||||||
self.fontHeight = f.pixelSize()
|
self.fontHeight = f.pixelSize()
|
||||||
@ -1093,8 +1091,7 @@ will be lost. Continue?"""))
|
|||||||
if isMac:
|
if isMac:
|
||||||
# mac users expect a minimize option
|
# mac users expect a minimize option
|
||||||
self.minimizeShortcut = QShortcut("Ctrl+M", self)
|
self.minimizeShortcut = QShortcut("Ctrl+M", self)
|
||||||
self.connect(self.minimizeShortcut, SIGNAL("activated()"),
|
self.minimizeShortcut.activated.connect(self.onMacMinimize)
|
||||||
self.onMacMinimize)
|
|
||||||
self.hideMenuAccels = True
|
self.hideMenuAccels = True
|
||||||
self.maybeHideAccelerators()
|
self.maybeHideAccelerators()
|
||||||
self.hideStatusTips()
|
self.hideStatusTips()
|
||||||
@ -1125,7 +1122,7 @@ will be lost. Continue?"""))
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def setupAppMsg(self):
|
def setupAppMsg(self):
|
||||||
self.connect(self.app, SIGNAL("appMsg"), self.onAppMsg)
|
self.app.appMsg.connect(self.onAppMsg)
|
||||||
|
|
||||||
def onAppMsg(self, buf):
|
def onAppMsg(self, buf):
|
||||||
if self.state == "startup":
|
if self.state == "startup":
|
||||||
|
@ -15,7 +15,7 @@ class ModelChooser(QHBoxLayout):
|
|||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.deck = mw.col
|
self.deck = mw.col
|
||||||
self.label = label
|
self.label = label
|
||||||
self.setMargin(0)
|
self.setContentsMargins(0,0,0,0)
|
||||||
self.setSpacing(8)
|
self.setSpacing(8)
|
||||||
self.setupModels()
|
self.setupModels()
|
||||||
addHook('reset', self.onReset)
|
addHook('reset', self.onReset)
|
||||||
@ -29,11 +29,10 @@ class ModelChooser(QHBoxLayout):
|
|||||||
self.models = QPushButton()
|
self.models = QPushButton()
|
||||||
#self.models.setStyleSheet("* { text-align: left; }")
|
#self.models.setStyleSheet("* { text-align: left; }")
|
||||||
self.models.setToolTip(shortcut(_("Change Note Type (Ctrl+N)")))
|
self.models.setToolTip(shortcut(_("Change Note Type (Ctrl+N)")))
|
||||||
s = QShortcut(QKeySequence(_("Ctrl+N")), self.widget)
|
s = QShortcut(QKeySequence(_("Ctrl+N")), self.widget, activated=self.onModelChange)
|
||||||
s.connect(s, SIGNAL("activated()"), self.onModelChange)
|
|
||||||
self.models.setAutoDefault(False)
|
self.models.setAutoDefault(False)
|
||||||
self.addWidget(self.models)
|
self.addWidget(self.models)
|
||||||
self.connect(self.models, SIGNAL("clicked()"), self.onModelChange)
|
self.models.clicked.connect(self.onModelChange)
|
||||||
# layout
|
# layout
|
||||||
sizePolicy = QSizePolicy(
|
sizePolicy = QSizePolicy(
|
||||||
QSizePolicy.Policy(7),
|
QSizePolicy.Policy(7),
|
||||||
@ -61,8 +60,7 @@ class ModelChooser(QHBoxLayout):
|
|||||||
from aqt.studydeck import StudyDeck
|
from aqt.studydeck import StudyDeck
|
||||||
current = self.deck.models.current()['name']
|
current = self.deck.models.current()['name']
|
||||||
# edit button
|
# edit button
|
||||||
edit = QPushButton(_("Manage"))
|
edit = QPushButton(_("Manage"), clicked=self.onEdit)
|
||||||
self.connect(edit, SIGNAL("clicked()"), self.onEdit)
|
|
||||||
def nameFunc():
|
def nameFunc():
|
||||||
return sorted(self.deck.models.allNames())
|
return sorted(self.deck.models.allNames())
|
||||||
ret = StudyDeck(
|
ret = StudyDeck(
|
||||||
|
@ -20,8 +20,7 @@ class Models(QDialog):
|
|||||||
self.mw.checkpoint(_("Note Types"))
|
self.mw.checkpoint(_("Note Types"))
|
||||||
self.form = aqt.forms.models.Ui_Dialog()
|
self.form = aqt.forms.models.Ui_Dialog()
|
||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp("notetypes"))
|
||||||
lambda: openHelp("notetypes"))
|
|
||||||
self.setupModels()
|
self.setupModels()
|
||||||
restoreGeom(self, "models")
|
restoreGeom(self, "models")
|
||||||
self.exec_()
|
self.exec_()
|
||||||
@ -31,25 +30,23 @@ class Models(QDialog):
|
|||||||
|
|
||||||
def setupModels(self):
|
def setupModels(self):
|
||||||
self.model = None
|
self.model = None
|
||||||
c = self.connect; f = self.form; box = f.buttonBox
|
f = self.form; box = f.buttonBox
|
||||||
s = SIGNAL("clicked()")
|
|
||||||
t = QDialogButtonBox.ActionRole
|
t = QDialogButtonBox.ActionRole
|
||||||
b = box.addButton(_("Add"), t)
|
b = box.addButton(_("Add"), t)
|
||||||
c(b, s, self.onAdd)
|
b.clicked.connect(self.onAdd)
|
||||||
b = box.addButton(_("Rename"), t)
|
b = box.addButton(_("Rename"), t)
|
||||||
c(b, s, self.onRename)
|
b.clicked.connect(self.onRename)
|
||||||
b = box.addButton(_("Delete"), t)
|
b = box.addButton(_("Delete"), t)
|
||||||
c(b, s, self.onDelete)
|
b.clicked.connect(self.onDelete)
|
||||||
if self.fromMain:
|
if self.fromMain:
|
||||||
b = box.addButton(_("Fields..."), t)
|
b = box.addButton(_("Fields..."), t)
|
||||||
c(b, s, self.onFields)
|
b.clicked.connect(self.onFields)
|
||||||
b = box.addButton(_("Cards..."), t)
|
b = box.addButton(_("Cards..."), t)
|
||||||
c(b, s, self.onCards)
|
b.clicked.connect(self.onCards)
|
||||||
b = box.addButton(_("Options..."), t)
|
b = box.addButton(_("Options..."), t)
|
||||||
c(b, s, self.onAdvanced)
|
b.clicked.connect(self.onAdvanced)
|
||||||
c(f.modelsList, SIGNAL("currentRowChanged(int)"), self.modelChanged)
|
f.modelsList.currentRowChanged.connect(self.modelChanged)
|
||||||
c(f.modelsList, SIGNAL("itemDoubleClicked(QListWidgetItem*)"),
|
f.modelsList.itemDoubleClicked.connect(self.onRename)
|
||||||
self.onRename)
|
|
||||||
self.updateModelsList()
|
self.updateModelsList()
|
||||||
f.modelsList.setCurrentRow(0)
|
f.modelsList.setCurrentRow(0)
|
||||||
maybeHideClose(box)
|
maybeHideClose(box)
|
||||||
@ -113,9 +110,7 @@ class Models(QDialog):
|
|||||||
frm.latexHeader.setText(self.model['latexPre'])
|
frm.latexHeader.setText(self.model['latexPre'])
|
||||||
frm.latexFooter.setText(self.model['latexPost'])
|
frm.latexFooter.setText(self.model['latexPost'])
|
||||||
d.setWindowTitle(_("Options for %s") % self.model['name'])
|
d.setWindowTitle(_("Options for %s") % self.model['name'])
|
||||||
self.connect(
|
frm.buttonBox.helpRequested.connect(lambda: openHelp("latex"))
|
||||||
frm.buttonBox, SIGNAL("helpRequested()"),
|
|
||||||
lambda: openHelp("latex"))
|
|
||||||
restoreGeom(d, "modelopts")
|
restoreGeom(d, "modelopts")
|
||||||
d.exec_()
|
d.exec_()
|
||||||
saveGeom(d, "modelopts")
|
saveGeom(d, "modelopts")
|
||||||
@ -185,9 +180,9 @@ class AddModel(QDialog):
|
|||||||
self.dialog.models.setCurrentRow(0)
|
self.dialog.models.setCurrentRow(0)
|
||||||
# the list widget will swallow the enter key
|
# the list widget will swallow the enter key
|
||||||
s = QShortcut(QKeySequence("Return"), self)
|
s = QShortcut(QKeySequence("Return"), self)
|
||||||
self.connect(s, SIGNAL("activated()"), self.accept)
|
s.activated.connect(self.accept)
|
||||||
# help
|
# help
|
||||||
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), self.onHelp)
|
self.dialog.buttonBox.helpRequested.connect(self.onHelp)
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
self.exec_()
|
self.exec_()
|
||||||
|
@ -17,16 +17,16 @@ class Overview(object):
|
|||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
clearAudioQueue()
|
clearAudioQueue()
|
||||||
self.web.setLinkHandler(self._linkHandler)
|
self.web.resetHandlers()
|
||||||
self.web.setKeyHandler(None)
|
self.web.onAnkiLink = self._linkHandler
|
||||||
self.mw.keyHandler = self._keyHandler
|
self.mw.keyHandler = self._keyHandler
|
||||||
self.mw.web.setFocus()
|
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.mw.col.reset()
|
self.mw.col.reset()
|
||||||
self._renderPage()
|
self._renderPage()
|
||||||
self._renderBottom()
|
self._renderBottom()
|
||||||
|
self.mw.web.setFocus()
|
||||||
|
|
||||||
# Handlers
|
# Handlers
|
||||||
############################################################
|
############################################################
|
||||||
@ -61,6 +61,7 @@ class Overview(object):
|
|||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
elif url.lower().startswith("http"):
|
elif url.lower().startswith("http"):
|
||||||
openLink(url)
|
openLink(url)
|
||||||
|
return False
|
||||||
|
|
||||||
def _keyHandler(self, evt):
|
def _keyHandler(self, evt):
|
||||||
cram = self.mw.col.decks.current()['dyn']
|
cram = self.mw.col.decks.current()['dyn']
|
||||||
@ -143,7 +144,7 @@ to their original deck.""")
|
|||||||
_("New"), counts[0],
|
_("New"), counts[0],
|
||||||
_("Learning"), counts[1],
|
_("Learning"), counts[1],
|
||||||
_("To Review"), counts[2],
|
_("To Review"), counts[2],
|
||||||
but("study", _("Study Now"), id="study"))
|
but("study", _("Study Now"), id="study",extra=" autofocus"))
|
||||||
|
|
||||||
|
|
||||||
_body = """
|
_body = """
|
||||||
@ -198,14 +199,14 @@ text-align: center;
|
|||||||
if b[0]:
|
if b[0]:
|
||||||
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
b[0] = _("Shortcut key: %s") % shortcut(b[0])
|
||||||
buf += """
|
buf += """
|
||||||
<button title="%s" onclick='py.link(\"%s\");'>%s</button>""" % tuple(b)
|
<button title="%s" onclick='openAnkiLink("%s")'>%s</button>""" % tuple(b)
|
||||||
self.bottom.draw(buf)
|
self.bottom.draw(buf)
|
||||||
if isMac:
|
if isMac:
|
||||||
size = 28
|
size = 28
|
||||||
else:
|
else:
|
||||||
size = 36 + self.mw.fontHeightDelta*3
|
size = 36 + self.mw.fontHeightDelta*3
|
||||||
self.bottom.web.setFixedHeight(size)
|
self.bottom.web.setFixedHeight(size)
|
||||||
self.bottom.web.setLinkHandler(self._linkHandler)
|
self.bottom.web.onAnkiLink = self._linkHandler
|
||||||
|
|
||||||
# Studying more
|
# Studying more
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -21,8 +21,7 @@ class Preferences(QDialog):
|
|||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
|
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
|
||||||
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
|
self.form.buttonBox.button(QDialogButtonBox.Close).setAutoDefault(False)
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp("profileprefs"))
|
||||||
lambda: openHelp("profileprefs"))
|
|
||||||
self.setupLang()
|
self.setupLang()
|
||||||
self.setupCollection()
|
self.setupCollection()
|
||||||
self.setupNetwork()
|
self.setupNetwork()
|
||||||
@ -52,8 +51,7 @@ class Preferences(QDialog):
|
|||||||
f = self.form
|
f = self.form
|
||||||
f.lang.addItems([x[0] for x in anki.lang.langs])
|
f.lang.addItems([x[0] for x in anki.lang.langs])
|
||||||
f.lang.setCurrentIndex(self.langIdx())
|
f.lang.setCurrentIndex(self.langIdx())
|
||||||
self.connect(f.lang, SIGNAL("currentIndexChanged(int)"),
|
f.lang.currentIndexChanged.connect(self.onLangIdxChanged)
|
||||||
self.onLangIdxChanged)
|
|
||||||
|
|
||||||
def langIdx(self):
|
def langIdx(self):
|
||||||
codes = [x[1] for x in anki.lang.langs]
|
codes = [x[1] for x in anki.lang.langs]
|
||||||
@ -113,8 +111,7 @@ class Preferences(QDialog):
|
|||||||
self._hideAuth()
|
self._hideAuth()
|
||||||
else:
|
else:
|
||||||
self.form.syncUser.setText(self.prof.get('syncUser', ""))
|
self.form.syncUser.setText(self.prof.get('syncUser', ""))
|
||||||
self.connect(self.form.syncDeauth, SIGNAL("clicked()"),
|
self.form.syncDeauth.clicked.connect(self.onSyncDeauth)
|
||||||
self.onSyncDeauth)
|
|
||||||
|
|
||||||
def _hideAuth(self):
|
def _hideAuth(self):
|
||||||
self.form.syncDeauth.setVisible(False)
|
self.form.syncDeauth.setVisible(False)
|
||||||
@ -141,9 +138,7 @@ Not currently enabled; click the sync button in the main window to enable."""))
|
|||||||
def setupBackup(self):
|
def setupBackup(self):
|
||||||
self.form.numBackups.setValue(self.prof['numBackups'])
|
self.form.numBackups.setValue(self.prof['numBackups'])
|
||||||
self.form.compressBackups.setChecked(self.prof.get("compressBackups", True))
|
self.form.compressBackups.setChecked(self.prof.get("compressBackups", True))
|
||||||
self.connect(self.form.openBackupFolder,
|
self.form.openBackupFolder.linkActivated.connect(self.onOpenBackup)
|
||||||
SIGNAL("linkActivated(QString)"),
|
|
||||||
self.onOpenBackup)
|
|
||||||
|
|
||||||
def onOpenBackup(self):
|
def onOpenBackup(self):
|
||||||
openFolder(self.mw.pm.backupFolder())
|
openFolder(self.mw.pm.backupFolder())
|
||||||
@ -158,9 +153,7 @@ Not currently enabled; click the sync button in the main window to enable."""))
|
|||||||
def setupOptions(self):
|
def setupOptions(self):
|
||||||
self.form.stripHTML.setChecked(self.prof['stripHTML'])
|
self.form.stripHTML.setChecked(self.prof['stripHTML'])
|
||||||
self.form.pastePNG.setChecked(self.prof.get("pastePNG", False))
|
self.form.pastePNG.setChecked(self.prof.get("pastePNG", False))
|
||||||
self.connect(
|
self.form.profilePass.clicked.connect(self.onProfilePass)
|
||||||
self.form.profilePass, SIGNAL("clicked()"),
|
|
||||||
self.onProfilePass)
|
|
||||||
|
|
||||||
def updateOptions(self):
|
def updateOptions(self):
|
||||||
self.prof['stripHTML'] = self.form.stripHTML.isChecked()
|
self.prof['stripHTML'] = self.form.stripHTML.isChecked()
|
||||||
|
@ -191,7 +191,7 @@ and no other programs are accessing your profile folders, then try again."""))
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
def addonFolder(self):
|
def addonFolder(self):
|
||||||
return self._ensureExists(os.path.join(self.base, "addons"))
|
return self._ensureExists(os.path.join(self.base, "addons21"))
|
||||||
|
|
||||||
def backupFolder(self):
|
def backupFolder(self):
|
||||||
return self._ensureExists(
|
return self._ensureExists(
|
||||||
@ -210,10 +210,7 @@ and no other programs are accessing your profile folders, then try again."""))
|
|||||||
|
|
||||||
def _defaultBase(self):
|
def _defaultBase(self):
|
||||||
if isWin:
|
if isWin:
|
||||||
if False: #qtmajor >= 5:
|
loc = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
|
||||||
loc = QStandardPaths.writeableLocation(QStandardPaths.DocumentsLocation)
|
|
||||||
else:
|
|
||||||
loc = QDesktopServices.storageLocation(QDesktopServices.DocumentsLocation)
|
|
||||||
return os.path.join(loc, "Anki")
|
return os.path.join(loc, "Anki")
|
||||||
elif isMac:
|
elif isMac:
|
||||||
return os.path.expanduser("~/Documents/Anki")
|
return os.path.expanduser("~/Documents/Anki")
|
||||||
@ -223,9 +220,9 @@ and no other programs are accessing your profile folders, then try again."""))
|
|||||||
if os.path.exists(p):
|
if os.path.exists(p):
|
||||||
return p
|
return p
|
||||||
else:
|
else:
|
||||||
loc = QDesktopServices.storageLocation(QDesktopServices.DocumentsLocation)
|
loc = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation)
|
||||||
if loc[:-1] == QDesktopServices.storageLocation(
|
if loc[:-1] == QStandardPaths.writableLocation(
|
||||||
QDesktopServices.HomeLocation):
|
QStandardPaths.HomeLocation):
|
||||||
# occasionally "documentsLocation" will return the home
|
# occasionally "documentsLocation" will return the home
|
||||||
# folder because the Documents folder isn't configured
|
# folder because the Documents folder isn't configured
|
||||||
# properly; fall back to an English path
|
# properly; fall back to an English path
|
||||||
@ -234,7 +231,7 @@ and no other programs are accessing your profile folders, then try again."""))
|
|||||||
return os.path.join(loc, "Anki")
|
return os.path.join(loc, "Anki")
|
||||||
|
|
||||||
def _loadMeta(self):
|
def _loadMeta(self):
|
||||||
path = os.path.join(self.base, "prefs.db")
|
path = os.path.join(self.base, "prefs21.db")
|
||||||
new = not os.path.exists(path)
|
new = not os.path.exists(path)
|
||||||
def recover():
|
def recover():
|
||||||
# if we can't load profile, start with a new one
|
# if we can't load profile, start with a new one
|
||||||
@ -249,7 +246,7 @@ and no other programs are accessing your profile folders, then try again."""))
|
|||||||
os.rename(path, broken)
|
os.rename(path, broken)
|
||||||
QMessageBox.warning(
|
QMessageBox.warning(
|
||||||
None, "Preferences Corrupt", """\
|
None, "Preferences Corrupt", """\
|
||||||
Anki's prefs.db file was corrupt and has been recreated. If you were using multiple \
|
Anki's prefs21.db file was corrupt and has been recreated. If you were using multiple \
|
||||||
profiles, please add them back using the same names to recover your cards.""")
|
profiles, please add them back using the same names to recover your cards.""")
|
||||||
try:
|
try:
|
||||||
self.db = DB(path)
|
self.db = DB(path)
|
||||||
@ -308,8 +305,8 @@ please see:
|
|||||||
d = self.langDiag = NoCloseDiag()
|
d = self.langDiag = NoCloseDiag()
|
||||||
f = self.langForm = aqt.forms.setlang.Ui_Dialog()
|
f = self.langForm = aqt.forms.setlang.Ui_Dialog()
|
||||||
f.setupUi(d)
|
f.setupUi(d)
|
||||||
d.connect(d, SIGNAL("accepted()"), self._onLangSelected)
|
d.accepted.connect(self._onLangSelected)
|
||||||
d.connect(d, SIGNAL("rejected()"), lambda: True)
|
d.rejected.connect(lambda: True)
|
||||||
# default to the system language
|
# default to the system language
|
||||||
try:
|
try:
|
||||||
(lang, enc) = locale.getdefaultlocale()
|
(lang, enc) = locale.getdefaultlocale()
|
||||||
|
@ -70,7 +70,7 @@ Your pysqlite2 is too old. Anki will appear frozen during long operations.""")
|
|||||||
t = QTimer(self.mw)
|
t = QTimer(self.mw)
|
||||||
if not repeat:
|
if not repeat:
|
||||||
t.setSingleShot(True)
|
t.setSingleShot(True)
|
||||||
t.connect(t, SIGNAL("timeout()"), handler)
|
t.timeout.connect(handler)
|
||||||
t.start(ms)
|
t.start(ms)
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
32
aqt/qt.py
32
aqt/qt.py
@ -1,30 +1,25 @@
|
|||||||
# Copyright: Damien Elmes <anki@ichi2.net>
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
# imports are all in this file to make moving to pyside easier in the future
|
|
||||||
# fixme: make sure not to optimize imports on this file
|
# fixme: make sure not to optimize imports on this file
|
||||||
|
|
||||||
import sip
|
import sip
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
# fix buggy ubuntu12.04 display of language selector
|
||||||
|
os.environ["LIBOVERLAY_SCROLLBAR"] = "0"
|
||||||
|
|
||||||
from anki.utils import isWin, isMac
|
from anki.utils import isWin, isMac
|
||||||
|
|
||||||
sip.setapi('QString', 2)
|
from PyQt5.QtCore import *
|
||||||
sip.setapi('QVariant', 2)
|
from PyQt5.QtGui import *
|
||||||
sip.setapi('QUrl', 2)
|
from PyQt5.QtWidgets import *
|
||||||
try:
|
from PyQt5.QtWebEngineWidgets import *
|
||||||
sip.setdestroyonexit(False)
|
|
||||||
except:
|
|
||||||
# missing in older versions
|
|
||||||
pass
|
|
||||||
from PyQt4.QtCore import *
|
|
||||||
from PyQt4.QtGui import *
|
|
||||||
from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
|
|
||||||
from PyQt4.QtNetwork import QLocalServer, QLocalSocket
|
|
||||||
|
|
||||||
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
||||||
|
|
||||||
def debug():
|
def debug():
|
||||||
from PyQt4.QtCore import pyqtRemoveInputHook
|
from PyQt5.QtCore import pyqtRemoveInputHook
|
||||||
from pdb import set_trace
|
from pdb import set_trace
|
||||||
pyqtRemoveInputHook()
|
pyqtRemoveInputHook()
|
||||||
set_trace()
|
set_trace()
|
||||||
@ -33,7 +28,7 @@ import sys, traceback
|
|||||||
|
|
||||||
if os.environ.get("DEBUG"):
|
if os.environ.get("DEBUG"):
|
||||||
def info(type, value, tb):
|
def info(type, value, tb):
|
||||||
from PyQt4.QtCore import pyqtRemoveInputHook
|
from PyQt5.QtCore import pyqtRemoveInputHook
|
||||||
for line in traceback.format_exception(type, value, tb):
|
for line in traceback.format_exception(type, value, tb):
|
||||||
sys.stdout.write(line)
|
sys.stdout.write(line)
|
||||||
pyqtRemoveInputHook()
|
pyqtRemoveInputHook()
|
||||||
@ -44,8 +39,5 @@ if os.environ.get("DEBUG"):
|
|||||||
qtmajor = (QT_VERSION & 0xff0000) >> 16
|
qtmajor = (QT_VERSION & 0xff0000) >> 16
|
||||||
qtminor = (QT_VERSION & 0x00ff00) >> 8
|
qtminor = (QT_VERSION & 0x00ff00) >> 8
|
||||||
|
|
||||||
# qt4.6 doesn't support ruby tags
|
if qtmajor < 5 or (qtmajor == 5 and qtminor < 5):
|
||||||
if qtmajor <= 4 and qtminor <= 6:
|
raise Exception("Qt must be 5.5+")
|
||||||
import anki.template.furigana
|
|
||||||
anki.template.furigana.ruby = r'<span style="display: inline-block; text-align: center; line-height: 1; white-space: nowrap; vertical-align: baseline; margin: 0; padding: 0"><span style="display: block; text-decoration: none; line-height: 1.2; font-weight: normal; font-size: 0.64em">\2</span>\1</span>'
|
|
||||||
|
|
||||||
|
@ -36,19 +36,21 @@ class Reviewer(object):
|
|||||||
# qshortcut so we don't autorepeat
|
# qshortcut so we don't autorepeat
|
||||||
self.delShortcut = QShortcut(QKeySequence("Delete"), self.mw)
|
self.delShortcut = QShortcut(QKeySequence("Delete"), self.mw)
|
||||||
self.delShortcut.setAutoRepeat(False)
|
self.delShortcut.setAutoRepeat(False)
|
||||||
self.mw.connect(self.delShortcut, SIGNAL("activated()"), self.onDelete)
|
self.delShortcut.activated.connect(self.onDelete)
|
||||||
addHook("leech", self.onLeech)
|
addHook("leech", self.onLeech)
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
self.mw.col.reset()
|
self.mw.col.reset()
|
||||||
|
self.web.resetHandlers()
|
||||||
self.mw.keyHandler = self._keyHandler
|
self.mw.keyHandler = self._keyHandler
|
||||||
self.web.setLinkHandler(self._linkHandler)
|
self.web.onAnkiLink = self._linkHandler
|
||||||
self.web.setKeyHandler(self._catchEsc)
|
self.web.setKeyHandler(self._catchEsc)
|
||||||
if isMac:
|
if isMac:
|
||||||
self.bottom.web.setFixedHeight(46)
|
self.bottom.web.setFixedHeight(46)
|
||||||
else:
|
else:
|
||||||
self.bottom.web.setFixedHeight(52+self.mw.fontHeightDelta*4)
|
self.bottom.web.setFixedHeight(52+self.mw.fontHeightDelta*4)
|
||||||
self.bottom.web.setLinkHandler(self._linkHandler)
|
self.bottom.web.resetHandlers()
|
||||||
|
self.bottom.web.onAnkiLink = self._linkHandler
|
||||||
self._reps = None
|
self._reps = None
|
||||||
self.nextCard()
|
self.nextCard()
|
||||||
|
|
||||||
@ -161,12 +163,12 @@ function _toggleStar (show) {
|
|||||||
|
|
||||||
function _getTypedText () {
|
function _getTypedText () {
|
||||||
if (typeans) {
|
if (typeans) {
|
||||||
py.link("typeans:"+typeans.value);
|
openAnkiLink("typeans:"+typeans.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
function _typeAnsPress() {
|
function _typeAnsPress() {
|
||||||
if (window.event.keyCode === 13) {
|
if (window.event.keyCode === 13) {
|
||||||
py.link("ansHack");
|
openAnkiLink("ans");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -177,15 +179,14 @@ function _typeAnsPress() {
|
|||||||
self._bottomReady = False
|
self._bottomReady = False
|
||||||
base = getBase(self.mw.col)
|
base = getBase(self.mw.col)
|
||||||
# main window
|
# main window
|
||||||
self.web.stdHtml(self._revHtml, self._styles(),
|
self.web.onLoadFinished = self._showQuestion
|
||||||
loadCB=lambda x: self._showQuestion(),
|
self.web.stdHtml(self._revHtml, self._styles(), head=base)
|
||||||
head=base)
|
|
||||||
# show answer / ease buttons
|
# show answer / ease buttons
|
||||||
self.bottom.web.show()
|
self.bottom.web.show()
|
||||||
|
self.bottom.web.onLoadFinished = self._showAnswerButton
|
||||||
self.bottom.web.stdHtml(
|
self.bottom.web.stdHtml(
|
||||||
self._bottomHTML(),
|
self._bottomHTML(),
|
||||||
self.bottom._css + self._bottomCSS,
|
self.bottom._css + self._bottomCSS)
|
||||||
loadCB=lambda x: self._showAnswerButton())
|
|
||||||
|
|
||||||
# Showing the question
|
# Showing the question
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -277,19 +278,13 @@ The front of this card is empty. Please run Tools>Empty Cards.""")
|
|||||||
self.web.eval("$('#typeans').blur();")
|
self.web.eval("$('#typeans').blur();")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _showAnswerHack(self):
|
|
||||||
# on <qt4.8, calling _showAnswer() directly fails to show images on
|
|
||||||
# the answer side. But if we trigger it via the bottom web's python
|
|
||||||
# link, it inexplicably works.
|
|
||||||
self.bottom.web.eval("py.link('ans');")
|
|
||||||
|
|
||||||
def _keyHandler(self, evt):
|
def _keyHandler(self, evt):
|
||||||
key = str(evt.text())
|
key = str(evt.text())
|
||||||
if key == "e":
|
if key == "e":
|
||||||
self.mw.onEditCurrent()
|
self.mw.onEditCurrent()
|
||||||
elif (key == " " or evt.key() in (Qt.Key_Return, Qt.Key_Enter)):
|
elif (key == " " or evt.key() in (Qt.Key_Return, Qt.Key_Enter)):
|
||||||
if self.state == "question":
|
if self.state == "question":
|
||||||
self._showAnswerHack()
|
self._showAnswer()
|
||||||
elif self.state == "answer":
|
elif self.state == "answer":
|
||||||
self._answerCard(self._defaultEase())
|
self._answerCard(self._defaultEase())
|
||||||
elif key == "r" or evt.key() == Qt.Key_F5:
|
elif key == "r" or evt.key() == Qt.Key_F5:
|
||||||
@ -316,8 +311,6 @@ The front of this card is empty. Please run Tools>Empty Cards.""")
|
|||||||
def _linkHandler(self, url):
|
def _linkHandler(self, url):
|
||||||
if url == "ans":
|
if url == "ans":
|
||||||
self._showAnswer()
|
self._showAnswer()
|
||||||
elif url == "ansHack":
|
|
||||||
self.mw.progress.timer(100, self._showAnswerHack, False)
|
|
||||||
elif url.startswith("ease"):
|
elif url.startswith("ease"):
|
||||||
self._answerCard(int(url[4:]))
|
self._answerCard(int(url[4:]))
|
||||||
elif url == "edit":
|
elif url == "edit":
|
||||||
@ -540,12 +533,12 @@ min-width: 60px; white-space: nowrap;
|
|||||||
<tr>
|
<tr>
|
||||||
<td align=left width=50 valign=top class=stat>
|
<td align=left width=50 valign=top class=stat>
|
||||||
<br>
|
<br>
|
||||||
<button title="%(editkey)s" onclick="py.link('edit');">%(edit)s</button></td>
|
<button title="%(editkey)s" onclick="openAnkiLink('edit');">%(edit)s</button></td>
|
||||||
<td align=center valign=top id=middle>
|
<td align=center valign=top id=middle>
|
||||||
</td>
|
</td>
|
||||||
<td width=50 align=right valign=top class=stat><span id=time class=stattxt>
|
<td width=50 align=right valign=top class=stat><span id=time class=stattxt>
|
||||||
</span><br>
|
</span><br>
|
||||||
<button onclick="py.link('more');">%(more)s %(downArrow)s</button>
|
<button onclick="openAnkiLink('more');">%(more)s %(downArrow)s</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -603,7 +596,7 @@ function showAnswer(txt) {
|
|||||||
self.bottom.web.setFocus()
|
self.bottom.web.setFocus()
|
||||||
middle = '''
|
middle = '''
|
||||||
<span class=stattxt>%s</span><br>
|
<span class=stattxt>%s</span><br>
|
||||||
<button title="%s" id=ansbut onclick='py.link(\"ans\");'>%s</button>''' % (
|
<button title="%s" id=ansbut onclick='openAnkiLink("ans");'>%s</button>''' % (
|
||||||
self._remaining(), _("Shortcut key: %s") % _("Space"), _("Show Answer"))
|
self._remaining(), _("Shortcut key: %s") % _("Space"), _("Show Answer"))
|
||||||
# wrap it in a table so it has the same top margin as the ease buttons
|
# wrap it in a table so it has the same top margin as the ease buttons
|
||||||
middle = "<table cellpadding=0><tr><td class=stat2 align=center>%s</td></tr></table>" % middle
|
middle = "<table cellpadding=0><tr><td class=stat2 align=center>%s</td></tr></table>" % middle
|
||||||
@ -661,7 +654,7 @@ function showAnswer(txt) {
|
|||||||
extra = ""
|
extra = ""
|
||||||
due = self._buttonTime(i)
|
due = self._buttonTime(i)
|
||||||
return '''
|
return '''
|
||||||
<td align=center>%s<button %s title="%s" onclick='py.link("ease%d");'>\
|
<td align=center>%s<button %s title="%s" onclick='openAnkiLink("ease%d");'>\
|
||||||
%s</button></td>''' % (due, extra, _("Shortcut key: %s") % i, i, label)
|
%s</button></td>''' % (due, extra, _("Shortcut key: %s") % i, i, label)
|
||||||
buf = "<center><table cellpading=0 cellspacing=0><tr>"
|
buf = "<center><table cellpading=0 cellspacing=0><tr>"
|
||||||
for ease, label in self._answerButtonList():
|
for ease, label in self._answerButtonList():
|
||||||
@ -713,7 +706,7 @@ function showAnswer(txt) {
|
|||||||
label, scut, func = row
|
label, scut, func = row
|
||||||
a = m.addAction(label)
|
a = m.addAction(label)
|
||||||
a.setShortcut(QKeySequence(scut))
|
a.setShortcut(QKeySequence(scut))
|
||||||
a.connect(a, SIGNAL("triggered()"), func)
|
a.triggered.connect(func)
|
||||||
runHook("Reviewer.contextMenuEvent",self,m)
|
runHook("Reviewer.contextMenuEvent",self,m)
|
||||||
m.exec_(QCursor.pos())
|
m.exec_(QCursor.pos())
|
||||||
|
|
||||||
|
24
aqt/stats.py
24
aqt/stats.py
@ -26,21 +26,19 @@ class DeckStats(QDialog):
|
|||||||
restoreGeom(self, self.name)
|
restoreGeom(self, self.name)
|
||||||
b = f.buttonBox.addButton(_("Save Image"),
|
b = f.buttonBox.addButton(_("Save Image"),
|
||||||
QDialogButtonBox.ActionRole)
|
QDialogButtonBox.ActionRole)
|
||||||
b.connect(b, SIGNAL("clicked()"), self.browser)
|
b.clicked.connect(self.browser)
|
||||||
b.setAutoDefault(False)
|
b.setAutoDefault(False)
|
||||||
c = self.connect
|
f.groups.clicked.connect(lambda: self.changeScope("deck"))
|
||||||
s = SIGNAL("clicked()")
|
|
||||||
c(f.groups, s, lambda: self.changeScope("deck"))
|
|
||||||
f.groups.setShortcut("g")
|
f.groups.setShortcut("g")
|
||||||
c(f.all, s, lambda: self.changeScope("collection"))
|
f.all.clicked.connect(lambda: self.changeScope("collection"))
|
||||||
c(f.month, s, lambda: self.changePeriod(0))
|
f.month.clicked.connect(lambda: self.changePeriod(0))
|
||||||
c(f.year, s, lambda: self.changePeriod(1))
|
f.year.clicked.connect(lambda: self.changePeriod(1))
|
||||||
c(f.life, s, lambda: self.changePeriod(2))
|
f.life.clicked.connect(lambda: self.changePeriod(2))
|
||||||
c(f.web, SIGNAL("loadFinished(bool)"), self.loadFin)
|
|
||||||
maybeHideClose(self.form.buttonBox)
|
maybeHideClose(self.form.buttonBox)
|
||||||
addCloseShortcut(self)
|
addCloseShortcut(self)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
self.exec_()
|
self.show()
|
||||||
|
print("fixme: save image support in deck stats")
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
saveGeom(self, self.name)
|
saveGeom(self, self.name)
|
||||||
@ -78,14 +76,10 @@ to your desktop."""))
|
|||||||
self.wholeCollection = type == "collection"
|
self.wholeCollection = type == "collection"
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
def loadFin(self, b):
|
|
||||||
self.form.web.page().mainFrame().setScrollPosition(self.oldPos)
|
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.mw.progress.start(immediate=True)
|
self.mw.progress.start(immediate=True)
|
||||||
self.oldPos = self.form.web.page().mainFrame().scrollPosition()
|
|
||||||
stats = self.mw.col.stats()
|
stats = self.mw.col.stats()
|
||||||
stats.wholeCollection = self.wholeCollection
|
stats.wholeCollection = self.wholeCollection
|
||||||
self.report = stats.report(type=self.period)
|
self.report = stats.report(type=self.period)
|
||||||
self.form.web.setHtml(self.report)
|
self.form.web.stdHtml("<html><body>"+self.report+"</body></html>")
|
||||||
self.mw.progress.finish()
|
self.mw.progress.finish()
|
||||||
|
@ -31,7 +31,7 @@ class StudyDeck(QDialog):
|
|||||||
b.setShortcut(QKeySequence("Ctrl+N"))
|
b.setShortcut(QKeySequence("Ctrl+N"))
|
||||||
b.setToolTip(shortcut(_("Add New Deck (Ctrl+N)")))
|
b.setToolTip(shortcut(_("Add New Deck (Ctrl+N)")))
|
||||||
self.form.buttonBox.addButton(b, QDialogButtonBox.ActionRole)
|
self.form.buttonBox.addButton(b, QDialogButtonBox.ActionRole)
|
||||||
b.connect(b, SIGNAL("clicked()"), self.onAddDeck)
|
b.clicked.connect(self.onAddDeck)
|
||||||
if title:
|
if title:
|
||||||
self.setWindowTitle(title)
|
self.setWindowTitle(title)
|
||||||
if not names:
|
if not names:
|
||||||
@ -45,15 +45,9 @@ class StudyDeck(QDialog):
|
|||||||
self.ok = self.form.buttonBox.addButton(
|
self.ok = self.form.buttonBox.addButton(
|
||||||
accept or _("Study"), QDialogButtonBox.AcceptRole)
|
accept or _("Study"), QDialogButtonBox.AcceptRole)
|
||||||
self.setWindowModality(Qt.WindowModal)
|
self.setWindowModality(Qt.WindowModal)
|
||||||
self.connect(self.form.buttonBox,
|
self.form.buttonBox.helpRequested.connect(lambda: openHelp(help))
|
||||||
SIGNAL("helpRequested()"),
|
self.form.filter.textEdited.connect(self.redraw)
|
||||||
lambda: openHelp(help))
|
self.form.list.itemDoubleClicked.connect(self.accept)
|
||||||
self.connect(self.form.filter,
|
|
||||||
SIGNAL("textEdited(QString)"),
|
|
||||||
self.redraw)
|
|
||||||
self.connect(self.form.list,
|
|
||||||
SIGNAL("itemDoubleClicked(QListWidgetItem*)"),
|
|
||||||
self.accept)
|
|
||||||
self.show()
|
self.show()
|
||||||
# redraw after show so position at center correct
|
# redraw after show so position at center correct
|
||||||
self.redraw("", current)
|
self.redraw("", current)
|
||||||
|
28
aqt/sync.py
28
aqt/sync.py
@ -45,7 +45,7 @@ class SyncManager(QObject):
|
|||||||
t = self.thread = SyncThread(
|
t = self.thread = SyncThread(
|
||||||
self.pm.collectionPath(), self.pm.profile['syncKey'],
|
self.pm.collectionPath(), self.pm.profile['syncKey'],
|
||||||
auth=auth, media=self.pm.profile['syncMedia'])
|
auth=auth, media=self.pm.profile['syncMedia'])
|
||||||
self.connect(t, SIGNAL("event"), self.onEvent)
|
t.event.connect(self.onEvent)
|
||||||
self.label = _("Connecting...")
|
self.label = _("Connecting...")
|
||||||
self.mw.progress.start(immediate=True, label=self.label)
|
self.mw.progress.start(immediate=True, label=self.label)
|
||||||
self.sentBytes = self.recvBytes = 0
|
self.sentBytes = self.recvBytes = 0
|
||||||
@ -136,10 +136,10 @@ sync again to correct the issue."""))
|
|||||||
self._confirmFullSync()
|
self._confirmFullSync()
|
||||||
elif evt == "send":
|
elif evt == "send":
|
||||||
# posted events not guaranteed to arrive in order
|
# posted events not guaranteed to arrive in order
|
||||||
self.sentBytes = max(self.sentBytes, args[0])
|
self.sentBytes = max(self.sentBytes, int(args[0]))
|
||||||
self._updateLabel()
|
self._updateLabel()
|
||||||
elif evt == "recv":
|
elif evt == "recv":
|
||||||
self.recvBytes = max(self.recvBytes, args[0])
|
self.recvBytes = max(self.recvBytes, int(args[0]))
|
||||||
self._updateLabel()
|
self._updateLabel()
|
||||||
|
|
||||||
def _rewriteError(self, err):
|
def _rewriteError(self, err):
|
||||||
@ -216,8 +216,8 @@ enter your details below.""") %
|
|||||||
vbox.addLayout(g)
|
vbox.addLayout(g)
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
|
||||||
bb.button(QDialogButtonBox.Ok).setAutoDefault(True)
|
bb.button(QDialogButtonBox.Ok).setAutoDefault(True)
|
||||||
self.connect(bb, SIGNAL("accepted()"), d.accept)
|
bb.accepted.connect(d.accept)
|
||||||
self.connect(bb, SIGNAL("rejected()"), d.reject)
|
bb.rejected.connect(d.reject)
|
||||||
vbox.addWidget(bb)
|
vbox.addWidget(bb)
|
||||||
d.setLayout(vbox)
|
d.setLayout(vbox)
|
||||||
d.show()
|
d.show()
|
||||||
@ -275,6 +275,8 @@ Check Database, then sync again."""))
|
|||||||
|
|
||||||
class SyncThread(QThread):
|
class SyncThread(QThread):
|
||||||
|
|
||||||
|
event = pyqtSignal(str, str)
|
||||||
|
|
||||||
def __init__(self, path, hkey, auth=None, media=True):
|
def __init__(self, path, hkey, auth=None, media=True):
|
||||||
QThread.__init__(self)
|
QThread.__init__(self)
|
||||||
self.path = path
|
self.path = path
|
||||||
@ -309,11 +311,11 @@ class SyncThread(QThread):
|
|||||||
def sendEvent(bytes):
|
def sendEvent(bytes):
|
||||||
self.sentTotal += bytes
|
self.sentTotal += bytes
|
||||||
if canPost():
|
if canPost():
|
||||||
self.fireEvent("send", self.sentTotal)
|
self.fireEvent("send", str(self.sentTotal))
|
||||||
def recvEvent(bytes):
|
def recvEvent(bytes):
|
||||||
self.recvTotal += bytes
|
self.recvTotal += bytes
|
||||||
if canPost():
|
if canPost():
|
||||||
self.fireEvent("recv", self.recvTotal)
|
self.fireEvent("recv", str(self.recvTotal))
|
||||||
addHook("sync", syncEvent)
|
addHook("sync", syncEvent)
|
||||||
addHook("syncMsg", syncMsg)
|
addHook("syncMsg", syncMsg)
|
||||||
addHook("httpSend", sendEvent)
|
addHook("httpSend", sendEvent)
|
||||||
@ -416,8 +418,8 @@ class SyncThread(QThread):
|
|||||||
else:
|
else:
|
||||||
self.fireEvent("mediaSuccess")
|
self.fireEvent("mediaSuccess")
|
||||||
|
|
||||||
def fireEvent(self, *args):
|
def fireEvent(self, cmd, arg=""):
|
||||||
self.emit(SIGNAL("event"), *args)
|
self.event.emit(cmd, arg)
|
||||||
|
|
||||||
|
|
||||||
# Monkey-patch httplib & httplib2 so we can get progress info
|
# Monkey-patch httplib & httplib2 so we can get progress info
|
||||||
@ -428,9 +430,10 @@ import http.client, httplib2
|
|||||||
from io import StringIO
|
from io import StringIO
|
||||||
from anki.hooks import runHook
|
from anki.hooks import runHook
|
||||||
|
|
||||||
|
print("fixme: _conn_request and _incrementalSend need updating for python3")
|
||||||
|
|
||||||
# sending in httplib
|
# sending in httplib
|
||||||
def _incrementalSend(self, data):
|
def _incrementalSend(self, data):
|
||||||
print("fixme: _incrementalSend needs updating for python3")
|
|
||||||
"""Send `data' to the server."""
|
"""Send `data' to the server."""
|
||||||
if self.sock is None:
|
if self.sock is None:
|
||||||
if self.auto_open:
|
if self.auto_open:
|
||||||
@ -447,7 +450,7 @@ def _incrementalSend(self, data):
|
|||||||
self.sock.sendall(block)
|
self.sock.sendall(block)
|
||||||
runHook("httpSend", len(block))
|
runHook("httpSend", len(block))
|
||||||
|
|
||||||
http.client.HTTPConnection.send = _incrementalSend
|
#http.client.HTTPConnection.send = _incrementalSend
|
||||||
|
|
||||||
# receiving in httplib2
|
# receiving in httplib2
|
||||||
# this is an augmented version of httplib's request routine that:
|
# this is an augmented version of httplib's request routine that:
|
||||||
@ -455,7 +458,6 @@ http.client.HTTPConnection.send = _incrementalSend
|
|||||||
# - calls a hook for each chunk of data so we can update the gui
|
# - calls a hook for each chunk of data so we can update the gui
|
||||||
# - retries only when keep-alive connection is closed
|
# - retries only when keep-alive connection is closed
|
||||||
def _conn_request(self, conn, request_uri, method, body, headers):
|
def _conn_request(self, conn, request_uri, method, body, headers):
|
||||||
print("fixme: _conn_request updating for python3")
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
try:
|
try:
|
||||||
if conn.sock is None:
|
if conn.sock is None:
|
||||||
@ -503,4 +505,4 @@ def _conn_request(self, conn, request_uri, method, body, headers):
|
|||||||
content = httplib2._decompressContent(response, content)
|
content = httplib2._decompressContent(response, content)
|
||||||
return (response, content)
|
return (response, content)
|
||||||
|
|
||||||
httplib2.Http._conn_request = _conn_request
|
#httplib2.Http._conn_request = _conn_request
|
||||||
|
@ -6,6 +6,8 @@ import re
|
|||||||
|
|
||||||
class TagEdit(QLineEdit):
|
class TagEdit(QLineEdit):
|
||||||
|
|
||||||
|
lostFocus = pyqtSignal()
|
||||||
|
|
||||||
# 0 = tags, 1 = decks
|
# 0 = tags, 1 = decks
|
||||||
def __init__(self, parent, type=0):
|
def __init__(self, parent, type=0):
|
||||||
QLineEdit.__init__(self, parent)
|
QLineEdit.__init__(self, parent)
|
||||||
@ -53,7 +55,7 @@ class TagEdit(QLineEdit):
|
|||||||
|
|
||||||
def focusOutEvent(self, evt):
|
def focusOutEvent(self, evt):
|
||||||
QLineEdit.focusOutEvent(self, evt)
|
QLineEdit.focusOutEvent(self, evt)
|
||||||
self.emit(SIGNAL("lostFocus"))
|
self.lostFocus.emit()
|
||||||
self.completer.popup().hide()
|
self.completer.popup().hide()
|
||||||
|
|
||||||
def hideCompleter(self):
|
def hideCompleter(self):
|
||||||
@ -67,20 +69,20 @@ class TagCompleter(QCompleter):
|
|||||||
self.edit = edit
|
self.edit = edit
|
||||||
self.cursor = None
|
self.cursor = None
|
||||||
|
|
||||||
def splitPath(self, str):
|
def splitPath(self, tags):
|
||||||
str = str(str).strip()
|
tags = tags.strip()
|
||||||
str = re.sub(" +", " ", str)
|
tags = re.sub(" +", " ", tags)
|
||||||
self.tags = self.edit.col.tags.split(str)
|
self.tags = self.edit.col.tags.split(tags)
|
||||||
self.tags.append("")
|
self.tags.append("")
|
||||||
p = self.edit.cursorPosition()
|
p = self.edit.cursorPosition()
|
||||||
self.cursor = str.count(" ", 0, p)
|
self.cursor = tags.count(" ", 0, p)
|
||||||
return [self.tags[self.cursor]]
|
return [self.tags[self.cursor]]
|
||||||
|
|
||||||
def pathFromIndex(self, idx):
|
def pathFromIndex(self, idx):
|
||||||
if self.cursor is None:
|
if self.cursor is None:
|
||||||
return self.edit.text()
|
return self.edit.text()
|
||||||
ret = QCompleter.pathFromIndex(self, idx)
|
ret = QCompleter.pathFromIndex(self, idx)
|
||||||
self.tags[self.cursor] = str(ret)
|
self.tags[self.cursor] = ret
|
||||||
try:
|
try:
|
||||||
self.tags.remove("")
|
self.tags.remove("")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -9,9 +9,8 @@ class Toolbar(object):
|
|||||||
def __init__(self, mw, web):
|
def __init__(self, mw, web):
|
||||||
self.mw = mw
|
self.mw = mw
|
||||||
self.web = web
|
self.web = web
|
||||||
self.web.page().mainFrame().setScrollBarPolicy(
|
self.web.resetHandlers()
|
||||||
Qt.Vertical, Qt.ScrollBarAlwaysOff)
|
self.web.onAnkiLink = self._linkHandler
|
||||||
self.web.setLinkHandler(self._linkHandler)
|
|
||||||
self.link_handlers = {
|
self.link_handlers = {
|
||||||
"decks": self._deckLinkHandler,
|
"decks": self._deckLinkHandler,
|
||||||
"study": self._studyLinkHandler,
|
"study": self._studyLinkHandler,
|
||||||
@ -51,7 +50,8 @@ class Toolbar(object):
|
|||||||
def _linkHTML(self, links):
|
def _linkHTML(self, links):
|
||||||
buf = ""
|
buf = ""
|
||||||
for ln, name, title in links:
|
for ln, name, title in links:
|
||||||
buf += '<a class=hitem title="%s" href="%s">%s</a>' % (
|
buf += '''
|
||||||
|
<a class=hitem title="%s" href=# onclick="openAnkiLink('%s')">%s</a>''' % (
|
||||||
title, ln, name)
|
title, ln, name)
|
||||||
buf += " "*3
|
buf += " "*3
|
||||||
return buf
|
return buf
|
||||||
@ -59,7 +59,8 @@ class Toolbar(object):
|
|||||||
def _rightIcons(self):
|
def _rightIcons(self):
|
||||||
buf = ""
|
buf = ""
|
||||||
for ln, icon, title in self._rightIconsList():
|
for ln, icon, title in self._rightIconsList():
|
||||||
buf += '<a class=hitem title="%s" href="%s"><img width="16px" height="16px" src="%s"></a>' % (
|
buf += '''
|
||||||
|
<a class=hitem title="%s" href=# onclick='openAnkiLink("%s")'><img width="16px" height="16px" src="%s"></a>''' % (
|
||||||
title, ln, icon)
|
title, ln, icon)
|
||||||
return buf
|
return buf
|
||||||
|
|
||||||
@ -67,11 +68,9 @@ class Toolbar(object):
|
|||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def _linkHandler(self, link):
|
def _linkHandler(self, link):
|
||||||
# first set focus back to main window, or we're left with an ugly
|
|
||||||
# focus ring around the clicked item
|
|
||||||
self.mw.web.setFocus()
|
|
||||||
if link in self.link_handlers:
|
if link in self.link_handlers:
|
||||||
self.link_handlers[link]()
|
self.link_handlers[link]()
|
||||||
|
return False
|
||||||
|
|
||||||
def _deckLinkHandler(self):
|
def _deckLinkHandler(self):
|
||||||
self.mw.moveToState("deckBrowser")
|
self.mw.moveToState("deckBrowser")
|
||||||
|
@ -14,6 +14,10 @@ from aqt.utils import showText
|
|||||||
|
|
||||||
class LatestVersionFinder(QThread):
|
class LatestVersionFinder(QThread):
|
||||||
|
|
||||||
|
newVerAvail = pyqtSignal(str)
|
||||||
|
newMsg = pyqtSignal(dict)
|
||||||
|
clockIsOff = pyqtSignal(float)
|
||||||
|
|
||||||
def __init__(self, main):
|
def __init__(self, main):
|
||||||
QThread.__init__(self)
|
QThread.__init__(self)
|
||||||
self.main = main
|
self.main = main
|
||||||
@ -32,23 +36,25 @@ class LatestVersionFinder(QThread):
|
|||||||
return
|
return
|
||||||
d = self._data()
|
d = self._data()
|
||||||
d['proto'] = 1
|
d['proto'] = 1
|
||||||
d = urllib.parse.urlencode(d)
|
d = urllib.parse.urlencode(d).encode("utf8")
|
||||||
try:
|
try:
|
||||||
f = urllib.request.urlopen(aqt.appUpdate, d)
|
f = urllib.request.urlopen(aqt.appUpdate, d)
|
||||||
resp = f.read()
|
resp = f.read()
|
||||||
if not resp:
|
if not resp:
|
||||||
|
print("update check load failed")
|
||||||
return
|
return
|
||||||
resp = json.loads(resp)
|
resp = json.loads(resp.decode("utf8"))
|
||||||
except:
|
except:
|
||||||
# behind proxy, corrupt message, etc
|
# behind proxy, corrupt message, etc
|
||||||
|
print("update check failed")
|
||||||
return
|
return
|
||||||
if resp['msg']:
|
if resp['msg']:
|
||||||
self.emit(SIGNAL("newMsg"), resp)
|
self.newMsg.emit(resp)
|
||||||
if resp['ver']:
|
if resp['ver']:
|
||||||
self.emit(SIGNAL("newVerAvail"), resp['ver'])
|
self.newVerAvail.emit(resp['ver'])
|
||||||
diff = resp['time'] - time.time()
|
diff = resp['time'] - time.time()
|
||||||
if abs(diff) > 300:
|
if abs(diff) > 300:
|
||||||
self.emit(SIGNAL("clockIsOff"), diff)
|
self.clockIsOff.emit(diff)
|
||||||
|
|
||||||
def askAndUpdate(mw, ver):
|
def askAndUpdate(mw, ver):
|
||||||
baseStr = (
|
baseStr = (
|
||||||
|
31
aqt/utils.py
31
aqt/utils.py
@ -45,7 +45,7 @@ def showInfo(text, parent=False, help="", type="info", title="Anki"):
|
|||||||
b.setDefault(True)
|
b.setDefault(True)
|
||||||
if help:
|
if help:
|
||||||
b = mb.addButton(QMessageBox.Help)
|
b = mb.addButton(QMessageBox.Help)
|
||||||
b.connect(b, SIGNAL("clicked()"), lambda: openHelp(help))
|
b.clicked.connect(lambda: openHelp(help))
|
||||||
b.setAutoDefault(False)
|
b.setAutoDefault(False)
|
||||||
return mb.exec_()
|
return mb.exec_()
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ def showText(txt, parent=None, type="text", run=True, geomKey=None, \
|
|||||||
if geomKey:
|
if geomKey:
|
||||||
saveGeom(diag, geomKey)
|
saveGeom(diag, geomKey)
|
||||||
QDialog.reject(diag)
|
QDialog.reject(diag)
|
||||||
diag.connect(box, SIGNAL("rejected()"), onReject)
|
box.rejected.connect(onReject)
|
||||||
diag.setMinimumHeight(minHeight)
|
diag.setMinimumHeight(minHeight)
|
||||||
diag.setMinimumWidth(minWidth)
|
diag.setMinimumWidth(minWidth)
|
||||||
if geomKey:
|
if geomKey:
|
||||||
@ -166,13 +166,10 @@ class GetTextDialog(QDialog):
|
|||||||
b = QDialogButtonBox(buts)
|
b = QDialogButtonBox(buts)
|
||||||
v.addWidget(b)
|
v.addWidget(b)
|
||||||
self.setLayout(v)
|
self.setLayout(v)
|
||||||
self.connect(b.button(QDialogButtonBox.Ok),
|
b.button(QDialogButtonBox.Ok).clicked.connect(self.accept)
|
||||||
SIGNAL("clicked()"), self.accept)
|
b.button(QDialogButtonBox.Cancel).clicked.connect(self.reject)
|
||||||
self.connect(b.button(QDialogButtonBox.Cancel),
|
|
||||||
SIGNAL("clicked()"), self.reject)
|
|
||||||
if help:
|
if help:
|
||||||
self.connect(b.button(QDialogButtonBox.Help),
|
b.button(QDialogButtonBox.Help).clicked.connect(self.helpRequested)
|
||||||
SIGNAL("clicked()"), self.helpRequested)
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
return QDialog.accept(self)
|
return QDialog.accept(self)
|
||||||
@ -214,7 +211,7 @@ def chooseList(prompt, choices, startrow=0, parent=None):
|
|||||||
c.setCurrentRow(startrow)
|
c.setCurrentRow(startrow)
|
||||||
l.addWidget(c)
|
l.addWidget(c)
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Ok)
|
bb = QDialogButtonBox(QDialogButtonBox.Ok)
|
||||||
bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
|
bb.accepted.connect(d.accept)
|
||||||
l.addWidget(bb)
|
l.addWidget(bb)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
return c.currentRow()
|
return c.currentRow()
|
||||||
@ -239,9 +236,6 @@ def getFile(parent, title, cb, filter="*.*", dir=None, key=None):
|
|||||||
else:
|
else:
|
||||||
dirkey = None
|
dirkey = None
|
||||||
d = QFileDialog(parent)
|
d = QFileDialog(parent)
|
||||||
# fix #233 crash
|
|
||||||
if isMac:
|
|
||||||
d.setOptions(QFileDialog.DontUseNativeDialog)
|
|
||||||
d.setFileMode(QFileDialog.ExistingFile)
|
d.setFileMode(QFileDialog.ExistingFile)
|
||||||
if os.path.exists(dir):
|
if os.path.exists(dir):
|
||||||
d.setDirectory(dir)
|
d.setDirectory(dir)
|
||||||
@ -249,8 +243,6 @@ def getFile(parent, title, cb, filter="*.*", dir=None, key=None):
|
|||||||
d.setNameFilter(filter)
|
d.setNameFilter(filter)
|
||||||
ret = []
|
ret = []
|
||||||
def accept():
|
def accept():
|
||||||
# work around an osx crash
|
|
||||||
#aqt.mw.app.processEvents()
|
|
||||||
file = str(list(d.selectedFiles())[0])
|
file = str(list(d.selectedFiles())[0])
|
||||||
if dirkey:
|
if dirkey:
|
||||||
dir = os.path.dirname(file)
|
dir = os.path.dirname(file)
|
||||||
@ -258,7 +250,7 @@ def getFile(parent, title, cb, filter="*.*", dir=None, key=None):
|
|||||||
if cb:
|
if cb:
|
||||||
cb(file)
|
cb(file)
|
||||||
ret.append(file)
|
ret.append(file)
|
||||||
d.connect(d, SIGNAL("accepted()"), accept)
|
d.accepted.connect(accept)
|
||||||
d.exec_()
|
d.exec_()
|
||||||
return ret and ret[0]
|
return ret and ret[0]
|
||||||
|
|
||||||
@ -268,9 +260,9 @@ def getSaveFile(parent, title, dir_description, key, ext, fname=None):
|
|||||||
config_key = dir_description + 'Directory'
|
config_key = dir_description + 'Directory'
|
||||||
base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
|
base = aqt.mw.pm.profile.get(config_key, aqt.mw.pm.base)
|
||||||
path = os.path.join(base, fname)
|
path = os.path.join(base, fname)
|
||||||
file = str(QFileDialog.getSaveFileName(
|
file = QFileDialog.getSaveFileName(
|
||||||
parent, title, path, "{0} (*{1})".format(key, ext),
|
parent, title, path, "{0} (*{1})".format(key, ext),
|
||||||
options=QFileDialog.DontConfirmOverwrite))
|
options=QFileDialog.DontConfirmOverwrite)[0]
|
||||||
if file:
|
if file:
|
||||||
# add extension
|
# add extension
|
||||||
if not file.lower().endswith(ext):
|
if not file.lower().endswith(ext):
|
||||||
@ -359,8 +351,6 @@ def getBase(col):
|
|||||||
|
|
||||||
def openFolder(path):
|
def openFolder(path):
|
||||||
if isWin:
|
if isWin:
|
||||||
if isinstance(path, str):
|
|
||||||
path = path.encode(sys.getfilesystemencoding())
|
|
||||||
subprocess.Popen(["explorer", path])
|
subprocess.Popen(["explorer", path])
|
||||||
else:
|
else:
|
||||||
QDesktopServices.openUrl(QUrl("file://" + path))
|
QDesktopServices.openUrl(QUrl("file://" + path))
|
||||||
@ -380,8 +370,7 @@ def addCloseShortcut(widg):
|
|||||||
if not isMac:
|
if not isMac:
|
||||||
return
|
return
|
||||||
widg._closeShortcut = QShortcut(QKeySequence("Ctrl+W"), widg)
|
widg._closeShortcut = QShortcut(QKeySequence("Ctrl+W"), widg)
|
||||||
widg.connect(widg._closeShortcut, SIGNAL("activated()"),
|
widg._closeShortcut.activated.connect(widg.reject)
|
||||||
widg, SLOT("reject()"))
|
|
||||||
|
|
||||||
def downArrow():
|
def downArrow():
|
||||||
if isWin:
|
if isWin:
|
||||||
|
136
aqt/webview.py
136
aqt/webview.py
@ -9,53 +9,34 @@ from aqt.utils import openLink
|
|||||||
from anki.utils import isMac, isWin
|
from anki.utils import isMac, isWin
|
||||||
import anki.js
|
import anki.js
|
||||||
|
|
||||||
# Bridge for Qt<->JS
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
class Bridge(QObject):
|
|
||||||
@pyqtSlot(str, result=str)
|
|
||||||
def run(self, str):
|
|
||||||
return self._bridge(str)
|
|
||||||
@pyqtSlot(str)
|
|
||||||
def link(self, str):
|
|
||||||
self._linkHandler(str)
|
|
||||||
def setBridge(self, func):
|
|
||||||
self._bridge = func
|
|
||||||
def setLinkHandler(self, func):
|
|
||||||
self._linkHandler = func
|
|
||||||
|
|
||||||
# Page for debug messages
|
# Page for debug messages
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
class AnkiWebPage(QWebPage):
|
class AnkiWebPage(QWebEnginePage):
|
||||||
|
|
||||||
def __init__(self, jsErr):
|
def __init__(self, jsErr, acceptNavReq):
|
||||||
QWebPage.__init__(self)
|
QWebEnginePage.__init__(self)
|
||||||
self._jsErr = jsErr
|
self._jsErr = jsErr
|
||||||
def javaScriptConsoleMessage(self, msg, line, srcID):
|
self._acceptNavReq = acceptNavReq
|
||||||
self._jsErr(msg, line, srcID)
|
|
||||||
|
def javaScriptConsoleMessage(self, lvl, msg, line, srcID):
|
||||||
|
self._jsErr(lvl, msg, line, srcID)
|
||||||
|
|
||||||
|
def acceptNavigationRequest(self, url, navType, isMainFrame):
|
||||||
|
return self._acceptNavReq(url, navType, isMainFrame)
|
||||||
|
|
||||||
# Main web view
|
# Main web view
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
class AnkiWebView(QWebView):
|
class AnkiWebView(QWebEngineView):
|
||||||
|
|
||||||
def __init__(self, canFocus=True):
|
def __init__(self, canFocus=True):
|
||||||
QWebView.__init__(self)
|
QWebEngineView.__init__(self)
|
||||||
self.setRenderHints(
|
|
||||||
QPainter.TextAntialiasing |
|
|
||||||
QPainter.SmoothPixmapTransform |
|
|
||||||
QPainter.HighQualityAntialiasing)
|
|
||||||
self.setObjectName("mainText")
|
self.setObjectName("mainText")
|
||||||
self._bridge = Bridge()
|
self._page = AnkiWebPage(self._jsErr, self._acceptNavReq)
|
||||||
self._page = AnkiWebPage(self._jsErr)
|
|
||||||
self._loadFinishedCB = None
|
self._loadFinishedCB = None
|
||||||
self.setPage(self._page)
|
self.setPage(self._page)
|
||||||
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
|
self.resetHandlers()
|
||||||
self.setLinkHandler()
|
|
||||||
self.setKeyHandler()
|
|
||||||
self.connect(self, SIGNAL("linkClicked(QUrl)"), self._linkHandler)
|
|
||||||
self.connect(self, SIGNAL("loadFinished(bool)"), self._loadFinished)
|
|
||||||
self.allowDrops = False
|
self.allowDrops = False
|
||||||
# reset each time new html is set; used to detect if still in same state
|
# reset each time new html is set; used to detect if still in same state
|
||||||
self.key = None
|
self.key = None
|
||||||
@ -63,72 +44,81 @@ class AnkiWebView(QWebView):
|
|||||||
|
|
||||||
def keyPressEvent(self, evt):
|
def keyPressEvent(self, evt):
|
||||||
if evt.matches(QKeySequence.Copy):
|
if evt.matches(QKeySequence.Copy):
|
||||||
self.triggerPageAction(QWebPage.Copy)
|
self.triggerPageAction(QWebEnginePage.Copy)
|
||||||
evt.accept()
|
evt.accept()
|
||||||
# work around a bug with windows qt where shift triggers buttons
|
# work around a bug with windows qt where shift triggers buttons
|
||||||
if isWin and evt.modifiers() & Qt.ShiftModifier and not evt.text():
|
if isWin and evt.modifiers() & Qt.ShiftModifier and not evt.text():
|
||||||
evt.accept()
|
evt.accept()
|
||||||
return
|
return
|
||||||
QWebView.keyPressEvent(self, evt)
|
QWebEngineView.keyPressEvent(self, evt)
|
||||||
|
|
||||||
def keyReleaseEvent(self, evt):
|
def keyReleaseEvent(self, evt):
|
||||||
if self._keyHandler:
|
if self._keyHandler:
|
||||||
if self._keyHandler(evt):
|
if self._keyHandler(evt):
|
||||||
evt.accept()
|
evt.accept()
|
||||||
return
|
return
|
||||||
QWebView.keyReleaseEvent(self, evt)
|
QWebEngineView.keyReleaseEvent(self, evt)
|
||||||
|
|
||||||
def contextMenuEvent(self, evt):
|
def contextMenuEvent(self, evt):
|
||||||
if not self._canFocus:
|
if not self._canFocus:
|
||||||
return
|
return
|
||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
a = m.addAction(_("Copy"))
|
a = m.addAction(_("Copy"))
|
||||||
a.connect(a, SIGNAL("triggered()"),
|
a.triggered.connect(lambda: self.triggerPageAction(QWebEnginePage.Copy))
|
||||||
lambda: self.triggerPageAction(QWebPage.Copy))
|
|
||||||
runHook("AnkiWebView.contextMenuEvent", self, m)
|
runHook("AnkiWebView.contextMenuEvent", self, m)
|
||||||
m.popup(QCursor.pos())
|
m.popup(QCursor.pos())
|
||||||
|
|
||||||
def dropEvent(self, evt):
|
def dropEvent(self, evt):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def setLinkHandler(self, handler=None):
|
|
||||||
if handler:
|
|
||||||
self.linkHandler = handler
|
|
||||||
else:
|
|
||||||
self.linkHandler = self._openLinksExternally
|
|
||||||
self._bridge.setLinkHandler(self.linkHandler)
|
|
||||||
|
|
||||||
def setKeyHandler(self, handler=None):
|
def setKeyHandler(self, handler=None):
|
||||||
# handler should return true if event should be swallowed
|
# handler should return true if event should be swallowed
|
||||||
self._keyHandler = handler
|
self._keyHandler = handler
|
||||||
|
|
||||||
def setHtml(self, html, loadCB=None):
|
def setHtml(self, html):
|
||||||
self.key = None
|
self.key = None
|
||||||
self._loadFinishedCB = loadCB
|
app = QApplication.instance()
|
||||||
QWebView.setHtml(self, html)
|
oldFocus = app.focusWidget()
|
||||||
|
self._page.setHtml(html)
|
||||||
|
# work around webengine stealing focus on setHtml()
|
||||||
|
if oldFocus:
|
||||||
|
oldFocus.setFocus()
|
||||||
|
|
||||||
def stdHtml(self, body, css="", bodyClass="", loadCB=None, js=None, head=""):
|
def stdHtml(self, body, css="", bodyClass="", js=None, head=""):
|
||||||
if isMac:
|
if isMac:
|
||||||
button = "font-weight: bold; height: 24px;"
|
button = "font-weight: bold; height: 24px;"
|
||||||
else:
|
else:
|
||||||
button = "font-weight: normal;"
|
button = "font-weight: normal;"
|
||||||
|
|
||||||
|
screen = QApplication.desktop().screen()
|
||||||
|
dpi = screen.logicalDpiX()
|
||||||
|
zoomFactor = max(1, dpi / 96.0)
|
||||||
|
|
||||||
self.setHtml("""
|
self.setHtml("""
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html><head><style>
|
<html><head><style>
|
||||||
|
body { zoom: %f; }
|
||||||
button {
|
button {
|
||||||
%s
|
%s
|
||||||
}
|
}
|
||||||
%s</style>
|
%s</style>
|
||||||
<script>%s</script>
|
<script>
|
||||||
|
%s
|
||||||
|
|
||||||
|
openAnkiLink = function(txt) {
|
||||||
|
window.location = "http://anki/"+txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function(event) {
|
||||||
|
openAnkiLink("domDone");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
%s
|
%s
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="%s">%s</body></html>""" % (
|
<body class="%s">%s</body></html>""" % (
|
||||||
button, css, js or anki.js.jquery+anki.js.browserSel,
|
zoomFactor, button, css, js or anki.js.jquery+anki.js.browserSel,
|
||||||
head, bodyClass, body), loadCB)
|
head, bodyClass, body))
|
||||||
|
|
||||||
def setBridge(self, bridge):
|
|
||||||
self._bridge.setBridge(bridge)
|
|
||||||
|
|
||||||
def setCanFocus(self, canFocus=False):
|
def setCanFocus(self, canFocus=False):
|
||||||
self._canFocus = canFocus
|
self._canFocus = canFocus
|
||||||
@ -138,21 +128,39 @@ button {
|
|||||||
self.setFocusPolicy(Qt.NoFocus)
|
self.setFocusPolicy(Qt.NoFocus)
|
||||||
|
|
||||||
def eval(self, js):
|
def eval(self, js):
|
||||||
self.page().mainFrame().evaluateJavaScript(js)
|
self.page().runJavaScript(js)
|
||||||
|
|
||||||
def _openLinksExternally(self, url):
|
def _openLinksExternally(self, url):
|
||||||
openLink(url)
|
openLink(url)
|
||||||
|
|
||||||
def _jsErr(self, msg, line, srcID):
|
def _jsErr(self, lvl, msg, line, srcID):
|
||||||
sys.stdout.write(
|
sys.stdout.write(
|
||||||
(_("JS error on line %(a)d: %(b)s") %
|
(_("JS error on line %(a)d: %(b)s") %
|
||||||
dict(a=line, b=msg+"\n")))
|
dict(a=line, b=msg+"\n")))
|
||||||
|
|
||||||
def _linkHandler(self, url):
|
def _acceptNavReq(self, url, navType, isMainFrame):
|
||||||
self.linkHandler(url.toString())
|
# 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 _loadFinished(self):
|
def defaultOnAnkiLink(self, link):
|
||||||
self.page().mainFrame().addToJavaScriptWindowObject("py", self._bridge)
|
print("unhandled anki link:", link)
|
||||||
if self._loadFinishedCB:
|
|
||||||
self._loadFinishedCB(self)
|
def defaultOnLoadFinished(self):
|
||||||
self._loadFinishedCB = None
|
pass
|
||||||
|
|
||||||
|
def resetHandlers(self):
|
||||||
|
self.setKeyHandler(None)
|
||||||
|
self.onAnkiLink = self.defaultOnAnkiLink
|
||||||
|
self.onLoadFinished = self.defaultOnLoadFinished
|
||||||
|
@ -20,12 +20,21 @@
|
|||||||
<string>About Anki</string>
|
<string>About Anki</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout">
|
<layout class="QVBoxLayout">
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWebView" name="label">
|
<widget class="AnkiWebView" name="label" native="true">
|
||||||
<property name="url">
|
<property name="url" stdset="0">
|
||||||
<url>
|
<url>
|
||||||
<string>about:blank</string>
|
<string>about:blank</string>
|
||||||
</url>
|
</url>
|
||||||
@ -46,9 +55,10 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QWebView</class>
|
<class>AnkiWebView</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>QtWebKit/QWebView</header>
|
<header location="global">aqt/webview</header>
|
||||||
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -28,7 +28,16 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -76,17 +85,20 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="verticalSpacing">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
@ -96,6 +108,12 @@
|
|||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QComboBox" name="searchEdit">
|
<widget class="QComboBox" name="searchEdit">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -523,7 +541,7 @@
|
|||||||
</connection>
|
</connection>
|
||||||
<connection>
|
<connection>
|
||||||
<sender>actionClose</sender>
|
<sender>actionClose</sender>
|
||||||
<signal>activated()</signal>
|
<signal>triggered()</signal>
|
||||||
<receiver>Dialog</receiver>
|
<receiver>Dialog</receiver>
|
||||||
<slot>close()</slot>
|
<slot>close()</slot>
|
||||||
<hints>
|
<hints>
|
||||||
|
@ -47,12 +47,21 @@
|
|||||||
<enum>QFrame::Raised</enum>
|
<enum>QFrame::Raised</enum>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWebView" name="webView">
|
<widget class="AnkiWebView" name="webView" native="true">
|
||||||
<property name="url">
|
<property name="url" stdset="0">
|
||||||
<url>
|
<url>
|
||||||
<string>about:blank</string>
|
<string>about:blank</string>
|
||||||
</url>
|
</url>
|
||||||
@ -76,9 +85,10 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QWebView</class>
|
<class>AnkiWebView</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>QtWebKit/QWebView</header>
|
<header location="global">aqt/webview</header>
|
||||||
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
|
@ -17,12 +17,21 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWebView" name="web">
|
<widget class="AnkiWebView" name="web" native="true">
|
||||||
<property name="url">
|
<property name="url" stdset="0">
|
||||||
<url>
|
<url>
|
||||||
<string>about:blank</string>
|
<string>about:blank</string>
|
||||||
</url>
|
</url>
|
||||||
@ -34,7 +43,16 @@
|
|||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>8</number>
|
<number>8</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="margin">
|
<property name="leftMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
@ -125,9 +143,10 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QWebView</class>
|
<class>AnkiWebView</class>
|
||||||
<extends>QWidget</extends>
|
<extends>QWidget</extends>
|
||||||
<header>QtWebKit/QWebView</header>
|
<header location="global">aqt/webview</header>
|
||||||
|
<container>1</container>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# generate python files based on the designer ui files. pyuic4 and pyrcc4
|
# generate python files based on the designer ui files. pyuic5 and pyrcc5
|
||||||
# should be on the path.
|
# should be on the path.
|
||||||
#
|
#
|
||||||
|
|
||||||
@ -24,10 +24,10 @@ do
|
|||||||
base=$(basename $i .ui)
|
base=$(basename $i .ui)
|
||||||
py="aqt/forms/${base}.py"
|
py="aqt/forms/${base}.py"
|
||||||
echo " \"$base\"," >> $init
|
echo " \"$base\"," >> $init
|
||||||
echo "import $base" >> $temp
|
echo "from . import $base" >> $temp
|
||||||
if [ $i -nt $py ]; then
|
if [ $i -nt $py ]; then
|
||||||
echo " * "$py
|
echo " * "$py
|
||||||
pyuic4 $i -o $py
|
pyuic5 --from-imports $i -o $py
|
||||||
# munge the output to use gettext
|
# munge the output to use gettext
|
||||||
perl -pi.bak -e 's/(QtGui\.QApplication\.)?_?translate\(".*?", /_(/; s/, None.*/))/' $py
|
perl -pi.bak -e 's/(QtGui\.QApplication\.)?_?translate\(".*?", /_(/; s/, None.*/))/' $py
|
||||||
rm $py.bak
|
rm $py.bak
|
||||||
@ -38,4 +38,4 @@ cat $temp >> $init
|
|||||||
rm $temp
|
rm $temp
|
||||||
|
|
||||||
echo "Building resources.."
|
echo "Building resources.."
|
||||||
pyrcc4 designer/icons.qrc -o aqt/forms/icons_rc.py
|
pyrcc5 designer/icons.qrc -o aqt/forms/icons_rc.py
|
||||||
|
Loading…
Reference in New Issue
Block a user