diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py
index f06bd038d..bd5795024 100644
--- a/pylib/anki/collection.py
+++ b/pylib/anki/collection.py
@@ -657,6 +657,7 @@ where c.nid = n.id and c.id in %s group by nid"""
self._startTime = time.time()
self._startReps = self.sched.reps
+ # FIXME: Use Literal[False] when on Python 3.8
def timeboxReached(self) -> Union[bool, Tuple[Any, int]]:
"Return (elapsedTime, reps) if timebox reached, or False."
if not self.conf["timeLim"]:
diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py
index 316c33803..b02b96ab5 100644
--- a/qt/aqt/reviewer.py
+++ b/qt/aqt/reviewer.py
@@ -6,11 +6,12 @@ from __future__ import annotations
import difflib
import html
-import html.parser
import json
import re
import unicodedata as ucd
-from typing import List, Optional
+from typing import Callable, List, Optional, Sequence, Tuple, Union
+
+from PyQt5.QtCore import Qt
from anki import hooks
from anki.cards import Card
@@ -25,7 +26,7 @@ from aqt.utils import askUserDialog, downArrow, qtMenuShortcutWorkaround, toolti
class ReviewerBottomBar:
- def __init__(self, reviewer: Reviewer):
+ def __init__(self, reviewer: Reviewer) -> None:
self.reviewer = reviewer
@@ -39,28 +40,29 @@ class Reviewer:
self.cardQueue: List[Card] = []
self.hadCardQueue = False
self._answeredIds: List[int] = []
- self._recordedAudio = None
- self.typeCorrect = None # web init happens before this is set
+ self._recordedAudio: Optional[str] = None
+ self.typeCorrect: str = None # web init happens before this is set
self.state: Optional[str] = None
self.bottom = BottomBar(mw, mw.bottomWeb)
hooks.card_did_leech.append(self.onLeech)
- def show(self):
+ def show(self) -> None:
self.mw.col.reset()
- self.mw.setStateShortcuts(self._shortcutKeys())
+ self.mw.setStateShortcuts(self._shortcutKeys()) # type: ignore
self.web.set_bridge_command(self._linkHandler, self)
self.bottom.web.set_bridge_command(self._linkHandler, ReviewerBottomBar(self))
- self._reps = None
+ self._reps: int = None
self.nextCard()
- def lastCard(self):
+ def lastCard(self) -> Optional[Card]:
if self._answeredIds:
if not self.card or self._answeredIds[-1] != self.card.id:
try:
return self.mw.col.getCard(self._answeredIds[-1])
except TypeError:
# id was deleted
- return
+ return None
+ return None
def cleanup(self) -> None:
gui_hooks.reviewer_will_end()
@@ -68,9 +70,10 @@ class Reviewer:
# Fetching a card
##########################################################################
- def nextCard(self):
+ def nextCard(self) -> None:
elapsed = self.mw.col.timeboxReached()
if elapsed:
+ assert not isinstance(elapsed, bool)
part1 = (
ngettext("%d card studied in", "%d cards studied in", elapsed[1])
% elapsed[1]
@@ -125,7 +128,7 @@ class Reviewer:
# Initializing the webview
##########################################################################
- def revHtml(self):
+ def revHtml(self) -> str:
extra = self.mw.col.conf.get("reviewExtra", "")
fade = ""
if self.mw.pm.glMode() == "software":
@@ -140,7 +143,7 @@ class Reviewer:
fade, extra
)
- def _initWeb(self):
+ def _initWeb(self) -> None:
self._reps = 0
# main window
self.web.stdHtml(
@@ -167,13 +170,13 @@ class Reviewer:
# Showing the question
##########################################################################
- def _mungeQA(self, buf):
+ def _mungeQA(self, buf: str) -> str:
return self.typeAnsFilter(self.mw.prepare_card_text_for_display(buf))
def _showQuestion(self) -> None:
self._reps += 1
self.state = "question"
- self.typedAnswer = None
+ self.typedAnswer: str = None
c = self.card
# grab the question and play audio
if c.isEmpty():
@@ -206,17 +209,17 @@ The front of this card is empty. Please run Tools>Empty Cards."""
# user hook
gui_hooks.reviewer_did_show_question(c)
- def autoplay(self, card):
+ def autoplay(self, card: Card) -> bool:
return self.mw.col.decks.confForDid(card.odid or card.did)["autoplay"]
def _replayq(self, card, previewer=None):
s = previewer if previewer else self
return s.mw.col.decks.confForDid(s.card.odid or s.card.did).get("replayq", True)
- def _drawFlag(self):
+ def _drawFlag(self) -> None:
self.web.eval("_drawFlag(%s);" % self.card.userFlag())
- def _drawMark(self):
+ def _drawMark(self) -> None:
self.web.eval("_drawMark(%s);" % json.dumps(self.card.note().hasTag("marked")))
# Showing the answer
@@ -246,7 +249,7 @@ The front of this card is empty. Please run Tools>Empty Cards."""
# Answering a card
############################################################
- def _answerCard(self, ease):
+ def _answerCard(self, ease: int) -> None:
"Reschedule card and show next."
if self.mw.state != "review":
# showing resetRequired screen; ignore key
@@ -269,7 +272,9 @@ The front of this card is empty. Please run Tools>Empty Cards."""
# Handlers
############################################################
- def _shortcutKeys(self):
+ def _shortcutKeys(
+ self,
+ ) -> List[Union[Tuple[str, Callable], Tuple[Qt.Key, Callable]]]:
return [
("e", self.mw.onEditCurrent),
(" ", self.onEnterKey),
@@ -299,18 +304,18 @@ The front of this card is empty. Please run Tools>Empty Cards."""
("7", self.on_seek_forward),
]
- def on_pause_audio(self):
+ def on_pause_audio(self) -> None:
av_player.toggle_pause()
seek_secs = 5
- def on_seek_backward(self):
+ def on_seek_backward(self) -> None:
av_player.seek_relative(-self.seek_secs)
- def on_seek_forward(self):
+ def on_seek_forward(self) -> None:
av_player.seek_relative(self.seek_secs)
- def onEnterKey(self):
+ def onEnterKey(self) -> None:
if self.state == "question":
self._getTypedAnswer()
elif self.state == "answer":
@@ -318,14 +323,14 @@ The front of this card is empty. Please run Tools>Empty Cards."""
"selectedAnswerButton()", self._onAnswerButton
)
- def _onAnswerButton(self, val):
+ def _onAnswerButton(self, val: str) -> None:
# button selected?
if val and val in "1234":
self._answerCard(int(val))
else:
self._answerCard(self._defaultEase())
- def _linkHandler(self, url):
+ def _linkHandler(self, url: str) -> None:
if url == "ans":
self._getTypedAnswer()
elif url.startswith("ease"):
@@ -344,13 +349,13 @@ The front of this card is empty. Please run Tools>Empty Cards."""
typeAnsPat = r"\[\[type:(.+?)\]\]"
- def typeAnsFilter(self, buf):
+ def typeAnsFilter(self, buf: str) -> str:
if self.state == "question":
return self.typeAnsQuestionFilter(buf)
else:
return self.typeAnsAnswerFilter(buf)
- def typeAnsQuestionFilter(self, buf):
+ def typeAnsQuestionFilter(self, buf: str) -> str:
self.typeCorrect = None
clozeIdx = None
m = re.search(self.typeAnsPat, buf)
@@ -397,20 +402,19 @@ Please run Tools>Empty Cards"""
buf,
)
- def typeAnsAnswerFilter(self, buf):
+ def typeAnsAnswerFilter(self, buf: str) -> str:
if not self.typeCorrect:
return re.sub(self.typeAnsPat, "", buf)
origSize = len(buf)
buf = buf.replace("
", "")
hadHR = len(buf) != origSize
# munge correct value
- parser = html.parser.HTMLParser()
cor = self.mw.col.media.strip(self.typeCorrect)
cor = re.sub("(\n|
|?div>)+", " ", cor)
cor = stripHTML(cor)
# ensure we don't chomp multiple whitespace
cor = cor.replace(" ", " ")
- cor = parser.unescape(cor)
+ cor = html.unescape(cor)
cor = cor.replace("\xa0", " ")
cor = cor.strip()
given = self.typedAnswer
@@ -434,7 +438,7 @@ Please run Tools>Empty Cards"""
return re.sub(self.typeAnsPat, repl, buf)
- def _contentForCloze(self, txt, idx):
+ def _contentForCloze(self, txt: str, idx) -> str:
matches = re.findall(r"\{\{c%s::(.+?)\}\}" % idx, txt, re.DOTALL)
if not matches:
return None
@@ -452,24 +456,28 @@ Please run Tools>Empty Cards"""
txt = ", ".join(matches)
return txt
- def tokenizeComparison(self, given, correct):
+ def tokenizeComparison(
+ self, given: str, correct: str
+ ) -> Tuple[List[Tuple[bool, str]], List[Tuple[bool, str]]]:
# compare in NFC form so accents appear correct
given = ucd.normalize("NFC", given)
correct = ucd.normalize("NFC", correct)
s = difflib.SequenceMatcher(None, given, correct, autojunk=False)
- givenElems = []
- correctElems = []
+ givenElems: List[Tuple[bool, str]] = []
+ correctElems: List[Tuple[bool, str]] = []
givenPoint = 0
correctPoint = 0
offby = 0
- def logBad(old, new, str, array):
+ def logBad(old: int, new: int, s: str, array: List[Tuple[bool, str]]) -> None:
if old != new:
- array.append((False, str[old:new]))
+ array.append((False, s[old:new]))
- def logGood(start, cnt, str, array):
+ def logGood(
+ start: int, cnt: int, s: str, array: List[Tuple[bool, str]]
+ ) -> None:
if cnt:
- array.append((True, str[start : start + cnt]))
+ array.append((True, s[start : start + cnt]))
for x, y, cnt in s.get_matching_blocks():
# if anything was missed in correct, pad given
@@ -486,17 +494,17 @@ Please run Tools>Empty Cards"""
logGood(y, cnt, correct, correctElems)
return givenElems, correctElems
- def correct(self, given, correct, showBad=True):
+ def correct(self, given: str, correct: str, showBad: bool = True) -> str:
"Diff-corrects the typed-in answer."
givenElems, correctElems = self.tokenizeComparison(given, correct)
- def good(s):
+ def good(s: str) -> str:
return "" + html.escape(s) + ""
- def bad(s):
+ def bad(s: str) -> str:
return "" + html.escape(s) + ""
- def missed(s):
+ def missed(s: str) -> str:
return "" + html.escape(s) + ""
if given == correct:
@@ -519,24 +527,24 @@ Please run Tools>Empty Cards"""
res = "" + res + "
"
return res
- def _noLoneMarks(self, s):
+ def _noLoneMarks(self, s: str) -> str:
# ensure a combining character at the start does not join to
# previous text
if s and ucd.category(s[0]).startswith("M"):
return "\xa0" + s
return s
- def _getTypedAnswer(self):
+ def _getTypedAnswer(self) -> None:
self.web.evalWithCallback("typeans ? typeans.value : null", self._onTypedAnswer)
- def _onTypedAnswer(self, val):
+ def _onTypedAnswer(self, val: None) -> None:
self.typedAnswer = val or ""
self._showAnswer()
# Bottom bar
##########################################################################
- def _bottomHTML(self):
+ def _bottomHTML(self) -> str:
return """
@@ -565,7 +573,7 @@ time = %(time)d;
time=self.card.timeTaken() // 1000,
)
- def _showAnswerButton(self):
+ def _showAnswerButton(self) -> None:
if not self.typeCorrect:
self.bottom.web.setFocus()
middle = """
@@ -587,17 +595,17 @@ time = %(time)d;
self.bottom.web.eval("showQuestion(%s,%d);" % (json.dumps(middle), maxTime))
self.bottom.web.adjustHeightToFit()
- def _showEaseButtons(self):
+ def _showEaseButtons(self) -> None:
self.bottom.web.setFocus()
middle = self._answerButtons()
self.bottom.web.eval("showAnswer(%s);" % json.dumps(middle))
- def _remaining(self):
+ def _remaining(self) -> str:
if not self.mw.col.conf["dueCounts"]:
return ""
if self.hadCardQueue:
# if it's come from the undo queue, don't count it separately
- counts = list(self.mw.col.sched.counts())
+ counts: List[Union[int, str]] = list(self.mw.col.sched.counts())
else:
counts = list(self.mw.col.sched.counts(self.card))
idx = self.mw.col.sched.countIdx(self.card)
@@ -608,13 +616,13 @@ time = %(time)d;
ctxt += space + "%s" % counts[2]
return ctxt
- def _defaultEase(self):
+ def _defaultEase(self) -> int:
if self.mw.col.sched.answerButtons(self.card) == 4:
return 3
else:
return 2
- def _answerButtonList(self):
+ def _answerButtonList(self) -> Sequence[Tuple[int, str]]:
l = ((1, _("Again")),)
cnt = self.mw.col.sched.answerButtons(self.card)
if cnt == 2:
@@ -624,7 +632,7 @@ time = %(time)d;
else:
return l + ((2, _("Hard")), (3, _("Good")), (4, _("Easy")))
- def _answerButtons(self):
+ def _answerButtons(self) -> str:
default = self._defaultEase()
def but(i, label):
@@ -652,7 +660,7 @@ time = %(time)d;
"""
return buf + script
- def _buttonTime(self, i):
+ def _buttonTime(self, i: int) -> str:
if not self.mw.col.conf["estTimes"]:
return ""
txt = self.mw.col.sched.nextIvlStr(self.card, i, True) or " "
@@ -661,7 +669,7 @@ time = %(time)d;
# Leeches
##########################################################################
- def onLeech(self, card):
+ def onLeech(self, card: Card) -> None:
# for now
s = _("Card was a leech.")
if card.queue < 0:
@@ -730,7 +738,7 @@ time = %(time)d;
qtMenuShortcutWorkaround(m)
m.exec_(QCursor.pos())
- def _addMenuItems(self, m, rows):
+ def _addMenuItems(self, m, rows) -> None:
for row in rows:
if not row:
m.addSeparator()
@@ -753,10 +761,10 @@ time = %(time)d;
a.setChecked(True)
a.triggered.connect(func)
- def onOptions(self):
+ def onOptions(self) -> None:
self.mw.onDeckConf(self.mw.col.decks.get(self.card.odid or self.card.did))
- def setFlag(self, flag):
+ def setFlag(self, flag: int) -> None:
# need to toggle off?
if self.card.userFlag() == flag:
flag = 0
@@ -764,7 +772,7 @@ time = %(time)d;
self.card.flush()
self._drawFlag()
- def onMark(self):
+ def onMark(self) -> None:
f = self.card.note()
if f.hasTag("marked"):
f.delTag("marked")
@@ -773,19 +781,19 @@ time = %(time)d;
f.flush()
self._drawMark()
- def onSuspend(self):
+ def onSuspend(self) -> None:
self.mw.checkpoint(_("Suspend"))
self.mw.col.sched.suspendCards([c.id for c in self.card.note().cards()])
tooltip(_("Note suspended."))
self.mw.reset()
- def onSuspendCard(self):
+ def onSuspendCard(self) -> None:
self.mw.checkpoint(_("Suspend"))
self.mw.col.sched.suspendCards([self.card.id])
tooltip(_("Card suspended."))
self.mw.reset()
- def onDelete(self):
+ def onDelete(self) -> None:
# need to check state because the shortcut is global to the main
# window
if self.mw.state != "review" or not self.card:
@@ -801,23 +809,24 @@ time = %(time)d;
% cnt
)
- def onBuryCard(self):
+ def onBuryCard(self) -> None:
self.mw.checkpoint(_("Bury"))
self.mw.col.sched.buryCards([self.card.id])
self.mw.reset()
tooltip(_("Card buried."))
- def onBuryNote(self):
+ def onBuryNote(self) -> None:
self.mw.checkpoint(_("Bury"))
self.mw.col.sched.buryNote(self.card.nid)
self.mw.reset()
tooltip(_("Note buried."))
- def onRecordVoice(self):
+ def onRecordVoice(self) -> None:
self._recordedAudio = getAudio(self.mw, encode=False)
self.onReplayRecorded()
- def onReplayRecorded(self):
+ def onReplayRecorded(self) -> None:
if not self._recordedAudio:
- return tooltip(_("You haven't recorded your voice yet."))
+ tooltip(_("You haven't recorded your voice yet."))
+ return
av_player.play_file(self._recordedAudio)
diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py
index 16611950c..12d3e8214 100644
--- a/qt/aqt/sound.py
+++ b/qt/aqt/sound.py
@@ -87,7 +87,7 @@ class AVPlayer:
# audio be stopped?
interrupt_current_audio = True
- def __init__(self):
+ def __init__(self) -> None:
self._enqueued: List[AVTag] = []
self.current_player: Optional[Player] = None
@@ -112,7 +112,7 @@ class AVPlayer:
self._enqueued.insert(0, SoundOrVideoTag(filename=filename))
self._play_next_if_idle()
- def toggle_pause(self):
+ def toggle_pause(self) -> None:
if self.current_player:
self.current_player.toggle_pause()
@@ -179,7 +179,7 @@ av_player = AVPlayer()
# return modified command array that points to bundled command, and return
# required environment
-def _packagedCmd(cmd) -> Tuple[Any, Dict[str, str]]:
+def _packagedCmd(cmd: List[str]) -> Tuple[Any, Dict[str, str]]:
cmd = cmd[:]
env = os.environ.copy()
if "LD_LIBRARY_PATH" in env:
@@ -205,7 +205,7 @@ def _packagedCmd(cmd) -> Tuple[Any, Dict[str, str]]:
si = startup_info()
# osx throws interrupted system call errors frequently
-def retryWait(proc) -> Any:
+def retryWait(proc: subprocess.Popen) -> int:
while 1:
try:
return proc.wait()
@@ -227,7 +227,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
args: List[str] = []
env: Optional[Dict[str, str]] = None
- def __init__(self, taskman: TaskManager):
+ def __init__(self, taskman: TaskManager) -> None:
self._taskman = taskman
self._terminate_flag = False
self._process: Optional[subprocess.Popen] = None
@@ -238,7 +238,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
lambda: self._play(tag), lambda res: self._on_done(res, on_done)
)
- def stop(self):
+ def stop(self) -> None:
self._terminate_flag = True
# block until stopped
t = time.time()
@@ -252,7 +252,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
)
self._wait_for_termination(tag)
- def _wait_for_termination(self, tag: AVTag):
+ def _wait_for_termination(self, tag: AVTag) -> None:
self._taskman.run_on_main(
lambda: gui_hooks.av_player_did_begin_playing(self, tag)
)
@@ -359,7 +359,7 @@ class MpvManager(MPV, SoundOrVideoPlayer):
def toggle_pause(self) -> None:
self.set_property("pause", not self.get_property("pause"))
- def seek_relative(self, secs) -> None:
+ def seek_relative(self, secs: int) -> None:
self.command("seek", secs, "relative")
def on_idle(self) -> None:
@@ -401,7 +401,7 @@ class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer):
)
self._wait_for_termination(tag)
- def command(self, *args) -> None:
+ def command(self, *args: Any) -> None:
"""Send a command over the slave interface.
The trailing newline is automatically added."""
@@ -412,7 +412,7 @@ class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer):
def seek_relative(self, secs: int) -> None:
self.command("seek", secs, 0)
- def toggle_pause(self):
+ def toggle_pause(self) -> None:
self.command("pause")
@@ -458,12 +458,12 @@ class _Recorder:
class PyAudioThreadedRecorder(threading.Thread):
- def __init__(self, startupDelay) -> None:
+ def __init__(self, startupDelay: float) -> None:
threading.Thread.__init__(self)
self.startupDelay = startupDelay
self.finish = False
- def run(self) -> Any:
+ def run(self) -> None:
chunk = 1024
p = pyaudio.PyAudio()
@@ -499,7 +499,7 @@ class PyAudioRecorder(_Recorder):
# discard first 250ms which may have pops/cracks
startupDelay = 0.25
- def __init__(self):
+ def __init__(self) -> None:
for t in recFiles + [processingSrc, processingDst]:
try:
os.unlink(t)
@@ -507,15 +507,15 @@ class PyAudioRecorder(_Recorder):
pass
self.encode = False
- def start(self):
+ def start(self) -> None:
self.thread = PyAudioThreadedRecorder(startupDelay=self.startupDelay)
self.thread.start()
- def stop(self):
+ def stop(self) -> None:
self.thread.finish = True
self.thread.join()
- def file(self):
+ def file(self) -> str:
if self.encode:
tgt = "rec%d.mp3" % time.time()
os.rename(processingDst, tgt)
@@ -530,7 +530,7 @@ Recorder = PyAudioRecorder
##########################################################################
-def getAudio(parent, encode=True):
+def getAudio(parent: QWidget, encode: bool = True) -> Optional[str]:
"Record and return filename"
# record first
r = Recorder()
@@ -547,16 +547,16 @@ def getAudio(parent, encode=True):
t = time.time()
r.start()
time.sleep(r.startupDelay)
- QApplication.instance().processEvents()
+ QApplication.instance().processEvents() # type: ignore
while not mb.clickedButton():
txt = _("Recording...
Time: %0.1f")
mb.setText(txt % (time.time() - t))
mb.show()
- QApplication.instance().processEvents()
+ QApplication.instance().processEvents() # type: ignore
if mb.clickedButton() == mb.escapeButton():
r.stop()
r.cleanup()
- return
+ return None
saveGeom(mb, "audioRecorder")
# ensure at least a second captured
while time.time() - t < 1:
diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py
index 6a25f9d6d..492538534 100644
--- a/qt/aqt/theme.py
+++ b/qt/aqt/theme.py
@@ -197,7 +197,7 @@ QTabWidget { background-color: %s; }
app.setPalette(palette)
- def _update_stat_colors(self):
+ def _update_stat_colors(self) -> None:
import anki.stats as s
s.colLearn = self.str_color("new-count")
diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py
index 17fc7dfc3..cec90d4b2 100644
--- a/qt/aqt/toolbar.py
+++ b/qt/aqt/toolbar.py
@@ -15,13 +15,13 @@ from aqt.webview import AnkiWebView
# wrapper class for set_bridge_command()
class TopToolbar:
- def __init__(self, toolbar: Toolbar):
+ def __init__(self, toolbar: Toolbar) -> None:
self.toolbar = toolbar
# wrapper class for set_bridge_command()
class BottomToolbar:
- def __init__(self, toolbar: Toolbar):
+ def __init__(self, toolbar: Toolbar) -> None:
self.toolbar = toolbar
@@ -40,7 +40,7 @@ class Toolbar:
buf: str = "",
web_context: Optional[Any] = None,
link_handler: Optional[Callable[[str], Any]] = None,
- ):
+ ) -> None:
web_context = web_context or TopToolbar(self)
link_handler = link_handler or self._linkHandler
self.web.set_bridge_command(link_handler, web_context)
@@ -90,7 +90,7 @@ class Toolbar:
f"""{label}"""
)
- def _centerLinks(self):
+ def _centerLinks(self) -> str:
links = [
self.create_link(
"decks",
@@ -149,15 +149,15 @@ class Toolbar:
# Link handling
######################################################################
- def _linkHandler(self, link):
+ def _linkHandler(self, link: str) -> bool:
if link in self.link_handlers:
self.link_handlers[link]()
return False
- def _deckLinkHandler(self):
+ def _deckLinkHandler(self) -> None:
self.mw.moveToState("deckBrowser")
- def _studyLinkHandler(self):
+ def _studyLinkHandler(self) -> None:
# if overview already shown, switch to review
if self.mw.state == "overview":
self.mw.col.startTimebox()
@@ -165,16 +165,16 @@ class Toolbar:
else:
self.mw.onOverview()
- def _addLinkHandler(self):
+ def _addLinkHandler(self) -> None:
self.mw.onAddCard()
- def _browseLinkHandler(self):
+ def _browseLinkHandler(self) -> None:
self.mw.onBrowse()
- def _statsLinkHandler(self):
+ def _statsLinkHandler(self) -> None:
self.mw.onStats()
- def _syncLinkHandler(self):
+ def _syncLinkHandler(self) -> None:
self.mw.onSync()
# HTML & CSS
@@ -206,7 +206,7 @@ class BottomBar(Toolbar):
buf: str = "",
web_context: Optional[Any] = None,
link_handler: Optional[Callable[[str], Any]] = None,
- ):
+ ) -> None:
# note: some screens may override this
web_context = web_context or BottomToolbar(self)
link_handler = link_handler or self._linkHandler
diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py
index 0efc96969..675b5ec52 100644
--- a/qt/aqt/webview.py
+++ b/qt/aqt/webview.py
@@ -5,7 +5,7 @@ import dataclasses
import json
import math
import sys
-from typing import Any, List, Optional, Tuple
+from typing import Any, Callable, List, Optional, Sequence, Tuple
from anki.lang import _
from anki.utils import isLin, isMac, isWin
@@ -176,7 +176,7 @@ class AnkiWebView(QWebEngineView): # type: ignore
self.onBridgeCmd: Callable[[str], Any] = self.defaultOnBridgeCmd
self._domDone = True
- self._pendingActions: List[Tuple[str, List[Any]]] = []
+ self._pendingActions: List[Tuple[str, Sequence[Any]]] = []
self.requiresCol = True
self.setPage(self._page)
@@ -258,13 +258,13 @@ class AnkiWebView(QWebEngineView): # type: ignore
def dropEvent(self, evt):
pass
- def setHtml(self, html):
+ def setHtml(self, html: str) -> None:
# discard any previous pending actions
self._pendingActions = []
self._domDone = True
self._queueAction("setHtml", html)
- def _setHtml(self, html):
+ def _setHtml(self, html: str) -> None:
app = QApplication.instance()
oldFocus = app.focusWidget()
self._domDone = False
@@ -273,7 +273,7 @@ class AnkiWebView(QWebEngineView): # type: ignore
if oldFocus:
oldFocus.setFocus()
- def zoomFactor(self):
+ def zoomFactor(self) -> float:
# overridden scale factor?
webscale = os.environ.get("ANKI_WEBSCALE")
if webscale:
@@ -295,7 +295,7 @@ class AnkiWebView(QWebEngineView): # type: ignore
newFactor = desiredScale / qtIntScale
return max(1, newFactor)
- def _getQtIntScale(self, screen):
+ def _getQtIntScale(self, screen) -> 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
@@ -430,13 +430,13 @@ body {{ zoom: {}; background: {}; {} }}
fname
)
- def eval(self, js):
+ def eval(self, js: str) -> None:
self.evalWithCallback(js, None)
- def evalWithCallback(self, js, cb):
+ def evalWithCallback(self, js: str, cb: Callable) -> None:
self._queueAction("eval", js, cb)
- def _evalWithCallback(self, js, cb):
+ def _evalWithCallback(self, js: str, cb: Callable[[Any], Any]) -> None:
if cb:
def handler(val):
@@ -449,11 +449,11 @@ body {{ zoom: {}; background: {}; {} }}
else:
self.page().runJavaScript(js)
- def _queueAction(self, name, *args):
+ def _queueAction(self, name: str, *args: Any) -> None:
self._pendingActions.append((name, args))
self._maybeRunActions()
- def _maybeRunActions(self):
+ def _maybeRunActions(self) -> None:
while self._pendingActions and self._domDone:
name, args = self._pendingActions.pop(0)
@@ -464,10 +464,10 @@ body {{ zoom: {}; background: {}; {} }}
else:
raise Exception("unknown action: {}".format(name))
- def _openLinksExternally(self, url):
+ def _openLinksExternally(self, url: str) -> None:
openLink(url)
- def _shouldIgnoreWebEvent(self):
+ def _shouldIgnoreWebEvent(self) -> bool:
# async web events may be received after the profile has been closed
# or the underlying webview has been deleted
from aqt import mw
@@ -499,18 +499,18 @@ body {{ zoom: {}; background: {}; {} }}
else:
return self.onBridgeCmd(cmd)
- def defaultOnBridgeCmd(self, cmd: str) -> Any:
+ def defaultOnBridgeCmd(self, cmd: str) -> None:
print("unhandled bridge cmd:", cmd)
# legacy
- def resetHandlers(self):
+ def resetHandlers(self) -> None:
self.onBridgeCmd = self.defaultOnBridgeCmd
self._bridge_context = None
- def adjustHeightToFit(self):
+ def adjustHeightToFit(self) -> None:
self.evalWithCallback("$(document.body).height()", self._onHeight)
- def _onHeight(self, qvar):
+ def _onHeight(self, qvar: Optional[int]) -> None:
from aqt import mw
if qvar is None: