diff --git a/anki/collection.py b/anki/collection.py index cf8cb82d5..30f447b67 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -417,7 +417,7 @@ insert into cards values (?,?,?,?,?,?,0,0,?,0,0,0,0,0,0,0,0,"")""", # type 0 - when previewing in add dialog, only non-empty # type 1 - when previewing edit, only existing # type 2 - when previewing in models dialog, all templates - def previewCards(self, note, type=0): + def previewCards(self, note, type=0, did=None): if type == 0: cms = self.findTemplates(note) elif type == 1: @@ -428,19 +428,23 @@ insert into cards values (?,?,?,?,?,?,0,0,?,0,0,0,0,0,0,0,0,"")""", return [] cards = [] for template in cms: - cards.append(self._newCard(note, template, 1, flush=False)) + cards.append(self._newCard(note, template, 1, flush=False, did=did)) return cards - def _newCard(self, note, template, due, flush=True): + def _newCard(self, note, template, due, flush=True, did=None): "Create a new card." card = anki.cards.Card(self) card.nid = note.id card.ord = template['ord'] - # Use template did (deck override) if valid, otherwise model did - if template['did'] and str(template['did']) in self.decks.decks: - card.did = template['did'] - else: - card.did = note.model()['did'] + card.did = self.db.scalar("select did from cards where nid = ? and ord = ?", card.nid, card.ord) + # Use template did (deck override) if valid, otherwise did in argument, otherwise model did + if not card.did: + if template['did'] and str(template['did']) in self.decks.decks: + card.did = template['did'] + elif did: + card.did = did + else: + card.did = note.model()['did'] # if invalid did, use default instead deck = self.decks.get(card.did) if deck['dyn']: diff --git a/anki/sync.py b/anki/sync.py index b4027b5d2..dc947d912 100644 --- a/anki/sync.py +++ b/anki/sync.py @@ -10,6 +10,7 @@ import requests from anki.db import DB, DBError from anki.utils import ids2str, intTime, json, platDesc, checksum, devMode from anki.consts import * +from aqt.utils import versionWithBuild from .hooks import runHook import anki from .lang import ngettext @@ -585,7 +586,7 @@ class RemoteServer(HttpSyncer): ) ret = self.req( "meta", io.BytesIO(json.dumps(dict( - v=SYNC_VER, cv="ankidesktop,%s,%s"%(anki.version, platDesc()))).encode("utf8")), + v=SYNC_VER, cv="ankidesktop,%s,%s"%(versionWithBuild(), platDesc()))).encode("utf8")), badAuthRaises=False) if not ret: # invalid auth diff --git a/aqt/about.py b/aqt/about.py index 718eaadf8..0baa2e9f7 100644 --- a/aqt/about.py +++ b/aqt/about.py @@ -117,6 +117,7 @@ system. It's free and open source.") "黃文龍", "David Bailey", "Arman High", + "Arthur Milchior", )) abouttext += '

' + _("Written by Damien Elmes, with patches, translation,\ diff --git a/aqt/addons.py b/aqt/addons.py index a2b03a7ff..665a6aea2 100644 --- a/aqt/addons.py +++ b/aqt/addons.py @@ -316,7 +316,7 @@ Are you sure you want to continue?""" updated = [] for dir, ts in mods: sid = str(dir) - if self.addonMeta(sid).get("mod") < ts: + if self.addonMeta(sid).get("mod",0) < ts: updated.append(sid) return updated @@ -426,6 +426,7 @@ class AddonsDialog(QDialog): self.form.addonList.currentRowChanged.connect(self._onAddonItemSelected) self.setAcceptDrops(True) self.redrawAddons() + restoreGeom(self, "addons") self.show() def dragEnterEvent(self, event): @@ -446,6 +447,10 @@ class AddonsDialog(QDialog): paths.append(path) self.onInstallFiles(paths) + def reject(self): + saveGeom(self, "addons") + return QDialog.reject(self) + def redrawAddons(self): self.addons = [(self.annotatedName(d), d) for d in self.mgr.allAddons()] self.addons.sort() diff --git a/aqt/clayout.py b/aqt/clayout.py index d0414c6bf..207537ee8 100644 --- a/aqt/clayout.py +++ b/aqt/clayout.py @@ -61,7 +61,10 @@ class CardLayout(QDialog): self.setFocus() def redraw(self): - self.cards = self.col.previewCards(self.note, 2) + did = None + if hasattr(self.parent,"deckChooser"): + did = self.parent.deckChooser.selectedId() + self.cards = self.col.previewCards(self.note, 2, did=did) idx = self.ord if idx >= len(self.cards): self.ord = len(self.cards) - 1 diff --git a/aqt/editcurrent.py b/aqt/editcurrent.py index 2b4f521ea..87be380ba 100644 --- a/aqt/editcurrent.py +++ b/aqt/editcurrent.py @@ -30,8 +30,8 @@ class EditCurrent(QDialog): addHook("reset", self.onReset) self.mw.requireReset() self.show() - # reset focus after open - self.mw.progress.timer(100, self.editor.web.setFocus, False) + # reset focus after open, taking care not to retain webview + self.mw.progress.timer(100, lambda: self.editor.web.setFocus(), False) def onReset(self): # lazy approach for now: throw away edits diff --git a/aqt/errors.py b/aqt/errors.py index 6b38174cc..821be049e 100644 --- a/aqt/errors.py +++ b/aqt/errors.py @@ -3,10 +3,12 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import sys, traceback import html +import re from anki.lang import _ from aqt.qt import * from aqt.utils import showText, showWarning +from aqt import mw if not os.environ.get("DEBUG"): def excepthook(etype,val,tb): @@ -121,16 +123,29 @@ report the issue on the add-ons section of our support site.

Debug info:

-""") +""") if self.mw.addonManager.dirty: txt = pluginText + error = self._supportText() + self._addonText(error) + "\n" + error else: txt = stdText + error = self._supportText() + "\n" + error + # show dialog - error = self._supportText() + "\n" + error - txt = txt + "
" + error + "
" - showText(txt, type="html") + showText(txt, type="html", copyBtn=True) + + def _addonText(self, error): + matches = re.findall(r"addons21/(.*?)/", error) + if not matches: + return "" + # reverse to list most likely suspect first, dict to deduplicate: + addons = [mw.addonManager.addonName(i) for i in + dict.fromkeys(reversed(matches))] + txt = _("""Add-ons possibly involved: {}\n""") + # highlight importance of first add-on: + addons[0] = "{}".format(addons[0]) + return txt.format(", ".join(addons)) def _supportText(self): import platform diff --git a/aqt/main.py b/aqt/main.py index bfac0e9f6..279e9f1d2 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -1171,11 +1171,19 @@ will be lost. Continue?""")) d.silentlyClose = True frm = aqt.forms.debug.Ui_Dialog() frm.setupUi(d) + font = QFontDatabase.systemFont(QFontDatabase.FixedFont) + font.setPointSize(frm.text.font().pointSize() + 1) + frm.text.setFont(font) + frm.log.setFont(font) s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+return"), d) s.activated.connect(lambda: self.onDebugRet(frm)) s = self.debugDiagShort = QShortcut( QKeySequence("ctrl+shift+return"), d) s.activated.connect(lambda: self.onDebugPrint(frm)) + s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+l"), d) + s.activated.connect(frm.log.clear) + s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d) + s.activated.connect(frm.text.clear) d.show() def _captureOutput(self, on): @@ -1201,7 +1209,16 @@ will be lost. Continue?""")) return aqt.dialogs._dialogs['Browser'][1].card.__dict__ def onDebugPrint(self, frm): - frm.text.setPlainText("pp(%s)" % frm.text.toPlainText()) + cursor = frm.text.textCursor() + position = cursor.position() + cursor.select(QTextCursor.LineUnderCursor) + line = cursor.selectedText() + pfx, sfx = "pp(", ")" + if not line.startswith(pfx): + line = "{}{}{}".format(pfx, line, sfx) + cursor.insertText(line) + cursor.setPosition(position + len(pfx)) + frm.text.setTextCursor(cursor) self.onDebugRet(frm) def onDebugRet(self, frm): diff --git a/aqt/update.py b/aqt/update.py index 1188c4b92..10272943d 100644 --- a/aqt/update.py +++ b/aqt/update.py @@ -10,6 +10,7 @@ import aqt from aqt.utils import openLink from anki.utils import json, platDesc from aqt.utils import showText +from aqt.utils import versionWithBuild class LatestVersionFinder(QThread): @@ -23,7 +24,7 @@ class LatestVersionFinder(QThread): self.config = main.pm.meta def _data(self): - d = {"ver": aqt.appVersion, + d = {"ver": versionWithBuild(), "os": platDesc(), "id": self.config['id'], "lm": self.config['lastMsg'], diff --git a/aqt/utils.py b/aqt/utils.py index fdfafb5ab..ee74071f7 100644 --- a/aqt/utils.py +++ b/aqt/utils.py @@ -50,7 +50,7 @@ def showInfo(text, parent=False, help="", type="info", title="Anki"): return mb.exec_() def showText(txt, parent=None, type="text", run=True, geomKey=None, \ - minWidth=500, minHeight=400, title="Anki"): + minWidth=500, minHeight=400, title="Anki", copyBtn=False): if not parent: parent = aqt.mw.app.activeWindow() or aqt.mw diag = QDialog(parent) @@ -66,6 +66,12 @@ def showText(txt, parent=None, type="text", run=True, geomKey=None, \ layout.addWidget(text) box = QDialogButtonBox(QDialogButtonBox.Close) layout.addWidget(box) + if copyBtn: + def onCopy(): + QApplication.clipboard().setText(text.toPlainText()) + btn = QPushButton(_("Copy to Clipboard")) + btn.clicked.connect(onCopy) + box.addButton(btn, QDialogButtonBox.ActionRole) def onReject(): if geomKey: saveGeom(diag, geomKey) diff --git a/designer/debug.ui b/designer/debug.ui index c8359bcbc..56a6cb44a 100644 --- a/designer/debug.ui +++ b/designer/debug.ui @@ -41,11 +41,6 @@ 8 - - - Courier - - Qt::ClickFocus