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