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

View File

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

View File

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

View File

@ -1,12 +1,13 @@
# Copyright: Ankitects Pty Ltd and contributors
# -*- coding: utf-8 -*-
# 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
from anki.collection import SearchTerm
from anki.errors import InvalidInput
from anki.lang import without_unicode_isolation
from aqt.main import AnkiQt
from aqt.qt import *
from aqt.utils import (
TR,
@ -23,7 +24,13 @@ from aqt.utils import (
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)
self.mw = mw
self.deck = deck or self.mw.col.decks.current()
@ -65,7 +72,7 @@ class DeckConf(QDialog):
self.exec_()
saveGeom(self, "dyndeckconf")
def initialSetup(self):
def initialSetup(self) -> None:
import anki.consts as cs
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)
def _onReschedToggled(self, _state):
def _onReschedToggled(self, _state: int) -> None:
self.form.previewDelayWidget.setVisible(
not self.form.resched.isChecked() and self.mw.col.schedVer() > 1
)
def loadConf(self):
def loadConf(self) -> None:
f = self.form
d = self.deck
@ -113,7 +120,7 @@ class DeckConf(QDialog):
f.secondFilter.setChecked(False)
f.filter2group.setVisible(False)
def saveConf(self):
def saveConf(self) -> None:
f = self.form
d = self.deck
d["resched"] = f.resched.isChecked()
@ -142,7 +149,7 @@ class DeckConf(QDialog):
self.ok = False
QDialog.reject(self)
def accept(self):
def accept(self) -> None:
try:
self.saveConf()
except InvalidInput as err:

View File

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

View File

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

View File

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

View File

@ -71,7 +71,7 @@ class ExportDialog(QDialog):
index = self.frm.deck.findText(name)
self.frm.deck.setCurrentIndex(index)
def exporterChanged(self, idx):
def exporterChanged(self, idx: int) -> None:
self.exporter = self.exporters[idx][1](self.col)
self.isApkg = self.exporter.ext == ".apkg"
self.isVerbatim = getattr(self.exporter, "verbatim", False)
@ -94,7 +94,7 @@ class ExportDialog(QDialog):
# show deck list?
self.frm.deck.setVisible(not self.isVerbatim)
def accept(self):
def accept(self) -> None:
self.exporter.includeSched = self.frm.includeSched.isChecked()
self.exporter.includeMedia = self.frm.includeMedia.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)
def on_export_finished(self):
def on_export_finished(self) -> None:
if self.isVerbatim:
msg = tr(TR.EXPORTING_COLLECTION_EXPORTED)
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.Cancel).setAutoDefault(False)
self.form.buttonBox.button(QDialogButtonBox.Save).setAutoDefault(False)
self.currentIdx = None
self.currentIdx: Optional[int] = None
self.oldSortField = self.model["sortf"]
self.fillFields()
self.setupSignals()
@ -52,13 +52,13 @@ class FieldDialog(QDialog):
##########################################################################
def fillFields(self):
def fillFields(self) -> None:
self.currentIdx = None
self.form.fieldList.clear()
for c, f in enumerate(self.model["flds"]):
self.form.fieldList.addItem("{}: {}".format(c + 1, f["name"]))
def setupSignals(self):
def setupSignals(self) -> None:
f = self.form
qconnect(f.fieldList.currentRowChanged, self.onRowChange)
qconnect(f.fieldAdd.clicked, self.onAdd)
@ -86,29 +86,31 @@ class FieldDialog(QDialog):
movePos -= 1
self.moveField(movePos + 1) # convert to 1 based.
def onRowChange(self, idx):
def onRowChange(self, idx: int) -> None:
if idx == -1:
return
self.saveField()
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()
if not txt:
return
return None
if txt[0] in "#^/":
showWarning(tr(TR.FIELDS_NAME_FIRST_LETTER_NOT_VALID))
return
return None
for letter in """:{"}""":
if letter in txt:
showWarning(tr(TR.FIELDS_NAME_INVALID_LETTER))
return
return None
for f in self.model["flds"]:
if ignoreOrd is not None and f["ord"] == ignoreOrd:
continue
if f["name"] == txt:
showWarning(tr(TR.FIELDS_THAT_FIELD_NAME_IS_ALREADY_USED))
return
return None
return txt
def onRename(self):
@ -127,7 +129,7 @@ class FieldDialog(QDialog):
self.fillFields()
self.form.fieldList.setCurrentRow(idx)
def onAdd(self):
def onAdd(self) -> None:
name = self._uniqueName(tr(TR.FIELDS_FIELD_NAME))
if not name:
return
@ -185,7 +187,7 @@ class FieldDialog(QDialog):
self.fillFields()
self.form.fieldList.setCurrentRow(pos - 1)
def loadField(self, idx):
def loadField(self, idx: int) -> None:
self.currentIdx = idx
fld = self.model["flds"][idx]
f = self.form
@ -195,7 +197,7 @@ class FieldDialog(QDialog):
f.sortField.setChecked(self.model["sortf"] == fld["ord"])
f.rtl.setChecked(fld["rtl"])
def saveField(self):
def saveField(self) -> None:
# not initialized yet?
if self.currentIdx is None:
return
@ -219,14 +221,14 @@ class FieldDialog(QDialog):
fld["rtl"] = rtl
self.change_tracker.mark_basic()
def reject(self):
def reject(self) -> None:
if self.change_tracker.changed():
if not askUser("Discard changes?"):
return
QDialog.reject(self)
def accept(self):
def accept(self) -> None:
self.saveField()
def save():

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -119,11 +119,11 @@ class AnkiRestart(SystemExit):
class ProfileManager:
def __init__(self, base=None):
def __init__(self, base: Optional[str] = None) -> None:
## Settings which should be forgotten each Anki restart
self.session = {}
self.name = None
self.db = None
self.session: Dict[str, Any] = {}
self.name: Optional[str] = None
self.db: Optional[DB] = None
self.profile: Optional[Dict] = None
# instantiate base folder
self.base: str
@ -170,7 +170,7 @@ class ProfileManager:
return p
return os.path.expanduser("~/Documents/Anki")
def maybeMigrateFolder(self):
def maybeMigrateFolder(self) -> None:
newBase = self.base
oldBase = self._oldFolderLocation()
@ -206,7 +206,7 @@ class ProfileManager:
confirmation.setText(
"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:
progress = QMessageBox()
@ -228,7 +228,7 @@ class ProfileManager:
completion.setWindowTitle(window_title)
completion.setText("Migration complete. Please start Anki again.")
completion.show()
completion.exec()
completion.exec_()
else:
diag = QMessageBox()
diag.setIcon(QMessageBox.Warning)
@ -239,7 +239,7 @@ class ProfileManager:
"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."
)
diag.exec()
diag.exec_()
raise AnkiRestart(exitcode=0)
@ -424,7 +424,7 @@ class ProfileManager:
os.makedirs(path)
return path
def _setBaseFolder(self, cmdlineBase: None) -> None:
def _setBaseFolder(self, cmdlineBase: Optional[str]) -> None:
if cmdlineBase:
self.base = os.path.abspath(cmdlineBase)
elif os.environ.get("ANKI_BASE"):

View File

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

View File

@ -8,7 +8,7 @@ import html
import json
import re
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
@ -697,7 +697,7 @@ time = %(time)d;
##########################################################################
# 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()
opts = [
[

View File

@ -20,7 +20,7 @@ class ChangeTracker:
def __init__(self, mw: AnkiQt):
self.mw = mw
def mark_basic(self):
def mark_basic(self) -> None:
if self._changed == Change.NO_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.activateWindow()
def reject(self):
def reject(self) -> None:
self.form.web = None
saveGeom(self, self.name)
aqt.dialogs.markClosed("NewDeckStats")
@ -98,14 +98,14 @@ class NewDeckStats(QDialog):
return False
def refresh(self):
def refresh(self) -> None:
self.form.web.load_ts_page("graphs")
class DeckStats(QDialog):
"""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)
mw.setupDialogGC(self)
self.mw = mw
@ -143,7 +143,7 @@ class DeckStats(QDialog):
self.refresh()
self.activateWindow()
def reject(self):
def reject(self) -> None:
self.form.web = None
saveGeom(self, self.name)
aqt.dialogs.markClosed("DeckStats")
@ -173,15 +173,15 @@ class DeckStats(QDialog):
self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED))
def changePeriod(self, n):
def changePeriod(self, n: int) -> None:
self.period = n
self.refresh()
def changeScope(self, type):
def changeScope(self, type: str) -> None:
self.wholeCollection = type == "collection"
self.refresh()
def refresh(self):
def refresh(self) -> None:
self.mw.progress.start(parent=self)
stats = self.mw.col.stats()
stats.wholeCollection = self.wholeCollection

View File

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

View File

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

View File

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

View File

@ -78,7 +78,7 @@ class TaskManager(QObject):
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."""
with self._closures_lock:
closures = self._closures

View File

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