From 7de5aa1233a5b6eab2f3fd445ae5e222b7adf852 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 17:57:11 -0700 Subject: [PATCH 01/27] Boolean naming convention --- .mypy.ini | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index ef32ed7a2ed..6f45b820e06 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,15 +1,15 @@ [mypy] python_version = 3.9 -pretty = false -no_strict_optional = true -show_error_codes = true -check_untyped_defs = true +pretty = False +no_strict_optional = True +show_error_codes = True +check_untyped_defs = True disallow_untyped_decorators = True warn_redundant_casts = True warn_unused_configs = True -strict_equality = true -namespace_packages = true -explicit_package_bases = true +strict_equality = True +namespace_packages = True +explicit_package_bases = True mypy_path = pylib, out/pylib, @@ -27,9 +27,9 @@ disallow_untyped_defs = False [mypy-anki.exporting] disallow_untyped_defs = False [mypy-aqt.operations.*] -no_strict_optional = false +no_strict_optional = False [mypy-anki.scheduler.base] -no_strict_optional = false +no_strict_optional = False [mypy-anki._backend.rsbridge] ignore_missing_imports = True [mypy-anki._vendor.stringcase] @@ -37,10 +37,10 @@ disallow_untyped_defs = False [mypy-stringcase] ignore_missing_imports = True [mypy-aqt.mpv] -disallow_untyped_defs=false -ignore_errors=true +disallow_untyped_defs=False +ignore_errors=True [mypy-aqt.winpaths] -disallow_untyped_defs=false +disallow_untyped_defs=False [mypy-win32file] ignore_missing_imports = True From 04c84f7ea3bbd9a909ac1be49dbbdc3ff9a63156 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 17:58:15 -0700 Subject: [PATCH 02/27] Rename no_strict_optional -> strict_optional --- .mypy.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.mypy.ini b/.mypy.ini index 6f45b820e06..dc892ed1389 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,7 +1,7 @@ [mypy] python_version = 3.9 pretty = False -no_strict_optional = True +strict_optional = False show_error_codes = True check_untyped_defs = True disallow_untyped_decorators = True @@ -27,9 +27,9 @@ disallow_untyped_defs = False [mypy-anki.exporting] disallow_untyped_defs = False [mypy-aqt.operations.*] -no_strict_optional = False +strict_optional = True [mypy-anki.scheduler.base] -no_strict_optional = False +strict_optional = True [mypy-anki._backend.rsbridge] ignore_missing_imports = True [mypy-anki._vendor.stringcase] From 28da550f09de9e461d72e2fda90c3c4afbb7df9d Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 17:58:51 -0700 Subject: [PATCH 03/27] Update CONTRIBUTORS --- CONTRIBUTORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f2a931c8995..a712bd477d4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -187,7 +187,7 @@ Christian Donat Asuka Minato Dillon Baldwin Voczi -Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com> +Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com> Themis Demetriades Luke Bartholomew Gregory Abrasaldo From f48d20c12b898382e9f89d9e40c5ba744f279157 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 18:30:52 -0700 Subject: [PATCH 04/27] Enable strict optional for aqt module --- .mypy.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index dc892ed1389..9a5d8112406 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -26,6 +26,8 @@ disallow_untyped_defs = True disallow_untyped_defs = False [mypy-anki.exporting] disallow_untyped_defs = False +[mypy-aqt] +strict_optional = True [mypy-aqt.operations.*] strict_optional = True [mypy-anki.scheduler.base] From 3a0b56a5d35f7e85cacfdd5f5a4788aa7635b2e0 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 18:33:37 -0700 Subject: [PATCH 05/27] Fix errors --- qt/aqt/__init__.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 349c110af59..b80a5e194fd 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -280,7 +280,10 @@ def fn_ngettext(a, b, c) -> None: # type: ignore if _qtrans.load(f"qtbase_{qt_lang}", qt_dir): app.installTranslator(_qtrans) - return anki.lang.current_i18n + backend = anki.lang.current_i18n + assert backend + + return backend # App initialisation @@ -291,8 +294,10 @@ class NativeEventFilter(QAbstractNativeEventFilter): def nativeEventFilter( self, eventType: Any, message: Any ) -> tuple[bool, sip.voidptr | None]: + assert mw + if eventType == "windows_generic_MSG": - import ctypes + import ctypes.wintypes msg = ctypes.wintypes.MSG.from_address(int(message)) if msg.message == 17: # WM_QUERYENDSESSION @@ -321,6 +326,8 @@ def __init__(self, argv: list[str]) -> None: self.installNativeEventFilter(self._native_event_filter) def _set_windows_shutdown_block_reason(self, reason: str) -> None: + assert mw + if is_win: import ctypes from ctypes import windll, wintypes # type: ignore @@ -331,6 +338,8 @@ def _set_windows_shutdown_block_reason(self, reason: str) -> None: ) def _unset_windows_shutdown_block_reason(self) -> None: + assert mw + if is_win: from ctypes import windll, wintypes # type: ignore @@ -388,16 +397,20 @@ def onRecv(self) -> None: # OS X file/url handler ################################################## - def event(self, evt: QEvent) -> bool: - if evt.type() == QEvent.Type.FileOpen: - self.appMsg.emit(evt.file() or "raise") # type: ignore + def event(self, a0: QEvent | None) -> bool: + assert a0 + + if a0.type() == QEvent.Type.FileOpen: + self.appMsg.emit(a0.file() or "raise") # type: ignore return True - return QApplication.event(self, evt) + return QApplication.event(self, a0) # Global cursor: pointer for Qt buttons ################################################## - def eventFilter(self, src: Any, evt: QEvent) -> bool: + def eventFilter(self, a0: Any, a1: QEvent | None) -> bool: + assert a1 + pointer_classes = ( QPushButton, QCheckBox, @@ -408,18 +421,18 @@ def eventFilter(self, src: Any, evt: QEvent) -> bool: without_qt5_compat_wrapper(QToolButton), without_qt5_compat_wrapper(QTabBar), ) - if evt.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]: - if (isinstance(src, pointer_classes) and src.isEnabled()) or ( - isinstance(src, without_qt5_compat_wrapper(QComboBox)) - and not src.isEditable() + if a1.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]: + if (isinstance(a0, pointer_classes) and a0.isEnabled()) or ( + isinstance(a0, without_qt5_compat_wrapper(QComboBox)) + and not a0.isEditable() ): self.setOverrideCursor(QCursor(Qt.CursorShape.PointingHandCursor)) else: self.restoreOverrideCursor() return False - elif evt.type() in [QEvent.Type.HoverLeave, QEvent.Type.Leave] or isinstance( - evt, QCloseEvent + elif a1.type() in [QEvent.Type.HoverLeave, QEvent.Type.Leave] or isinstance( + a1, QCloseEvent ): self.restoreOverrideCursor() return False @@ -547,6 +560,8 @@ def msgHandler(category: Any, ctx: Any, msg: Any) -> None: def write_profile_results() -> None: + assert profiler + profiler.disable() profile = "out/anki.prof" profiler.dump_stats(profile) From 38640611f1891afb2a6c6fa0bff82cfcb08578fa Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 18:55:53 -0700 Subject: [PATCH 06/27] Enable strict optional for aqt browser --- .mypy.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.mypy.ini b/.mypy.ini index 9a5d8112406..be7fcefa87c 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -28,6 +28,8 @@ disallow_untyped_defs = False disallow_untyped_defs = False [mypy-aqt] strict_optional = True +[mypy-aqt.browser.*] +strict_optional = True [mypy-aqt.operations.*] strict_optional = True [mypy-anki.scheduler.base] From 7aeedf13a18feef5bb9f5b9f9c6dc6bce595b2d4 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 19:44:44 -0700 Subject: [PATCH 07/27] Fix layout.py errors --- qt/aqt/browser/layout.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qt/aqt/browser/layout.py b/qt/aqt/browser/layout.py index 616a6818761..2c0b01a97b1 100644 --- a/qt/aqt/browser/layout.py +++ b/qt/aqt/browser/layout.py @@ -1,6 +1,8 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + from enum import Enum from aqt.qt import QEvent, QObject, QSplitter, Qt @@ -19,9 +21,14 @@ def __init__(self, splitter: QSplitter): super().__init__(splitter) self._splitter = splitter - def eventFilter(self, object: QObject, event: QEvent) -> bool: + def eventFilter(self, object: QObject | None, event: QEvent | None) -> bool: + assert event + if event.type() == QEvent.Type.MouseButtonDblClick: splitter_parent = self._splitter.parentWidget() + + assert splitter_parent + if self._splitter.orientation() == Qt.Orientation.Horizontal: half_size = splitter_parent.width() // 2 else: From ffd4d7413f7f4ebd22feb71e2357718432459fa7 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 19:44:53 -0700 Subject: [PATCH 08/27] Fix find_duplicates.py errors --- qt/aqt/browser/find_duplicates.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qt/aqt/browser/find_duplicates.py b/qt/aqt/browser/find_duplicates.py index 60cd1124232..37fe39bfe27 100644 --- a/qt/aqt/browser/find_duplicates.py +++ b/qt/aqt/browser/find_duplicates.py @@ -76,6 +76,9 @@ def on_click() -> None: search = form.buttonBox.addButton( tr.actions_search(), QDialogButtonBox.ButtonRole.ActionRole ) + + assert search + qconnect(search.clicked, on_click) self.show() @@ -87,6 +90,9 @@ def show_duplicates_report(self, dupes: list[tuple[str, list[NoteId]]]) -> None: self._dupesButton = b = self.form.buttonBox.addButton( tr.browsing_tag_duplicates(), QDialogButtonBox.ButtonRole.ActionRole ) + + assert b + qconnect(b.clicked, self._tag_duplicates) text = "" groups = len(dupes) From 5d2b473361799cdfbcefcb9ce1a9e86d742f366c Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 19:45:00 -0700 Subject: [PATCH 09/27] Fix browser.py errors --- qt/aqt/browser/browser.py | 103 +++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 0ddb4737177..fb726f4ee35 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -137,7 +137,11 @@ def __init__( self.form.setupUi(self) self.form.splitter.setChildrenCollapsible(False) splitter_handle_event_filter = QSplitterHandleEventFilter(self.form.splitter) - self.form.splitter.handle(1).installEventFilter(splitter_handle_event_filter) + + splitter_handle = self.form.splitter.handle(1) + assert splitter_handle + + splitter_handle.installEventFilter(splitter_handle_event_filter) # set if exactly 1 row is selected; used by the previewer self.card: Card | None = None self.current_card: Card | None = None @@ -180,6 +184,8 @@ def on_operation_did_execute( if handler is not self.editor: # fixme: this will leave the splitter shown, but with no current # note being edited + assert self.editor + note = self.editor.note if note: try: @@ -241,7 +247,9 @@ def maybe_update_layout(self, aspect_ratio: float, force: bool = False) -> None: else: self.form.splitter.setOrientation(Qt.Orientation.Horizontal) - def resizeEvent(self, event: QResizeEvent) -> None: + def resizeEvent(self, event: QResizeEvent | None) -> None: + assert event + if self.height() != 0: aspect_ratio = self.width() / self.height() @@ -283,19 +291,24 @@ def setupMenus(self) -> None: qconnect(f.actionCreateFilteredDeck.triggered, self.createFilteredDeck) f.actionCreateFilteredDeck.setShortcuts(["Ctrl+G", "Ctrl+Alt+G"]) + editor = self.editor + assert editor + editor_web_view = editor.web + assert editor_web_view + # view qconnect(f.actionFullScreen.triggered, self.mw.on_toggle_full_screen) qconnect( f.actionZoomIn.triggered, - lambda: self.editor.web.setZoomFactor(self.editor.web.zoomFactor() + 0.1), + lambda: editor_web_view.setZoomFactor(editor_web_view.zoomFactor() + 0.1), ) qconnect( f.actionZoomOut.triggered, - lambda: self.editor.web.setZoomFactor(self.editor.web.zoomFactor() - 0.1), + lambda: editor_web_view.setZoomFactor(editor_web_view.zoomFactor() - 0.1), ) qconnect( f.actionResetZoom.triggered, - lambda: self.editor.web.setZoomFactor(1), + lambda: editor_web_view.setZoomFactor(1), ) qconnect( self.form.actionLayoutAuto.triggered, @@ -368,14 +381,21 @@ def set_flag_func(desired_flag: int) -> Callable: add_ellipsis_to_action_label(f.actionCopy) add_ellipsis_to_action_label(f.action_forget) - def closeEvent(self, evt: QCloseEvent) -> None: + def closeEvent(self, evt: QCloseEvent | None) -> None: + assert evt + if self._closeEventHasCleanedUp: evt.accept() return + + assert self.editor + self.editor.call_after_note_saved(self._closeWindow) evt.ignore() def _closeWindow(self) -> None: + assert self.editor + self._cleanup_preview() self._card_info.close() self.editor.cleanup() @@ -396,7 +416,9 @@ def closeWithCallback(self, onsuccess: Callable) -> None: self._closeWindow() onsuccess() - def keyPressEvent(self, evt: QKeyEvent) -> None: + def keyPressEvent(self, evt: QKeyEvent | None) -> None: + assert evt + if evt.key() == Qt.Key.Key_Escape: self.close() else: @@ -426,12 +448,13 @@ def setupSearch( card: Card | None = None, search: tuple[str | SearchNode] | None = None, ) -> None: - qconnect(self.form.searchEdit.lineEdit().returnPressed, self.onSearchActivated) + assert self.mw.pm.profile + + line_edit = self._line_edit() + qconnect(line_edit.returnPressed, self.onSearchActivated) self.form.searchEdit.setCompleter(None) - self.form.searchEdit.lineEdit().setPlaceholderText( - tr.browsing_search_bar_hint() - ) - self.form.searchEdit.lineEdit().setMaxLength(2000000) + line_edit.setPlaceholderText(tr.browsing_search_bar_hint()) + line_edit.setMaxLength(2000000) self.form.searchEdit.addItems( [""] + self.mw.pm.profile.get("searchHistory", []) ) @@ -464,11 +487,11 @@ def search_for(self, search: str, prompt: str | None = None) -> None: self._lastSearchTxt = search prompt = search if prompt is None else prompt self.form.searchEdit.setCurrentIndex(-1) - self.form.searchEdit.lineEdit().setText(prompt) + self._line_edit().setText(prompt) self.search() def current_search(self) -> str: - return self.form.searchEdit.lineEdit().text() + return self._line_edit().text() def search(self) -> None: """Search triggered programmatically. Caller must have saved note first.""" @@ -479,6 +502,8 @@ def search(self) -> None: showWarning(str(err)) def update_history(self) -> None: + assert self.mw.pm.profile + sh = self.mw.pm.profile.get("searchHistory", []) if self._lastSearchTxt in sh: sh.remove(self._lastSearchTxt) @@ -524,6 +549,8 @@ def onReset(self) -> None: # caller must have called editor.saveNow() before calling this or .reset() def begin_reset(self) -> None: + assert self.editor + self.editor.set_note(None, hide=False) self.mw.progress.start() self.table.begin_reset() @@ -573,8 +600,17 @@ def on_all_or_selected_rows_changed(self) -> None: # it might differ from the current card self.card = self.table.get_single_selected_card() self.singleCard = bool(self.card) - self.form.splitter.widget(1).setVisible(self.singleCard) + + splitter_widget = self.form.splitter.widget(1) + assert splitter_widget + + splitter_widget.setVisible(self.singleCard) + + assert self.editor + if self.singleCard: + assert self.card + self.editor.set_note(self.card.note(), focusTo=self.focusTo) self.focusTo = None self.editor.card = self.card @@ -740,7 +776,10 @@ def onHelp(self) -> None: def on_create_copy(self) -> None: if note := self.table.get_current_note(): - deck_id = self.table.get_current_card().current_deck_id() + current_card = self.table.get_current_card() + assert current_card + + deck_id = current_card.current_deck_id() aqt.dialogs.open("AddCards", self.mw).set_note(note, deck_id) @no_arg_trigger @@ -761,6 +800,8 @@ def createFilteredDeck(self) -> None: ###################################################################### def onTogglePreview(self) -> None: + assert self.editor + if self._previewer: self._previewer.close() elif self.editor.note: @@ -776,6 +817,8 @@ def _renderPreview(self) -> None: self.onTogglePreview() def toggle_preview_button_state(self, active: bool) -> None: + assert self.editor + if self.editor.web: self.editor.web.eval(f"togglePreviewButtonState({json.dumps(active)});") @@ -801,6 +844,8 @@ def delete_selected_notes(self) -> None: if focus != self.form.tableView: return + assert self.editor + self.editor.set_note(None) nids = self.table.to_row_of_unselected_note() remove_notes(parent=self, note_ids=nids).run_in_background() @@ -818,14 +863,24 @@ def delete_selected_notes(self) -> None: def set_deck_of_selected_cards(self) -> None: from aqt.studydeck import StudyDeck + assert self.mw.col + assert self.mw.col.db + cids = self.table.get_selected_card_ids() did = self.mw.col.db.scalar("select did from cards where id = ?", cids[0]) - current = self.mw.col.decks.get(did)["name"] + + deck_dict = self.mw.col.decks.get(did) + assert deck_dict + + current = deck_dict["name"] def callback(ret: StudyDeck) -> None: if not ret.name: return did = self.col.decks.id(ret.name) + + assert did + set_card_deck(parent=self, card_ids=cids, deck_id=did).run_in_background() StudyDeck( @@ -1105,10 +1160,14 @@ def has_next_card(self) -> bool: return self.table.has_next() def onPreviousCard(self) -> None: + assert self.editor + self.focusTo = self.editor.currentField self.editor.call_after_note_saved(self.table.to_previous_row) def onNextCard(self) -> None: + assert self.editor + self.focusTo = self.editor.currentField self.editor.call_after_note_saved(self.table.to_next_row) @@ -1120,11 +1179,19 @@ def onLastCard(self) -> None: def onFind(self) -> None: self.form.searchEdit.setFocus() - self.form.searchEdit.lineEdit().selectAll() + self._line_edit().selectAll() def onNote(self) -> None: + assert self.editor + assert self.editor.web + self.editor.web.setFocus() self.editor.loadNote(focusTo=0) def onCardList(self) -> None: self.form.tableView.setFocus() + + def _line_edit(self) -> QLineEdit: + line_edit = self.form.searchEdit.lineEdit() + assert line_edit + return line_edit From 38b16b5a0d296b72abf5b35cddd098d63c1c20c3 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Sun, 6 Oct 2024 19:48:17 -0700 Subject: [PATCH 10/27] Revert a0 a1 names --- qt/aqt/__init__.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index b80a5e194fd..9d987b66fc8 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -397,19 +397,19 @@ def onRecv(self) -> None: # OS X file/url handler ################################################## - def event(self, a0: QEvent | None) -> bool: - assert a0 + def event(self, evt: QEvent | None) -> bool: + assert evt - if a0.type() == QEvent.Type.FileOpen: + if evt.type() == QEvent.Type.FileOpen: self.appMsg.emit(a0.file() or "raise") # type: ignore return True - return QApplication.event(self, a0) + return QApplication.event(self, evt) # Global cursor: pointer for Qt buttons ################################################## - def eventFilter(self, a0: Any, a1: QEvent | None) -> bool: - assert a1 + def eventFilter(self, src: Any, evt: QEvent | None) -> bool: + assert evt pointer_classes = ( QPushButton, @@ -421,18 +421,18 @@ def eventFilter(self, a0: Any, a1: QEvent | None) -> bool: without_qt5_compat_wrapper(QToolButton), without_qt5_compat_wrapper(QTabBar), ) - if a1.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]: - if (isinstance(a0, pointer_classes) and a0.isEnabled()) or ( - isinstance(a0, without_qt5_compat_wrapper(QComboBox)) - and not a0.isEditable() + if evt.type() in [QEvent.Type.Enter, QEvent.Type.HoverEnter]: + if (isinstance(src, pointer_classes) and src.isEnabled()) or ( + isinstance(src, without_qt5_compat_wrapper(QComboBox)) + and not src.isEditable() ): self.setOverrideCursor(QCursor(Qt.CursorShape.PointingHandCursor)) else: self.restoreOverrideCursor() return False - elif a1.type() in [QEvent.Type.HoverLeave, QEvent.Type.Leave] or isinstance( - a1, QCloseEvent + elif evt.type() in [QEvent.Type.HoverLeave, QEvent.Type.Leave] or isinstance( + evt, QCloseEvent ): self.restoreOverrideCursor() return False From e3f745fce1d23435c94b94b346c0b1aaed26d36c Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 00:41:17 -0700 Subject: [PATCH 11/27] Fix tree.py errors --- qt/aqt/browser/sidebar/tree.py | 50 +++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index a10cc2cedce..e32d5e714bb 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -190,16 +190,19 @@ def on_done(root: SidebarItem) -> None: self.setUpdatesEnabled(True) # needs to be set after changing model - qconnect(self.selectionModel().selectionChanged, self._on_selection_changed) + qconnect( + self._selection_model().selectionChanged, self._on_selection_changed + ) QueryOp( parent=self.browser, op=lambda _: self._root_tree(), success=on_done ).run_in_background() def restore_current(self, current: SidebarItem) -> None: - if current := self.find_item(current.has_same_id): - index = self.model().index_for_item(current) - self.selectionModel().setCurrentIndex( + if current_item := self.find_item(current.has_same_id): + index = self.model().index_for_item(current_item) + + self._selection_model().setCurrentIndex( index, QItemSelectionModel.SelectionFlag.SelectCurrent ) self.scrollTo(index, QAbstractItemView.ScrollHint.PositionAtCenter) @@ -255,7 +258,7 @@ def expand_node(parent: QModelIndex) -> None: if item.show_expanded(searching): self.setExpanded(idx, True) if item.is_highlighted() and scroll_to_first_match: - self.selectionModel().setCurrentIndex( + self._selection_model().setCurrentIndex( idx, QItemSelectionModel.SelectionFlag.SelectCurrent, ) @@ -301,17 +304,21 @@ def update_search( ########### def drawRow( - self, painter: QPainter, options: QStyleOptionViewItem, idx: QModelIndex + self, painter: QPainter | None, options: QStyleOptionViewItem, idx: QModelIndex ) -> None: if self.current_search and (item := self.model().item_for_index(idx)): if item.is_highlighted(): + assert painter + brush = QBrush(theme_manager.qcolor(colors.HIGHLIGHT_BG)) painter.save() painter.fillRect(options.rect, brush) painter.restore() return super().drawRow(painter, options, idx) - def dropEvent(self, event: QDropEvent) -> None: + def dropEvent(self, event: QDropEvent | None) -> None: + assert event + model = self.model() if qtmajor == 5: pos = event.pos() # type: ignore @@ -321,7 +328,9 @@ def dropEvent(self, event: QDropEvent) -> None: if self.handle_drag_drop(self._selected_items(), target_item): event.acceptProposedAction() - def mouseReleaseEvent(self, event: QMouseEvent) -> None: + def mouseReleaseEvent(self, event: QMouseEvent | None) -> None: + assert event + super().mouseReleaseEvent(event) if ( self.tool == SidebarTool.SEARCH @@ -334,7 +343,9 @@ def mouseReleaseEvent(self, event: QMouseEvent) -> None: if (index := self.currentIndex()) == self.indexAt(pos): self._on_search(index) - def keyPressEvent(self, event: QKeyEvent) -> None: + def keyPressEvent(self, event: QKeyEvent | None) -> None: + assert event + index = self.currentIndex() if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): if not self.isPersistentEditorOpen(index): @@ -491,11 +502,9 @@ def _on_collapse(self, idx: QModelIndex) -> None: ########################### def _root_tree(self) -> SidebarItem: - root: SidebarItem | None = None + root = SidebarItem("", "", item_type=SidebarItemType.ROOT) for stage in SidebarStage: - if stage == SidebarStage.ROOT: - root = SidebarItem("", "", item_type=SidebarItemType.ROOT) handled = gui_hooks.browser_will_build_tree( False, root, stage, self.browser ) @@ -533,6 +542,8 @@ def _section_root( collapse_key: Config.Bool.V, type: SidebarItemType | None = None, ) -> SidebarItem: + assert type + def update(expanded: bool) -> None: CollectionOp( self.browser, @@ -889,7 +900,7 @@ def _notetype_tree(self, root: SidebarItem) -> None: def onContextMenu(self, point: QPoint) -> None: index: QModelIndex = self.indexAt(point) item = self.model().item_for_index(index) - if item and self.selectionModel().isSelected(index): + if item and self._selection_model().isSelected(index): self.show_context_menu(item, index) def show_context_menu(self, item: SidebarItem, index: QModelIndex) -> None: @@ -981,6 +992,8 @@ def _maybe_add_search_actions(self, menu: QMenu) -> None: menu.addAction(tr.actions_search(), lambda: self.update_search(*nodes)) return sub_menu = menu.addMenu(tr.actions_search()) + assert sub_menu + sub_menu.addAction( tr.actions_all_selected(), lambda: self.update_search(*nodes) ) @@ -1223,11 +1236,17 @@ def manage_notetype(self, item: SidebarItem) -> None: ) def manage_template(self, item: SidebarItem) -> None: + assert item._parent_item + note = Note(self.col, self.col.models.get(NotetypeId(item._parent_item.id))) CardLayout(self.mw, note, ord=item.id, parent=self, fill_empty=True) def manage_fields(self, item: SidebarItem) -> None: + assert item._parent_item + notetype = self.mw.col.models.get(NotetypeId(item._parent_item.id)) + assert notetype + FieldDialog(self.mw, notetype, parent=self, open_at=item.id) # Helpers @@ -1256,3 +1275,8 @@ def _selected_tags(self) -> list[str]: for item in self._selected_items() if item.item_type == SidebarItemType.TAG ] + + def _selection_model(self) -> QItemSelectionModel: + selection_model = self.selectionModel() + assert selection_model + return selection_model From 439454f86263d07d57c334933cb6b06c2ca7e667 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:28:55 -0700 Subject: [PATCH 12/27] Fix previewer.py errors --- qt/aqt/browser/previewer.py | 67 ++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index e35623a6ff6..ec5051443df 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -23,7 +23,6 @@ Qt, QTimer, QVBoxLayout, - QWidget, qconnect, ) from aqt.reviewer import replay_audio @@ -43,7 +42,10 @@ class Previewer(QDialog): _show_both_sides = False def __init__( - self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None] + self, + parent: aqt.browser.Browser | None, + mw: AnkiQt, + on_close: Callable[[], None], ) -> None: super().__init__(None, Qt.WindowType.Window) mw.garbage_collect_on_dialog_finish(self) @@ -80,7 +82,7 @@ def _create_gui(self) -> None: self.silentlyClose = True self.vbox = QVBoxLayout() self.vbox.setContentsMargins(0, 0, 0, 0) - self._web = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER) + self._web: AnkiWebView | None = AnkiWebView(kind=AnkiWebViewKind.PREVIEWER) self.vbox.addWidget(self._web) self.bbox = QDialogButtonBox() self.bbox.setLayoutDirection(Qt.LayoutDirection.LeftToRight) @@ -90,6 +92,7 @@ def _create_gui(self) -> None: self._replay = self.bbox.addButton( tr.actions_replay_audio(), QDialogButtonBox.ButtonRole.ActionRole ) + assert self._replay self._replay.setAutoDefault(False) self._replay.setShortcut(QKeySequence("R")) self._replay.setToolTip(tr.actions_shortcut_key(val="R")) @@ -113,20 +116,29 @@ def _on_finished(self, ok: int) -> None: self._on_close() def _on_replay_audio(self) -> None: - gui_hooks.audio_will_replay(self._web, self.card(), self._state == "question") + assert self._web + card = self.card() + assert card + + gui_hooks.audio_will_replay(self._web, card, self._state == "question") if self._state == "question": - replay_audio(self.card(), True) + replay_audio(card, True) elif self._state == "answer": - replay_audio(self.card(), False) + replay_audio(card, False) def _on_close(self) -> None: self._open = False self._close_callback() + + assert self._web + self._web.cleanup() self._web = None def _setup_web_view(self) -> None: + assert self._web + self._web.stdHtml( self.mw.reviewer.revHtml(), css=["css/reviewer.css"], @@ -143,7 +155,10 @@ def _setup_web_view(self) -> None: def _on_bridge_cmd(self, cmd: str) -> Any: if cmd.startswith("play:"): - play_clicked_audio(cmd, self.card()) + card = self.card() + assert card + + play_clicked_audio(cmd, card) def _update_flag_and_mark_icons(self, card: Card | None) -> None: if card: @@ -152,6 +167,9 @@ def _update_flag_and_mark_icons(self, card: Card | None) -> None: else: flag = 0 marked = False + + assert self._web + self._web.eval(f"_drawFlag({flag}); _drawMark({json.dumps(marked)});") def render_card(self) -> None: @@ -210,6 +228,8 @@ def _render_scheduled(self) -> None: bodyclass = theme_manager.body_classes_for_card_ord(c.ord) + assert self._web + if c.autoplay(): self._web.setPlaybackRequiresGesture(False) if self._show_both_sides: @@ -239,14 +259,22 @@ def _render_scheduled(self) -> None: js = f"{func}({json.dumps(txt)}, {json.dumps(ans_txt)}, '{bodyclass}');" else: js = f"{func}({json.dumps(txt)}, '{bodyclass}');" + + assert self._web self._web.eval(js) self._card_changed = False def _on_show_both_sides(self, toggle: bool) -> None: + assert self._web + self._show_both_sides = toggle self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle) + + card = self.card() + assert card + gui_hooks.previewer_will_redraw_after_show_both_sides_toggled( - self._web, self.card(), self._state == "question", toggle + self._web, card, self._state == "question", toggle ) if self._state == "answer" and not toggle: @@ -255,6 +283,9 @@ def _on_show_both_sides(self, toggle: bool) -> None: def _state_and_mod(self) -> tuple[str, int, int]: c = self.card() + + assert c + n = c.note() n.load() return (self._state, c.id, n.mod) @@ -278,6 +309,9 @@ def _create_gui(self) -> None: ">" if self.layoutDirection() == Qt.LayoutDirection.RightToLeft else "<", QDialogButtonBox.ButtonRole.ActionRole, ) + + assert self._prev + self._prev.setAutoDefault(False) self._prev.setShortcut(QKeySequence("Left")) self._prev.setToolTip(tr.qt_misc_shortcut_key_left_arrow()) @@ -286,6 +320,9 @@ def _create_gui(self) -> None: "<" if self.layoutDirection() == Qt.LayoutDirection.RightToLeft else ">", QDialogButtonBox.ButtonRole.ActionRole, ) + + assert self._next + self._next.setAutoDefault(True) self._next.setShortcut(QKeySequence("Right")) self._next.setToolTip(tr.qt_misc_shortcut_key_right_arrow_or_enter()) @@ -316,6 +353,10 @@ def _on_next_card(self) -> None: def _updateButtons(self) -> None: if not self._open: return + + assert self._prev + assert self._next + self._prev.setEnabled(self._should_enable_prev()) self._next.setEnabled(self._should_enable_next()) @@ -341,6 +382,8 @@ def __init__( super().__init__(parent=parent, mw=mw, on_close=on_close) def card(self) -> Card | None: + assert self._parent + if self._parent.singleCard: return self._parent.card else: @@ -356,15 +399,23 @@ def card_changed(self) -> bool: return changed def _on_prev_card(self) -> None: + assert self._parent + self._parent.onPreviousCard() def _on_next_card(self) -> None: + assert self._parent + self._parent.onNextCard() def _should_enable_prev(self) -> bool: + assert self._parent + return super()._should_enable_prev() or self._parent.has_previous_card() def _should_enable_next(self) -> bool: + assert self._parent + return super()._should_enable_next() or self._parent.has_next_card() def _render_scheduled(self) -> None: From eb66b20ca289bfb4125ac56e05df7ba39fdaa299 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:37:41 -0700 Subject: [PATCH 13/27] Fix model.py errors --- qt/aqt/browser/sidebar/model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qt/aqt/browser/sidebar/model.py b/qt/aqt/browser/sidebar/model.py index a3c8b41bc87..95438b4b6f3 100644 --- a/qt/aqt/browser/sidebar/model.py +++ b/qt/aqt/browser/sidebar/model.py @@ -30,6 +30,7 @@ def item_for_index(self, idx: QModelIndex) -> SidebarItem: return idx.internalPointer() def index_for_item(self, item: SidebarItem) -> QModelIndex: + assert item._row_in_parent return self.createIndex(item._row_in_parent, 0, item) def search(self, text: str) -> bool: @@ -74,6 +75,7 @@ def parent(self, child: QModelIndex) -> QModelIndex: # type: ignore return QModelIndex() row = parentItem._row_in_parent + assert row return self.createIndex(row, 0, parentItem) From d46297e60d19dbe70a17399054700eb786212b6c Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:37:52 -0700 Subject: [PATCH 14/27] Fix find_and_replace.py errors --- qt/aqt/browser/find_and_replace.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/qt/aqt/browser/find_and_replace.py b/qt/aqt/browser/find_and_replace.py index c1d51e43c6b..3c24b90b77d 100644 --- a/qt/aqt/browser/find_and_replace.py +++ b/qt/aqt/browser/find_and_replace.py @@ -80,13 +80,16 @@ def _show(self, field_names: Sequence[str]) -> None: self._find_history = restore_combo_history( self.form.find, self.COMBO_NAME + "Find" ) - self.form.find.completer().setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive) + + find_completer = self.form.find.completer() + assert find_completer + find_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive) self._replace_history = restore_combo_history( self.form.replace, self.COMBO_NAME + "Replace" ) - self.form.replace.completer().setCaseSensitivity( - Qt.CaseSensitivity.CaseSensitive - ) + replace_completer = self.form.replace.completer() + assert replace_completer + replace_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive) if not self.note_ids: # no selected notes to affect @@ -131,10 +134,13 @@ def accept(self) -> None: # an empty list means *all* notes self.note_ids = [] + parent_widget = self.parentWidget() + assert parent_widget + # tags? if self.form.field.currentIndex() == 1: op = find_and_replace_tag( - parent=self.parentWidget(), + parent=parent_widget, note_ids=self.note_ids, search=search, replacement=replace, @@ -149,7 +155,7 @@ def accept(self) -> None: field = self.field_names[self.form.field.currentIndex()] op = find_and_replace( - parent=self.parentWidget(), + parent=parent_widget, note_ids=self.note_ids, search=search, replacement=replace, From 2abdb8b3f17c672e5d98915c524eb29db3934ea8 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:37:58 -0700 Subject: [PATCH 15/27] Fix item.py errors --- qt/aqt/browser/sidebar/item.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py index 8e1f166c890..23bf13a8851 100644 --- a/qt/aqt/browser/sidebar/item.py +++ b/qt/aqt/browser/sidebar/item.py @@ -153,6 +153,9 @@ def has_same_id(self, other: SidebarItem) -> bool: SidebarItemType.NOTETYPE_TEMPLATE, SidebarItemType.NOTETYPE_FIELD, ]: + assert other._parent_item + assert self._parent_item + return ( other.id == self.id and other._parent_item.id == self._parent_item.id From 182d01ebf244b08e4f0ae37c339d24141f12ce50 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:38:04 -0700 Subject: [PATCH 16/27] Fix toolbar.py errors --- qt/aqt/browser/sidebar/toolbar.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qt/aqt/browser/sidebar/toolbar.py b/qt/aqt/browser/sidebar/toolbar.py index 78906b5803c..f4e041cd3ad 100644 --- a/qt/aqt/browser/sidebar/toolbar.py +++ b/qt/aqt/browser/sidebar/toolbar.py @@ -49,6 +49,7 @@ def _setup_tools(self) -> None: action = self.addAction( theme_manager.icon_from_resources(tool[1]), tool[2]() ) + assert action action.setCheckable(True) action.setShortcut(f"Alt+{row + 1}") self._action_group.addAction(action) From 330544afee3392f843f47cce7e641f74eef7e309 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 14:42:57 -0700 Subject: [PATCH 17/27] Fix table/__init__.py errors --- qt/aqt/browser/table/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/browser/table/__init__.py b/qt/aqt/browser/table/__init__.py index a5a8853117c..ac3348bbb7b 100644 --- a/qt/aqt/browser/table/__init__.py +++ b/qt/aqt/browser/table/__init__.py @@ -122,7 +122,7 @@ def backend_color_to_aqt_color(color: BrowserRow.Color.V) -> dict[str, str] | No return adjusted_bg_color(temp_color) -def adjusted_bg_color(color: dict[str, str]) -> dict[str, str]: +def adjusted_bg_color(color: dict[str, str] | None) -> dict[str, str] | None: if color: adjusted_color = copy.copy(color) light = QColor(color["light"]).lighter(150) From 0047146a3ed5ac17fe2475b795a035529fa525f8 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 20:39:11 -0700 Subject: [PATCH 18/27] Fix model.py errors --- qt/aqt/browser/table/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qt/aqt/browser/table/model.py b/qt/aqt/browser/table/model.py index 55f467f5363..0236f586c22 100644 --- a/qt/aqt/browser/table/model.py +++ b/qt/aqt/browser/table/model.py @@ -54,6 +54,7 @@ def __init__( self._stale_cutoff = 0.0 self._on_row_state_will_change = row_state_will_change_callback self._on_row_state_changed = row_state_changed_callback + assert aqt.mw self._want_tooltips = aqt.mw.pm.show_browser_table_tooltips() # Row Object Interface From e2506a73a1ac29a05ac0b05b3647f0d277411d0c Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 20:39:18 -0700 Subject: [PATCH 19/27] Fix state.py errors --- qt/aqt/browser/table/state.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qt/aqt/browser/table/state.py b/qt/aqt/browser/table/state.py index 75ee69ccb25..c275bfe4f3e 100644 --- a/qt/aqt/browser/table/state.py +++ b/qt/aqt/browser/table/state.py @@ -33,11 +33,13 @@ def is_notes_mode(self) -> bool: # Stateless Helpers def note_ids_from_card_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]: + assert self.col.db return self.col.db.list( f"select distinct nid from cards where id in {ids2str(items)}" ) def card_ids_from_note_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]: + assert self.col.db return self.col.db.list(f"select id from cards where nid in {ids2str(items)}") def column_key_at(self, index: int) -> str: From 59e6a11b741a68e38aa9ac3d05ba66d03fab2f68 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 23:50:09 -0700 Subject: [PATCH 20/27] Fix table.py errors --- qt/aqt/browser/table/table.py | 121 ++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index d7ba18baa5a..3edf825af02 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -77,7 +77,7 @@ def len_selection(self, refresh: bool = False) -> int: return self._len_selection def has_current(self) -> bool: - return self._view.selectionModel().currentIndex().isValid() + return self._selection_model().currentIndex().isValid() def has_previous(self) -> bool: return self.has_current() and self._current().row() > 0 @@ -117,17 +117,19 @@ def get_card_ids_from_selected_note_ids(self) -> Sequence[CardId]: # Selecting def select_all(self) -> None: + assert self._view self._view.selectAll() def clear_selection(self) -> None: self._len_selection = 0 self._selected_rows = None - self._view.selectionModel().clear() + self._selection_model().clear() def invert_selection(self) -> None: - selection = self._view.selectionModel().selection() + selection_model = self._selection_model() + selection = selection_model.selection() self.select_all() - self._view.selectionModel().select( + selection_model.select( selection, QItemSelectionModel.SelectionFlag.Deselect | QItemSelectionModel.SelectionFlag.Rows, @@ -139,6 +141,7 @@ def select_single_card( """Try to set the selection to the item corresponding to the given card.""" self._reset_selection() if (row := self._model.get_card_row(card_id)) is not None: + assert self._view self._view.selectRow(row) self._scroll_to_row(row, scroll_even_if_visible) else: @@ -249,7 +252,7 @@ def to_row_of_unselected_note(self) -> Sequence[NoteId]: return nids def clear_current(self) -> None: - self._view.selectionModel().setCurrentIndex( + self._selection_model().setCurrentIndex( QModelIndex(), QItemSelectionModel.SelectionFlag.NoUpdate, ) @@ -260,18 +263,16 @@ def clear_current(self) -> None: # Helpers def _current(self) -> QModelIndex: - return self._view.selectionModel().currentIndex() + return self._selection_model().currentIndex() def _selected(self) -> list[QModelIndex]: if self._selected_rows is None: - self._selected_rows = self._view.selectionModel().selectedRows() + self._selected_rows = self._selection_model().selectedRows() return self._selected_rows def _set_current(self, row: int, column: int = 0) -> None: - index = self._model.index( - row, self._view.horizontalHeader().logicalIndex(column) - ) - self._view.selectionModel().setCurrentIndex( + index = self._model.index(row, self._horizontal_header().logicalIndex(column)) + self._selection_model().setCurrentIndex( index, QItemSelectionModel.SelectionFlag.NoUpdate, ) @@ -281,7 +282,7 @@ def _reset_selection(self) -> None: If no selection change is triggered afterwards, `browser.on_all_or_selected_rows_changed()` and `browser.on_current_row_changed()` must be called. """ - self._view.selectionModel().reset() + self._selection_model().reset() self._len_selection = 0 self._selected_rows = None @@ -292,12 +293,12 @@ def _select_rows(self, rows: list[int]) -> None: self._model.index(row, 0), self._model.index(row, self._model.len_columns() - 1), ) - self._view.selectionModel().select( + self._selection_model().select( selection, QItemSelectionModel.SelectionFlag.SelectCurrent ) def _set_sort_indicator(self) -> None: - hh = self._view.horizontalHeader() + hh = self._horizontal_header() index = self._model.active_column_index(self._state.sort_column) if index is None: hh.setSortIndicatorShown(False) @@ -312,7 +313,7 @@ def _set_sort_indicator(self) -> None: hh.setSortIndicatorShown(True) def _set_column_sizes(self) -> None: - hh = self._view.horizontalHeader() + hh = self._horizontal_header() hh.setSectionResizeMode(QHeaderView.ResizeMode.Interactive) hh.setSectionResizeMode( hh.logicalIndex(self._model.len_columns() - 1), @@ -322,29 +323,32 @@ def _set_column_sizes(self) -> None: hh.setCascadingSectionResizes(False) def _save_header(self) -> None: - saveHeader(self._view.horizontalHeader(), self._state.GEOMETRY_KEY_PREFIX) + saveHeader(self._horizontal_header(), self._state.GEOMETRY_KEY_PREFIX) def _restore_header(self) -> None: - self._view.horizontalHeader().blockSignals(True) - restoreHeader(self._view.horizontalHeader(), self._state.GEOMETRY_KEY_PREFIX) + hh = self._horizontal_header() + hh.blockSignals(True) + restoreHeader(hh, self._state.GEOMETRY_KEY_PREFIX) self._set_column_sizes() self._set_sort_indicator() - self._view.horizontalHeader().blockSignals(False) + hh.blockSignals(False) # Setup def _setup_view(self) -> None: + assert self._view self._view.setSortingEnabled(True) self._view.setModel(self._model) self._view.selectionModel() self._view.setItemDelegate(StatusDelegate(self.browser, self._model)) - qconnect( - self._view.selectionModel().selectionChanged, self._on_selection_changed - ) - qconnect(self._view.selectionModel().currentChanged, self._on_current_changed) + selection_model = self._selection_model() + qconnect(selection_model.selectionChanged, self._on_selection_changed) + qconnect(selection_model.currentChanged, self._on_current_changed) self._view.setWordWrap(False) self._view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) - self._view.horizontalScrollBar().setSingleStep(10) + horizontal_scroll_bar = self._view.horizontalScrollBar() + assert horizontal_scroll_bar + horizontal_scroll_bar.setSingleStep(10) self._update_font() self._view.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) qconnect(self._view.customContextMenuRequested, self._on_context_menu) @@ -358,11 +362,17 @@ def _update_font(self) -> None: bsize = t.get("bsize", 0) if bsize > curmax: curmax = bsize - self._view.verticalHeader().setDefaultSectionSize(curmax + 6) + + assert self._view + vh = self._view.verticalHeader() + assert vh + vh.setDefaultSectionSize(curmax + 6) def _setup_headers(self) -> None: + assert self._view vh = self._view.verticalHeader() - hh = self._view.horizontalHeader() + assert vh + hh = self._horizontal_header() vh.hide() hh.show() hh.setHighlightSections(False) @@ -397,13 +407,13 @@ def _on_selection_changed( ) // self._model.len_columns() else: # New selection is created. Usually a single row or none at all. - self._len_selection = len(self._view.selectionModel().selectedRows()) + self._len_selection = len(self._selection_model().selectedRows()) self._selected_rows = None self.browser.on_all_or_selected_rows_changed() def _on_row_state_will_change(self, index: QModelIndex, was_restored: bool) -> None: if not was_restored: - if self._view.selectionModel().isSelected(index): + if self._selection_model().isSelected(index): self._len_selection -= 1 self._selected_rows = None self.browser.on_all_or_selected_rows_changed() @@ -414,7 +424,7 @@ def _on_row_state_will_change(self, index: QModelIndex, was_restored: bool) -> N def _on_row_state_changed(self, index: QModelIndex, was_restored: bool) -> None: if was_restored: - if self._view.selectionModel().isSelected(index): + if self._selection_model().isSelected(index): self._len_selection += 1 self._selected_rows = None self.browser.on_all_or_selected_rows_changed() @@ -445,6 +455,7 @@ def _on_context_menu(self, _point: QPoint) -> None: menu.addAction(action) menu.addSeparator() sub_menu = menu.addMenu(other_name) + assert sub_menu for action in other.actions(): sub_menu.addAction(action) gui_hooks.browser_will_show_context_menu(self.browser, menu) @@ -452,11 +463,13 @@ def _on_context_menu(self, _point: QPoint) -> None: menu.exec(QCursor.pos()) def _on_header_context(self, pos: QPoint) -> None: + assert self._view gpos = self._view.mapToGlobal(pos) m = QMenu() m.setToolTipsVisible(True) for key, column in self._model.columns.items(): a = m.addAction(self._state.column_label(column)) + assert a a.setCheckable(True) a.setChecked(self._model.active_column_index(key) is not None) a.setToolTip(self._state.column_tooltip(column)) @@ -577,27 +590,37 @@ def _toggled_selection(self) -> tuple[list[int], int | None]: def _scroll_to_row(self, row: int, scroll_even_if_visible: bool = False) -> None: """Scroll vertically to row.""" + assert self._view top_border = self._view.rowViewportPosition(row) bottom_border = top_border + self._view.rowHeight(0) - visible = top_border >= 0 and bottom_border < self._view.viewport().height() + viewport = self._view.viewport() + assert viewport + visible = top_border >= 0 and bottom_border < viewport.height() if not visible or scroll_even_if_visible: - horizontal = self._view.horizontalScrollBar().value() + horizontal_scroll_bar = self._view.horizontalScrollBar() + assert horizontal_scroll_bar + horizontal = horizontal_scroll_bar.value() self._view.scrollTo( self._model.index(row, 0), QAbstractItemView.ScrollHint.PositionAtTop ) - self._view.horizontalScrollBar().setValue(horizontal) + horizontal_scroll_bar.setValue(horizontal) def _scroll_to_column(self, column: int) -> None: """Scroll horizontally to column.""" + assert self._view position = self._view.columnViewportPosition(column) - visible = 0 <= position < self._view.viewport().width() + viewport = self._view.viewport() + assert viewport + visible = 0 <= position < viewport.width() if not visible: - vertical = self._view.verticalScrollBar().value() + vertical_scroll_bar = self._view.verticalScrollBar() + assert vertical_scroll_bar + vertical = vertical_scroll_bar.value() self._view.scrollTo( self._model.index(0, column), QAbstractItemView.ScrollHint.PositionAtCenter, ) - self._view.verticalScrollBar().setValue(vertical) + vertical_scroll_bar.setValue(vertical) def _move_current( self, @@ -606,6 +629,8 @@ def _move_current( ) -> None: if not self.has_current(): return + + assert self._view if index is None: index = self._view.moveCursor( direction, @@ -614,7 +639,7 @@ def _move_current( # Setting current like this avoids a bug with shift-click selection # https://github.com/ankitects/anki/issues/2469 self._view.setCurrentIndex(index) - self._view.selectionModel().select( + self._selection_model().select( index, QItemSelectionModel.SelectionFlag.Clear | QItemSelectionModel.SelectionFlag.Select @@ -622,18 +647,31 @@ def _move_current( ) def _move_current_to_row(self, row: int) -> None: - old = self._view.selectionModel().currentIndex() - self._move_current(None, self._model.index(row, 0)) + selection_model = self._selection_model() + old = selection_model.currentIndex() + self._move_current(None, self._model.index(row, 0)) # type: ignore if not KeyboardModifiersPressed().shift: return - new = self._view.selectionModel().currentIndex() + new = selection_model.currentIndex() selection = QItemSelection(new, old) - self._view.selectionModel().select( + selection_model.select( selection, QItemSelectionModel.SelectionFlag.SelectCurrent | QItemSelectionModel.SelectionFlag.Rows, ) + def _selection_model(self) -> QItemSelectionModel: + assert self._view + selection_model = self._view.selectionModel() + assert selection_model + return selection_model + + def _horizontal_header(self) -> QHeaderView: + assert self._view + hh = self._view.horizontalHeader() + assert hh + return hh + class StatusDelegate(QItemDelegate): def __init__(self, browser: aqt.browser.Browser, model: DataModel) -> None: @@ -641,13 +679,14 @@ def __init__(self, browser: aqt.browser.Browser, model: DataModel) -> None: self._model = model def paint( - self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex + self, painter: QPainter | None, option: QStyleOptionViewItem, index: QModelIndex ) -> None: option.textElideMode = self._model.get_cell(index).elide_mode if self._model.get_cell(index).is_rtl: option.direction = Qt.LayoutDirection.RightToLeft if row_color := self._model.get_row(index).color: brush = QBrush(theme_manager.qcolor(row_color)) + assert painter painter.save() painter.fillRect(option.rect, brush) painter.restore() From 9fe51a9da3178cf83377d6e075ad94ba4a16039b Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 23:56:23 -0700 Subject: [PATCH 21/27] Fix errors in card_info.py --- pylib/anki/collection.py | 2 +- qt/aqt/browser/card_info.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 66b2fb61857..ae5caa050dc 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -472,7 +472,7 @@ def update_image_occlusion_note( # Object helpers ########################################################################## - def get_card(self, id: CardId) -> Card: + def get_card(self, id: CardId | None) -> Card: return Card(self, id) def update_cards( diff --git a/qt/aqt/browser/card_info.py b/qt/aqt/browser/card_info.py index 75403edc0d5..47b5a3d7ee7 100644 --- a/qt/aqt/browser/card_info.py +++ b/qt/aqt/browser/card_info.py @@ -52,7 +52,9 @@ def _setup_ui(self, card_id: CardId | None) -> None: addCloseShortcut(self) setWindowIcon(self) - self.web = AnkiWebView(kind=AnkiWebViewKind.BROWSER_CARD_INFO) + self.web: AnkiWebView | None = AnkiWebView( + kind=AnkiWebViewKind.BROWSER_CARD_INFO + ) self.web.setVisible(False) self.web.load_sveltekit_page(f"card-info/{card_id}") layout = QVBoxLayout() @@ -76,11 +78,13 @@ def update_card(self, card_id: CardId | None) -> None: extra = "#night" else: extra = "" + assert self.web self.web.eval(f"window.location.href = '/card-info/{card_id}{extra}';") def reject(self) -> None: if self._on_close: self._on_close() + assert self.web self.web.cleanup() self.web = None saveGeom(self, self.GEOMETRY_KEY) From 7da7dba6619c13894425f4f8f3c2a61d7d1a605c Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 23:58:09 -0700 Subject: [PATCH 22/27] Fix searchbar.py errors --- qt/aqt/browser/sidebar/searchbar.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qt/aqt/browser/sidebar/searchbar.py b/qt/aqt/browser/sidebar/searchbar.py index b73d90f12cf..4b330674af9 100644 --- a/qt/aqt/browser/sidebar/searchbar.py +++ b/qt/aqt/browser/sidebar/searchbar.py @@ -29,7 +29,8 @@ def onTextChanged(self, text: str) -> None: def onSearch(self) -> None: self.sidebar.search_for(self.text()) - def keyPressEvent(self, evt: QKeyEvent) -> None: + def keyPressEvent(self, evt: QKeyEvent | None) -> None: + assert evt if evt.key() in (Qt.Key.Key_Up, Qt.Key.Key_Down): self.sidebar.setFocus() elif evt.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return): From bdc09567b005ee043ea7e61cecf8b0a722e77f31 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Mon, 7 Oct 2024 23:58:43 -0700 Subject: [PATCH 23/27] Fix name --- qt/aqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 9d987b66fc8..258a2e1942b 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -401,7 +401,7 @@ def event(self, evt: QEvent | None) -> bool: assert evt if evt.type() == QEvent.Type.FileOpen: - self.appMsg.emit(a0.file() or "raise") # type: ignore + self.appMsg.emit(evt.file() or "raise") # type: ignore return True return QApplication.event(self, evt) From d97399d3728a5c274924d226a404aa22a0eac034 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Tue, 8 Oct 2024 20:48:05 -0700 Subject: [PATCH 24/27] Fix assert in browser.py --- qt/aqt/browser/browser.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index fb726f4ee35..3f748e4bced 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -27,7 +27,7 @@ from anki.tags import MARKED_TAG from anki.utils import is_mac from aqt import AnkiQt, gui_hooks -from aqt.editor import Editor +from aqt.editor import Editor, EditorWebView from aqt.errors import show_exception from aqt.exporting import ExportDialog as LegacyExportDialog from aqt.import_export.exporting import ExportDialog @@ -291,24 +291,19 @@ def setupMenus(self) -> None: qconnect(f.actionCreateFilteredDeck.triggered, self.createFilteredDeck) f.actionCreateFilteredDeck.setShortcuts(["Ctrl+G", "Ctrl+Alt+G"]) - editor = self.editor - assert editor - editor_web_view = editor.web - assert editor_web_view - # view qconnect(f.actionFullScreen.triggered, self.mw.on_toggle_full_screen) qconnect( f.actionZoomIn.triggered, - lambda: editor_web_view.setZoomFactor(editor_web_view.zoomFactor() + 0.1), + lambda: self._editor_web_view().setZoomFactor(self._editor_web_view().zoomFactor() + 0.1), ) qconnect( f.actionZoomOut.triggered, - lambda: editor_web_view.setZoomFactor(editor_web_view.zoomFactor() - 0.1), + lambda: self._editor_web_view().setZoomFactor(self._editor_web_view().zoomFactor() - 0.1), ) qconnect( f.actionResetZoom.triggered, - lambda: editor_web_view.setZoomFactor(1), + lambda: self._editor_web_view().setZoomFactor(1), ) qconnect( self.form.actionLayoutAuto.triggered, @@ -381,6 +376,12 @@ def set_flag_func(desired_flag: int) -> Callable: add_ellipsis_to_action_label(f.actionCopy) add_ellipsis_to_action_label(f.action_forget) + def _editor_web_view(self) -> EditorWebView: + assert self.editor + editor_web_view = self.editor.web + assert editor_web_view + return editor_web_view + def closeEvent(self, evt: QCloseEvent | None) -> None: assert evt From f9b6d66732ee8c4476de4588e1a380c6fe8600b0 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Tue, 8 Oct 2024 20:57:29 -0700 Subject: [PATCH 25/27] Formatting --- qt/aqt/browser/browser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index 3f748e4bced..e62515d80d6 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -295,11 +295,15 @@ def setupMenus(self) -> None: qconnect(f.actionFullScreen.triggered, self.mw.on_toggle_full_screen) qconnect( f.actionZoomIn.triggered, - lambda: self._editor_web_view().setZoomFactor(self._editor_web_view().zoomFactor() + 0.1), + lambda: self._editor_web_view().setZoomFactor( + self._editor_web_view().zoomFactor() + 0.1 + ), ) qconnect( f.actionZoomOut.triggered, - lambda: self._editor_web_view().setZoomFactor(self._editor_web_view().zoomFactor() - 0.1), + lambda: self._editor_web_view().setZoomFactor( + self._editor_web_view().zoomFactor() - 0.1 + ), ) qconnect( f.actionResetZoom.triggered, From d8c5300af3bc835fe7c05cffb87d82d1afd85d54 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Tue, 8 Oct 2024 20:57:53 -0700 Subject: [PATCH 26/27] Fix assert vh --- qt/aqt/browser/table/table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index 3edf825af02..c556c2ff32c 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -365,13 +365,13 @@ def _update_font(self) -> None: assert self._view vh = self._view.verticalHeader() - assert vh + assert vh is not None vh.setDefaultSectionSize(curmax + 6) def _setup_headers(self) -> None: assert self._view vh = self._view.verticalHeader() - assert vh + assert vh is not None hh = self._horizontal_header() vh.hide() hh.show() From b39de459b4942686d8301b2aff546560a3431ad5 Mon Sep 17 00:00:00 2001 From: bpnguyen107 Date: Tue, 8 Oct 2024 21:29:51 -0700 Subject: [PATCH 27/27] assert is not None instead of truthy --- qt/aqt/__init__.py | 16 ++++----- qt/aqt/browser/browser.py | 56 ++++++++++++++--------------- qt/aqt/browser/card_info.py | 4 +-- qt/aqt/browser/find_and_replace.py | 6 ++-- qt/aqt/browser/find_duplicates.py | 4 +-- qt/aqt/browser/layout.py | 4 +-- qt/aqt/browser/previewer.py | 42 +++++++++++----------- qt/aqt/browser/sidebar/item.py | 4 +-- qt/aqt/browser/sidebar/model.py | 4 +-- qt/aqt/browser/sidebar/searchbar.py | 2 +- qt/aqt/browser/sidebar/toolbar.py | 2 +- qt/aqt/browser/sidebar/tree.py | 20 +++++------ qt/aqt/browser/table/model.py | 2 +- qt/aqt/browser/table/state.py | 4 +-- qt/aqt/browser/table/table.py | 40 ++++++++++----------- 15 files changed, 104 insertions(+), 106 deletions(-) diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 258a2e1942b..350a2233c06 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -281,7 +281,7 @@ def fn_ngettext(a, b, c) -> None: # type: ignore app.installTranslator(_qtrans) backend = anki.lang.current_i18n - assert backend + assert backend is not None return backend @@ -294,13 +294,13 @@ class NativeEventFilter(QAbstractNativeEventFilter): def nativeEventFilter( self, eventType: Any, message: Any ) -> tuple[bool, sip.voidptr | None]: - assert mw if eventType == "windows_generic_MSG": import ctypes.wintypes msg = ctypes.wintypes.MSG.from_address(int(message)) if msg.message == 17: # WM_QUERYENDSESSION + assert mw is not None if mw.can_auto_sync(): mw.app._set_windows_shutdown_block_reason(tr.sync_syncing()) mw.progress.single_shot(100, mw.unloadProfileAndExit) @@ -326,23 +326,21 @@ def __init__(self, argv: list[str]) -> None: self.installNativeEventFilter(self._native_event_filter) def _set_windows_shutdown_block_reason(self, reason: str) -> None: - assert mw - if is_win: import ctypes from ctypes import windll, wintypes # type: ignore + assert mw is not None windll.user32.ShutdownBlockReasonCreate( wintypes.HWND.from_param(int(mw.effectiveWinId())), ctypes.c_wchar_p(reason), ) def _unset_windows_shutdown_block_reason(self) -> None: - assert mw - if is_win: from ctypes import windll, wintypes # type: ignore + assert mw is not None windll.user32.ShutdownBlockReasonDestroy( wintypes.HWND.from_param(int(mw.effectiveWinId())), ) @@ -398,7 +396,7 @@ def onRecv(self) -> None: ################################################## def event(self, evt: QEvent | None) -> bool: - assert evt + assert evt is not None if evt.type() == QEvent.Type.FileOpen: self.appMsg.emit(evt.file() or "raise") # type: ignore @@ -409,7 +407,7 @@ def event(self, evt: QEvent | None) -> bool: ################################################## def eventFilter(self, src: Any, evt: QEvent | None) -> bool: - assert evt + assert evt is not None pointer_classes = ( QPushButton, @@ -560,7 +558,7 @@ def msgHandler(category: Any, ctx: Any, msg: Any) -> None: def write_profile_results() -> None: - assert profiler + assert profiler is not None profiler.disable() profile = "out/anki.prof" diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py index e62515d80d6..524887b08c1 100644 --- a/qt/aqt/browser/browser.py +++ b/qt/aqt/browser/browser.py @@ -139,7 +139,7 @@ def __init__( splitter_handle_event_filter = QSplitterHandleEventFilter(self.form.splitter) splitter_handle = self.form.splitter.handle(1) - assert splitter_handle + assert splitter_handle is not None splitter_handle.installEventFilter(splitter_handle_event_filter) # set if exactly 1 row is selected; used by the previewer @@ -184,7 +184,7 @@ def on_operation_did_execute( if handler is not self.editor: # fixme: this will leave the splitter shown, but with no current # note being edited - assert self.editor + assert self.editor is not None note = self.editor.note if note: @@ -248,7 +248,7 @@ def maybe_update_layout(self, aspect_ratio: float, force: bool = False) -> None: self.form.splitter.setOrientation(Qt.Orientation.Horizontal) def resizeEvent(self, event: QResizeEvent | None) -> None: - assert event + assert event is not None if self.height() != 0: aspect_ratio = self.width() / self.height() @@ -381,25 +381,25 @@ def set_flag_func(desired_flag: int) -> Callable: add_ellipsis_to_action_label(f.action_forget) def _editor_web_view(self) -> EditorWebView: - assert self.editor + assert self.editor is not None editor_web_view = self.editor.web - assert editor_web_view + assert editor_web_view is not None return editor_web_view def closeEvent(self, evt: QCloseEvent | None) -> None: - assert evt + assert evt is not None if self._closeEventHasCleanedUp: evt.accept() return - assert self.editor + assert self.editor is not None self.editor.call_after_note_saved(self._closeWindow) evt.ignore() def _closeWindow(self) -> None: - assert self.editor + assert self.editor is not None self._cleanup_preview() self._card_info.close() @@ -422,7 +422,7 @@ def closeWithCallback(self, onsuccess: Callable) -> None: onsuccess() def keyPressEvent(self, evt: QKeyEvent | None) -> None: - assert evt + assert evt is not None if evt.key() == Qt.Key.Key_Escape: self.close() @@ -453,7 +453,7 @@ def setupSearch( card: Card | None = None, search: tuple[str | SearchNode] | None = None, ) -> None: - assert self.mw.pm.profile + assert self.mw.pm.profile is not None line_edit = self._line_edit() qconnect(line_edit.returnPressed, self.onSearchActivated) @@ -507,7 +507,7 @@ def search(self) -> None: showWarning(str(err)) def update_history(self) -> None: - assert self.mw.pm.profile + assert self.mw.pm.profile is not None sh = self.mw.pm.profile.get("searchHistory", []) if self._lastSearchTxt in sh: @@ -554,7 +554,7 @@ def onReset(self) -> None: # caller must have called editor.saveNow() before calling this or .reset() def begin_reset(self) -> None: - assert self.editor + assert self.editor is not None self.editor.set_note(None, hide=False) self.mw.progress.start() @@ -607,14 +607,14 @@ def on_all_or_selected_rows_changed(self) -> None: self.singleCard = bool(self.card) splitter_widget = self.form.splitter.widget(1) - assert splitter_widget + assert splitter_widget is not None splitter_widget.setVisible(self.singleCard) - assert self.editor + assert self.editor is not None if self.singleCard: - assert self.card + assert self.card is not None self.editor.set_note(self.card.note(), focusTo=self.focusTo) self.focusTo = None @@ -782,7 +782,7 @@ def onHelp(self) -> None: def on_create_copy(self) -> None: if note := self.table.get_current_note(): current_card = self.table.get_current_card() - assert current_card + assert current_card is not None deck_id = current_card.current_deck_id() aqt.dialogs.open("AddCards", self.mw).set_note(note, deck_id) @@ -805,7 +805,7 @@ def createFilteredDeck(self) -> None: ###################################################################### def onTogglePreview(self) -> None: - assert self.editor + assert self.editor is not None if self._previewer: self._previewer.close() @@ -822,7 +822,7 @@ def _renderPreview(self) -> None: self.onTogglePreview() def toggle_preview_button_state(self, active: bool) -> None: - assert self.editor + assert self.editor is not None if self.editor.web: self.editor.web.eval(f"togglePreviewButtonState({json.dumps(active)});") @@ -849,7 +849,7 @@ def delete_selected_notes(self) -> None: if focus != self.form.tableView: return - assert self.editor + assert self.editor is not None self.editor.set_note(None) nids = self.table.to_row_of_unselected_note() @@ -868,14 +868,14 @@ def delete_selected_notes(self) -> None: def set_deck_of_selected_cards(self) -> None: from aqt.studydeck import StudyDeck - assert self.mw.col - assert self.mw.col.db + assert self.mw.col is not None + assert self.mw.col.db is not None cids = self.table.get_selected_card_ids() did = self.mw.col.db.scalar("select did from cards where id = ?", cids[0]) deck_dict = self.mw.col.decks.get(did) - assert deck_dict + assert deck_dict is not None current = deck_dict["name"] @@ -884,7 +884,7 @@ def callback(ret: StudyDeck) -> None: return did = self.col.decks.id(ret.name) - assert did + assert did is not None set_card_deck(parent=self, card_ids=cids, deck_id=did).run_in_background() @@ -1165,13 +1165,13 @@ def has_next_card(self) -> bool: return self.table.has_next() def onPreviousCard(self) -> None: - assert self.editor + assert self.editor is not None self.focusTo = self.editor.currentField self.editor.call_after_note_saved(self.table.to_previous_row) def onNextCard(self) -> None: - assert self.editor + assert self.editor is not None self.focusTo = self.editor.currentField self.editor.call_after_note_saved(self.table.to_next_row) @@ -1187,8 +1187,8 @@ def onFind(self) -> None: self._line_edit().selectAll() def onNote(self) -> None: - assert self.editor - assert self.editor.web + assert self.editor is not None + assert self.editor.web is not None self.editor.web.setFocus() self.editor.loadNote(focusTo=0) @@ -1198,5 +1198,5 @@ def onCardList(self) -> None: def _line_edit(self) -> QLineEdit: line_edit = self.form.searchEdit.lineEdit() - assert line_edit + assert line_edit is not None return line_edit diff --git a/qt/aqt/browser/card_info.py b/qt/aqt/browser/card_info.py index 47b5a3d7ee7..6042ea1db7a 100644 --- a/qt/aqt/browser/card_info.py +++ b/qt/aqt/browser/card_info.py @@ -78,13 +78,13 @@ def update_card(self, card_id: CardId | None) -> None: extra = "#night" else: extra = "" - assert self.web + assert self.web is not None self.web.eval(f"window.location.href = '/card-info/{card_id}{extra}';") def reject(self) -> None: if self._on_close: self._on_close() - assert self.web + assert self.web is not None self.web.cleanup() self.web = None saveGeom(self, self.GEOMETRY_KEY) diff --git a/qt/aqt/browser/find_and_replace.py b/qt/aqt/browser/find_and_replace.py index 3c24b90b77d..1dfc9648c24 100644 --- a/qt/aqt/browser/find_and_replace.py +++ b/qt/aqt/browser/find_and_replace.py @@ -82,13 +82,13 @@ def _show(self, field_names: Sequence[str]) -> None: ) find_completer = self.form.find.completer() - assert find_completer + assert find_completer is not None find_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive) self._replace_history = restore_combo_history( self.form.replace, self.COMBO_NAME + "Replace" ) replace_completer = self.form.replace.completer() - assert replace_completer + assert replace_completer is not None replace_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseSensitive) if not self.note_ids: @@ -135,7 +135,7 @@ def accept(self) -> None: self.note_ids = [] parent_widget = self.parentWidget() - assert parent_widget + assert parent_widget is not None # tags? if self.form.field.currentIndex() == 1: diff --git a/qt/aqt/browser/find_duplicates.py b/qt/aqt/browser/find_duplicates.py index 37fe39bfe27..5ffb97ba585 100644 --- a/qt/aqt/browser/find_duplicates.py +++ b/qt/aqt/browser/find_duplicates.py @@ -77,7 +77,7 @@ def on_click() -> None: tr.actions_search(), QDialogButtonBox.ButtonRole.ActionRole ) - assert search + assert search is not None qconnect(search.clicked, on_click) self.show() @@ -91,7 +91,7 @@ def show_duplicates_report(self, dupes: list[tuple[str, list[NoteId]]]) -> None: tr.browsing_tag_duplicates(), QDialogButtonBox.ButtonRole.ActionRole ) - assert b + assert b is not None qconnect(b.clicked, self._tag_duplicates) text = "" diff --git a/qt/aqt/browser/layout.py b/qt/aqt/browser/layout.py index 2c0b01a97b1..4acc7600a01 100644 --- a/qt/aqt/browser/layout.py +++ b/qt/aqt/browser/layout.py @@ -22,12 +22,12 @@ def __init__(self, splitter: QSplitter): self._splitter = splitter def eventFilter(self, object: QObject | None, event: QEvent | None) -> bool: - assert event + assert event is not None if event.type() == QEvent.Type.MouseButtonDblClick: splitter_parent = self._splitter.parentWidget() - assert splitter_parent + assert splitter_parent is not None if self._splitter.orientation() == Qt.Orientation.Horizontal: half_size = splitter_parent.width() // 2 diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index ec5051443df..4c9a97fb8b1 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -92,7 +92,7 @@ def _create_gui(self) -> None: self._replay = self.bbox.addButton( tr.actions_replay_audio(), QDialogButtonBox.ButtonRole.ActionRole ) - assert self._replay + assert self._replay is not None self._replay.setAutoDefault(False) self._replay.setShortcut(QKeySequence("R")) self._replay.setToolTip(tr.actions_shortcut_key(val="R")) @@ -116,9 +116,9 @@ def _on_finished(self, ok: int) -> None: self._on_close() def _on_replay_audio(self) -> None: - assert self._web + assert self._web is not None card = self.card() - assert card + assert card is not None gui_hooks.audio_will_replay(self._web, card, self._state == "question") @@ -131,13 +131,13 @@ def _on_close(self) -> None: self._open = False self._close_callback() - assert self._web + assert self._web is not None self._web.cleanup() self._web = None def _setup_web_view(self) -> None: - assert self._web + assert self._web is not None self._web.stdHtml( self.mw.reviewer.revHtml(), @@ -156,7 +156,7 @@ def _setup_web_view(self) -> None: def _on_bridge_cmd(self, cmd: str) -> Any: if cmd.startswith("play:"): card = self.card() - assert card + assert card is not None play_clicked_audio(cmd, card) @@ -168,7 +168,7 @@ def _update_flag_and_mark_icons(self, card: Card | None) -> None: flag = 0 marked = False - assert self._web + assert self._web is not None self._web.eval(f"_drawFlag({flag}); _drawMark({json.dumps(marked)});") @@ -228,7 +228,7 @@ def _render_scheduled(self) -> None: bodyclass = theme_manager.body_classes_for_card_ord(c.ord) - assert self._web + assert self._web is not None if c.autoplay(): self._web.setPlaybackRequiresGesture(False) @@ -260,18 +260,18 @@ def _render_scheduled(self) -> None: else: js = f"{func}({json.dumps(txt)}, '{bodyclass}');" - assert self._web + assert self._web is not None self._web.eval(js) self._card_changed = False def _on_show_both_sides(self, toggle: bool) -> None: - assert self._web + assert self._web is not None self._show_both_sides = toggle self.mw.col.set_config_bool(Config.Bool.PREVIEW_BOTH_SIDES, toggle) card = self.card() - assert card + assert card is not None gui_hooks.previewer_will_redraw_after_show_both_sides_toggled( self._web, card, self._state == "question", toggle @@ -284,7 +284,7 @@ def _on_show_both_sides(self, toggle: bool) -> None: def _state_and_mod(self) -> tuple[str, int, int]: c = self.card() - assert c + assert c is not None n = c.note() n.load() @@ -310,7 +310,7 @@ def _create_gui(self) -> None: QDialogButtonBox.ButtonRole.ActionRole, ) - assert self._prev + assert self._prev is not None self._prev.setAutoDefault(False) self._prev.setShortcut(QKeySequence("Left")) @@ -321,7 +321,7 @@ def _create_gui(self) -> None: QDialogButtonBox.ButtonRole.ActionRole, ) - assert self._next + assert self._next is not None self._next.setAutoDefault(True) self._next.setShortcut(QKeySequence("Right")) @@ -354,8 +354,8 @@ def _updateButtons(self) -> None: if not self._open: return - assert self._prev - assert self._next + assert self._prev is not None + assert self._next is not None self._prev.setEnabled(self._should_enable_prev()) self._next.setEnabled(self._should_enable_next()) @@ -382,7 +382,7 @@ def __init__( super().__init__(parent=parent, mw=mw, on_close=on_close) def card(self) -> Card | None: - assert self._parent + assert self._parent is not None if self._parent.singleCard: return self._parent.card @@ -399,22 +399,22 @@ def card_changed(self) -> bool: return changed def _on_prev_card(self) -> None: - assert self._parent + assert self._parent is not None self._parent.onPreviousCard() def _on_next_card(self) -> None: - assert self._parent + assert self._parent is not None self._parent.onNextCard() def _should_enable_prev(self) -> bool: - assert self._parent + assert self._parent is not None return super()._should_enable_prev() or self._parent.has_previous_card() def _should_enable_next(self) -> bool: - assert self._parent + assert self._parent is not None return super()._should_enable_next() or self._parent.has_next_card() diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py index 23bf13a8851..ce5ccb62f92 100644 --- a/qt/aqt/browser/sidebar/item.py +++ b/qt/aqt/browser/sidebar/item.py @@ -153,8 +153,8 @@ def has_same_id(self, other: SidebarItem) -> bool: SidebarItemType.NOTETYPE_TEMPLATE, SidebarItemType.NOTETYPE_FIELD, ]: - assert other._parent_item - assert self._parent_item + assert other._parent_item is not None + assert self._parent_item is not None return ( other.id == self.id diff --git a/qt/aqt/browser/sidebar/model.py b/qt/aqt/browser/sidebar/model.py index 95438b4b6f3..286811acaf3 100644 --- a/qt/aqt/browser/sidebar/model.py +++ b/qt/aqt/browser/sidebar/model.py @@ -30,7 +30,7 @@ def item_for_index(self, idx: QModelIndex) -> SidebarItem: return idx.internalPointer() def index_for_item(self, item: SidebarItem) -> QModelIndex: - assert item._row_in_parent + assert item._row_in_parent is not None return self.createIndex(item._row_in_parent, 0, item) def search(self, text: str) -> bool: @@ -75,7 +75,7 @@ def parent(self, child: QModelIndex) -> QModelIndex: # type: ignore return QModelIndex() row = parentItem._row_in_parent - assert row + assert row is not None return self.createIndex(row, 0, parentItem) diff --git a/qt/aqt/browser/sidebar/searchbar.py b/qt/aqt/browser/sidebar/searchbar.py index 4b330674af9..cb56d74afb2 100644 --- a/qt/aqt/browser/sidebar/searchbar.py +++ b/qt/aqt/browser/sidebar/searchbar.py @@ -30,7 +30,7 @@ def onSearch(self) -> None: self.sidebar.search_for(self.text()) def keyPressEvent(self, evt: QKeyEvent | None) -> None: - assert evt + assert evt is not None if evt.key() in (Qt.Key.Key_Up, Qt.Key.Key_Down): self.sidebar.setFocus() elif evt.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return): diff --git a/qt/aqt/browser/sidebar/toolbar.py b/qt/aqt/browser/sidebar/toolbar.py index f4e041cd3ad..0b16c4647ee 100644 --- a/qt/aqt/browser/sidebar/toolbar.py +++ b/qt/aqt/browser/sidebar/toolbar.py @@ -49,7 +49,7 @@ def _setup_tools(self) -> None: action = self.addAction( theme_manager.icon_from_resources(tool[1]), tool[2]() ) - assert action + assert action is not None action.setCheckable(True) action.setShortcut(f"Alt+{row + 1}") self._action_group.addAction(action) diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index e32d5e714bb..6f2029dc10e 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -308,7 +308,7 @@ def drawRow( ) -> None: if self.current_search and (item := self.model().item_for_index(idx)): if item.is_highlighted(): - assert painter + assert painter is not None brush = QBrush(theme_manager.qcolor(colors.HIGHLIGHT_BG)) painter.save() @@ -317,7 +317,7 @@ def drawRow( return super().drawRow(painter, options, idx) def dropEvent(self, event: QDropEvent | None) -> None: - assert event + assert event is not None model = self.model() if qtmajor == 5: @@ -329,7 +329,7 @@ def dropEvent(self, event: QDropEvent | None) -> None: event.acceptProposedAction() def mouseReleaseEvent(self, event: QMouseEvent | None) -> None: - assert event + assert event is not None super().mouseReleaseEvent(event) if ( @@ -344,7 +344,7 @@ def mouseReleaseEvent(self, event: QMouseEvent | None) -> None: self._on_search(index) def keyPressEvent(self, event: QKeyEvent | None) -> None: - assert event + assert event is not None index = self.currentIndex() if event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter): @@ -542,7 +542,7 @@ def _section_root( collapse_key: Config.Bool.V, type: SidebarItemType | None = None, ) -> SidebarItem: - assert type + assert type is not None def update(expanded: bool) -> None: CollectionOp( @@ -992,7 +992,7 @@ def _maybe_add_search_actions(self, menu: QMenu) -> None: menu.addAction(tr.actions_search(), lambda: self.update_search(*nodes)) return sub_menu = menu.addMenu(tr.actions_search()) - assert sub_menu + assert sub_menu is not None sub_menu.addAction( tr.actions_all_selected(), lambda: self.update_search(*nodes) @@ -1236,16 +1236,16 @@ def manage_notetype(self, item: SidebarItem) -> None: ) def manage_template(self, item: SidebarItem) -> None: - assert item._parent_item + assert item._parent_item is not None note = Note(self.col, self.col.models.get(NotetypeId(item._parent_item.id))) CardLayout(self.mw, note, ord=item.id, parent=self, fill_empty=True) def manage_fields(self, item: SidebarItem) -> None: - assert item._parent_item + assert item._parent_item is not None notetype = self.mw.col.models.get(NotetypeId(item._parent_item.id)) - assert notetype + assert notetype is not None FieldDialog(self.mw, notetype, parent=self, open_at=item.id) @@ -1278,5 +1278,5 @@ def _selected_tags(self) -> list[str]: def _selection_model(self) -> QItemSelectionModel: selection_model = self.selectionModel() - assert selection_model + assert selection_model is not None return selection_model diff --git a/qt/aqt/browser/table/model.py b/qt/aqt/browser/table/model.py index 0236f586c22..12a5162d187 100644 --- a/qt/aqt/browser/table/model.py +++ b/qt/aqt/browser/table/model.py @@ -54,7 +54,7 @@ def __init__( self._stale_cutoff = 0.0 self._on_row_state_will_change = row_state_will_change_callback self._on_row_state_changed = row_state_changed_callback - assert aqt.mw + assert aqt.mw is not None self._want_tooltips = aqt.mw.pm.show_browser_table_tooltips() # Row Object Interface diff --git a/qt/aqt/browser/table/state.py b/qt/aqt/browser/table/state.py index c275bfe4f3e..8054d259707 100644 --- a/qt/aqt/browser/table/state.py +++ b/qt/aqt/browser/table/state.py @@ -33,13 +33,13 @@ def is_notes_mode(self) -> bool: # Stateless Helpers def note_ids_from_card_ids(self, items: Sequence[ItemId]) -> Sequence[NoteId]: - assert self.col.db + assert self.col.db is not None return self.col.db.list( f"select distinct nid from cards where id in {ids2str(items)}" ) def card_ids_from_note_ids(self, items: Sequence[ItemId]) -> Sequence[CardId]: - assert self.col.db + assert self.col.db is not None return self.col.db.list(f"select id from cards where nid in {ids2str(items)}") def column_key_at(self, index: int) -> str: diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index c556c2ff32c..55975b85467 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -117,7 +117,7 @@ def get_card_ids_from_selected_note_ids(self) -> Sequence[CardId]: # Selecting def select_all(self) -> None: - assert self._view + assert self._view is not None self._view.selectAll() def clear_selection(self) -> None: @@ -141,7 +141,7 @@ def select_single_card( """Try to set the selection to the item corresponding to the given card.""" self._reset_selection() if (row := self._model.get_card_row(card_id)) is not None: - assert self._view + assert self._view is not None self._view.selectRow(row) self._scroll_to_row(row, scroll_even_if_visible) else: @@ -336,7 +336,7 @@ def _restore_header(self) -> None: # Setup def _setup_view(self) -> None: - assert self._view + assert self._view is not None self._view.setSortingEnabled(True) self._view.setModel(self._model) self._view.selectionModel() @@ -347,7 +347,7 @@ def _setup_view(self) -> None: self._view.setWordWrap(False) self._view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) horizontal_scroll_bar = self._view.horizontalScrollBar() - assert horizontal_scroll_bar + assert horizontal_scroll_bar is not None horizontal_scroll_bar.setSingleStep(10) self._update_font() self._view.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) @@ -363,13 +363,13 @@ def _update_font(self) -> None: if bsize > curmax: curmax = bsize - assert self._view + assert self._view is not None vh = self._view.verticalHeader() assert vh is not None vh.setDefaultSectionSize(curmax + 6) def _setup_headers(self) -> None: - assert self._view + assert self._view is not None vh = self._view.verticalHeader() assert vh is not None hh = self._horizontal_header() @@ -455,7 +455,7 @@ def _on_context_menu(self, _point: QPoint) -> None: menu.addAction(action) menu.addSeparator() sub_menu = menu.addMenu(other_name) - assert sub_menu + assert sub_menu is not None for action in other.actions(): sub_menu.addAction(action) gui_hooks.browser_will_show_context_menu(self.browser, menu) @@ -463,13 +463,13 @@ def _on_context_menu(self, _point: QPoint) -> None: menu.exec(QCursor.pos()) def _on_header_context(self, pos: QPoint) -> None: - assert self._view + assert self._view is not None gpos = self._view.mapToGlobal(pos) m = QMenu() m.setToolTipsVisible(True) for key, column in self._model.columns.items(): a = m.addAction(self._state.column_label(column)) - assert a + assert a is not None a.setCheckable(True) a.setChecked(self._model.active_column_index(key) is not None) a.setToolTip(self._state.column_tooltip(column)) @@ -590,15 +590,15 @@ def _toggled_selection(self) -> tuple[list[int], int | None]: def _scroll_to_row(self, row: int, scroll_even_if_visible: bool = False) -> None: """Scroll vertically to row.""" - assert self._view + assert self._view is not None top_border = self._view.rowViewportPosition(row) bottom_border = top_border + self._view.rowHeight(0) viewport = self._view.viewport() - assert viewport + assert viewport is not None visible = top_border >= 0 and bottom_border < viewport.height() if not visible or scroll_even_if_visible: horizontal_scroll_bar = self._view.horizontalScrollBar() - assert horizontal_scroll_bar + assert horizontal_scroll_bar is not None horizontal = horizontal_scroll_bar.value() self._view.scrollTo( self._model.index(row, 0), QAbstractItemView.ScrollHint.PositionAtTop @@ -607,14 +607,14 @@ def _scroll_to_row(self, row: int, scroll_even_if_visible: bool = False) -> None def _scroll_to_column(self, column: int) -> None: """Scroll horizontally to column.""" - assert self._view + assert self._view is not None position = self._view.columnViewportPosition(column) viewport = self._view.viewport() - assert viewport + assert viewport is not None visible = 0 <= position < viewport.width() if not visible: vertical_scroll_bar = self._view.verticalScrollBar() - assert vertical_scroll_bar + assert vertical_scroll_bar is not None vertical = vertical_scroll_bar.value() self._view.scrollTo( self._model.index(0, column), @@ -630,7 +630,7 @@ def _move_current( if not self.has_current(): return - assert self._view + assert self._view is not None if index is None: index = self._view.moveCursor( direction, @@ -661,15 +661,15 @@ def _move_current_to_row(self, row: int) -> None: ) def _selection_model(self) -> QItemSelectionModel: - assert self._view + assert self._view is not None selection_model = self._view.selectionModel() - assert selection_model + assert selection_model is not None return selection_model def _horizontal_header(self) -> QHeaderView: - assert self._view + assert self._view is not None hh = self._view.horizontalHeader() - assert hh + assert hh is not None return hh