From d629ceb3b5ab92faafe8f96387882b7c7e69a5bc Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sat, 16 Feb 2019 10:26:49 +0100 Subject: [PATCH 01/16] Use system-default fixed font for debug entry and log --- aqt/main.py | 4 ++++ designer/debug.ui | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/aqt/main.py b/aqt/main.py index bfac0e9f6..a418d7735 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -1171,6 +1171,10 @@ 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( 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 From e22f691ae071eb56104cad8615f408942b5a12d9 Mon Sep 17 00:00:00 2001 From: Arthur-Milchior Date: Sat, 16 Feb 2019 11:28:02 +0100 Subject: [PATCH 02/16] Adding Arthur Milchior to contributors --- aqt/about.py | 1 + 1 file changed, 1 insertion(+) 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,\ From 89719ee5d534969f77efd09abc437bfae89885b3 Mon Sep 17 00:00:00 2001 From: Arthur-Milchior Date: Sat, 16 Feb 2019 11:28:50 +0100 Subject: [PATCH 03/16] Correct deck when previewing an existing card --- anki/collection.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/anki/collection.py b/anki/collection.py index cf8cb82d5..2a9e48223 100644 --- a/anki/collection.py +++ b/anki/collection.py @@ -437,10 +437,12 @@ insert into cards values (?,?,?,?,?,?,0,0,?,0,0,0,0,0,0,0,0,"")""", 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) + if not card.did: + if template['did'] and str(template['did']) in self.decks.decks: + card.did = template['did'] + else: + card.did = note.model()['did'] # if invalid did, use default instead deck = self.decks.get(card.did) if deck['dyn']: From 0b2869660e5dee1e55542a654c509e0b4238755f Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sat, 16 Feb 2019 10:31:35 +0100 Subject: [PATCH 04/16] Add hotkeys to clear debug log and entry (Ctrl+L / Ctrl+Shift+L) --- aqt/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aqt/main.py b/aqt/main.py index a418d7735..4c0b38f4d 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -1180,6 +1180,10 @@ will be lost. Continue?""")) 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): From fd4b5c11694310197a10c5ad9e351e75d0afc17d Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sat, 16 Feb 2019 10:57:09 +0100 Subject: [PATCH 05/16] Print-wrap current line rather than the entire field Also: Avoid duplicate wraps, retain cursor position, and preserve undo history. --- aqt/main.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/aqt/main.py b/aqt/main.py index 4c0b38f4d..279e9f1d2 100644 --- a/aqt/main.py +++ b/aqt/main.py @@ -1209,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): From 20ff61e59eae6c32e4671c035a0d5438d169ab2f Mon Sep 17 00:00:00 2001 From: Arthur-Milchior Date: Sat, 16 Feb 2019 12:25:22 +0100 Subject: [PATCH 06/16] Show deck of the addCard window --- anki/collection.py | 10 ++++++---- aqt/clayout.py | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/anki/collection.py b/anki/collection.py index 2a9e48223..8f6221ba3 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,21 @@ 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, did = None, flush=True): "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 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 diff --git a/aqt/clayout.py b/aqt/clayout.py index d0414c6bf..2e9764cee 100644 --- a/aqt/clayout.py +++ b/aqt/clayout.py @@ -61,7 +61,8 @@ class CardLayout(QDialog): self.setFocus() def redraw(self): - self.cards = self.col.previewCards(self.note, 2) + did = self.parent.deckChooser.selectedId() if self.addMode else None + self.cards = self.col.previewCards(self.note, 2, did = did) idx = self.ord if idx >= len(self.cards): self.ord = len(self.cards) - 1 From 5deb905b27351c46fc1bf9ae2675cb8f14391b0e Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sat, 16 Feb 2019 23:05:06 +0100 Subject: [PATCH 07/16] Extend showInfo with ability to copy text to clipboard Use this in error dialog --- aqt/errors.py | 2 +- aqt/utils.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/aqt/errors.py b/aqt/errors.py index 6b38174cc..6da93b8fd 100644 --- a/aqt/errors.py +++ b/aqt/errors.py @@ -130,7 +130,7 @@ add-ons section of our support site. error = self._supportText() + "\n" + error txt = txt + "

" + error + "
" - showText(txt, type="html") + showText(txt, type="html", copyBtn=True) def _supportText(self): import platform diff --git a/aqt/utils.py b/aqt/utils.py index 1221ce8b7..0e6168417 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) From 5bfdc740f5162cefd30570c6b93aeaaedbc93087 Mon Sep 17 00:00:00 2001 From: Glutanimate Date: Sun, 17 Feb 2019 00:35:44 +0100 Subject: [PATCH 08/16] Show a list of potentially affected add-ons when raising error --- aqt/errors.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/aqt/errors.py b/aqt/errors.py index 6da93b8fd..699593552 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,17 +123,30 @@ 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", 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 = _("""Potentially affected add-ons: {}\n""") + # highlight importance of first add-on: + addons[0] = "{}".format(addons[0]) + return txt.format(", ".join(addons)) + def _supportText(self): import platform from aqt.utils import versionWithBuild From 6532023c3eb7ad69f1ca9e528a1a467a26ebe448 Mon Sep 17 00:00:00 2001 From: Arthur-Milchior Date: Sun, 17 Feb 2019 19:26:31 +0100 Subject: [PATCH 09/16] Correcting a small bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I though «addMode» meant «call from addcard window». Instead, to know whether parent is addcard, I check whether it has attribute deckChooser --- aqt/clayout.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aqt/clayout.py b/aqt/clayout.py index 2e9764cee..037f3b954 100644 --- a/aqt/clayout.py +++ b/aqt/clayout.py @@ -61,7 +61,9 @@ class CardLayout(QDialog): self.setFocus() def redraw(self): - did = self.parent.deckChooser.selectedId() if self.addMode else None + 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): From d8f059b570a8f2e99348a8b6c2b521f46bf141ef Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 18 Feb 2019 12:44:04 +1000 Subject: [PATCH 10/16] style and arg order fix --- anki/collection.py | 6 +++--- aqt/clayout.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/anki/collection.py b/anki/collection.py index 8f6221ba3..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, did = None): + def previewCards(self, note, type=0, did=None): if type == 0: cms = self.findTemplates(note) elif type == 1: @@ -428,10 +428,10 @@ 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, did = did)) + cards.append(self._newCard(note, template, 1, flush=False, did=did)) return cards - def _newCard(self, note, template, due, did = None, 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 diff --git a/aqt/clayout.py b/aqt/clayout.py index 037f3b954..207537ee8 100644 --- a/aqt/clayout.py +++ b/aqt/clayout.py @@ -64,7 +64,7 @@ class CardLayout(QDialog): did = None if hasattr(self.parent,"deckChooser"): did = self.parent.deckChooser.selectedId() - self.cards = self.col.previewCards(self.note, 2, did = did) + self.cards = self.col.previewCards(self.note, 2, did=did) idx = self.ord if idx >= len(self.cards): self.ord = len(self.cards) - 1 From dcec361410cebb1af17975975b907aa21ec2c420 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 18 Feb 2019 13:07:20 +1000 Subject: [PATCH 11/16] wording tweak --- aqt/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aqt/errors.py b/aqt/errors.py index 699593552..821be049e 100644 --- a/aqt/errors.py +++ b/aqt/errors.py @@ -142,7 +142,7 @@ add-ons section of our support site. # reverse to list most likely suspect first, dict to deduplicate: addons = [mw.addonManager.addonName(i) for i in dict.fromkeys(reversed(matches))] - txt = _("""Potentially affected add-ons: {}\n""") + txt = _("""Add-ons possibly involved: {}\n""") # highlight importance of first add-on: addons[0] = "{}".format(addons[0]) return txt.format(", ".join(addons)) From bca31bf38b7ea321960b35e25c20450e449e00b1 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 18 Feb 2019 14:07:43 +1000 Subject: [PATCH 12/16] send build hash in update check so broken betas can be notified --- aqt/update.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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'], From c670dd65b4265b01331ba5c82f1a6b62aa802e92 Mon Sep 17 00:00:00 2001 From: Arthur-Milchior Date: Mon, 18 Feb 2019 12:19:43 +0100 Subject: [PATCH 13/16] Correct a bug during add-on update If an add-on folder contains only number, but does not contains a meta.json file, or if this file does not contains a "mod" value, then the following uninformative message error occur: ```Python File "aqt/addons.py", line 387, in onCheckForUpdates File "aqt/addons.py", line 183, in checkForUpdates File "aqt/addons.py", line 199, in _updatedIds : '<' not supported between instances of 'NoneType' and 'int' ``` This is because there is a .get in a code while the None value makes no sens. Thus, I replaced None by a 0 value. Which ensure that, if the last modification time is missing, the update will be done. Three case may occur: * either the addon is already up to date, and it's only a waste of bandwidth * either the add-on is not up to date, and updating was the initial goal anyway * Or some change did occur in the add-on folder (which is actually probably, since it would explain the "missing mod problem"; in this case this change may be lost, but thout would be the same problem if the mod number was still there. Other solutions which I could implement would be: * asking for the user whether they want to update * considering that it's not an ankiweb related add-on anymore, and ignore it. --- aqt/addons.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aqt/addons.py b/aqt/addons.py index 5a3868245..a74b3ddd5 100644 --- a/aqt/addons.py +++ b/aqt/addons.py @@ -196,7 +196,7 @@ When loading '%(name)s': 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 From 819d0bdaf3719dee0477305b801c98fd2e33d8fe Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 19 Feb 2019 08:01:11 +1000 Subject: [PATCH 14/16] fix editcurrent not freeing webview on close --- aqt/editcurrent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 95ccbfdd3679dd46f22847c539c7fddb8fa904ea Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Tue, 19 Feb 2019 08:04:25 +1000 Subject: [PATCH 15/16] send build number when syncing too --- anki/sync.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From ae67c97610e0e9aa355d3fa60af07982bd2dd87a Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Wed, 20 Feb 2019 14:38:22 +1000 Subject: [PATCH 16/16] remember add-ons window geometry --- aqt/addons.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/aqt/addons.py b/aqt/addons.py index a74b3ddd5..fd529d7bb 100644 --- a/aqt/addons.py +++ b/aqt/addons.py @@ -304,8 +304,13 @@ class AddonsDialog(QDialog): f.config.clicked.connect(self.onConfig) self.form.addonList.currentRowChanged.connect(self._onAddonItemSelected) self.redrawAddons() + restoreGeom(self, "addons") self.show() + 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()