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