enable type checking of aqt/forms, and fix the new typing issues

Referencing an invalid translation should now break the build
This commit is contained in:
Damien Elmes 2021-03-26 16:06:02 +10:00
parent 3079eaa460
commit 5a094e78fa
12 changed files with 83 additions and 40 deletions

View file

@ -381,7 +381,11 @@ class DataModel(QAbstractTableModel):
if count < 500: if count < 500:
# discard large selections; they're too slow # discard large selections; they're too slow
sm.select( sm.select(
items, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows items,
cast(
QItemSelectionModel.SelectionFlags,
QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows,
),
) )
else: else:
tv.selectRow(0) tv.selectRow(0)
@ -1361,7 +1365,13 @@ where id in %s"""
sm = self.form.tableView.selectionModel() sm = self.form.tableView.selectionModel()
items = sm.selection() items = sm.selection()
self.form.tableView.selectAll() self.form.tableView.selectAll()
sm.select(items, QItemSelectionModel.Deselect | QItemSelectionModel.Rows) sm.select(
items,
cast(
QItemSelectionModel.SelectionFlags,
QItemSelectionModel.Deselect | QItemSelectionModel.Rows,
),
)
# Hooks # Hooks
###################################################################### ######################################################################
@ -1432,10 +1442,10 @@ where id in %s"""
) )
frm.fields.addItems(fields) frm.fields.addItems(fields)
restore_combo_index_for_session(frm.fields, fields, "findDupesFields") restore_combo_index_for_session(frm.fields, fields, "findDupesFields")
self._dupesButton = None self._dupesButton: Optional[QPushButton] = None
# links # links
frm.webView.title = "find duplicates" frm.webView.set_title("find duplicates")
web_context = FindDupesDialog(dialog=d, browser=self) web_context = FindDupesDialog(dialog=d, browser=self)
frm.webView.set_bridge_command(self.dupeLinkClicked, web_context) frm.webView.set_bridge_command(self.dupeLinkClicked, web_context)
frm.webView.stdHtml("", context=web_context) frm.webView.stdHtml("", context=web_context)
@ -1522,17 +1532,22 @@ where id in %s"""
# Jumping # Jumping
###################################################################### ######################################################################
def _moveCur(self, dir: int, idx: QModelIndex = None) -> None: def _moveCur(
self, dir: Optional[QTableView.CursorAction], idx: QModelIndex = None
) -> None:
if not self.model.cards: if not self.model.cards:
return return
tv = self.form.tableView tv = self.form.tableView
if idx is None: if dir is not None:
idx = tv.moveCursor(dir, self.mw.app.keyboardModifiers()) idx = tv.moveCursor(dir, self.mw.app.keyboardModifiers())
tv.selectionModel().setCurrentIndex( tv.selectionModel().setCurrentIndex(
idx, idx,
QItemSelectionModel.Clear cast(
| QItemSelectionModel.Select QItemSelectionModel.SelectionFlags,
| QItemSelectionModel.Rows, QItemSelectionModel.Clear
| QItemSelectionModel.Select
| QItemSelectionModel.Rows,
),
) )
def onPreviousCard(self) -> None: def onPreviousCard(self) -> None:
@ -1557,7 +1572,13 @@ where id in %s"""
return return
idx2 = sm.currentIndex() idx2 = sm.currentIndex()
item = QItemSelection(idx2, idx) item = QItemSelection(idx2, idx)
sm.select(item, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) sm.select(
item,
cast(
QItemSelectionModel.SelectionFlags,
QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows,
),
)
def onLastCard(self) -> None: def onLastCard(self) -> None:
sm = self.form.tableView.selectionModel() sm = self.form.tableView.selectionModel()
@ -1567,7 +1588,13 @@ where id in %s"""
return return
idx2 = sm.currentIndex() idx2 = sm.currentIndex()
item = QItemSelection(idx, idx2) item = QItemSelection(idx, idx2)
sm.select(item, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows) sm.select(
item,
cast(
QItemSelectionModel.SelectionFlags,
QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows,
),
)
def onFind(self) -> None: def onFind(self) -> None:
# workaround for PyQt focus bug # workaround for PyQt focus bug

View file

@ -43,7 +43,7 @@ class EmptyCardsDialog(QDialog):
self.setWindowTitle(tr.empty_cards_window_title()) self.setWindowTitle(tr.empty_cards_window_title())
disable_help_button(self) disable_help_button(self)
self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox()) self.form.keep_notes.setText(tr.empty_cards_preserve_notes_checkbox())
self.form.webview.title = "empty cards" self.form.webview.set_title("empty cards")
self.form.webview.set_bridge_command(self._on_note_link_clicked, self) self.form.webview.set_bridge_command(self._on_note_link_clicked, self)
gui_hooks.empty_cards_will_show(self) gui_hooks.empty_cards_will_show(self)
@ -66,7 +66,7 @@ class EmptyCardsDialog(QDialog):
tr.empty_cards_delete_button(), QDialogButtonBox.ActionRole tr.empty_cards_delete_button(), QDialogButtonBox.ActionRole
) )
self._delete_button.setAutoDefault(False) self._delete_button.setAutoDefault(False)
self._delete_button.clicked.connect(self._on_delete) qconnect(self._delete_button.clicked, self._on_delete)
def _on_note_link_clicked(self, link: str) -> None: def _on_note_link_clicked(self, link: str) -> None:
aqt.dialogs.open("Browser", self.mw, search=(link,)) aqt.dialogs.open("Browser", self.mw, search=(link,))

View file

@ -49,7 +49,7 @@ class FieldDialog(QDialog):
self.fillFields() self.fillFields()
self.setupSignals() self.setupSignals()
self.form.fieldList.setDragDropMode(QAbstractItemView.InternalMove) self.form.fieldList.setDragDropMode(QAbstractItemView.InternalMove)
self.form.fieldList.dropEvent = self.onDrop self.form.fieldList.dropEvent = self.onDrop # type: ignore[assignment]
self.form.fieldList.setCurrentRow(0) self.form.fieldList.setCurrentRow(0)
self.exec_() self.exec_()

View file

@ -273,7 +273,7 @@ class FilteredDeckConfigDialog(QDialog):
FilteredDeckConfig.SearchTerm( FilteredDeckConfig.SearchTerm(
search=form.search.text(), search=form.search.text(),
limit=form.limit.value(), limit=form.limit.value(),
order=form.order.currentIndex(), order=form.order.currentIndex(), # type: ignore[arg-type]
) )
] ]
@ -282,7 +282,7 @@ class FilteredDeckConfigDialog(QDialog):
FilteredDeckConfig.SearchTerm( FilteredDeckConfig.SearchTerm(
search=form.search_2.text(), search=form.search_2.text(),
limit=form.limit_2.value(), limit=form.limit_2.value(),
order=form.order_2.currentIndex(), order=form.order_2.currentIndex(), # type: ignore[arg-type]
) )
) )

View file

@ -111,11 +111,11 @@ class FindAndReplaceDialog(QDialog):
self._find_history = restore_combo_history( self._find_history = restore_combo_history(
self.form.find, self.COMBO_NAME + "Find" self.form.find, self.COMBO_NAME + "Find"
) )
self.form.find.completer().setCaseSensitivity(True) self.form.find.completer().setCaseSensitivity(Qt.CaseSensitive)
self._replace_history = restore_combo_history( self._replace_history = restore_combo_history(
self.form.replace, self.COMBO_NAME + "Replace" self.form.replace, self.COMBO_NAME + "Replace"
) )
self.form.replace.completer().setCaseSensitivity(True) self.form.replace.completer().setCaseSensitivity(Qt.CaseSensitive)
restore_is_checked(self.form.re, self.COMBO_NAME + "Regex") restore_is_checked(self.form.re, self.COMBO_NAME + "Regex")
restore_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase") restore_is_checked(self.form.ignoreCase, self.COMBO_NAME + "ignoreCase")

View file

@ -16,5 +16,15 @@ outdata = re.sub(
r'(?:QtGui\.QApplication\.)?_?translate\(".*?", "(.*?)"', "tr.\\1(", outdata r'(?:QtGui\.QApplication\.)?_?translate\(".*?", "(.*?)"', "tr.\\1(", outdata
) )
outlines = []
qt_bad_types = [".connect(", "setStandardButtons", "setTextInteractionFlags", "setAlignment"]
for line in outdata.splitlines():
for substr in qt_bad_types:
if substr in line:
line = line + " # type: ignore"
break
outlines.append(line)
with open(py_file, "w") as file: with open(py_file, "w") as file:
file.write(outdata) file.write("\n".join(outlines))

View file

@ -1622,22 +1622,24 @@ title="%s" %s>%s</button>""" % (
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d) s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d)
qconnect(s.activated, frm.text.clear) qconnect(s.activated, frm.text.clear)
def addContextMenu(ev: QCloseEvent, name: str) -> None: def addContextMenu(
ev: Union[QCloseEvent, QContextMenuEvent], name: str
) -> None:
ev.accept() ev.accept()
menu = frm.log.createStandardContextMenu(QCursor.pos()) menu = frm.log.createStandardContextMenu(QCursor.pos())
menu.addSeparator() menu.addSeparator()
if name == "log": if name == "log":
a = menu.addAction("Clear Log") a = menu.addAction("Clear Log")
a.setShortcuts(QKeySequence("ctrl+l")) a.setShortcut(QKeySequence("ctrl+l"))
qconnect(a.triggered, frm.log.clear) qconnect(a.triggered, frm.log.clear)
elif name == "text": elif name == "text":
a = menu.addAction("Clear Code") a = menu.addAction("Clear Code")
a.setShortcuts(QKeySequence("ctrl+shift+l")) a.setShortcut(QKeySequence("ctrl+shift+l"))
qconnect(a.triggered, frm.text.clear) qconnect(a.triggered, frm.text.clear)
menu.exec_(QCursor.pos()) menu.exec_(QCursor.pos())
frm.log.contextMenuEvent = lambda ev: addContextMenu(ev, "log") frm.log.contextMenuEvent = lambda ev: addContextMenu(ev, "log") # type: ignore[assignment]
frm.text.contextMenuEvent = lambda ev: addContextMenu(ev, "text") frm.text.contextMenuEvent = lambda ev: addContextMenu(ev, "text") # type: ignore[assignment]
gui_hooks.debug_console_will_show(d) gui_hooks.debug_console_will_show(d)
d.show() d.show()

View file

@ -3,10 +3,10 @@
from concurrent.futures import Future from concurrent.futures import Future
from operator import itemgetter from operator import itemgetter
from typing import Any, List, Optional, Sequence from typing import List, Optional, Sequence
import aqt.clayout import aqt.clayout
from anki import stdmodels from anki import Collection, stdmodels
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from anki.models import NoteType, NoteTypeID, NoteTypeNameIDUseCount from anki.models import NoteType, NoteTypeID, NoteTypeNameIDUseCount
from anki.notes import Note from anki.notes import Note
@ -133,7 +133,7 @@ class Models(QDialog):
def current_notetype(self) -> NoteType: def current_notetype(self) -> NoteType:
row = self.form.modelsList.currentRow() row = self.form.modelsList.currentRow()
return self.mm.get(self.models[row].id) return self.mm.get(NoteTypeID(self.models[row].id))
def onAdd(self) -> None: def onAdd(self) -> None:
m = AddModel(self.mw, self).get() m = AddModel(self.mw, self).get()
@ -229,16 +229,16 @@ class AddModel(QDialog):
self.dialog.setupUi(self) self.dialog.setupUi(self)
disable_help_button(self) disable_help_button(self)
# standard models # standard models
self.models = [] self.notetypes: List[Union[NoteType, Callable[[Collection], NoteType]]] = []
for (name, func) in stdmodels.get_stock_notetypes(self.col): for (name, func) in stdmodels.get_stock_notetypes(self.col):
item = QListWidgetItem(tr.notetypes_add(val=name)) item = QListWidgetItem(tr.notetypes_add(val=name))
self.dialog.models.addItem(item) self.dialog.models.addItem(item)
self.models.append((True, func)) self.notetypes.append(func)
# add copies # add copies
for m in sorted(self.col.models.all(), key=itemgetter("name")): for m in sorted(self.col.models.all(), key=itemgetter("name")):
item = QListWidgetItem(tr.notetypes_clone(val=m["name"])) item = QListWidgetItem(tr.notetypes_clone(val=m["name"]))
self.dialog.models.addItem(item) self.dialog.models.addItem(item)
self.models.append((False, m)) # type: ignore self.notetypes.append(m)
self.dialog.models.setCurrentRow(0) self.dialog.models.setCurrentRow(0)
# the list widget will swallow the enter key # the list widget will swallow the enter key
s = QShortcut(QKeySequence("Return"), self) s = QShortcut(QKeySequence("Return"), self)
@ -246,7 +246,7 @@ class AddModel(QDialog):
# help # help
qconnect(self.dialog.buttonBox.helpRequested, self.onHelp) qconnect(self.dialog.buttonBox.helpRequested, self.onHelp)
def get(self) -> Any: def get(self) -> Optional[NoteType]:
self.exec_() self.exec_()
return self.model return self.model
@ -254,14 +254,14 @@ class AddModel(QDialog):
QDialog.reject(self) QDialog.reject(self)
def accept(self) -> None: def accept(self) -> None:
(isStd, model) = self.models[self.dialog.models.currentRow()] model = self.notetypes[self.dialog.models.currentRow()]
if isStd: if isinstance(model, dict):
# create
self.model = model(self.col)
else:
# add copy to deck # add copy to deck
self.model = self.mw.col.models.copy(model) self.model = self.mw.col.models.copy(model)
self.mw.col.models.setCurrent(self.model) self.mw.col.models.setCurrent(self.model)
else:
# create
self.model = model(self.col)
QDialog.accept(self) QDialog.accept(self)
def onHelp(self) -> None: def onHelp(self) -> None:

View file

@ -1,6 +1,8 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# 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 Any, cast
import anki.lang import anki.lang
import aqt import aqt
from anki.consts import newCardSchedulingLabels from anki.consts import newCardSchedulingLabels
@ -82,7 +84,7 @@ class Preferences(QDialog):
form = self.form form = self.form
scheduling = self.prefs.scheduling scheduling = self.prefs.scheduling
scheduling.new_review_mix = form.newSpread.currentIndex() scheduling.new_review_mix = cast(Any, form.newSpread.currentIndex())
scheduling.learn_ahead_secs = form.lrnCutoff.value() * 60 scheduling.learn_ahead_secs = form.lrnCutoff.value() * 60
scheduling.day_learn_first = form.dayLearnFirst.isChecked() scheduling.day_learn_first = form.dayLearnFirst.isChecked()
scheduling.rollover = form.dayOffset.value() scheduling.rollover = form.dayOffset.value()

View file

@ -182,7 +182,7 @@ class DeckStats(QDialog):
stats = self.mw.col.stats() stats = self.mw.col.stats()
stats.wholeCollection = self.wholeCollection stats.wholeCollection = self.wholeCollection
self.report = stats.report(type=self.period) self.report = stats.report(type=self.period)
self.form.web.title = "deck stats" self.form.web.set_title("deck stats")
self.form.web.stdHtml( self.form.web.stdHtml(
f"<html><body>{self.report}</body></html>", f"<html><body>{self.report}</body></html>",
js=["js/vendor/jquery.min.js", "js/vendor/plot.js"], js=["js/vendor/jquery.min.js", "js/vendor/plot.js"],

View file

@ -213,7 +213,7 @@ class AnkiWebView(QWebEngineView):
self, parent: Optional[QWidget] = None, title: str = "default" self, parent: Optional[QWidget] = None, title: str = "default"
) -> None: ) -> None:
QWebEngineView.__init__(self, parent=parent) QWebEngineView.__init__(self, parent=parent)
self.title = title # type: ignore self.set_title(title)
self._page = AnkiWebPage(self._onBridgeCmd) self._page = AnkiWebPage(self._onBridgeCmd)
self._page.setBackgroundColor(self._getWindowColor()) # reduce flicker self._page.setBackgroundColor(self._getWindowColor()) # reduce flicker
@ -252,6 +252,9 @@ class AnkiWebView(QWebEngineView):
activated=self.onPaste, activated=self.onPaste,
) )
def set_title(self, title: str) -> None:
self.title = title # type: ignore[assignment]
def eventFilter(self, obj: QObject, evt: QEvent) -> bool: def eventFilter(self, obj: QObject, evt: QEvent) -> bool:
# disable pinch to zoom gesture # disable pinch to zoom gesture
if isinstance(evt, QNativeGestureEvent): if isinstance(evt, QNativeGestureEvent):

View file

@ -72,7 +72,6 @@ ignore_missing_imports = True
ignore_missing_imports = True ignore_missing_imports = True
[mypy-aqt.forms.*] [mypy-aqt.forms.*]
check_untyped_defs=false
disallow_untyped_defs=false disallow_untyped_defs=false
[mypy-anki.*] [mypy-anki.*]
disallow_untyped_defs=false disallow_untyped_defs=false