mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
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:
parent
3079eaa460
commit
5a094e78fa
12 changed files with 83 additions and 40 deletions
|
@ -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
|
||||||
|
|
|
@ -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,))
|
||||||
|
|
|
@ -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_()
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"],
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue