add qconnect helper and some type hints

The type hints allow mypy to check the gui_hook calls, revealing a
bunch of places that are broken as they expect no arguments like the
legacy hooks.

To make mypy happy about PyQt's signal.connect(func), a qconnect()
helper has been added.
This commit is contained in:
Damien Elmes 2020-01-16 07:41:23 +10:00
parent c6d0425020
commit 8310cb7a0e
14 changed files with 83 additions and 74 deletions

View file

@ -1,7 +1,7 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import Callable, List from typing import Callable, List, Optional
import aqt.deckchooser import aqt.deckchooser
import aqt.editor import aqt.editor
@ -143,7 +143,7 @@ class AddCards(QDialog):
self.history = self.history[:15] self.history = self.history[:15]
self.historyButton.setEnabled(True) self.historyButton.setEnabled(True)
def onHistory(self): def onHistory(self) -> None:
m = QMenu(self) m = QMenu(self)
for nid in self.history: for nid in self.history:
if self.mw.col.findNotes("nid:%s" % nid): if self.mw.col.findNotes("nid:%s" % nid):
@ -152,7 +152,7 @@ class AddCards(QDialog):
if len(txt) > 30: if len(txt) > 30:
txt = txt[:30] + "..." txt = txt[:30] + "..."
a = m.addAction(_('Edit "%s"') % txt) a = m.addAction(_('Edit "%s"') % txt)
a.triggered.connect(lambda b, nid=nid: self.editHistory(nid)) qconnect(a.triggered, lambda b, nid=nid: self.editHistory(nid))
else: else:
a = m.addAction(_("(Note deleted)")) a = m.addAction(_("(Note deleted)"))
a.setEnabled(False) a.setEnabled(False)
@ -164,12 +164,12 @@ class AddCards(QDialog):
browser.form.searchEdit.lineEdit().setText("nid:%d" % nid) browser.form.searchEdit.lineEdit().setText("nid:%d" % nid)
browser.onSearchActivated() browser.onSearchActivated()
def addNote(self, note): def addNote(self, note) -> Optional[Note]:
note.model()["did"] = self.deckChooser.selectedId() note.model()["did"] = self.deckChooser.selectedId()
ret = note.dupeOrEmpty() ret = note.dupeOrEmpty()
if ret == 1: if ret == 1:
showWarning(_("The first field is empty."), help="AddItems#AddError") showWarning(_("The first field is empty."), help="AddItems#AddError")
return return None
if "{{cloze:" in note.model()["tmpls"][0]["qfmt"]: if "{{cloze:" in note.model()["tmpls"][0]["qfmt"]:
if not self.mw.col.models._availClozeOrds( if not self.mw.col.models._availClozeOrds(
note.model(), note.joinedFields(), False note.model(), note.joinedFields(), False
@ -180,7 +180,7 @@ class AddCards(QDialog):
"but have not made any cloze deletions. Proceed?" "but have not made any cloze deletions. Proceed?"
) )
): ):
return return None
cards = self.mw.col.addNote(note) cards = self.mw.col.addNote(note)
if not cards: if not cards:
showWarning( showWarning(
@ -191,7 +191,7 @@ question on all cards."""
), ),
help="AddItems", help="AddItems",
) )
return return None
self.mw.col.clearUndo() self.mw.col.clearUndo()
self.addHistory(note) self.addHistory(note)
self.mw.requireReset() self.mw.requireReset()

View file

@ -9,11 +9,12 @@ import sre_constants
import time import time
import unicodedata import unicodedata
from operator import itemgetter from operator import itemgetter
from typing import Callable, List, Optional from typing import Callable, List, Optional, Union
import anki import anki
import aqt.forms import aqt.forms
from anki import hooks from anki import hooks
from anki.cards import Card
from anki.collection import _Collection from anki.collection import _Collection
from anki.consts import * from anki.consts import *
from anki.lang import _, ngettext from anki.lang import _, ngettext
@ -27,6 +28,7 @@ from anki.utils import (
isWin, isWin,
) )
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.editor import Editor
from aqt.qt import * from aqt.qt import *
from aqt.sound import allSounds, clearAudioQueue, play from aqt.sound import allSounds, clearAudioQueue, play
from aqt.utils import ( from aqt.utils import (
@ -556,8 +558,9 @@ class Browser(QMainWindow):
model: DataModel model: DataModel
mw: AnkiQt mw: AnkiQt
col: _Collection col: _Collection
editor: Optional[Editor]
def __init__(self, mw: AnkiQt): def __init__(self, mw: AnkiQt) -> None:
QMainWindow.__init__(self, None, Qt.Window) QMainWindow.__init__(self, None, Qt.Window)
self.mw = mw self.mw = mw
self.col = self.mw.col self.col = self.mw.col
@ -572,7 +575,7 @@ class Browser(QMainWindow):
restoreState(self, "editor") restoreState(self, "editor")
restoreSplitter(self.form.splitter, "editor3") restoreSplitter(self.form.splitter, "editor3")
self.form.splitter.setChildrenCollapsible(False) self.form.splitter.setChildrenCollapsible(False)
self.card = None self.card: Optional[Card] = None
self.setupColumns() self.setupColumns()
self.setupTable() self.setupTable()
self.setupMenus() self.setupMenus()
@ -584,7 +587,7 @@ class Browser(QMainWindow):
self.setupSearch() self.setupSearch()
self.show() self.show()
def setupMenus(self): def setupMenus(self) -> None:
# pylint: disable=unnecessary-lambda # pylint: disable=unnecessary-lambda
# actions # actions
f = self.form f = self.form
@ -635,9 +638,9 @@ class Browser(QMainWindow):
f.actionGuide.triggered.connect(self.onHelp) f.actionGuide.triggered.connect(self.onHelp)
# keyboard shortcut for shift+home/end # keyboard shortcut for shift+home/end
self.pgUpCut = QShortcut(QKeySequence("Shift+Home"), self) self.pgUpCut = QShortcut(QKeySequence("Shift+Home"), self)
self.pgUpCut.activated.connect(self.onFirstCard) qconnect(self.pgUpCut.activated, self.onFirstCard)
self.pgDownCut = QShortcut(QKeySequence("Shift+End"), self) self.pgDownCut = QShortcut(QKeySequence("Shift+End"), self)
self.pgDownCut.activated.connect(self.onLastCard) qconnect(self.pgDownCut.activated, self.onLastCard)
# add-on hook # add-on hook
gui_hooks.browser_menus_did_init(self) gui_hooks.browser_menus_did_init(self)
self.mw.maybeHideAccelerators(self) self.mw.maybeHideAccelerators(self)
@ -646,14 +649,14 @@ class Browser(QMainWindow):
self.form.tableView.setContextMenuPolicy(Qt.CustomContextMenu) self.form.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
self.form.tableView.customContextMenuRequested.connect(self.onContextMenu) self.form.tableView.customContextMenuRequested.connect(self.onContextMenu)
def onContextMenu(self, _point): def onContextMenu(self, _point) -> None:
m = QMenu() m = QMenu()
for act in self.form.menu_Cards.actions(): for act in self.form.menu_Cards.actions():
m.addAction(act) m.addAction(act)
m.addSeparator() m.addSeparator()
for act in self.form.menu_Notes.actions(): for act in self.form.menu_Notes.actions():
m.addAction(act) m.addAction(act)
gui_hooks.browser_will_show_context_menu(self, m) gui_hooks.browser_will_show_context_menu(self)
qtMenuShortcutWorkaround(m) qtMenuShortcutWorkaround(m)
m.exec_(QCursor.pos()) m.exec_(QCursor.pos())
@ -771,7 +774,7 @@ class Browser(QMainWindow):
self.search() self.search()
# search triggered programmatically. caller must have saved note first. # search triggered programmatically. caller must have saved note first.
def search(self): def search(self) -> None:
if "is:current" in self._lastSearchTxt: if "is:current" in self._lastSearchTxt:
# show current card if there is one # show current card if there is one
c = self.mw.reviewer.card c = self.mw.reviewer.card
@ -827,7 +830,7 @@ class Browser(QMainWindow):
"Update current note and hide/show editor." "Update current note and hide/show editor."
self.editor.saveNow(lambda: self._onRowChanged(current, previous)) self.editor.saveNow(lambda: self._onRowChanged(current, previous))
def _onRowChanged(self, current, previous): def _onRowChanged(self, current, previous) -> None:
update = self.updateTitle() update = self.updateTitle()
show = self.model.cards and update == 1 show = self.model.cards and update == 1
self.form.splitter.widget(1).setVisible(not not show) self.form.splitter.widget(1).setVisible(not not show)
@ -841,7 +844,7 @@ class Browser(QMainWindow):
else: else:
self.editor.setNote(self.card.note(reload=True), focusTo=self.focusTo) self.editor.setNote(self.card.note(reload=True), focusTo=self.focusTo)
self.focusTo = None self.focusTo = None
self.editor.card = self.card self.editor.card = self.card # type: ignore
self.singleCard = True self.singleCard = True
self._updateFlagsMenu() self._updateFlagsMenu()
gui_hooks.browser_did_change_row(self) gui_hooks.browser_did_change_row(self)
@ -1530,7 +1533,7 @@ where id in %s"""
###################################################################### ######################################################################
_previewTimer = None _previewTimer = None
_lastPreviewRender = 0 _lastPreviewRender: Union[int,float] = 0
_lastPreviewState = None _lastPreviewState = None
_previewCardChanged = False _previewCardChanged = False
@ -1669,7 +1672,7 @@ where id in %s"""
self._previewTimer.stop() self._previewTimer.stop()
self._previewTimer = None self._previewTimer = None
def _renderScheduledPreview(self): def _renderScheduledPreview(self) -> None:
self._cancelPreviewTimer() self._cancelPreviewTimer()
self._lastPreviewRender = time.time() self._lastPreviewRender = time.time()
@ -2019,7 +2022,7 @@ update cards set usn=?, mod=?, did=? where id in """
# Edit: undo # Edit: undo
###################################################################### ######################################################################
def setupHooks(self): def setupHooks(self) -> None:
gui_hooks.undo_state_did_change.append(self.onUndoState) gui_hooks.undo_state_did_change.append(self.onUndoState)
gui_hooks.state_did_reset.append(self.onReset) gui_hooks.state_did_reset.append(self.onReset)
gui_hooks.editor_did_fire_typing_timer.append(self.refreshCurrentCard) gui_hooks.editor_did_fire_typing_timer.append(self.refreshCurrentCard)
@ -2029,7 +2032,7 @@ update cards set usn=?, mod=?, did=? where id in """
hooks.note_type_added.append(self.maybeRefreshSidebar) hooks.note_type_added.append(self.maybeRefreshSidebar)
hooks.deck_added.append(self.maybeRefreshSidebar) hooks.deck_added.append(self.maybeRefreshSidebar)
def teardownHooks(self): def teardownHooks(self) -> None:
gui_hooks.undo_state_did_change.remove(self.onUndoState) gui_hooks.undo_state_did_change.remove(self.onUndoState)
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
gui_hooks.editor_did_fire_typing_timer.remove(self.refreshCurrentCard) gui_hooks.editor_did_fire_typing_timer.remove(self.refreshCurrentCard)
@ -2261,7 +2264,7 @@ update cards set usn=?, mod=?, did=? where id in """
class ChangeModel(QDialog): class ChangeModel(QDialog):
def __init__(self, browser, nids): def __init__(self, browser, nids) -> None:
QDialog.__init__(self, browser) QDialog.__init__(self, browser)
self.browser = browser self.browser = browser
self.nids = nids self.nids = nids
@ -2394,7 +2397,7 @@ class ChangeModel(QDialog):
old=self.oldModel["flds"], combos=self.fcombos, new=self.targetModel["flds"] old=self.oldModel["flds"], combos=self.fcombos, new=self.targetModel["flds"]
) )
def cleanup(self): def cleanup(self) -> None:
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
gui_hooks.current_note_type_did_change.remove(self.onReset) gui_hooks.current_note_type_did_change.remove(self.onReset)
self.modelChooser.cleanup() self.modelChooser.cleanup()

View file

@ -326,7 +326,7 @@ Please create a new card type first."""
self._previewTimer.stop() self._previewTimer.stop()
self._previewTimer = None self._previewTimer = None
def _renderPreview(self): def _renderPreview(self) -> None:
self.cancelPreviewTimer() self.cancelPreviewTimer()
c = self.card c = self.card

View file

@ -235,16 +235,16 @@ where id > ?""",
# Options # Options
########################################################################## ##########################################################################
def _showOptions(self, did): def _showOptions(self, did) -> None:
m = QMenu(self.mw) m = QMenu(self.mw)
a = m.addAction(_("Rename")) a = m.addAction(_("Rename"))
a.triggered.connect(lambda b, did=did: self._rename(did)) qconnect(a.triggered, lambda b, did=did: self._rename(did))
a = m.addAction(_("Options")) a = m.addAction(_("Options"))
a.triggered.connect(lambda b, did=did: self._options(did)) qconnect(a.triggered, lambda b, did=did: self._options(did))
a = m.addAction(_("Export")) a = m.addAction(_("Export"))
a.triggered.connect(lambda b, did=did: self._export(did)) qconnect(a.triggered, lambda b, did=did: self._export(did))
a = m.addAction(_("Delete")) a = m.addAction(_("Delete"))
a.triggered.connect(lambda b, did=did: self._delete(did)) qconnect(a.triggered, lambda b, did=did: self._delete(did))
gui_hooks.deck_browser_will_show_options_menu(m, did) gui_hooks.deck_browser_will_show_options_menu(m, did)
m.exec_(QCursor.pos()) m.exec_(QCursor.pos())

View file

@ -9,9 +9,9 @@ from aqt.utils import shortcut
class DeckChooser(QHBoxLayout): class DeckChooser(QHBoxLayout):
def __init__(self, mw, widget, label=True, start=None): def __init__(self, mw, widget: QWidget, label=True, start=None) -> None:
QHBoxLayout.__init__(self) QHBoxLayout.__init__(self)
self.widget = widget self.widget = widget # type: ignore
self.mw = mw self.mw = mw
self.deck = mw.col self.deck = mw.col
self.label = label self.label = label
@ -63,7 +63,7 @@ class DeckChooser(QHBoxLayout):
def hide(self): def hide(self):
self.widget.hide() self.widget.hide()
def cleanup(self): def cleanup(self) -> None:
gui_hooks.current_note_type_did_change.remove(self.onModelChange) gui_hooks.current_note_type_did_change.remove(self.onModelChange)
def onModelChange(self): def onModelChange(self):

View file

@ -10,7 +10,7 @@ from aqt.utils import restoreGeom, saveGeom, tooltip
class EditCurrent(QDialog): class EditCurrent(QDialog):
def __init__(self, mw): def __init__(self, mw) -> None:
QDialog.__init__(self, None, Qt.Window) QDialog.__init__(self, None, Qt.Window)
mw.setupDialogGC(self) mw.setupDialogGC(self)
self.mw = mw self.mw = mw
@ -33,7 +33,7 @@ class EditCurrent(QDialog):
# pylint: disable=unnecessary-lambda # pylint: disable=unnecessary-lambda
self.mw.progress.timer(100, lambda: self.editor.web.setFocus(), False) self.mw.progress.timer(100, lambda: self.editor.web.setFocus(), False)
def onReset(self): def onReset(self) -> None:
# lazy approach for now: throw away edits # lazy approach for now: throw away edits
try: try:
n = self.editor.note n = self.editor.note
@ -58,7 +58,7 @@ class EditCurrent(QDialog):
def saveAndClose(self): def saveAndClose(self):
self.editor.saveNow(self._saveAndClose) self.editor.saveNow(self._saveAndClose)
def _saveAndClose(self): def _saveAndClose(self) -> None:
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
r = self.mw.reviewer r = self.mw.reviewer
try: try:

View file

@ -21,6 +21,7 @@ import aqt
import aqt.sound import aqt.sound
from anki.hooks import runFilter from anki.hooks import runFilter
from anki.lang import _ from anki.lang import _
from anki.notes import Note
from anki.sync import AnkiRequestsClient from anki.sync import AnkiRequestsClient
from anki.utils import checksum, isWin, namedtmp, stripHTMLMedia from anki.utils import checksum, isWin, namedtmp, stripHTMLMedia
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
@ -67,13 +68,13 @@ html { background: %s; }
# caller is responsible for resetting note on reset # caller is responsible for resetting note on reset
class Editor: class Editor:
def __init__(self, mw: AnkiQt, widget, parentWindow, addMode=False): def __init__(self, mw: AnkiQt, widget, parentWindow, addMode=False) -> None:
self.mw = mw self.mw = mw
self.widget = widget self.widget = widget
self.parentWindow = parentWindow self.parentWindow = parentWindow
self.note = None self.note: Optional[Note] = None
self.addMode = addMode self.addMode = addMode
self.currentField = None self.currentField: Optional[int] = None
# current card, for card layout # current card, for card layout
self.card = None self.card = None
self.setupOuter() self.setupOuter()
@ -91,7 +92,7 @@ class Editor:
self.widget.setLayout(l) self.widget.setLayout(l)
self.outerLayout = l self.outerLayout = l
def setupWeb(self): def setupWeb(self) -> None:
self.web = EditorWebView(self.widget, self) self.web = EditorWebView(self.widget, self)
self.web.title = "editor" self.web.title = "editor"
self.web.allowDrops = True self.web.allowDrops = True
@ -159,7 +160,7 @@ class Editor:
fldsTitle=_("Customize Fields"), fldsTitle=_("Customize Fields"),
cardsTitle=shortcut(_("Customize Card Templates (Ctrl+L)")), cardsTitle=shortcut(_("Customize Card Templates (Ctrl+L)")),
) )
bgcol = self.mw.app.palette().window().color().name() bgcol = self.mw.app.palette().window().color().name() # type: ignore
# then load page # then load page
self.web.stdHtml( self.web.stdHtml(
_html % (bgcol, bgcol, topbuts, _("Show Duplicates")), _html % (bgcol, bgcol, topbuts, _("Show Duplicates")),
@ -260,7 +261,7 @@ class Editor:
) )
) )
def setupShortcuts(self): def setupShortcuts(self) -> None:
# if a third element is provided, enable shortcut even when no field selected # if a third element is provided, enable shortcut even when no field selected
cuts: List[Tuple] = [ cuts: List[Tuple] = [
("Ctrl+L", self.onCardLayout, True), ("Ctrl+L", self.onCardLayout, True),
@ -292,7 +293,7 @@ class Editor:
fn = self._addFocusCheck(fn) fn = self._addFocusCheck(fn)
else: else:
keys, fn, _ = row keys, fn, _ = row
QShortcut(QKeySequence(keys), self.widget, activated=fn) QShortcut(QKeySequence(keys), self.widget, activated=fn) # type: ignore
def _addFocusCheck(self, fn): def _addFocusCheck(self, fn):
def checkFocus(): def checkFocus():
@ -329,7 +330,7 @@ class Editor:
# JS->Python bridge # JS->Python bridge
###################################################################### ######################################################################
def onBridgeCmd(self, cmd): def onBridgeCmd(self, cmd) -> None:
if not self.note: if not self.note:
# shutdown # shutdown
return return
@ -399,7 +400,7 @@ class Editor:
def loadNoteKeepingFocus(self): def loadNoteKeepingFocus(self):
self.loadNote(self.currentField) self.loadNote(self.currentField)
def loadNote(self, focusTo=None): def loadNote(self, focusTo=None) -> None:
if not self.note: if not self.note:
return return
@ -426,7 +427,7 @@ class Editor:
) )
self.web.evalWithCallback(js, oncallback) self.web.evalWithCallback(js, oncallback)
def fonts(self): def fonts(self) -> List[Tuple[str,int,bool]]:
return [ return [
(gui_hooks.editor_will_use_font_for_field(f["font"]), f["size"], f["rtl"]) (gui_hooks.editor_will_use_font_for_field(f["font"]), f["size"], f["rtl"])
for f in self.note.model()["flds"] for f in self.note.model()["flds"]
@ -528,7 +529,7 @@ class Editor:
if not self.tags.text() or not self.addMode: if not self.tags.text() or not self.addMode:
self.tags.setText(self.note.stringTags().strip()) self.tags.setText(self.note.stringTags().strip())
def saveTags(self): def saveTags(self) -> None:
if not self.note: if not self.note:
return return
tagsTxt = unicodedata.normalize("NFC", self.tags.text()) tagsTxt = unicodedata.normalize("NFC", self.tags.text())
@ -1103,14 +1104,14 @@ class EditorWebView(AnkiWebView):
mime.setHtml("<!--anki-->" + html) mime.setHtml("<!--anki-->" + html)
clip.setMimeData(mime) clip.setMimeData(mime)
def contextMenuEvent(self, evt): def contextMenuEvent(self, evt) -> None:
m = QMenu(self) m = QMenu(self)
a = m.addAction(_("Cut")) a = m.addAction(_("Cut"))
a.triggered.connect(self.onCut) qconnect(a.triggered, self.onCut)
a = m.addAction(_("Copy")) a = m.addAction(_("Copy"))
a.triggered.connect(self.onCopy) qconnect(a.triggered, self.onCopy)
a = m.addAction(_("Paste")) a = m.addAction(_("Paste"))
a.triggered.connect(self.onPaste) qconnect(a.triggered, self.onPaste)
gui_hooks.editor_will_show_context_menu(self, m) gui_hooks.editor_will_show_context_menu(self, m)
m.popup(QCursor.pos()) m.popup(QCursor.pos())

View file

@ -73,7 +73,7 @@ class ChangeMap(QDialog):
class ImportDialog(QDialog): class ImportDialog(QDialog):
def __init__(self, mw: AnkiQt, importer): def __init__(self, mw: AnkiQt, importer) -> None:
QDialog.__init__(self, mw, Qt.Window) QDialog.__init__(self, mw, Qt.Window)
self.mw = mw self.mw = mw
self.importer = importer self.importer = importer
@ -278,7 +278,7 @@ you can enter it here. Use \\t to represent tab."""
else: else:
self.showMapping(keepMapping=True) self.showMapping(keepMapping=True)
def reject(self): def reject(self) -> None:
self.modelChooser.cleanup() self.modelChooser.cleanup()
self.deck.cleanup() self.deck.cleanup()
gui_hooks.current_note_type_did_change.remove(self.modelChanged) gui_hooks.current_note_type_did_change.remove(self.modelChanged)

View file

@ -569,7 +569,7 @@ from the profile screen."
def _deckBrowserState(self, oldState: str) -> None: def _deckBrowserState(self, oldState: str) -> None:
self.deckBrowser.show() self.deckBrowser.show()
def _colLoadingState(self, oldState): def _colLoadingState(self, oldState) -> None:
"Run once, when col is loaded." "Run once, when col is loaded."
self.enableColMenuItems() self.enableColMenuItems()
# ensure cwd is set if media dir exists # ensure cwd is set if media dir exists
@ -918,7 +918,7 @@ QTreeWidget {
# Undo & autosave # Undo & autosave
########################################################################## ##########################################################################
def onUndo(self): def onUndo(self) -> None:
n = self.col.undoName() n = self.col.undoName()
if not n: if not n:
return return

View file

@ -9,9 +9,9 @@ from aqt.utils import shortcut
class ModelChooser(QHBoxLayout): class ModelChooser(QHBoxLayout):
def __init__(self, mw, widget, label=True): def __init__(self, mw, widget, label=True) -> None:
QHBoxLayout.__init__(self) QHBoxLayout.__init__(self)
self.widget = widget self.widget = widget # type: ignore
self.mw = mw self.mw = mw
self.deck = mw.col self.deck = mw.col
self.label = label self.label = label
@ -19,7 +19,7 @@ class ModelChooser(QHBoxLayout):
self.setSpacing(8) self.setSpacing(8)
self.setupModels() self.setupModels()
gui_hooks.state_did_reset.append(self.onReset) gui_hooks.state_did_reset.append(self.onReset)
self.widget.setLayout(self) self.widget.setLayout(self) # type: ignore
def setupModels(self): def setupModels(self):
if self.label: if self.label:
@ -40,7 +40,7 @@ class ModelChooser(QHBoxLayout):
self.models.setSizePolicy(sizePolicy) self.models.setSizePolicy(sizePolicy)
self.updateModels() self.updateModels()
def cleanup(self): def cleanup(self) -> None:
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
def onReset(self): def onReset(self):
@ -57,12 +57,12 @@ class ModelChooser(QHBoxLayout):
aqt.models.Models(self.mw, self.widget) aqt.models.Models(self.mw, self.widget)
def onModelChange(self): def onModelChange(self) -> None:
from aqt.studydeck import StudyDeck from aqt.studydeck import StudyDeck
current = self.deck.models.current()["name"] current = self.deck.models.current()["name"]
# edit button # edit button
edit = QPushButton(_("Manage"), clicked=self.onEdit) edit = QPushButton(_("Manage"), clicked=self.onEdit) # type: ignore
def nameFunc(): def nameFunc():
return sorted(self.deck.models.allNames()) return sorted(self.deck.models.allNames())

View file

@ -14,6 +14,7 @@ from PyQt5.QtCore import pyqtRemoveInputHook # pylint: disable=no-name-in-modul
from PyQt5.QtGui import * # type: ignore from PyQt5.QtGui import * # type: ignore
from PyQt5.QtWebEngineWidgets import * # type: ignore from PyQt5.QtWebEngineWidgets import * # type: ignore
from PyQt5.QtWidgets import * from PyQt5.QtWidgets import *
from typing import Callable
from anki.utils import isMac, isWin from anki.utils import isMac, isWin
@ -52,3 +53,7 @@ qtpoint = QT_VERSION & 0xFF
if qtmajor != 5 or qtminor < 9 or qtminor == 10: if qtmajor != 5 or qtminor < 9 or qtminor == 10:
raise Exception("Anki does not support your Qt version.") raise Exception("Anki does not support your Qt version.")
def qconnect(signal: Callable, func: Callable) -> None:
"Helper to work around type checking not working with signal.connect(func)."
signal.connect(func) # type: ignore

View file

@ -8,7 +8,7 @@ import html.parser
import json import json
import re import re
import unicodedata as ucd import unicodedata as ucd
from typing import List from typing import List, Optional
import aqt import aqt
from anki import hooks from anki import hooks
@ -33,13 +33,13 @@ class Reviewer:
def __init__(self, mw: AnkiQt) -> None: def __init__(self, mw: AnkiQt) -> None:
self.mw = mw self.mw = mw
self.web = mw.web self.web = mw.web
self.card = None self.card: Optional[Card] = None
self.cardQueue: List[Card] = [] self.cardQueue: List[Card] = []
self.hadCardQueue = False self.hadCardQueue = False
self._answeredIds: List[int] = [] self._answeredIds: List[int] = []
self._recordedAudio = None self._recordedAudio = None
self.typeCorrect = None # web init happens before this is set self.typeCorrect = None # web init happens before this is set
self.state = None self.state: Optional[str] = None
self.bottom = aqt.toolbar.BottomBar(mw, mw.bottomWeb) self.bottom = aqt.toolbar.BottomBar(mw, mw.bottomWeb)
hooks.card_did_leech.append(self.onLeech) hooks.card_did_leech.append(self.onLeech)
@ -61,7 +61,7 @@ class Reviewer:
# id was deleted # id was deleted
return return
def cleanup(self): def cleanup(self) -> None:
gui_hooks.reviewer_will_end() gui_hooks.reviewer_will_end()
# Fetching a card # Fetching a card
@ -170,7 +170,7 @@ class Reviewer:
def _mungeQA(self, buf): def _mungeQA(self, buf):
return self.typeAnsFilter(mungeQA(self.mw.col, buf)) return self.typeAnsFilter(mungeQA(self.mw.col, buf))
def _showQuestion(self): def _showQuestion(self) -> None:
self._reps += 1 self._reps += 1
self.state = "question" self.state = "question"
self.typedAnswer = None self.typedAnswer = None
@ -217,7 +217,7 @@ The front of this card is empty. Please run Tools>Empty Cards."""
# Showing the answer # Showing the answer
########################################################################## ##########################################################################
def _showAnswer(self): def _showAnswer(self) -> None:
if self.mw.state != "review": if self.mw.state != "review":
# showing resetRequired screen; ignore space # showing resetRequired screen; ignore space
return return
@ -689,7 +689,7 @@ time = %(time)d;
] ]
return opts return opts
def showContextMenu(self): def showContextMenu(self) -> None:
opts = self._contextMenu() opts = self._contextMenu()
m = QMenu(self.mw) m = QMenu(self.mw)
self._addMenuItems(m, opts) self._addMenuItems(m, opts)

View file

@ -23,7 +23,7 @@ class StudyDeck(QDialog):
dyn=False, dyn=False,
buttons=None, buttons=None,
geomKey="default", geomKey="default",
): ) -> None:
QDialog.__init__(self, parent or mw) QDialog.__init__(self, parent or mw)
if buttons is None: if buttons is None:
buttons = [] buttons = []
@ -118,7 +118,7 @@ class StudyDeck(QDialog):
self.origNames = self.nameFunc() self.origNames = self.nameFunc()
self.redraw(self.filt, self.focus) self.redraw(self.filt, self.focus)
def accept(self): def accept(self) -> None:
saveGeom(self, self.geomKey) saveGeom(self, self.geomKey)
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
row = self.form.list.currentRow() row = self.form.list.currentRow()
@ -128,12 +128,12 @@ class StudyDeck(QDialog):
self.name = self.names[self.form.list.currentRow()] self.name = self.names[self.form.list.currentRow()]
QDialog.accept(self) QDialog.accept(self)
def reject(self): def reject(self) -> None:
saveGeom(self, self.geomKey) saveGeom(self, self.geomKey)
gui_hooks.state_did_reset.remove(self.onReset) gui_hooks.state_did_reset.remove(self.onReset)
QDialog.reject(self) QDialog.reject(self)
def onAddDeck(self): def onAddDeck(self) -> None:
row = self.form.list.currentRow() row = self.form.list.currentRow()
if row < 0: if row < 0:
default = self.form.filter.text() default = self.form.filter.text()

View file

@ -178,10 +178,10 @@ class AnkiWebView(QWebEngineView): # type: ignore
def onSelectAll(self): def onSelectAll(self):
self.triggerPageAction(QWebEnginePage.SelectAll) self.triggerPageAction(QWebEnginePage.SelectAll)
def contextMenuEvent(self, evt): def contextMenuEvent(self, evt) -> None:
m = QMenu(self) m = QMenu(self)
a = m.addAction(_("Copy")) a = m.addAction(_("Copy"))
a.triggered.connect(self.onCopy) a.triggered.connect(self.onCopy) # type: ignore
gui_hooks.webview_will_show_context_menu(self, m) gui_hooks.webview_will_show_context_menu(self, m)
m.popup(QCursor.pos()) m.popup(QCursor.pos())