add types to various other files

Mainly automated with MonkeyType
This commit is contained in:
Damien Elmes 2021-02-01 22:08:56 +10:00
parent 84f8d7f604
commit f15715fb07
25 changed files with 227 additions and 182 deletions

View file

@ -137,7 +137,7 @@ class AddCards(QDialog):
def removeTempNote(self, note: Note) -> None: def removeTempNote(self, note: Note) -> None:
print("removeTempNote() will go away") print("removeTempNote() will go away")
def addHistory(self, note): def addHistory(self, note: Note) -> None:
self.history.insert(0, note.id) self.history.insert(0, note.id)
self.history = self.history[:15] self.history = self.history[:15]
self.historyButton.setEnabled(True) self.historyButton.setEnabled(True)
@ -186,10 +186,10 @@ class AddCards(QDialog):
gui_hooks.add_cards_did_add_note(note) gui_hooks.add_cards_did_add_note(note)
return note return note
def addCards(self): def addCards(self) -> None:
self.editor.saveNow(self._addCards) self.editor.saveNow(self._addCards)
def _addCards(self): def _addCards(self) -> None:
self.editor.saveAddModeVars() self.editor.saveAddModeVars()
if not self.addNote(self.editor.note): if not self.addNote(self.editor.note):
return return
@ -202,7 +202,7 @@ class AddCards(QDialog):
self.onReset(keep=True) self.onReset(keep=True)
self.mw.col.autosave() self.mw.col.autosave()
def keyPressEvent(self, evt): def keyPressEvent(self, evt: QKeyEvent) -> None:
"Show answer on RET or register answer." "Show answer on RET or register answer."
if evt.key() in (Qt.Key_Enter, Qt.Key_Return) and self.editor.tags.hasFocus(): if evt.key() in (Qt.Key_Enter, Qt.Key_Return) and self.editor.tags.hasFocus():
evt.accept() evt.accept()

View file

@ -14,6 +14,7 @@ from anki.lang import without_unicode_isolation
from anki.notes import Note from anki.notes import Note
from anki.template import TemplateRenderContext from anki.template import TemplateRenderContext
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.forms.browserdisp import Ui_Dialog
from aqt.qt import * from aqt.qt import *
from aqt.schema_change_tracker import ChangeTracker from aqt.schema_change_tracker import ChangeTracker
from aqt.sound import av_player, play_clicked_audio from aqt.sound import av_player, play_clicked_audio
@ -90,7 +91,7 @@ class CardLayout(QDialog):
# as users tend to accidentally type into the template # as users tend to accidentally type into the template
self.setFocus() self.setFocus()
def redraw_everything(self): def redraw_everything(self) -> None:
self.ignore_change_signals = True self.ignore_change_signals = True
self.updateTopArea() self.updateTopArea()
self.ignore_change_signals = False self.ignore_change_signals = False
@ -104,13 +105,13 @@ class CardLayout(QDialog):
self.fill_fields_from_template() self.fill_fields_from_template()
self.renderPreview() self.renderPreview()
def _isCloze(self): def _isCloze(self) -> bool:
return self.model["type"] == MODEL_CLOZE return self.model["type"] == MODEL_CLOZE
# Top area # Top area
########################################################################## ##########################################################################
def setupTopArea(self): def setupTopArea(self) -> None:
self.topArea = QWidget() self.topArea = QWidget()
self.topArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self.topArea.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
self.topAreaForm = aqt.forms.clayout_top.Ui_Form() self.topAreaForm = aqt.forms.clayout_top.Ui_Form()
@ -125,10 +126,10 @@ class CardLayout(QDialog):
) )
self.topAreaForm.card_type_label.setText(tr(TR.CARD_TEMPLATES_CARD_TYPE)) self.topAreaForm.card_type_label.setText(tr(TR.CARD_TEMPLATES_CARD_TYPE))
def updateTopArea(self): def updateTopArea(self) -> None:
self.updateCardNames() self.updateCardNames()
def updateCardNames(self): def updateCardNames(self) -> None:
self.ignore_change_signals = True self.ignore_change_signals = True
combo = self.topAreaForm.templatesBox combo = self.topAreaForm.templatesBox
combo.clear() combo.clear()
@ -170,7 +171,7 @@ class CardLayout(QDialog):
s += "+..." s += "+..."
return s return s
def setupShortcuts(self): def setupShortcuts(self) -> None:
self.tform.front_button.setToolTip(shortcut("Ctrl+1")) self.tform.front_button.setToolTip(shortcut("Ctrl+1"))
self.tform.back_button.setToolTip(shortcut("Ctrl+2")) self.tform.back_button.setToolTip(shortcut("Ctrl+2"))
self.tform.style_button.setToolTip(shortcut("Ctrl+3")) self.tform.style_button.setToolTip(shortcut("Ctrl+3"))
@ -193,7 +194,7 @@ class CardLayout(QDialog):
# Main area setup # Main area setup
########################################################################## ##########################################################################
def setupMainArea(self): def setupMainArea(self) -> None:
split = self.mainArea = QSplitter() split = self.mainArea = QSplitter()
split.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) split.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
split.setOrientation(Qt.Horizontal) split.setOrientation(Qt.Horizontal)
@ -216,7 +217,7 @@ class CardLayout(QDialog):
split.addWidget(right) split.addWidget(right)
split.setCollapsible(1, False) split.setCollapsible(1, False)
def setup_edit_area(self): def setup_edit_area(self) -> None:
tform = self.tform tform = self.tform
tform.front_button.setText(tr(TR.CARD_TEMPLATES_FRONT_TEMPLATE)) tform.front_button.setText(tr(TR.CARD_TEMPLATES_FRONT_TEMPLATE))
@ -248,7 +249,7 @@ class CardLayout(QDialog):
qconnect(widg.textChanged, self.on_search_changed) qconnect(widg.textChanged, self.on_search_changed)
qconnect(widg.returnPressed, self.on_search_next) qconnect(widg.returnPressed, self.on_search_next)
def setup_cloze_number_box(self): def setup_cloze_number_box(self) -> None:
names = (tr(TR.CARD_TEMPLATES_CLOZE, val=n) for n in self.cloze_numbers) names = (tr(TR.CARD_TEMPLATES_CLOZE, val=n) for n in self.cloze_numbers)
self.pform.cloze_number_combo.addItems(names) self.pform.cloze_number_combo.addItems(names)
try: try:
@ -266,7 +267,7 @@ class CardLayout(QDialog):
self.have_autoplayed = False self.have_autoplayed = False
self._renderPreview() self._renderPreview()
def on_editor_toggled(self): def on_editor_toggled(self) -> None:
if self.tform.front_button.isChecked(): if self.tform.front_button.isChecked():
self.current_editor_index = 0 self.current_editor_index = 0
self.pform.preview_front.setChecked(True) self.pform.preview_front.setChecked(True)
@ -297,7 +298,7 @@ class CardLayout(QDialog):
text = self.tform.search_edit.text() text = self.tform.search_edit.text()
self.on_search_changed(text) self.on_search_changed(text)
def setup_preview(self): def setup_preview(self) -> None:
pform = self.pform pform = self.pform
self.preview_web = AnkiWebView(title="card layout") self.preview_web = AnkiWebView(title="card layout")
pform.verticalLayout.addWidget(self.preview_web) pform.verticalLayout.addWidget(self.preview_web)
@ -336,7 +337,7 @@ class CardLayout(QDialog):
self.cloze_numbers = [] self.cloze_numbers = []
self.pform.cloze_number_combo.setHidden(True) self.pform.cloze_number_combo.setHidden(True)
def on_fill_empty_action_toggled(self): def on_fill_empty_action_toggled(self) -> None:
self.fill_empty_action_toggled = not self.fill_empty_action_toggled self.fill_empty_action_toggled = not self.fill_empty_action_toggled
self.on_preview_toggled() self.on_preview_toggled()
@ -344,11 +345,11 @@ class CardLayout(QDialog):
self.night_mode_is_enabled = not self.night_mode_is_enabled self.night_mode_is_enabled = not self.night_mode_is_enabled
self.on_preview_toggled() self.on_preview_toggled()
def on_mobile_class_action_toggled(self): def on_mobile_class_action_toggled(self) -> None:
self.mobile_emulation_enabled = not self.mobile_emulation_enabled self.mobile_emulation_enabled = not self.mobile_emulation_enabled
self.on_preview_toggled() self.on_preview_toggled()
def on_preview_settings(self): def on_preview_settings(self) -> None:
m = QMenu(self) m = QMenu(self)
a = m.addAction(tr(TR.CARD_TEMPLATES_FILL_EMPTY)) a = m.addAction(tr(TR.CARD_TEMPLATES_FILL_EMPTY))
@ -370,7 +371,7 @@ class CardLayout(QDialog):
m.exec_(self.pform.preview_settings.mapToGlobal(QPoint(0, 0))) m.exec_(self.pform.preview_settings.mapToGlobal(QPoint(0, 0)))
def on_preview_toggled(self): def on_preview_toggled(self) -> None:
self.have_autoplayed = False self.have_autoplayed = False
self._renderPreview() self._renderPreview()
@ -388,7 +389,7 @@ class CardLayout(QDialog):
# Buttons # Buttons
########################################################################## ##########################################################################
def setupButtons(self): def setupButtons(self) -> None:
l = self.buttons = QHBoxLayout() l = self.buttons = QHBoxLayout()
help = QPushButton(tr(TR.ACTIONS_HELP)) help = QPushButton(tr(TR.ACTIONS_HELP))
help.setAutoDefault(False) help.setAutoDefault(False)
@ -424,7 +425,7 @@ class CardLayout(QDialog):
return self.templates[0] return self.templates[0]
return self.templates[self.ord] return self.templates[self.ord]
def fill_fields_from_template(self): def fill_fields_from_template(self) -> None:
t = self.current_template() t = self.current_template()
self.ignore_change_signals = True self.ignore_change_signals = True
@ -438,7 +439,7 @@ class CardLayout(QDialog):
self.tform.edit_area.setPlainText(text) self.tform.edit_area.setPlainText(text)
self.ignore_change_signals = False self.ignore_change_signals = False
def write_edits_to_template_and_redraw(self): def write_edits_to_template_and_redraw(self) -> None:
if self.ignore_change_signals: if self.ignore_change_signals:
return return
@ -458,14 +459,14 @@ class CardLayout(QDialog):
# Preview # Preview
########################################################################## ##########################################################################
_previewTimer = None _previewTimer: Optional[QTimer] = None
def renderPreview(self): def renderPreview(self) -> None:
# schedule a preview when timing stops # schedule a preview when timing stops
self.cancelPreviewTimer() self.cancelPreviewTimer()
self._previewTimer = self.mw.progress.timer(200, self._renderPreview, False) self._previewTimer = self.mw.progress.timer(200, self._renderPreview, False)
def cancelPreviewTimer(self): def cancelPreviewTimer(self) -> None:
if self._previewTimer: if self._previewTimer:
self._previewTimer.stop() self._previewTimer.stop()
self._previewTimer = None self._previewTimer = None
@ -512,7 +513,7 @@ class CardLayout(QDialog):
self.updateCardNames() self.updateCardNames()
def maybeTextInput(self, txt, type="q"): def maybeTextInput(self, txt: str, type: str = "q") -> str:
if "[[type:" not in txt: if "[[type:" not in txt:
return txt return txt
origLen = len(txt) origLen = len(txt)
@ -668,7 +669,7 @@ class CardLayout(QDialog):
dst["qfmt"] = m.group(2).strip() dst["qfmt"] = m.group(2).strip()
return True return True
def onMore(self): def onMore(self) -> None:
m = QMenu(self) m = QMenu(self)
if not self._isCloze(): if not self._isCloze():
@ -699,7 +700,7 @@ class CardLayout(QDialog):
m.exec_(self.topAreaForm.templateOptions.mapToGlobal(QPoint(0, 0))) m.exec_(self.topAreaForm.templateOptions.mapToGlobal(QPoint(0, 0)))
def onBrowserDisplay(self): def onBrowserDisplay(self) -> None:
d = QDialog() d = QDialog()
disable_help_button(d) disable_help_button(d)
f = aqt.forms.browserdisp.Ui_Dialog() f = aqt.forms.browserdisp.Ui_Dialog()
@ -714,7 +715,7 @@ class CardLayout(QDialog):
qconnect(f.buttonBox.accepted, lambda: self.onBrowserDisplayOk(f)) qconnect(f.buttonBox.accepted, lambda: self.onBrowserDisplayOk(f))
d.exec_() d.exec_()
def onBrowserDisplayOk(self, f): def onBrowserDisplayOk(self, f: Ui_Dialog) -> None:
t = self.current_template() t = self.current_template()
self.change_tracker.mark_basic() self.change_tracker.mark_basic()
t["bqfmt"] = f.qfmt.text().strip() t["bqfmt"] = f.qfmt.text().strip()

View file

@ -1,6 +1,7 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import aqt import aqt
from anki.collection import SearchTerm from anki.collection import SearchTerm
from anki.consts import * from anki.consts import *
@ -35,7 +36,7 @@ class CustomStudy(QDialog):
f.radioNew.click() f.radioNew.click()
self.exec_() self.exec_()
def setupSignals(self): def setupSignals(self) -> None:
f = self.form f = self.form
qconnect(f.radioNew.clicked, lambda: self.onRadioChange(RADIO_NEW)) qconnect(f.radioNew.clicked, lambda: self.onRadioChange(RADIO_NEW))
qconnect(f.radioRev.clicked, lambda: self.onRadioChange(RADIO_REV)) qconnect(f.radioRev.clicked, lambda: self.onRadioChange(RADIO_REV))
@ -44,7 +45,7 @@ class CustomStudy(QDialog):
qconnect(f.radioPreview.clicked, lambda: self.onRadioChange(RADIO_PREVIEW)) qconnect(f.radioPreview.clicked, lambda: self.onRadioChange(RADIO_PREVIEW))
qconnect(f.radioCram.clicked, lambda: self.onRadioChange(RADIO_CRAM)) qconnect(f.radioCram.clicked, lambda: self.onRadioChange(RADIO_CRAM))
def onRadioChange(self, idx): def onRadioChange(self, idx: int) -> None:
f = self.form f = self.form
sp = f.spin sp = f.spin
smin = 1 smin = 1
@ -123,7 +124,7 @@ class CustomStudy(QDialog):
f.buttonBox.button(QDialogButtonBox.Ok).setText(ok) f.buttonBox.button(QDialogButtonBox.Ok).setText(ok)
self.radioIdx = idx self.radioIdx = idx
def accept(self): def accept(self) -> None:
f = self.form f = self.form
i = self.radioIdx i = self.radioIdx
spin = f.spin.value() spin = f.spin.value()
@ -132,13 +133,15 @@ class CustomStudy(QDialog):
self.mw.col.decks.save(self.deck) self.mw.col.decks.save(self.deck)
self.mw.col.sched.extendLimits(spin, 0) self.mw.col.sched.extendLimits(spin, 0)
self.mw.reset() self.mw.reset()
return QDialog.accept(self) QDialog.accept(self)
return
elif i == RADIO_REV: elif i == RADIO_REV:
self.deck["extendRev"] = spin self.deck["extendRev"] = spin
self.mw.col.decks.save(self.deck) self.mw.col.decks.save(self.deck)
self.mw.col.sched.extendLimits(0, spin) self.mw.col.sched.extendLimits(0, spin)
self.mw.reset() self.mw.reset()
return QDialog.accept(self) QDialog.accept(self)
return
elif i == RADIO_CRAM: elif i == RADIO_CRAM:
tags = self._getTags() tags = self._getTags()
# the rest create a filtered deck # the rest create a filtered deck
@ -146,7 +149,8 @@ class CustomStudy(QDialog):
if cur: if cur:
if not cur["dyn"]: if not cur["dyn"]:
showInfo(tr(TR.CUSTOM_STUDY_MUST_RENAME_DECK)) showInfo(tr(TR.CUSTOM_STUDY_MUST_RENAME_DECK))
return QDialog.accept(self) QDialog.accept(self)
return
else: else:
# safe to empty # safe to empty
self.mw.col.sched.empty_filtered_deck(cur["id"]) self.mw.col.sched.empty_filtered_deck(cur["id"])
@ -211,7 +215,8 @@ class CustomStudy(QDialog):
# generate cards # generate cards
self.created_custom_study = True self.created_custom_study = True
if not self.mw.col.sched.rebuild_filtered_deck(dyn["id"]): if not self.mw.col.sched.rebuild_filtered_deck(dyn["id"]):
return showWarning(tr(TR.CUSTOM_STUDY_NO_CARDS_MATCHED_THE_CRITERIA_YOU)) showWarning(tr(TR.CUSTOM_STUDY_NO_CARDS_MATCHED_THE_CRITERIA_YOU))
return
self.mw.moveToState("overview") self.mw.moveToState("overview")
QDialog.accept(self) QDialog.accept(self)
@ -222,7 +227,7 @@ class CustomStudy(QDialog):
# fixme: clean up the empty custom study deck # fixme: clean up the empty custom study deck
QDialog.reject(self) QDialog.reject(self)
def _getTags(self): def _getTags(self) -> str:
from aqt.taglimit import TagLimit from aqt.taglimit import TagLimit
return TagLimit(self.mw, self).tags return TagLimit(self.mw, self).tags

View file

@ -1,12 +1,13 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import List, Optional from typing import Dict, List, Optional
import aqt import aqt
from anki.collection import SearchTerm from anki.collection import SearchTerm
from anki.errors import InvalidInput from anki.errors import InvalidInput
from anki.lang import without_unicode_isolation from anki.lang import without_unicode_isolation
from aqt.main import AnkiQt
from aqt.qt import * from aqt.qt import *
from aqt.utils import ( from aqt.utils import (
TR, TR,
@ -23,7 +24,13 @@ from aqt.utils import (
class DeckConf(QDialog): class DeckConf(QDialog):
def __init__(self, mw, first=False, search="", deck=None): def __init__(
self,
mw: AnkiQt,
first: bool = False,
search: str = "",
deck: Optional[Dict] = None,
) -> None:
QDialog.__init__(self, mw) QDialog.__init__(self, mw)
self.mw = mw self.mw = mw
self.deck = deck or self.mw.col.decks.current() self.deck = deck or self.mw.col.decks.current()
@ -65,7 +72,7 @@ class DeckConf(QDialog):
self.exec_() self.exec_()
saveGeom(self, "dyndeckconf") saveGeom(self, "dyndeckconf")
def initialSetup(self): def initialSetup(self) -> None:
import anki.consts as cs import anki.consts as cs
self.form.order.addItems(list(cs.dynOrderLabels(self.mw.col).values())) self.form.order.addItems(list(cs.dynOrderLabels(self.mw.col).values()))
@ -73,12 +80,12 @@ class DeckConf(QDialog):
qconnect(self.form.resched.stateChanged, self._onReschedToggled) qconnect(self.form.resched.stateChanged, self._onReschedToggled)
def _onReschedToggled(self, _state): def _onReschedToggled(self, _state: int) -> None:
self.form.previewDelayWidget.setVisible( self.form.previewDelayWidget.setVisible(
not self.form.resched.isChecked() and self.mw.col.schedVer() > 1 not self.form.resched.isChecked() and self.mw.col.schedVer() > 1
) )
def loadConf(self): def loadConf(self) -> None:
f = self.form f = self.form
d = self.deck d = self.deck
@ -113,7 +120,7 @@ class DeckConf(QDialog):
f.secondFilter.setChecked(False) f.secondFilter.setChecked(False)
f.filter2group.setVisible(False) f.filter2group.setVisible(False)
def saveConf(self): def saveConf(self) -> None:
f = self.form f = self.form
d = self.deck d = self.deck
d["resched"] = f.resched.isChecked() d["resched"] = f.resched.isChecked()
@ -142,7 +149,7 @@ class DeckConf(QDialog):
self.ok = False self.ok = False
QDialog.reject(self) QDialog.reject(self)
def accept(self): def accept(self) -> None:
try: try:
self.saveConf() self.saveConf()
except InvalidInput as err: except InvalidInput as err:

View file

@ -52,10 +52,10 @@ class EditCurrent(QDialog):
tooltip("Please finish editing the existing card first.") tooltip("Please finish editing the existing card first.")
self.onReset() self.onReset()
def reject(self): def reject(self) -> None:
self.saveAndClose() self.saveAndClose()
def saveAndClose(self): def saveAndClose(self) -> None:
self.editor.saveNow(self._saveAndClose) self.editor.saveNow(self._saveAndClose)
def _saveAndClose(self) -> None: def _saveAndClose(self) -> None:

View file

@ -68,7 +68,7 @@ class EmptyCardsDialog(QDialog):
def _on_note_link_clicked(self, link): def _on_note_link_clicked(self, link):
self.mw.browser_search(link) self.mw.browser_search(link)
def _on_delete(self): def _on_delete(self) -> None:
self.mw.progress.start() self.mw.progress.start()
def delete(): def delete():

View file

@ -5,10 +5,12 @@ import html
import re import re
import sys import sys
import traceback import traceback
from typing import Optional
from markdown import markdown from markdown import markdown
from aqt import mw from aqt import mw
from aqt.main import AnkiQt
from aqt.qt import * from aqt.qt import *
from aqt.utils import TR, showText, showWarning, supportText, tr from aqt.utils import TR, showText, showWarning, supportText, tr
@ -29,20 +31,20 @@ class ErrorHandler(QObject):
errorTimer = pyqtSignal() errorTimer = pyqtSignal()
def __init__(self, mw): def __init__(self, mw: AnkiQt) -> None:
QObject.__init__(self, mw) QObject.__init__(self, mw)
self.mw = mw self.mw = mw
self.timer = None self.timer: Optional[QTimer] = None
qconnect(self.errorTimer, self._setTimer) qconnect(self.errorTimer, self._setTimer)
self.pool = "" self.pool = ""
self._oldstderr = sys.stderr self._oldstderr = sys.stderr
sys.stderr = self sys.stderr = self
def unload(self): def unload(self) -> None:
sys.stderr = self._oldstderr sys.stderr = self._oldstderr
sys.excepthook = None sys.excepthook = None
def write(self, data): def write(self, data: str) -> None:
# dump to stdout # dump to stdout
sys.stdout.write(data) sys.stdout.write(data)
# save in buffer # save in buffer
@ -50,12 +52,12 @@ class ErrorHandler(QObject):
# and update timer # and update timer
self.setTimer() self.setTimer()
def setTimer(self): def setTimer(self) -> None:
# we can't create a timer from a different thread, so we post a # we can't create a timer from a different thread, so we post a
# message to the object on the main thread # message to the object on the main thread
self.errorTimer.emit() # type: ignore self.errorTimer.emit() # type: ignore
def _setTimer(self): def _setTimer(self) -> None:
if not self.timer: if not self.timer:
self.timer = QTimer(self.mw) self.timer = QTimer(self.mw)
qconnect(self.timer.timeout, self.onTimeout) qconnect(self.timer.timeout, self.onTimeout)
@ -66,7 +68,7 @@ class ErrorHandler(QObject):
def tempFolderMsg(self): def tempFolderMsg(self):
return tr(TR.QT_MISC_UNABLE_TO_ACCESS_ANKI_MEDIA_FOLDER) return tr(TR.QT_MISC_UNABLE_TO_ACCESS_ANKI_MEDIA_FOLDER)
def onTimeout(self): def onTimeout(self) -> None:
error = html.escape(self.pool) error = html.escape(self.pool)
self.pool = "" self.pool = ""
self.mw.progress.clear() self.mw.progress.clear()
@ -75,15 +77,19 @@ class ErrorHandler(QObject):
if "DeprecationWarning" in error: if "DeprecationWarning" in error:
return return
if "10013" in error: if "10013" in error:
return showWarning(tr(TR.QT_MISC_YOUR_FIREWALL_OR_ANTIVIRUS_PROGRAM_IS)) showWarning(tr(TR.QT_MISC_YOUR_FIREWALL_OR_ANTIVIRUS_PROGRAM_IS))
return
if "no default input" in error.lower(): if "no default input" in error.lower():
return showWarning(tr(TR.QT_MISC_PLEASE_CONNECT_A_MICROPHONE_AND_ENSURE)) showWarning(tr(TR.QT_MISC_PLEASE_CONNECT_A_MICROPHONE_AND_ENSURE))
return
if "invalidTempFolder" in error: if "invalidTempFolder" in error:
return showWarning(self.tempFolderMsg()) showWarning(self.tempFolderMsg())
return
if "Beautiful Soup is not an HTTP client" in error: if "Beautiful Soup is not an HTTP client" in error:
return return
if "database or disk is full" in error or "Errno 28" in error: if "database or disk is full" in error or "Errno 28" in error:
return showWarning(tr(TR.QT_MISC_YOUR_COMPUTERS_STORAGE_MAY_BE_FULL)) showWarning(tr(TR.QT_MISC_YOUR_COMPUTERS_STORAGE_MAY_BE_FULL))
return
if "disk I/O error" in error: if "disk I/O error" in error:
showWarning(markdown(tr(TR.ERRORS_ACCESSING_DB))) showWarning(markdown(tr(TR.ERRORS_ACCESSING_DB)))
return return

View file

@ -71,7 +71,7 @@ class ExportDialog(QDialog):
index = self.frm.deck.findText(name) index = self.frm.deck.findText(name)
self.frm.deck.setCurrentIndex(index) self.frm.deck.setCurrentIndex(index)
def exporterChanged(self, idx): def exporterChanged(self, idx: int) -> None:
self.exporter = self.exporters[idx][1](self.col) self.exporter = self.exporters[idx][1](self.col)
self.isApkg = self.exporter.ext == ".apkg" self.isApkg = self.exporter.ext == ".apkg"
self.isVerbatim = getattr(self.exporter, "verbatim", False) self.isVerbatim = getattr(self.exporter, "verbatim", False)
@ -94,7 +94,7 @@ class ExportDialog(QDialog):
# show deck list? # show deck list?
self.frm.deck.setVisible(not self.isVerbatim) self.frm.deck.setVisible(not self.isVerbatim)
def accept(self): def accept(self) -> None:
self.exporter.includeSched = self.frm.includeSched.isChecked() self.exporter.includeSched = self.frm.includeSched.isChecked()
self.exporter.includeMedia = self.frm.includeMedia.isChecked() self.exporter.includeMedia = self.frm.includeMedia.isChecked()
self.exporter.includeTags = self.frm.includeTags.isChecked() self.exporter.includeTags = self.frm.includeTags.isChecked()
@ -177,7 +177,7 @@ class ExportDialog(QDialog):
self.mw.taskman.run_in_background(do_export, on_done) self.mw.taskman.run_in_background(do_export, on_done)
def on_export_finished(self): def on_export_finished(self) -> None:
if self.isVerbatim: if self.isVerbatim:
msg = tr(TR.EXPORTING_COLLECTION_EXPORTED) msg = tr(TR.EXPORTING_COLLECTION_EXPORTED)
self.mw.reopen() self.mw.reopen()

View file

@ -41,7 +41,7 @@ class FieldDialog(QDialog):
self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False) self.form.buttonBox.button(QDialogButtonBox.Help).setAutoDefault(False)
self.form.buttonBox.button(QDialogButtonBox.Cancel).setAutoDefault(False) self.form.buttonBox.button(QDialogButtonBox.Cancel).setAutoDefault(False)
self.form.buttonBox.button(QDialogButtonBox.Save).setAutoDefault(False) self.form.buttonBox.button(QDialogButtonBox.Save).setAutoDefault(False)
self.currentIdx = None self.currentIdx: Optional[int] = None
self.oldSortField = self.model["sortf"] self.oldSortField = self.model["sortf"]
self.fillFields() self.fillFields()
self.setupSignals() self.setupSignals()
@ -52,13 +52,13 @@ class FieldDialog(QDialog):
########################################################################## ##########################################################################
def fillFields(self): def fillFields(self) -> None:
self.currentIdx = None self.currentIdx = None
self.form.fieldList.clear() self.form.fieldList.clear()
for c, f in enumerate(self.model["flds"]): for c, f in enumerate(self.model["flds"]):
self.form.fieldList.addItem("{}: {}".format(c + 1, f["name"])) self.form.fieldList.addItem("{}: {}".format(c + 1, f["name"]))
def setupSignals(self): def setupSignals(self) -> None:
f = self.form f = self.form
qconnect(f.fieldList.currentRowChanged, self.onRowChange) qconnect(f.fieldList.currentRowChanged, self.onRowChange)
qconnect(f.fieldAdd.clicked, self.onAdd) qconnect(f.fieldAdd.clicked, self.onAdd)
@ -86,29 +86,31 @@ class FieldDialog(QDialog):
movePos -= 1 movePos -= 1
self.moveField(movePos + 1) # convert to 1 based. self.moveField(movePos + 1) # convert to 1 based.
def onRowChange(self, idx): def onRowChange(self, idx: int) -> None:
if idx == -1: if idx == -1:
return return
self.saveField() self.saveField()
self.loadField(idx) self.loadField(idx)
def _uniqueName(self, prompt, ignoreOrd=None, old=""): def _uniqueName(
self, prompt: str, ignoreOrd: Optional[int] = None, old: str = ""
) -> Optional[str]:
txt = getOnlyText(prompt, default=old).replace('"', "").strip() txt = getOnlyText(prompt, default=old).replace('"', "").strip()
if not txt: if not txt:
return return None
if txt[0] in "#^/": if txt[0] in "#^/":
showWarning(tr(TR.FIELDS_NAME_FIRST_LETTER_NOT_VALID)) showWarning(tr(TR.FIELDS_NAME_FIRST_LETTER_NOT_VALID))
return return None
for letter in """:{"}""": for letter in """:{"}""":
if letter in txt: if letter in txt:
showWarning(tr(TR.FIELDS_NAME_INVALID_LETTER)) showWarning(tr(TR.FIELDS_NAME_INVALID_LETTER))
return return None
for f in self.model["flds"]: for f in self.model["flds"]:
if ignoreOrd is not None and f["ord"] == ignoreOrd: if ignoreOrd is not None and f["ord"] == ignoreOrd:
continue continue
if f["name"] == txt: if f["name"] == txt:
showWarning(tr(TR.FIELDS_THAT_FIELD_NAME_IS_ALREADY_USED)) showWarning(tr(TR.FIELDS_THAT_FIELD_NAME_IS_ALREADY_USED))
return return None
return txt return txt
def onRename(self): def onRename(self):
@ -127,7 +129,7 @@ class FieldDialog(QDialog):
self.fillFields() self.fillFields()
self.form.fieldList.setCurrentRow(idx) self.form.fieldList.setCurrentRow(idx)
def onAdd(self): def onAdd(self) -> None:
name = self._uniqueName(tr(TR.FIELDS_FIELD_NAME)) name = self._uniqueName(tr(TR.FIELDS_FIELD_NAME))
if not name: if not name:
return return
@ -185,7 +187,7 @@ class FieldDialog(QDialog):
self.fillFields() self.fillFields()
self.form.fieldList.setCurrentRow(pos - 1) self.form.fieldList.setCurrentRow(pos - 1)
def loadField(self, idx): def loadField(self, idx: int) -> None:
self.currentIdx = idx self.currentIdx = idx
fld = self.model["flds"][idx] fld = self.model["flds"][idx]
f = self.form f = self.form
@ -195,7 +197,7 @@ class FieldDialog(QDialog):
f.sortField.setChecked(self.model["sortf"] == fld["ord"]) f.sortField.setChecked(self.model["sortf"] == fld["ord"])
f.rtl.setChecked(fld["rtl"]) f.rtl.setChecked(fld["rtl"])
def saveField(self): def saveField(self) -> None:
# not initialized yet? # not initialized yet?
if self.currentIdx is None: if self.currentIdx is None:
return return
@ -219,14 +221,14 @@ class FieldDialog(QDialog):
fld["rtl"] = rtl fld["rtl"] = rtl
self.change_tracker.mark_basic() self.change_tracker.mark_basic()
def reject(self): def reject(self) -> None:
if self.change_tracker.changed(): if self.change_tracker.changed():
if not askUser("Discard changes?"): if not askUser("Discard changes?"):
return return
QDialog.reject(self) QDialog.reject(self)
def accept(self): def accept(self) -> None:
self.saveField() self.saveField()
def save(): def save():

View file

@ -9,12 +9,13 @@ import traceback
import unicodedata import unicodedata
import zipfile import zipfile
from concurrent.futures import Future from concurrent.futures import Future
from typing import Optional from typing import Any, Optional
import anki.importing as importing import anki.importing as importing
import aqt.deckchooser import aqt.deckchooser
import aqt.forms import aqt.forms
import aqt.modelchooser import aqt.modelchooser
from anki.importing.apkg import AnkiPackageImporter
from aqt import AnkiQt, gui_hooks from aqt import AnkiQt, gui_hooks
from aqt.qt import * from aqt.qt import *
from aqt.utils import ( from aqt.utils import (
@ -106,14 +107,14 @@ class ImportDialog(QDialog):
self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole) self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole)
self.exec_() self.exec_()
def setupOptions(self): def setupOptions(self) -> None:
self.model = self.mw.col.models.current() self.model = self.mw.col.models.current()
self.modelChooser = aqt.modelchooser.ModelChooser( self.modelChooser = aqt.modelchooser.ModelChooser(
self.mw, self.frm.modelArea, label=False self.mw, self.frm.modelArea, label=False
) )
self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False) self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False)
def modelChanged(self, unused=None): def modelChanged(self, unused: Any = None) -> None:
self.importer.model = self.mw.col.models.current() self.importer.model = self.mw.col.models.current()
self.importer.initMapping() self.importer.initMapping()
self.showMapping() self.showMapping()
@ -142,7 +143,7 @@ class ImportDialog(QDialog):
self.showMapping(hook=updateDelim) self.showMapping(hook=updateDelim)
self.updateDelimiterButtonText() self.updateDelimiterButtonText()
def updateDelimiterButtonText(self): def updateDelimiterButtonText(self) -> None:
if not self.importer.needDelimiter: if not self.importer.needDelimiter:
return return
if self.importer.delimiter: if self.importer.delimiter:
@ -164,7 +165,7 @@ class ImportDialog(QDialog):
txt = tr(TR.IMPORTING_FIELDS_SEPARATED_BY, val=d) txt = tr(TR.IMPORTING_FIELDS_SEPARATED_BY, val=d)
self.frm.autoDetect.setText(txt) self.frm.autoDetect.setText(txt)
def accept(self): def accept(self) -> None:
self.importer.mapping = self.mapping self.importer.mapping = self.mapping
if not self.importer.mappingOk(): if not self.importer.mappingOk():
showWarning(tr(TR.IMPORTING_THE_FIRST_FIELD_OF_THE_NOTE)) showWarning(tr(TR.IMPORTING_THE_FIRST_FIELD_OF_THE_NOTE))
@ -211,19 +212,21 @@ class ImportDialog(QDialog):
self.mw.taskman.run_in_background(self.importer.run, on_done) self.mw.taskman.run_in_background(self.importer.run, on_done)
def setupMappingFrame(self): def setupMappingFrame(self) -> None:
# qt seems to have a bug with adding/removing from a grid, so we add # qt seems to have a bug with adding/removing from a grid, so we add
# to a separate object and add/remove that instead # to a separate object and add/remove that instead
self.frame = QFrame(self.frm.mappingArea) self.frame = QFrame(self.frm.mappingArea)
self.frm.mappingArea.setWidget(self.frame) self.frm.mappingArea.setWidget(self.frame)
self.mapbox = QVBoxLayout(self.frame) self.mapbox = QVBoxLayout(self.frame)
self.mapbox.setContentsMargins(0, 0, 0, 0) self.mapbox.setContentsMargins(0, 0, 0, 0)
self.mapwidget = None self.mapwidget: Optional[QWidget] = None
def hideMapping(self): def hideMapping(self):
self.frm.mappingGroup.hide() self.frm.mappingGroup.hide()
def showMapping(self, keepMapping=False, hook=None): def showMapping(
self, keepMapping: bool = False, hook: Optional[Callable] = None
) -> None:
if hook: if hook:
hook() hook()
if not keepMapping: if not keepMapping:
@ -295,7 +298,7 @@ def showUnicodeWarning():
showWarning(tr(TR.IMPORTING_SELECTED_FILE_WAS_NOT_IN_UTF8)) showWarning(tr(TR.IMPORTING_SELECTED_FILE_WAS_NOT_IN_UTF8))
def onImport(mw): def onImport(mw: AnkiQt) -> None:
filt = ";;".join([x[0] for x in importing.Importers]) filt = ";;".join([x[0] for x in importing.Importers])
file = getFile(mw, tr(TR.ACTIONS_IMPORT), None, key="import", filter=filt) file = getFile(mw, tr(TR.ACTIONS_IMPORT), None, key="import", filter=filt)
if not file: if not file:
@ -314,7 +317,7 @@ def onImport(mw):
importFile(mw, file) importFile(mw, file)
def importFile(mw, file): def importFile(mw: AnkiQt, file: str) -> None:
importerClass = None importerClass = None
done = False done = False
for i in importing.Importers: for i in importing.Importers:
@ -406,7 +409,7 @@ def invalidZipMsg():
return tr(TR.IMPORTING_THIS_FILE_DOES_NOT_APPEAR_TO) return tr(TR.IMPORTING_THIS_FILE_DOES_NOT_APPEAR_TO)
def setupApkgImport(mw, importer): def setupApkgImport(mw: AnkiQt, importer: AnkiPackageImporter) -> bool:
base = os.path.basename(importer.file).lower() base = os.path.basename(importer.file).lower()
full = ( full = (
(base == "collection.apkg") (base == "collection.apkg")
@ -424,6 +427,7 @@ def setupApkgImport(mw, importer):
return False return False
replaceWithApkg(mw, importer.file, mw.restoringBackup) replaceWithApkg(mw, importer.file, mw.restoringBackup)
return False
def replaceWithApkg(mw, file, backup): def replaceWithApkg(mw, file, backup):

View file

@ -1141,7 +1141,7 @@ title="%s" %s>%s</button>""" % (
# Cramming # Cramming
########################################################################## ##########################################################################
def onCram(self, search=""): def onCram(self, search: str = "") -> None:
import aqt.dyndeckconf import aqt.dyndeckconf
n = 1 n = 1

View file

@ -26,7 +26,7 @@ from aqt.qt import *
from aqt.utils import aqt_data_folder from aqt.utils import aqt_data_folder
def _getExportFolder(): def _getExportFolder() -> str:
data_folder = aqt_data_folder() data_folder = aqt_data_folder()
webInSrcFolder = os.path.abspath(os.path.join(data_folder, "web")) webInSrcFolder = os.path.abspath(os.path.join(data_folder, "web"))
if os.path.exists(webInSrcFolder): if os.path.exists(webInSrcFolder):
@ -83,7 +83,7 @@ class MediaServer(threading.Thread):
if not self.is_shutdown: if not self.is_shutdown:
raise raise
def shutdown(self): def shutdown(self) -> None:
self.is_shutdown = True self.is_shutdown = True
sockets = list(self.server._map.values()) # type: ignore sockets = list(self.server._map.values()) # type: ignore
for socket in sockets: for socket in sockets:
@ -91,7 +91,7 @@ class MediaServer(threading.Thread):
# https://github.com/Pylons/webtest/blob/4b8a3ebf984185ff4fefb31b4d0cf82682e1fcf7/webtest/http.py#L93-L104 # https://github.com/Pylons/webtest/blob/4b8a3ebf984185ff4fefb31b4d0cf82682e1fcf7/webtest/http.py#L93-L104
self.server.task_dispatcher.shutdown() self.server.task_dispatcher.shutdown()
def getPort(self): def getPort(self) -> int:
self._ready.wait() self._ready.wait()
return int(self.server.effective_port) # type: ignore return int(self.server.effective_port) # type: ignore

View file

@ -57,7 +57,7 @@ class Models(QDialog):
# Models # Models
########################################################################## ##########################################################################
def maybe_select_provided_notetype(self): def maybe_select_provided_notetype(self) -> None:
if not self.selected_notetype_id: if not self.selected_notetype_id:
self.form.modelsList.setCurrentRow(0) self.form.modelsList.setCurrentRow(0)
return return

View file

@ -4,7 +4,7 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Any, Callable, Dict, List, Optional, Tuple
import aqt import aqt
from anki.collection import SearchTerm from anki.collection import SearchTerm
@ -45,13 +45,13 @@ class Overview:
self.web = mw.web self.web = mw.web
self.bottom = BottomBar(mw, mw.bottomWeb) self.bottom = BottomBar(mw, mw.bottomWeb)
def show(self): def show(self) -> None:
av_player.stop_and_clear_queue() av_player.stop_and_clear_queue()
self.web.set_bridge_command(self._linkHandler, self) self.web.set_bridge_command(self._linkHandler, self)
self.mw.setStateShortcuts(self._shortcutKeys()) self.mw.setStateShortcuts(self._shortcutKeys())
self.refresh() self.refresh()
def refresh(self): def refresh(self) -> None:
self.mw.col.reset() self.mw.col.reset()
self._renderPage() self._renderPage()
self._renderBottom() self._renderBottom()
@ -61,7 +61,7 @@ class Overview:
# Handlers # Handlers
############################################################ ############################################################
def _linkHandler(self, url): def _linkHandler(self, url: str) -> bool:
if url == "study": if url == "study":
self.mw.col.startTimebox() self.mw.col.startTimebox()
self.mw.moveToState("review") self.mw.moveToState("review")
@ -92,7 +92,7 @@ class Overview:
openLink(url) openLink(url)
return False return False
def _shortcutKeys(self): def _shortcutKeys(self) -> List[Tuple[str, Callable]]:
return [ return [
("o", self.mw.onDeckConf), ("o", self.mw.onDeckConf),
("r", self.onRebuildKey), ("r", self.onRebuildKey),
@ -101,7 +101,7 @@ class Overview:
("u", self.onUnbury), ("u", self.onUnbury),
] ]
def _filteredDeck(self): def _filteredDeck(self) -> int:
return self.mw.col.decks.current()["dyn"] return self.mw.col.decks.current()["dyn"]
def onRebuildKey(self): def onRebuildKey(self):
@ -114,7 +114,7 @@ class Overview:
self.mw.col.sched.empty_filtered_deck(self.mw.col.decks.selected()) self.mw.col.sched.empty_filtered_deck(self.mw.col.decks.selected())
self.mw.reset() self.mw.reset()
def onCustomStudyKey(self): def onCustomStudyKey(self) -> None:
if not self._filteredDeck(): if not self._filteredDeck():
self.onStudyMore() self.onStudyMore()
@ -150,7 +150,7 @@ class Overview:
# HTML # HTML
############################################################ ############################################################
def _renderPage(self): def _renderPage(self) -> None:
but = self.mw.button but = self.mw.button
deck = self.mw.col.decks.current() deck = self.mw.col.decks.current()
self.sid = deck.get("sharedFrom") self.sid = deck.get("sharedFrom")
@ -177,10 +177,10 @@ class Overview:
context=self, context=self,
) )
def _show_finished_screen(self): def _show_finished_screen(self) -> None:
self.web.load_ts_page("congrats") self.web.load_ts_page("congrats")
def _desc(self, deck): def _desc(self, deck: Dict[str, Any]) -> str:
if deck["dyn"]: if deck["dyn"]:
desc = tr(TR.STUDYING_THIS_IS_A_SPECIAL_DECK_FOR) desc = tr(TR.STUDYING_THIS_IS_A_SPECIAL_DECK_FOR)
desc += " " + tr(TR.STUDYING_CARDS_WILL_BE_AUTOMATICALLY_RETURNED_TO) desc += " " + tr(TR.STUDYING_CARDS_WILL_BE_AUTOMATICALLY_RETURNED_TO)
@ -229,7 +229,7 @@ class Overview:
# Bottom area # Bottom area
###################################################################### ######################################################################
def _renderBottom(self): def _renderBottom(self) -> None:
links = [ links = [
["O", "opts", tr(TR.ACTIONS_OPTIONS)], ["O", "opts", tr(TR.ACTIONS_OPTIONS)],
] ]
@ -256,7 +256,7 @@ class Overview:
# Studying more # Studying more
###################################################################### ######################################################################
def onStudyMore(self): def onStudyMore(self) -> None:
import aqt.customstudy import aqt.customstudy
aqt.customstudy.CustomStudy(self.mw) aqt.customstudy.CustomStudy(self.mw)

View file

@ -4,7 +4,7 @@
import json import json
import re import re
import time import time
from typing import Any, Callable, Optional, Union from typing import Any, Callable, Optional, Tuple, Union
from anki.cards import Card from anki.cards import Card
from anki.collection import ConfigBoolKey from anki.collection import ConfigBoolKey
@ -19,6 +19,7 @@ from aqt.qt import (
QPixmap, QPixmap,
QShortcut, QShortcut,
Qt, Qt,
QTimer,
QVBoxLayout, QVBoxLayout,
QWidget, QWidget,
qconnect, qconnect,
@ -29,12 +30,14 @@ from aqt.theme import theme_manager
from aqt.utils import TR, disable_help_button, restoreGeom, saveGeom, tr from aqt.utils import TR, disable_help_button, restoreGeom, saveGeom, tr
from aqt.webview import AnkiWebView from aqt.webview import AnkiWebView
LastStateAndMod = Tuple[str, int, int]
class Previewer(QDialog): class Previewer(QDialog):
_last_state = None _last_state: Optional[LastStateAndMod] = None
_card_changed = False _card_changed = False
_last_render: Union[int, float] = 0 _last_render: Union[int, float] = 0
_timer = None _timer: Optional[QTimer] = None
_show_both_sides = False _show_both_sides = False
def __init__(self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]): def __init__(self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]):
@ -54,7 +57,7 @@ class Previewer(QDialog):
def card_changed(self) -> bool: def card_changed(self) -> bool:
raise NotImplementedError raise NotImplementedError
def open(self): def open(self) -> None:
self._state = "question" self._state = "question"
self._last_state = None self._last_state = None
self._create_gui() self._create_gui()
@ -62,7 +65,7 @@ class Previewer(QDialog):
self.render_card() self.render_card()
self.show() self.show()
def _create_gui(self): def _create_gui(self) -> None:
self.setWindowTitle(tr(TR.ACTIONS_PREVIEW)) self.setWindowTitle(tr(TR.ACTIONS_PREVIEW))
self.close_shortcut = QShortcut(QKeySequence("Ctrl+Shift+P"), self) self.close_shortcut = QShortcut(QKeySequence("Ctrl+Shift+P"), self)
@ -98,25 +101,25 @@ class Previewer(QDialog):
self.setLayout(self.vbox) self.setLayout(self.vbox)
restoreGeom(self, "preview") restoreGeom(self, "preview")
def _on_finished(self, ok): def _on_finished(self, ok: int) -> None:
saveGeom(self, "preview") saveGeom(self, "preview")
self.mw.progress.timer(100, self._on_close, False) self.mw.progress.timer(100, self._on_close, False)
def _on_replay_audio(self): def _on_replay_audio(self) -> None:
if self._state == "question": if self._state == "question":
replay_audio(self.card(), True) replay_audio(self.card(), True)
elif self._state == "answer": elif self._state == "answer":
replay_audio(self.card(), False) replay_audio(self.card(), False)
def close(self): def close(self) -> None:
self._on_close() self._on_close()
super().close() super().close()
def _on_close(self): def _on_close(self) -> None:
self._open = False self._open = False
self._close_callback() self._close_callback()
def _setup_web_view(self): def _setup_web_view(self) -> None:
jsinc = [ jsinc = [
"js/vendor/jquery.min.js", "js/vendor/jquery.min.js",
"js/vendor/css_browser_selector.min.js", "js/vendor/css_browser_selector.min.js",
@ -136,7 +139,7 @@ class Previewer(QDialog):
if cmd.startswith("play:"): if cmd.startswith("play:"):
play_clicked_audio(cmd, self.card()) play_clicked_audio(cmd, self.card())
def render_card(self): def render_card(self) -> None:
self.cancel_timer() self.cancel_timer()
# Keep track of whether render() has ever been called # Keep track of whether render() has ever been called
# with cardChanged=True since the last successful render # with cardChanged=True since the last successful render
@ -151,7 +154,7 @@ class Previewer(QDialog):
else: else:
self._render_scheduled() self._render_scheduled()
def cancel_timer(self): def cancel_timer(self) -> None:
if self._timer: if self._timer:
self._timer.stop() self._timer.stop()
self._timer = None self._timer = None
@ -214,7 +217,7 @@ class Previewer(QDialog):
self._web.eval("{}({},'{}');".format(func, json.dumps(txt), bodyclass)) self._web.eval("{}({},'{}');".format(func, json.dumps(txt), bodyclass))
self._card_changed = False self._card_changed = False
def _on_show_both_sides(self, toggle): def _on_show_both_sides(self, toggle: bool) -> None:
self._show_both_sides = toggle self._show_both_sides = toggle
self.mw.col.set_config_bool(ConfigBoolKey.PREVIEW_BOTH_SIDES, toggle) self.mw.col.set_config_bool(ConfigBoolKey.PREVIEW_BOTH_SIDES, toggle)
self.mw.col.setMod() self.mw.col.setMod()
@ -222,7 +225,7 @@ class Previewer(QDialog):
self._state = "question" self._state = "question"
self.render_card() self.render_card()
def _state_and_mod(self): def _state_and_mod(self) -> Tuple[str, int, int]:
c = self.card() c = self.card()
n = c.note() n = c.note()
n.load() n.load()
@ -241,7 +244,7 @@ class MultiCardPreviewer(Previewer):
# need to state explicitly it's not implement to avoid W0223 # need to state explicitly it's not implement to avoid W0223
raise NotImplementedError raise NotImplementedError
def _create_gui(self): def _create_gui(self) -> None:
super()._create_gui() super()._create_gui()
self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole) self._prev = self.bbox.addButton("<", QDialogButtonBox.ActionRole)
self._prev.setAutoDefault(False) self._prev.setAutoDefault(False)
@ -266,7 +269,7 @@ class MultiCardPreviewer(Previewer):
def _on_prev_card(self): def _on_prev_card(self):
pass pass
def _on_next(self): def _on_next(self) -> None:
if self._state == "question": if self._state == "question":
self._state = "answer" self._state = "answer"
self.render_card() self.render_card()
@ -276,19 +279,19 @@ class MultiCardPreviewer(Previewer):
def _on_next_card(self): def _on_next_card(self):
pass pass
def _updateButtons(self): def _updateButtons(self) -> None:
if not self._open: if not self._open:
return return
self._prev.setEnabled(self._should_enable_prev()) self._prev.setEnabled(self._should_enable_prev())
self._next.setEnabled(self._should_enable_next()) self._next.setEnabled(self._should_enable_next())
def _should_enable_prev(self): def _should_enable_prev(self) -> bool:
return self._state == "answer" and not self._show_both_sides return self._state == "answer" and not self._show_both_sides
def _should_enable_next(self): def _should_enable_next(self) -> bool:
return self._state == "question" return self._state == "question"
def _on_close(self): def _on_close(self) -> None:
super()._on_close() super()._on_close()
self._prev = None self._prev = None
self._next = None self._next = None
@ -317,15 +320,15 @@ class BrowserPreviewer(MultiCardPreviewer):
lambda: self._parent._moveCur(QAbstractItemView.MoveUp) lambda: self._parent._moveCur(QAbstractItemView.MoveUp)
) )
def _on_next_card(self): def _on_next_card(self) -> None:
self._parent.editor.saveNow( self._parent.editor.saveNow(
lambda: self._parent._moveCur(QAbstractItemView.MoveDown) lambda: self._parent._moveCur(QAbstractItemView.MoveDown)
) )
def _should_enable_prev(self): def _should_enable_prev(self) -> bool:
return super()._should_enable_prev() or self._parent.currentRow() > 0 return super()._should_enable_prev() or self._parent.currentRow() > 0
def _should_enable_next(self): def _should_enable_next(self) -> bool:
return ( return (
super()._should_enable_next() super()._should_enable_next()
or self._parent.currentRow() < self._parent.model.rowCount(None) - 1 or self._parent.currentRow() < self._parent.model.rowCount(None) - 1

View file

@ -119,11 +119,11 @@ class AnkiRestart(SystemExit):
class ProfileManager: class ProfileManager:
def __init__(self, base=None): def __init__(self, base: Optional[str] = None) -> None:
## Settings which should be forgotten each Anki restart ## Settings which should be forgotten each Anki restart
self.session = {} self.session: Dict[str, Any] = {}
self.name = None self.name: Optional[str] = None
self.db = None self.db: Optional[DB] = None
self.profile: Optional[Dict] = None self.profile: Optional[Dict] = None
# instantiate base folder # instantiate base folder
self.base: str self.base: str
@ -170,7 +170,7 @@ class ProfileManager:
return p return p
return os.path.expanduser("~/Documents/Anki") return os.path.expanduser("~/Documents/Anki")
def maybeMigrateFolder(self): def maybeMigrateFolder(self) -> None:
newBase = self.base newBase = self.base
oldBase = self._oldFolderLocation() oldBase = self._oldFolderLocation()
@ -206,7 +206,7 @@ class ProfileManager:
confirmation.setText( confirmation.setText(
"Anki needs to move its data folder from Documents/Anki to a new location. Proceed?" "Anki needs to move its data folder from Documents/Anki to a new location. Proceed?"
) )
retval = confirmation.exec() retval = confirmation.exec_()
if retval == QMessageBox.Ok: if retval == QMessageBox.Ok:
progress = QMessageBox() progress = QMessageBox()
@ -228,7 +228,7 @@ class ProfileManager:
completion.setWindowTitle(window_title) completion.setWindowTitle(window_title)
completion.setText("Migration complete. Please start Anki again.") completion.setText("Migration complete. Please start Anki again.")
completion.show() completion.show()
completion.exec() completion.exec_()
else: else:
diag = QMessageBox() diag = QMessageBox()
diag.setIcon(QMessageBox.Warning) diag.setIcon(QMessageBox.Warning)
@ -239,7 +239,7 @@ class ProfileManager:
"Migration aborted. If you would like to keep the old folder location, please " "Migration aborted. If you would like to keep the old folder location, please "
"see the Startup Options section of the manual. Anki will now quit." "see the Startup Options section of the manual. Anki will now quit."
) )
diag.exec() diag.exec_()
raise AnkiRestart(exitcode=0) raise AnkiRestart(exitcode=0)
@ -424,7 +424,7 @@ class ProfileManager:
os.makedirs(path) os.makedirs(path)
return path return path
def _setBaseFolder(self, cmdlineBase: None) -> None: def _setBaseFolder(self, cmdlineBase: Optional[str]) -> None:
if cmdlineBase: if cmdlineBase:
self.base = os.path.abspath(cmdlineBase) self.base = os.path.abspath(cmdlineBase)
elif os.environ.get("ANKI_BASE"): elif os.environ.get("ANKI_BASE"):

View file

@ -4,7 +4,7 @@
from __future__ import annotations from __future__ import annotations
import time import time
from typing import Optional from typing import Callable, Optional
import aqt.forms import aqt.forms
from aqt.qt import * from aqt.qt import *
@ -15,13 +15,13 @@ from aqt.utils import TR, disable_help_button, tr
class ProgressManager: class ProgressManager:
def __init__(self, mw): def __init__(self, mw: aqt.AnkiQt) -> None:
self.mw = mw self.mw = mw
self.app = QApplication.instance() self.app = QApplication.instance()
self.inDB = False self.inDB = False
self.blockUpdates = False self.blockUpdates = False
self._show_timer: Optional[QTimer] = None self._show_timer: Optional[QTimer] = None
self._win = None self._win: Optional[ProgressDialog] = None
self._levels = 0 self._levels = 0
# Safer timers # Safer timers
@ -29,7 +29,9 @@ class ProgressManager:
# A custom timer which avoids firing while a progress dialog is active # A custom timer which avoids firing while a progress dialog is active
# (likely due to some long-running DB operation) # (likely due to some long-running DB operation)
def timer(self, ms, func, repeat, requiresCollection=True): def timer(
self, ms: int, func: Callable, repeat: bool, requiresCollection: bool = True
) -> QTimer:
"""Create and start a standard Anki timer. """Create and start a standard Anki timer.
If the timer fires while a progress window is shown: If the timer fires while a progress window is shown:
@ -136,7 +138,7 @@ class ProgressManager:
self._updating = False self._updating = False
self._lastUpdate = time.time() self._lastUpdate = time.time()
def finish(self): def finish(self) -> None:
self._levels -= 1 self._levels -= 1
self._levels = max(0, self._levels) self._levels = max(0, self._levels)
if self._levels == 0: if self._levels == 0:
@ -147,13 +149,13 @@ class ProgressManager:
self._show_timer.stop() self._show_timer.stop()
self._show_timer = None self._show_timer = None
def clear(self): def clear(self) -> None:
"Restore the interface after an error." "Restore the interface after an error."
if self._levels: if self._levels:
self._levels = 1 self._levels = 1
self.finish() self.finish()
def _maybeShow(self): def _maybeShow(self) -> None:
if not self._levels: if not self._levels:
return return
if self._shown: if self._shown:
@ -181,17 +183,17 @@ class ProgressManager:
self._win = None self._win = None
self._shown = 0 self._shown = 0
def _setBusy(self): def _setBusy(self) -> None:
self.mw.app.setOverrideCursor(QCursor(Qt.WaitCursor)) self.mw.app.setOverrideCursor(QCursor(Qt.WaitCursor))
def _unsetBusy(self): def _unsetBusy(self) -> None:
self.app.restoreOverrideCursor() self.app.restoreOverrideCursor()
def busy(self): def busy(self) -> int:
"True if processing." "True if processing."
return self._levels return self._levels
def _on_show_timer(self): def _on_show_timer(self) -> None:
self._show_timer = None self._show_timer = None
self._showWin() self._showWin()
@ -209,7 +211,7 @@ class ProgressManager:
class ProgressDialog(QDialog): class ProgressDialog(QDialog):
def __init__(self, parent): def __init__(self, parent: QWidget) -> None:
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
disable_help_button(self) disable_help_button(self)
self.form = aqt.forms.progress.Ui_Dialog() self.form = aqt.forms.progress.Ui_Dialog()
@ -219,7 +221,7 @@ class ProgressDialog(QDialog):
# required for smooth progress bars # required for smooth progress bars
self.form.progressBar.setStyleSheet("QProgressBar::chunk { width: 1px; }") self.form.progressBar.setStyleSheet("QProgressBar::chunk { width: 1px; }")
def cancel(self): def cancel(self) -> None:
self._closingDown = True self._closingDown = True
self.hide() self.hide()

View file

@ -8,7 +8,7 @@ import html
import json import json
import re import re
import unicodedata as ucd import unicodedata as ucd
from typing import Callable, List, Optional, Tuple, Union from typing import Any, Callable, List, Optional, Tuple, Union
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
@ -697,7 +697,7 @@ time = %(time)d;
########################################################################## ##########################################################################
# note the shortcuts listed here also need to be defined above # note the shortcuts listed here also need to be defined above
def _contextMenu(self): def _contextMenu(self) -> List[Any]:
currentFlag = self.card and self.card.userFlag() currentFlag = self.card and self.card.userFlag()
opts = [ opts = [
[ [

View file

@ -20,7 +20,7 @@ class ChangeTracker:
def __init__(self, mw: AnkiQt): def __init__(self, mw: AnkiQt):
self.mw = mw self.mw = mw
def mark_basic(self): def mark_basic(self) -> None:
if self._changed == Change.NO_CHANGE: if self._changed == Change.NO_CHANGE:
self._changed = Change.BASIC_CHANGE self._changed = Change.BASIC_CHANGE

View file

@ -54,7 +54,7 @@ class NewDeckStats(QDialog):
self.form.web.set_bridge_command(self._on_bridge_cmd, self) self.form.web.set_bridge_command(self._on_bridge_cmd, self)
self.activateWindow() self.activateWindow()
def reject(self): def reject(self) -> None:
self.form.web = None self.form.web = None
saveGeom(self, self.name) saveGeom(self, self.name)
aqt.dialogs.markClosed("NewDeckStats") aqt.dialogs.markClosed("NewDeckStats")
@ -98,14 +98,14 @@ class NewDeckStats(QDialog):
return False return False
def refresh(self): def refresh(self) -> None:
self.form.web.load_ts_page("graphs") self.form.web.load_ts_page("graphs")
class DeckStats(QDialog): class DeckStats(QDialog):
"""Legacy deck stats, used by some add-ons.""" """Legacy deck stats, used by some add-ons."""
def __init__(self, mw): def __init__(self, mw: aqt.main.AnkiQt) -> None:
QDialog.__init__(self, mw, Qt.Window) QDialog.__init__(self, mw, Qt.Window)
mw.setupDialogGC(self) mw.setupDialogGC(self)
self.mw = mw self.mw = mw
@ -143,7 +143,7 @@ class DeckStats(QDialog):
self.refresh() self.refresh()
self.activateWindow() self.activateWindow()
def reject(self): def reject(self) -> None:
self.form.web = None self.form.web = None
saveGeom(self, self.name) saveGeom(self, self.name)
aqt.dialogs.markClosed("DeckStats") aqt.dialogs.markClosed("DeckStats")
@ -173,15 +173,15 @@ class DeckStats(QDialog):
self.form.web.page().printToPdf(path) self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED)) tooltip(tr(TR.STATISTICS_SAVED))
def changePeriod(self, n): def changePeriod(self, n: int) -> None:
self.period = n self.period = n
self.refresh() self.refresh()
def changeScope(self, type): def changeScope(self, type: str) -> None:
self.wholeCollection = type == "collection" self.wholeCollection = type == "collection"
self.refresh() self.refresh()
def refresh(self): def refresh(self) -> None:
self.mw.progress.start(parent=self) self.mw.progress.start(parent=self)
stats = self.mw.col.stats() stats = self.mw.col.stats()
stats.wholeCollection = self.wholeCollection stats.wholeCollection = self.wholeCollection

View file

@ -2,6 +2,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import Optional
import aqt import aqt
from aqt import gui_hooks from aqt import gui_hooks
from aqt.qt import * from aqt.qt import *
@ -109,7 +111,7 @@ class StudyDeck(QDialog):
return True return True
return False return False
def redraw(self, filt, focus=None): def redraw(self, filt: str, focus: Optional[str] = None) -> None:
self.filt = filt self.filt = filt
self.focus = focus self.focus = focus
self.names = [n for n in self.origNames if self._matches(n, filt)] self.names = [n for n in self.origNames if self._matches(n, filt)]
@ -123,7 +125,7 @@ class StudyDeck(QDialog):
l.setCurrentRow(idx) l.setCurrentRow(idx)
l.scrollToItem(l.item(idx), QAbstractItemView.PositionAtCenter) l.scrollToItem(l.item(idx), QAbstractItemView.PositionAtCenter)
def _matches(self, name, filt): def _matches(self, name: str, filt: str) -> bool:
name = name.lower() name = name.lower()
filt = filt.lower() filt = filt.lower()
if not filt: if not filt:

View file

@ -4,7 +4,9 @@
from __future__ import annotations from __future__ import annotations
import re import re
from typing import Iterable, List, Optional, Union
from anki.collection import Collection
from aqt import gui_hooks from aqt import gui_hooks
from aqt.qt import * from aqt.qt import *
@ -15,9 +17,9 @@ class TagEdit(QLineEdit):
lostFocus = pyqtSignal() lostFocus = pyqtSignal()
# 0 = tags, 1 = decks # 0 = tags, 1 = decks
def __init__(self, parent, type=0): def __init__(self, parent: QDialog, type: int = 0) -> None:
QLineEdit.__init__(self, parent) QLineEdit.__init__(self, parent)
self.col = None self.col: Optional[Collection] = None
self.model = QStringListModel() self.model = QStringListModel()
self.type = type self.type = type
if type == 0: if type == 0:
@ -28,19 +30,20 @@ class TagEdit(QLineEdit):
self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setCaseSensitivity(Qt.CaseInsensitive)
self.setCompleter(self.completer) self.setCompleter(self.completer)
def setCol(self, col): def setCol(self, col: Collection) -> None:
"Set the current col, updating list of available tags." "Set the current col, updating list of available tags."
self.col = col self.col = col
l: Iterable[str]
if self.type == 0: if self.type == 0:
l = self.col.tags.all() l = self.col.tags.all()
else: else:
l = (d.name for d in self.col.decks.all_names_and_ids()) l = (d.name for d in self.col.decks.all_names_and_ids())
self.model.setStringList(l) self.model.setStringList(l)
def focusInEvent(self, evt): def focusInEvent(self, evt: QFocusEvent) -> None:
QLineEdit.focusInEvent(self, evt) QLineEdit.focusInEvent(self, evt)
def keyPressEvent(self, evt): def keyPressEvent(self, evt: QKeyEvent) -> None:
if evt.key() in (Qt.Key_Up, Qt.Key_Down): if evt.key() in (Qt.Key_Up, Qt.Key_Down):
# show completer on arrow key up/down # show completer on arrow key up/down
if not self.completer.popup().isVisible(): if not self.completer.popup().isVisible():
@ -85,7 +88,7 @@ class TagEdit(QLineEdit):
self.showCompleter() self.showCompleter()
gui_hooks.tag_editor_did_process_key(self, evt) gui_hooks.tag_editor_did_process_key(self, evt)
def showCompleter(self): def showCompleter(self) -> None:
self.completer.setCompletionPrefix(self.text()) self.completer.setCompletionPrefix(self.text())
self.completer.complete() self.completer.complete()
@ -94,20 +97,26 @@ class TagEdit(QLineEdit):
self.lostFocus.emit() # type: ignore self.lostFocus.emit() # type: ignore
self.completer.popup().hide() self.completer.popup().hide()
def hideCompleter(self): def hideCompleter(self) -> None:
if sip.isdeleted(self.completer): if sip.isdeleted(self.completer):
return return
self.completer.popup().hide() self.completer.popup().hide()
class TagCompleter(QCompleter): class TagCompleter(QCompleter):
def __init__(self, model, parent, edit, *args): def __init__(
self,
model: QStringListModel,
parent: QWidget,
edit: TagEdit,
*args,
) -> None:
QCompleter.__init__(self, model, parent) QCompleter.__init__(self, model, parent)
self.tags = [] self.tags: List[str] = []
self.edit = edit self.edit = edit
self.cursor = None self.cursor: Optional[int] = None
def splitPath(self, tags): def splitPath(self, tags: str) -> List[str]:
stripped_tags = tags.strip() stripped_tags = tags.strip()
stripped_tags = re.sub(" +", " ", stripped_tags) stripped_tags = re.sub(" +", " ", stripped_tags)
self.tags = self.edit.col.tags.split(stripped_tags) self.tags = self.edit.col.tags.split(stripped_tags)
@ -119,7 +128,7 @@ class TagCompleter(QCompleter):
self.cursor = stripped_tags.count(" ", 0, p) self.cursor = stripped_tags.count(" ", 0, p)
return [self.tags[self.cursor]] return [self.tags[self.cursor]]
def pathFromIndex(self, idx): def pathFromIndex(self, idx: QModelIndex) -> str:
if self.cursor is None: if self.cursor is None:
return self.edit.text() return self.edit.text()
ret = QCompleter.pathFromIndex(self, idx) ret = QCompleter.pathFromIndex(self, idx)

View file

@ -3,14 +3,17 @@
from typing import List, Optional from typing import List, Optional
import aqt import aqt
from aqt.customstudy import CustomStudy
from aqt.main import AnkiQt
from aqt.qt import * from aqt.qt import *
from aqt.utils import disable_help_button, restoreGeom, saveGeom from aqt.utils import disable_help_button, restoreGeom, saveGeom
class TagLimit(QDialog): class TagLimit(QDialog):
def __init__(self, mw, parent): def __init__(self, mw: AnkiQt, parent: CustomStudy) -> None:
QDialog.__init__(self, parent, Qt.Window) QDialog.__init__(self, parent, Qt.Window)
self.tags: Union[str, List] = "" self.tags: str = ""
self.tags_list: List[str] = []
self.mw = mw self.mw = mw
self.parent: Optional[QWidget] = parent self.parent: Optional[QWidget] = parent
self.deck = self.parent.deck self.deck = self.parent.deck
@ -29,7 +32,7 @@ class TagLimit(QDialog):
restoreGeom(self, "tagLimit") restoreGeom(self, "tagLimit")
self.exec_() self.exec_()
def rebuildTagList(self): def rebuildTagList(self) -> None:
usertags = self.mw.col.tags.byDeck(self.deck["id"], True) usertags = self.mw.col.tags.byDeck(self.deck["id"], True)
yes = self.deck.get("activeTags", []) yes = self.deck.get("activeTags", [])
no = self.deck.get("inactiveTags", []) no = self.deck.get("inactiveTags", [])
@ -42,10 +45,10 @@ class TagLimit(QDialog):
groupedTags = [] groupedTags = []
usertags.sort() usertags.sort()
groupedTags.append(usertags) groupedTags.append(usertags)
self.tags = [] self.tags_list = []
for tags in groupedTags: for tags in groupedTags:
for t in tags: for t in tags:
self.tags.append(t) self.tags_list.append(t)
item = QListWidgetItem(t.replace("_", " ")) item = QListWidgetItem(t.replace("_", " "))
self.dialog.activeList.addItem(item) self.dialog.activeList.addItem(item)
if t in yesHash: if t in yesHash:
@ -69,7 +72,7 @@ class TagLimit(QDialog):
self.tags = "" self.tags = ""
QDialog.reject(self) QDialog.reject(self)
def accept(self): def accept(self) -> None:
self.hide() self.hide()
# gather yes/no tags # gather yes/no tags
yes = [] yes = []
@ -80,12 +83,12 @@ class TagLimit(QDialog):
item = self.dialog.activeList.item(c) item = self.dialog.activeList.item(c)
idx = self.dialog.activeList.indexFromItem(item) idx = self.dialog.activeList.indexFromItem(item)
if self.dialog.activeList.selectionModel().isSelected(idx): if self.dialog.activeList.selectionModel().isSelected(idx):
yes.append(self.tags[c]) yes.append(self.tags_list[c])
# inactive # inactive
item = self.dialog.inactiveList.item(c) item = self.dialog.inactiveList.item(c)
idx = self.dialog.inactiveList.indexFromItem(item) idx = self.dialog.inactiveList.indexFromItem(item)
if self.dialog.inactiveList.selectionModel().isSelected(idx): if self.dialog.inactiveList.selectionModel().isSelected(idx):
no.append(self.tags[c]) no.append(self.tags_list[c])
# save in the deck for future invocations # save in the deck for future invocations
self.deck["activeTags"] = yes self.deck["activeTags"] = yes
self.deck["inactiveTags"] = no self.deck["inactiveTags"] = no

View file

@ -78,7 +78,7 @@ class TaskManager(QObject):
self.run_in_background(task, wrapped_done) self.run_in_background(task, wrapped_done)
def _on_closures_pending(self): def _on_closures_pending(self) -> None:
"""Run any pending closures. This runs in the main thread.""" """Run any pending closures. This runs in the main thread."""
with self._closures_lock: with self._closures_lock:
closures = self._closures closures = self._closures

View file

@ -7,6 +7,7 @@ import requests
import aqt import aqt
from anki.utils import platDesc, versionWithBuild from anki.utils import platDesc, versionWithBuild
from aqt.main import AnkiQt
from aqt.qt import * from aqt.qt import *
from aqt.utils import TR, openLink, showText, tr from aqt.utils import TR, openLink, showText, tr
@ -17,7 +18,7 @@ class LatestVersionFinder(QThread):
newMsg = pyqtSignal(dict) newMsg = pyqtSignal(dict)
clockIsOff = pyqtSignal(float) clockIsOff = pyqtSignal(float)
def __init__(self, main): def __init__(self, main: AnkiQt) -> None:
QThread.__init__(self) QThread.__init__(self)
self.main = main self.main = main
self.config = main.pm.meta self.config = main.pm.meta