diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py
index 8b213c96a..93192d574 100644
--- a/qt/aqt/__init__.py
+++ b/qt/aqt/__init__.py
@@ -136,7 +136,7 @@ class DialogManager:
def register_dialog(
self, name: str, creator: Union[Callable, type], instance: Optional[Any] = None
- ):
+ ) -> None:
"""Allows add-ons to register a custom dialog to be managed by Anki's dialog
manager, which ensures that only one copy of the window is open at once,
and that the dialog cleans up asynchronously when the collection closes
@@ -189,12 +189,12 @@ def setupLangAndBackend(
pass
# add _ and ngettext globals used by legacy code
- def fn__(arg) -> None:
+ def fn__(arg) -> None: # type: ignore
print("".join(traceback.format_stack()[-2]))
print("_ global will break in the future; please see anki/lang.py")
return arg
- def fn_ngettext(a, b, c) -> None:
+ def fn_ngettext(a, b, c) -> None: # type: ignore
print("".join(traceback.format_stack()[-2]))
print("ngettext global will break in the future; please see anki/lang.py")
return b
@@ -244,7 +244,7 @@ class AnkiApp(QApplication):
KEY = "anki" + checksum(getpass.getuser())
TMOUT = 30000
- def __init__(self, argv) -> None:
+ def __init__(self, argv: List[str]) -> None:
QApplication.__init__(self, argv)
self._argv = argv
@@ -267,7 +267,7 @@ class AnkiApp(QApplication):
self._srv.listen(self.KEY)
return False
- def sendMsg(self, txt) -> bool:
+ def sendMsg(self, txt: str) -> bool:
sock = QLocalSocket(self)
sock.connectToServer(self.KEY, QIODevice.WriteOnly)
if not sock.waitForConnected(self.TMOUT):
@@ -298,14 +298,14 @@ class AnkiApp(QApplication):
# OS X file/url handler
##################################################
- def event(self, evt) -> bool:
+ def event(self, evt: QEvent) -> bool:
if evt.type() == QEvent.FileOpen:
self.appMsg.emit(evt.file() or "raise") # type: ignore
return True
return QApplication.event(self, evt)
-def parseArgs(argv) -> Tuple[argparse.Namespace, List[str]]:
+def parseArgs(argv: List[str]) -> Tuple[argparse.Namespace, List[str]]:
"Returns (opts, args)."
# py2app fails to strip this in some instances, then anki dies
# as there's no such profile
@@ -330,7 +330,7 @@ def parseArgs(argv) -> Tuple[argparse.Namespace, List[str]]:
return parser.parse_known_args(argv[1:])
-def setupGL(pm) -> None:
+def setupGL(pm: aqt.profiles.ProfileManager) -> None:
if isMac:
return
@@ -343,7 +343,7 @@ def setupGL(pm) -> None:
ctypes.CDLL("libGL.so.1", ctypes.RTLD_GLOBAL)
# catch opengl errors
- def msgHandler(category, ctx, msg) -> None:
+ def msgHandler(category: Any, ctx: Any, msg: Any) -> None:
if category == QtDebugMsg:
category = "debug"
elif category == QtInfoMsg:
@@ -420,7 +420,7 @@ def run() -> None:
)
-def _run(argv=None, exec=True) -> Optional[AnkiApp]:
+def _run(argv: Optional[List[str]] = None, exec: bool = True) -> Optional[AnkiApp]:
"""Start AnkiQt application or reuse an existing instance if one exists.
If the function is invoked with exec=False, the AnkiQt will not enter
diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py
index 2386bb5b3..9844e18f1 100644
--- a/qt/aqt/clayout.py
+++ b/qt/aqt/clayout.py
@@ -4,6 +4,7 @@
import copy
import json
import re
+from concurrent.futures import Future
from typing import Any, Dict, List, Match, Optional
import aqt
@@ -45,10 +46,10 @@ class CardLayout(QDialog):
self,
mw: AnkiQt,
note: Note,
- ord=0,
+ ord: int = 0,
parent: Optional[QWidget] = None,
fill_empty: bool = False,
- ):
+ ) -> None:
QDialog.__init__(self, parent or mw, Qt.Window)
mw.setupDialogGC(self)
self.mw = aqt.mw
@@ -564,7 +565,7 @@ class CardLayout(QDialog):
def get_count() -> int:
return self.mm.template_use_count(self.model["id"], self.ord)
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
card_cnt = fut.result()
template = self.current_template()
@@ -798,7 +799,7 @@ class CardLayout(QDialog):
def save() -> None:
self.mm.save(self.model)
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
try:
fut.result()
except TemplateError as e:
diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py
index 70fe0483b..94468cb8b 100644
--- a/qt/aqt/customstudy.py
+++ b/qt/aqt/customstudy.py
@@ -22,7 +22,7 @@ TYPE_ALL = 3
class CustomStudy(QDialog):
- def __init__(self, mw) -> None:
+ def __init__(self, mw: aqt.AnkiQt) -> None:
QDialog.__init__(self, mw)
self.mw = mw
self.deck = self.mw.col.decks.current()
@@ -57,7 +57,7 @@ class CustomStudy(QDialog):
typeShow = False
ok = tr(TR.CUSTOM_STUDY_OK)
- def plus(num) -> str:
+ def plus(num: Union[int, str]) -> str:
if num == 1000:
num = "1000+"
return "" + str(num) + ""
diff --git a/qt/aqt/dbcheck.py b/qt/aqt/dbcheck.py
index 3024973b6..c8cec9b86 100644
--- a/qt/aqt/dbcheck.py
+++ b/qt/aqt/dbcheck.py
@@ -3,6 +3,8 @@
from __future__ import annotations
+from concurrent.futures import Future
+
import aqt
from anki.collection import DatabaseCheckProgress, ProgressKind
from aqt.qt import *
@@ -31,7 +33,7 @@ def check_db(mw: aqt.AnkiQt) -> None:
qconnect(timer.timeout, on_timer)
timer.start(100)
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
timer.stop()
ret, ok = fut.result()
diff --git a/qt/aqt/deckchooser.py b/qt/aqt/deckchooser.py
index 85842ebe1..d1097981e 100644
--- a/qt/aqt/deckchooser.py
+++ b/qt/aqt/deckchooser.py
@@ -13,13 +13,13 @@ class DeckChooser(QHBoxLayout):
self, mw: AnkiQt, widget: QWidget, label: bool = True, start: Any = None
) -> None:
QHBoxLayout.__init__(self)
- self.widget = widget # type: ignore
+ self._widget = widget # type: ignore
self.mw = mw
self.label = label
self.setContentsMargins(0, 0, 0, 0)
self.setSpacing(8)
self.setupDecks()
- self.widget.setLayout(self)
+ self._widget.setLayout(self)
gui_hooks.current_note_type_did_change.append(self.onModelChangeNew)
def setupDecks(self) -> None:
@@ -30,7 +30,7 @@ class DeckChooser(QHBoxLayout):
self.deck = QPushButton(clicked=self.onDeckChange) # type: ignore
self.deck.setAutoDefault(False)
self.deck.setToolTip(shortcut(tr(TR.QT_MISC_TARGET_DECK_CTRLANDD)))
- QShortcut(QKeySequence("Ctrl+D"), self.widget, activated=self.onDeckChange) # type: ignore
+ QShortcut(QKeySequence("Ctrl+D"), self._widget, activated=self.onDeckChange) # type: ignore
self.addWidget(self.deck)
# starting label
if self.mw.col.conf.get("addToCur", True):
@@ -59,10 +59,10 @@ class DeckChooser(QHBoxLayout):
self.deck.setSizePolicy(sizePolicy)
def show(self) -> None:
- self.widget.show() # type: ignore
+ self._widget.show() # type: ignore
def hide(self) -> None:
- self.widget.hide() # type: ignore
+ self._widget.hide() # type: ignore
def cleanup(self) -> None:
gui_hooks.current_note_type_did_change.remove(self.onModelChangeNew)
@@ -88,7 +88,7 @@ class DeckChooser(QHBoxLayout):
title=tr(TR.QT_MISC_CHOOSE_DECK),
help=HelpPage.EDITING,
cancel=False,
- parent=self.widget,
+ parent=self._widget,
geomKey="selectDeck",
)
if ret.name:
diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py
index 98f7b800b..500e7a950 100644
--- a/qt/aqt/emptycards.py
+++ b/qt/aqt/emptycards.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import re
+from concurrent.futures import Future
import aqt
from anki.collection import EmptyCardsReport, NoteWithEmptyCards
@@ -15,7 +16,7 @@ from aqt.utils import TR, disable_help_button, restoreGeom, saveGeom, tooltip, t
def show_empty_cards(mw: aqt.main.AnkiQt) -> None:
mw.progress.start()
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
mw.progress.finish()
report: EmptyCardsReport = fut.result()
if not report.notes:
@@ -74,7 +75,7 @@ class EmptyCardsDialog(QDialog):
def delete() -> int:
return self._delete_cards(self.form.keep_notes.isChecked())
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
self.mw.progress.finish()
try:
count = fut.result()
diff --git a/qt/aqt/fields.py b/qt/aqt/fields.py
index 1f7c2a804..c51fab49f 100644
--- a/qt/aqt/fields.py
+++ b/qt/aqt/fields.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 concurrent.futures import Future
+
import aqt
from anki.consts import *
from anki.errors import TemplateError
@@ -235,7 +237,7 @@ class FieldDialog(QDialog):
def save() -> None:
self.mm.save(self.model)
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
try:
fut.result()
except TemplateError as e:
diff --git a/qt/aqt/main.py b/qt/aqt/main.py
index 28cd61113..eb2d3dc55 100644
--- a/qt/aqt/main.py
+++ b/qt/aqt/main.py
@@ -13,8 +13,9 @@ import time
import weakref
import zipfile
from argparse import Namespace
+from concurrent.futures import Future
from threading import Thread
-from typing import Any, Callable, List, Optional, Sequence, TextIO, Tuple, cast
+from typing import Any, Callable, Dict, List, Optional, Sequence, TextIO, Tuple, cast
import anki
import aqt
@@ -337,13 +338,13 @@ class AnkiQt(QMainWindow):
):
return
- def doOpen(path) -> None:
+ def doOpen(path: str) -> None:
self._openBackup(path)
getFile(
self.profileDiag,
tr(TR.QT_MISC_REVERT_TO_BACKUP),
- cb=doOpen,
+ cb=doOpen, # type: ignore
filter="*.colpkg",
dir=self.pm.backupFolder(),
)
@@ -370,7 +371,7 @@ class AnkiQt(QMainWindow):
def downgrade() -> List[str]:
return self.pm.downgrade(profiles)
- def on_done(future) -> None:
+ def on_done(future: Future) -> None:
self.progress.finish()
problems = future.result()
if not problems:
@@ -568,7 +569,7 @@ class AnkiQt(QMainWindow):
##########################################################################
class BackupThread(Thread):
- def __init__(self, path, data) -> None:
+ def __init__(self, path: str, data: bytes) -> None:
Thread.__init__(self)
self.path = path
self.data = data
@@ -629,7 +630,7 @@ class AnkiQt(QMainWindow):
# State machine
##########################################################################
- def moveToState(self, state: str, *args) -> None:
+ def moveToState(self, state: str, *args: Any) -> None:
# print("-> move from", self.state, "to", state)
oldState = self.state or "dummy"
cleanup = getattr(self, "_" + oldState + "Cleanup", None)
@@ -804,7 +805,7 @@ title="%s" %s>%s""" % (
signal.signal(signal.SIGINT, self.onUnixSignal)
signal.signal(signal.SIGTERM, self.onUnixSignal)
- def onUnixSignal(self, signum, frame) -> None:
+ def onUnixSignal(self, signum: Any, frame: Any) -> None:
# schedule a rollback & quit
def quit() -> None:
self.col.db.rollback()
@@ -1184,14 +1185,14 @@ title="%s" %s>%s""" % (
qconnect(self.autoUpdate.clockIsOff, self.clockIsOff)
self.autoUpdate.start()
- def newVerAvail(self, ver) -> None:
+ def newVerAvail(self, ver: str) -> None:
if self.pm.meta.get("suppressUpdate", None) != ver:
aqt.update.askAndUpdate(self, ver)
- def newMsg(self, data) -> None:
+ def newMsg(self, data: Dict) -> None:
aqt.update.showMessages(self, data)
- def clockIsOff(self, diff) -> None:
+ def clockIsOff(self, diff: int) -> None:
if devMode:
print("clock is off; ignoring")
return
@@ -1374,11 +1375,11 @@ title="%s" %s>%s""" % (
d.show()
def _captureOutput(self, on: bool) -> None:
- mw = self
+ mw2 = self
class Stream:
- def write(self, data) -> None:
- mw._output += data
+ def write(self, data: str) -> None:
+ mw2._output += data
if on:
self._output = ""
@@ -1428,7 +1429,7 @@ title="%s" %s>%s""" % (
self._card_repr(card)
return card
- def onDebugPrint(self, frm) -> None:
+ def onDebugPrint(self, frm: aqt.forms.debug.Ui_Dialog) -> None:
cursor = frm.text.textCursor()
position = cursor.position()
cursor.select(QTextCursor.LineUnderCursor)
@@ -1441,7 +1442,7 @@ title="%s" %s>%s""" % (
frm.text.setTextCursor(cursor)
self.onDebugRet(frm)
- def onDebugRet(self, frm) -> None:
+ def onDebugRet(self, frm: aqt.forms.debug.Ui_Dialog) -> None:
import pprint
import traceback
diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py
index d94dd6f03..8a9bcf84b 100644
--- a/qt/aqt/modelchooser.py
+++ b/qt/aqt/modelchooser.py
@@ -20,7 +20,7 @@ class ModelChooser(QHBoxLayout):
and the caller can call .onModelChange() to pull up the dialog when they
are ready."""
QHBoxLayout.__init__(self)
- self.widget = widget # type: ignore
+ self._widget = widget # type: ignore
self.mw = mw
self.deck = mw.col
self.label = label
@@ -32,7 +32,7 @@ class ModelChooser(QHBoxLayout):
self.setSpacing(8)
self.setupModels()
gui_hooks.state_did_reset.append(self.onReset)
- self.widget.setLayout(self)
+ self._widget.setLayout(self)
def setupModels(self) -> None:
if self.label:
@@ -41,7 +41,7 @@ class ModelChooser(QHBoxLayout):
# models box
self.models = QPushButton()
self.models.setToolTip(shortcut(tr(TR.QT_MISC_CHANGE_NOTE_TYPE_CTRLANDN)))
- QShortcut(QKeySequence("Ctrl+N"), self.widget, activated=self.on_activated) # type: ignore
+ QShortcut(QKeySequence("Ctrl+N"), self._widget, activated=self.on_activated) # type: ignore
self.models.setAutoDefault(False)
self.addWidget(self.models)
qconnect(self.models.clicked, self.onModelChange)
@@ -57,15 +57,15 @@ class ModelChooser(QHBoxLayout):
self.updateModels()
def show(self) -> None:
- self.widget.show() # type: ignore
+ self._widget.show() # type: ignore
def hide(self) -> None:
- self.widget.hide() # type: ignore
+ self._widget.hide() # type: ignore
def onEdit(self) -> None:
import aqt.models
- aqt.models.Models(self.mw, self.widget)
+ aqt.models.Models(self.mw, self._widget)
def onModelChange(self) -> None:
from aqt.studydeck import StudyDeck
@@ -84,7 +84,7 @@ class ModelChooser(QHBoxLayout):
title=tr(TR.QT_MISC_CHOOSE_NOTE_TYPE),
help=HelpPage.NOTE_TYPE,
current=current,
- parent=self.widget,
+ parent=self._widget,
buttons=[edit],
cancel=True,
geomKey="selectModel",
diff --git a/qt/aqt/models.py b/qt/aqt/models.py
index ef17820b2..3d3f49dc5 100644
--- a/qt/aqt/models.py
+++ b/qt/aqt/models.py
@@ -1,6 +1,7 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+from concurrent.futures import Future
from operator import itemgetter
from typing import Any, List, Optional, Sequence
@@ -93,7 +94,7 @@ class Models(QDialog):
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
self.updateModelsList(fut.result())
self.maybe_select_provided_notetype()
@@ -113,7 +114,7 @@ class Models(QDialog):
self.mm.save(nt)
return self.col.models.all_use_counts()
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
@@ -163,7 +164,7 @@ class Models(QDialog):
self.mm.rem(nt)
return self.col.models.all_use_counts()
- def on_done(fut) -> None:
+ def on_done(fut: Future) -> None:
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py
index efecd2525..bdcb4f21e 100644
--- a/qt/aqt/profiles.py
+++ b/qt/aqt/profiles.py
@@ -113,9 +113,9 @@ class LoadMetaResult:
class AnkiRestart(SystemExit):
- def __init__(self, *args, **kwargs) -> None:
- self.exitcode = kwargs.pop("exitcode", 0)
- super().__init__(*args, **kwargs) # type: ignore
+ def __init__(self, exitcode: int = 0) -> None:
+ self.exitcode = exitcode
+ super().__init__()
class ProfileManager:
@@ -136,7 +136,7 @@ class ProfileManager:
return res
# profile load on startup
- def openProfile(self, profile) -> None:
+ def openProfile(self, profile: str) -> None:
if profile:
if profile not in self.profiles():
QMessageBox.critical(
@@ -185,7 +185,7 @@ class ProfileManager:
self.base = newBase
shutil.move(oldBase, self.base)
- def _tryToMigrateFolder(self, oldBase) -> None:
+ def _tryToMigrateFolder(self, oldBase: str) -> None:
from PyQt5 import QtGui, QtWidgets
app = QtWidgets.QApplication([])
@@ -257,7 +257,7 @@ class ProfileManager:
return n
- def _unpickle(self, data) -> Any:
+ def _unpickle(self, data: bytes) -> Any:
class Unpickler(pickle.Unpickler):
def find_class(self, module: str, name: str) -> Any:
if module == "PyQt5.sip":
@@ -282,7 +282,7 @@ class ProfileManager:
up = Unpickler(io.BytesIO(data), errors="ignore")
return up.load()
- def _pickle(self, obj) -> bytes:
+ def _pickle(self, obj: Any) -> bytes:
# pyqt needs to be updated to fix
# 'PY_SSIZE_T_CLEAN will be required for '#' formats' warning
# check if this is still required for pyqt6
@@ -290,7 +290,7 @@ class ProfileManager:
warnings.simplefilter("ignore")
return pickle.dumps(obj, protocol=4)
- def load(self, name) -> bool:
+ def load(self, name: str) -> bool:
assert name != "_global"
data = self.db.scalar(
"select cast(data as blob) from profiles where name = ?", name
@@ -316,14 +316,14 @@ class ProfileManager:
self.db.execute(sql, self._pickle(self.meta), "_global")
self.db.commit()
- def create(self, name) -> None:
+ def create(self, name: str) -> None:
prof = profileConf.copy()
self.db.execute(
"insert or ignore into profiles values (?, ?)", name, self._pickle(prof)
)
self.db.commit()
- def remove(self, name) -> None:
+ def remove(self, name: str) -> None:
p = self.profileFolder()
if os.path.exists(p):
send2trash(p)
@@ -335,7 +335,7 @@ class ProfileManager:
if os.path.exists(p):
send2trash(p)
- def rename(self, name) -> None:
+ def rename(self, name: str) -> None:
oldName = self.name
oldFolder = self.profileFolder()
self.name = name
@@ -379,7 +379,7 @@ class ProfileManager:
# Folder handling
######################################################################
- def profileFolder(self, create=True) -> str:
+ def profileFolder(self, create: bool = True) -> str:
path = os.path.join(self.base, self.name)
if create:
self._ensureExists(path)
@@ -397,7 +397,7 @@ class ProfileManager:
# Downgrade
######################################################################
- def downgrade(self, profiles=List[str]) -> List[str]:
+ def downgrade(self, profiles: List[str]) -> List[str]:
"Downgrade all profiles. Return a list of profiles that couldn't be opened."
problem_profiles = []
for name in profiles:
@@ -449,7 +449,7 @@ class ProfileManager:
os.makedirs(dataDir)
return os.path.join(dataDir, "Anki2")
- def _loadMeta(self, retrying=False) -> LoadMetaResult:
+ def _loadMeta(self, retrying: bool = False) -> LoadMetaResult:
result = LoadMetaResult()
result.firstTime = False
result.loadError = retrying
@@ -560,7 +560,7 @@ create table if not exists profiles
return self.setDefaultLang(f.lang.currentRow())
self.setLang(code)
- def setLang(self, code) -> None:
+ def setLang(self, code: str) -> None:
self.meta["defaultLang"] = code
sql = "update profiles set data = ? where name = ?"
self.db.execute(sql, self._pickle(self.meta), "_global")
@@ -602,7 +602,7 @@ create table if not exists profiles
def last_addon_update_check(self) -> int:
return self.meta.get("last_addon_update_check", 0)
- def set_last_addon_update_check(self, secs) -> None:
+ def set_last_addon_update_check(self, secs: int) -> None:
self.meta["last_addon_update_check"] = secs
def night_mode(self) -> bool:
diff --git a/qt/aqt/qt.py b/qt/aqt/qt.py
index c31e3346b..3d9f1496c 100644
--- a/qt/aqt/qt.py
+++ b/qt/aqt/qt.py
@@ -39,7 +39,7 @@ def debug() -> None:
if os.environ.get("DEBUG"):
- def info(type, value, tb) -> None:
+ def info(type, value, tb) -> None: # type: ignore
for line in traceback.format_exception(type, value, tb):
sys.stdout.write(line)
pyqtRemoveInputHook()
diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py
index 04b835e3f..6fc1281a5 100644
--- a/qt/aqt/sound.py
+++ b/qt/aqt/sound.py
@@ -595,7 +595,7 @@ class QtAudioInputRecorder(Recorder):
wf.writeframes(self._buffer)
wf.close()
- def and_then(fut) -> None:
+ def and_then(fut: Future) -> None:
fut.result()
Recorder.stop(self, on_done)
@@ -686,7 +686,7 @@ class PyAudioRecorder(Recorder):
while self.duration() < 1:
time.sleep(0.1)
- def func(fut) -> None:
+ def func(fut: Future) -> None:
Recorder.stop(self, on_done)
self.thread.finish = True
@@ -789,7 +789,7 @@ class RecordDialog(QDialog):
def record_audio(
parent: QWidget, mw: aqt.AnkiQt, encode: bool, on_done: Callable[[str], None]
-):
+) -> None:
def after_record(path: str) -> None:
if not encode:
on_done(path)
@@ -812,7 +812,7 @@ def play(filename: str) -> None:
av_player.play_file(filename)
-def playFromText(text) -> None:
+def playFromText(text: Any) -> None:
print("playFromText() deprecated")
diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py
index a1c290fc6..c375beea5 100644
--- a/qt/aqt/stats.py
+++ b/qt/aqt/stats.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import time
+from typing import Any
import aqt
from aqt import gui_hooks
@@ -60,7 +61,7 @@ class NewDeckStats(QDialog):
aqt.dialogs.markClosed("NewDeckStats")
QDialog.reject(self)
- def closeWithCallback(self, callback) -> None:
+ def closeWithCallback(self, callback: Callable[[], None]) -> None:
self.reject()
callback()
@@ -84,10 +85,11 @@ class NewDeckStats(QDialog):
self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED))
- def changePeriod(self, n) -> None:
+ # legacy add-ons
+ def changePeriod(self, n: Any) -> None:
pass
- def changeScope(self, type) -> None:
+ def changeScope(self, type: Any) -> None:
pass
def _on_bridge_cmd(self, cmd: str) -> bool:
@@ -149,7 +151,7 @@ class DeckStats(QDialog):
aqt.dialogs.markClosed("DeckStats")
QDialog.reject(self)
- def closeWithCallback(self, callback) -> None:
+ def closeWithCallback(self, callback: Callable[[], None]) -> None:
self.reject()
callback()
diff --git a/qt/aqt/studydeck.py b/qt/aqt/studydeck.py
index 61f0ec38c..277465071 100644
--- a/qt/aqt/studydeck.py
+++ b/qt/aqt/studydeck.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-from typing import Optional
+from typing import List, Optional
import aqt
from aqt import gui_hooks
@@ -25,17 +25,17 @@ from aqt.utils import (
class StudyDeck(QDialog):
def __init__(
self,
- mw,
- names=None,
- accept=None,
- title=None,
+ mw: aqt.AnkiQt,
+ names: Callable = None,
+ accept: str = None,
+ title: str = None,
help: HelpPageArgument = HelpPage.KEYBOARD_SHORTCUTS,
- current=None,
- cancel=True,
- parent=None,
- dyn=False,
- buttons=None,
- geomKey="default",
+ current: Optional[str] = None,
+ cancel: bool = True,
+ parent: Optional[QDialog] = None,
+ dyn: bool = False,
+ buttons: Optional[List[str]] = None,
+ geomKey: str = "default",
) -> None:
QDialog.__init__(self, parent or mw)
if buttons is None:
@@ -65,18 +65,18 @@ class StudyDeck(QDialog):
if title:
self.setWindowTitle(title)
if not names:
- names = [
+ names_ = [
d.name
for d in self.mw.col.decks.all_names_and_ids(
include_filtered=dyn, skip_empty_default=True
)
]
self.nameFunc = None
- self.origNames = names
+ self.origNames = names_
else:
self.nameFunc = names
self.origNames = names()
- self.name = None
+ self.name: Optional[str] = None
self.ok = self.form.buttonBox.addButton(
accept or tr(TR.DECKS_STUDY), QDialogButtonBox.AcceptRole
)
diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py
index 73759d81e..9686515c9 100644
--- a/qt/aqt/sync.py
+++ b/qt/aqt/sync.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import enum
import os
+from concurrent.futures import Future
from typing import Callable, Tuple
import aqt
@@ -48,7 +49,7 @@ def get_sync_status(
callback(SyncStatus(required=SyncStatus.NO_CHANGES)) # pylint:disable=no-member
return
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
try:
out = fut.result()
except Exception as e:
@@ -97,7 +98,7 @@ def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
qconnect(timer.timeout, on_timer)
timer.start(150)
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
mw.col.db.begin()
timer.stop()
try:
@@ -181,7 +182,7 @@ def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
qconnect(timer.timeout, on_timer)
timer.start(150)
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@@ -209,7 +210,7 @@ def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
qconnect(timer.timeout, on_timer)
timer.start(150)
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@@ -229,7 +230,10 @@ def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
def sync_login(
- mw: aqt.main.AnkiQt, on_success: Callable[[], None], username="", password=""
+ mw: aqt.main.AnkiQt,
+ on_success: Callable[[], None],
+ username: str = "",
+ password: str = "",
) -> None:
while True:
(username, password) = get_id_and_pass_from_user(mw, username, password)
@@ -238,7 +242,7 @@ def sync_login(
if username and password:
break
- def on_future_done(fut) -> None:
+ def on_future_done(fut: Future) -> None:
try:
auth = fut.result()
except SyncError as e:
@@ -282,7 +286,7 @@ def ask_user_to_decide_direction() -> FullSyncChoice:
def get_id_and_pass_from_user(
- mw: aqt.main.AnkiQt, username="", password=""
+ mw: aqt.main.AnkiQt, username: str = "", password: str = ""
) -> Tuple[str, str]:
diag = QDialog(mw)
diag.setWindowTitle("Anki")
diff --git a/qt/aqt/tagedit.py b/qt/aqt/tagedit.py
index ff81f9eae..c3850ccb8 100644
--- a/qt/aqt/tagedit.py
+++ b/qt/aqt/tagedit.py
@@ -92,7 +92,7 @@ class TagEdit(QLineEdit):
self.completer.setCompletionPrefix(self.text())
self.completer.complete()
- def focusOutEvent(self, evt) -> None:
+ def focusOutEvent(self, evt: QFocusEvent) -> None:
QLineEdit.focusOutEvent(self, evt)
self.lostFocus.emit() # type: ignore
self.completer.popup().hide()
@@ -109,7 +109,6 @@ class TagCompleter(QCompleter):
model: QStringListModel,
parent: QWidget,
edit: TagEdit,
- *args,
) -> None:
QCompleter.__init__(self, model, parent)
self.tags: List[str] = []
diff --git a/qt/aqt/taskman.py b/qt/aqt/taskman.py
index 53040d7b4..0a059e910 100644
--- a/qt/aqt/taskman.py
+++ b/qt/aqt/taskman.py
@@ -68,10 +68,10 @@ class TaskManager(QObject):
parent: Optional[QWidget] = None,
label: Optional[str] = None,
immediate: bool = False,
- ):
+ ) -> None:
self.mw.progress.start(parent=parent, label=label, immediate=immediate)
- def wrapped_done(fut) -> None:
+ def wrapped_done(fut: Future) -> None:
self.mw.progress.finish()
if on_done:
on_done(fut)
diff --git a/qt/aqt/tts.py b/qt/aqt/tts.py
index 875983685..ddf61b1b9 100644
--- a/qt/aqt/tts.py
+++ b/qt/aqt/tts.py
@@ -35,6 +35,7 @@ from dataclasses import dataclass
from operator import attrgetter
from typing import Any, List, Optional, cast
+import anki
from anki import hooks
from anki.sound import AVTag, TTSTag
from anki.utils import checksum, isWin, tmpdir
@@ -125,7 +126,9 @@ def all_tts_voices() -> List[TTSVoice]:
return all_voices
-def on_tts_voices(text: str, field, filter: str, ctx) -> str:
+def on_tts_voices(
+ text: str, field: str, filter: str, ctx: anki.template.TemplateRenderContext
+) -> str:
if filter != "tts-voices":
return text
voices = all_tts_voices()
@@ -572,7 +575,7 @@ if isWin:
# then tell player to advance, which will cause the file to be played
cb()
- async def speakText(self, tag: TTSTag, voice_id) -> None:
+ async def speakText(self, tag: TTSTag, voice_id: Any) -> None:
import winrt.windows.media.speechsynthesis as speechsynthesis # type: ignore
import winrt.windows.storage.streams as streams # type: ignore
diff --git a/qt/aqt/update.py b/qt/aqt/update.py
index 9c940fe3c..c36fcadd8 100644
--- a/qt/aqt/update.py
+++ b/qt/aqt/update.py
@@ -56,7 +56,7 @@ class LatestVersionFinder(QThread):
self.clockIsOff.emit(diff) # type: ignore
-def askAndUpdate(mw, ver) -> None:
+def askAndUpdate(mw: aqt.AnkiQt, ver: str) -> None:
baseStr = tr(TR.QT_MISC_ANKI_UPDATEDANKI_HAS_BEEN_RELEASED, val=ver)
msg = QMessageBox(mw)
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) # type: ignore
@@ -73,6 +73,6 @@ def askAndUpdate(mw, ver) -> None:
openLink(aqt.appWebsite)
-def showMessages(mw, data) -> None:
+def showMessages(mw: aqt.AnkiQt, data: Dict) -> None:
showText(data["msg"], parent=mw, type="html")
mw.pm.meta["lastMsg"] = data["msgId"]
diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py
index ceaa25b7c..fe855fd6f 100644
--- a/qt/aqt/utils.py
+++ b/qt/aqt/utils.py
@@ -468,6 +468,7 @@ def disable_help_button(widget: QWidget) -> None:
def getFile(
parent: QDialog,
title: str,
+ # single file returned unless multi=True
cb: Optional[Callable[[Union[str, Sequence[str]]], None]],
filter: str = "*.*",
dir: Optional[str] = None,
diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py
index 869f30928..8727a11f9 100644
--- a/qt/aqt/webview.py
+++ b/qt/aqt/webview.py
@@ -5,7 +5,7 @@ import dataclasses
import json
import re
import sys
-from typing import Any, Callable, List, Optional, Sequence, Tuple
+from typing import Any, Callable, List, Optional, Sequence, Tuple, cast
import anki
from anki.lang import is_rtl
@@ -20,9 +20,11 @@ serverbaseurl = re.compile(r"^.+:\/\/[^\/]+")
# Page for debug messages
##########################################################################
+BridgeCommandHandler = Callable[[str], Any]
+
class AnkiWebPage(QWebEnginePage):
- def __init__(self, onBridgeCmd) -> None:
+ def __init__(self, onBridgeCmd: BridgeCommandHandler) -> None:
QWebEnginePage.__init__(self)
self._onBridgeCmd = onBridgeCmd
self._setupBridge()
@@ -31,7 +33,7 @@ class AnkiWebPage(QWebEnginePage):
def _setupBridge(self) -> None:
class Bridge(QObject):
@pyqtSlot(str, result=str) # type: ignore
- def cmd(self, str) -> Any:
+ def cmd(self, str: str) -> Any:
return json.dumps(self.onCmd(str))
self._bridge = Bridge()
@@ -74,7 +76,13 @@ class AnkiWebPage(QWebEnginePage):
script.setRunsOnSubFrames(False)
self.profile().scripts().insert(script)
- def javaScriptConsoleMessage(self, level, msg, line, srcID) -> None:
+ def javaScriptConsoleMessage(
+ self,
+ level: QWebEnginePage.JavaScriptConsoleMessageLevel,
+ msg: str,
+ line: int,
+ srcID: str,
+ ) -> None:
# not translated because console usually not visible,
# and may only accept ascii text
if srcID.startswith("data"):
@@ -82,13 +90,15 @@ class AnkiWebPage(QWebEnginePage):
else:
srcID = serverbaseurl.sub("", srcID[:80], 1)
if level == QWebEnginePage.InfoMessageLevel:
- level = "info"
+ level_str = "info"
elif level == QWebEnginePage.WarningMessageLevel:
- level = "warning"
+ level_str = "warning"
elif level == QWebEnginePage.ErrorMessageLevel:
- level = "error"
+ level_str = "error"
+ else:
+ level_str = str(level)
buf = "JS %(t)s %(f)s:%(a)d %(b)s" % dict(
- t=level, a=line, f=srcID, b=msg + "\n"
+ t=level_str, a=line, f=srcID, b=msg + "\n"
)
if "MathJax localStorage" in buf:
# silence localStorage noise
@@ -101,7 +111,9 @@ class AnkiWebPage(QWebEnginePage):
# https://github.com/ankitects/anki/pull/560
sys.stdout.write(buf)
- def acceptNavigationRequest(self, url, navType, isMainFrame) -> bool:
+ def acceptNavigationRequest(
+ self, url: QUrl, navType: Any, isMainFrame: bool
+ ) -> bool:
if not self.open_links_externally:
return super().acceptNavigationRequest(url, navType, isMainFrame)
@@ -113,14 +125,14 @@ class AnkiWebPage(QWebEnginePage):
# catch buggy links
from aqt import mw
- if url.matches(QUrl(mw.serverURL()), QUrl.RemoveFragment):
+ if url.matches(QUrl(mw.serverURL()), cast(Any, QUrl.RemoveFragment)):
print("onclick handler needs to return false")
return False
# load all other links in browser
openLink(url)
return False
- def _onCmd(self, str) -> None:
+ def _onCmd(self, str: str) -> None:
return self._onBridgeCmd(str)
def javaScriptAlert(self, url: QUrl, text: str) -> None:
@@ -293,7 +305,7 @@ class AnkiWebView(QWebEngineView):
gui_hooks.webview_will_show_context_menu(self, m)
m.popup(QCursor.pos())
- def dropEvent(self, evt) -> None:
+ def dropEvent(self, evt: QDropEvent) -> None:
pass
def setHtml(self, html: str) -> None: # type: ignore
@@ -348,7 +360,7 @@ class AnkiWebView(QWebEngineView):
QWebEngineSettings.PlaybackRequiresUserGesture, value
)
- def _getQtIntScale(self, screen) -> int:
+ def _getQtIntScale(self, screen: QWidget) -> int:
# try to detect if Qt has scaled the screen
# - qt will round the scale factor to a whole number, so a dpi of 125% = 1x,
# and a dpi of 150% = 2x
@@ -434,7 +446,7 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
js: Optional[List[str]] = None,
head: str = "",
context: Optional[Any] = None,
- ):
+ ) -> None:
web_content = WebContent(
body=body,
@@ -508,7 +520,7 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
def _evalWithCallback(self, js: str, cb: Callable[[Any], Any]) -> None:
if cb:
- def handler(val) -> None:
+ def handler(val: Any) -> None:
if self._shouldIgnoreWebEvent():
print("ignored late js callback", cb)
return
@@ -608,7 +620,7 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
"Add dynamic styling, and reveal."
css = self.standard_css()
- def after_style(arg) -> None:
+ def after_style(arg: Any) -> None:
gui_hooks.webview_did_inject_style_into_page(self)
self.show()
diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py
index fdc3af8c8..b983a5ae3 100644
--- a/qt/tools/genhooks_gui.py
+++ b/qt/tools/genhooks_gui.py
@@ -165,7 +165,7 @@ hooks = [
),
Hook(
name="debug_console_did_evaluate_python",
- args=["output: str", "query: str", "debug_window: QDialog"],
+ args=["output: str", "query: str", "debug_window: aqt.forms.debug.Ui_Dialog"],
return_type="str",
doc="""Allows processing the debug result. E.g. logging queries and
result, saving last query to display it later...""",