anki/qt/aqt/models.py

290 lines
9.5 KiB
Python
Raw Normal View History

2019-02-05 04:59:03 +01:00
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
2020-05-14 12:58:45 +02:00
from __future__ import annotations
2021-02-02 14:30:53 +01:00
from concurrent.futures import Future
from operator import itemgetter
from typing import Any, Optional, Sequence
2019-12-20 10:19:03 +01:00
2018-12-15 03:45:38 +01:00
import aqt.clayout
from anki import stdmodels
from anki.collection import Collection
from anki.lang import without_unicode_isolation
from anki.models import NotetypeDict, NotetypeId, NotetypeNameIdUseCount
2020-05-14 12:58:45 +02:00
from anki.notes import Note
from aqt import AnkiQt, gui_hooks
from aqt.operations import QueryOp
2021-04-30 08:09:44 +02:00
from aqt.operations.notetype import (
add_notetype_legacy,
remove_notetype,
update_notetype_legacy,
)
2019-12-20 10:19:03 +01:00
from aqt.qt import *
2021-04-30 08:01:47 +02:00
from aqt.schema_change_tracker import ChangeTracker
2019-12-23 01:34:10 +01:00
from aqt.utils import (
HelpPage,
2019-12-23 01:34:10 +01:00
askUser,
disable_help_button,
2019-12-23 01:34:10 +01:00
getText,
maybeHideClose,
openHelp,
restoreGeom,
saveGeom,
showInfo,
tr,
2019-12-23 01:34:10 +01:00
)
2019-12-20 10:19:03 +01:00
class Models(QDialog):
def __init__(
self,
mw: AnkiQt,
parent: Optional[QWidget] = None,
fromMain: bool = False,
selected_notetype_id: Optional[NotetypeId] = None,
):
self.mw = mw
parent = parent or mw
2012-12-22 00:21:24 +01:00
self.fromMain = fromMain
self.selected_notetype_id = selected_notetype_id
QDialog.__init__(self, parent or mw)
self.col = mw.col.weakref()
2019-12-23 01:34:10 +01:00
assert self.col
self.mm = self.col.models
self.form = aqt.forms.models.Ui_Dialog()
self.form.setupUi(self)
2020-10-12 04:17:02 +02:00
qconnect(
self.form.buttonBox.helpRequested,
lambda: openHelp(HelpPage.ADDING_A_NOTE_TYPE),
2020-10-12 04:17:02 +02:00
)
self.models: Sequence[NotetypeNameIdUseCount] = []
self.setupModels()
restoreGeom(self, "models")
self.show()
# Models
##########################################################################
def maybe_select_provided_notetype(self) -> None:
if not self.selected_notetype_id:
self.form.modelsList.setCurrentRow(0)
return
for i, m in enumerate(self.models):
if m.id == self.selected_notetype_id:
self.form.modelsList.setCurrentRow(i)
break
2020-07-30 05:28:52 +02:00
def setupModels(self) -> None:
self.model = None
2019-12-23 01:34:10 +01:00
f = self.form
box = f.buttonBox
2020-08-23 15:52:02 +02:00
default_buttons = [
2021-03-26 04:48:26 +01:00
(tr.actions_add(), self.onAdd),
(tr.actions_rename(), self.onRename),
(tr.actions_delete(), self.onDelete),
2020-08-23 15:52:02 +02:00
]
2012-12-22 00:21:24 +01:00
if self.fromMain:
default_buttons.extend(
[
2021-03-26 04:48:26 +01:00
(tr.notetypes_fields(), self.onFields),
(tr.notetypes_cards(), self.onCards),
]
)
2020-08-23 15:52:02 +02:00
2021-03-26 04:48:26 +01:00
default_buttons.append((tr.notetypes_options(), self.onAdvanced))
2020-08-23 15:52:02 +02:00
2020-08-23 17:10:56 +02:00
for label, func in gui_hooks.models_did_init_buttons(default_buttons, self):
button = box.addButton(label, QDialogButtonBox.ButtonRole.ActionRole)
2020-08-23 15:52:02 +02:00
qconnect(button.clicked, func)
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
2021-02-02 14:30:53 +01:00
def on_done(fut: Future) -> None:
self.updateModelsList(fut.result())
self.maybe_select_provided_notetype()
self.mw.taskman.with_progress(self.col.models.all_use_counts, on_done, self)
maybeHideClose(box)
2021-04-30 08:09:44 +02:00
def refresh_list(self, *ignored_args: Any) -> None:
QueryOp(
parent=self,
op=lambda col: col.models.all_use_counts(),
success=self.updateModelsList,
).run_in_background()
2020-07-30 05:28:52 +02:00
def onRename(self) -> None:
nt = self.current_notetype()
2021-04-30 08:09:44 +02:00
text, ok = getText(tr.actions_new_name(), default=nt["name"])
if ok and text.strip():
nt["name"] = text
2021-04-30 08:09:44 +02:00
update_notetype_legacy(parent=self, notetype=nt).success(
self.refresh_list
).run_in_background()
def updateModelsList(self, notetypes: Sequence[NotetypeNameIdUseCount]) -> None:
row = self.form.modelsList.currentRow()
if row == -1:
row = 0
self.form.modelsList.clear()
self.models = notetypes
for m in self.models:
mUse = tr.browsing_note_count(count=m.use_count)
item = QListWidgetItem(f"{m.name} [{mUse}]")
self.form.modelsList.addItem(item)
self.form.modelsList.setCurrentRow(row)
def current_notetype(self) -> NotetypeDict:
row = self.form.modelsList.currentRow()
return self.mm.get(NotetypeId(self.models[row].id))
2020-07-30 05:28:52 +02:00
def onAdd(self) -> None:
def on_success(notetype: NotetypeDict) -> None:
# if legacy add-ons already added the notetype, skip adding
if notetype["id"]:
self.refresh_list()
return
# prompt for name
text, ok = getText(tr.actions_name(), default=notetype["name"], parent=self)
if not ok or not text.strip():
return
notetype["name"] = text
add_notetype_legacy(parent=self, notetype=notetype).success(
2021-04-30 08:09:44 +02:00
self.refresh_list
).run_in_background()
AddModel(self.mw, on_success, self)
2020-07-30 05:28:52 +02:00
def onDelete(self) -> None:
if len(self.models) < 2:
2021-03-26 04:48:26 +01:00
showInfo(tr.notetypes_please_add_another_note_type_first(), parent=self)
return
idx = self.form.modelsList.currentRow()
if self.models[idx].use_count:
2021-03-26 04:48:26 +01:00
msg = tr.notetypes_delete_this_note_type_and_all()
else:
2021-03-26 04:48:26 +01:00
msg = tr.notetypes_delete_this_unused_note_type()
if not askUser(msg, parent=self):
return
2021-04-30 08:01:47 +02:00
tracker = ChangeTracker(self.mw)
if not tracker.mark_schema():
return
nt = self.current_notetype()
2021-04-30 08:01:47 +02:00
remove_notetype(parent=self, notetype_id=nt["id"]).success(
lambda _: self.refresh_list()
).run_in_background()
2020-07-30 05:28:52 +02:00
def onAdvanced(self) -> None:
nt = self.current_notetype()
d = QDialog(self)
disable_help_button(d)
frm = aqt.forms.modelopts.Ui_Dialog()
frm.setupUi(d)
frm.latexsvg.setChecked(nt.get("latexsvg", False))
frm.latexHeader.setText(nt["latexPre"])
frm.latexFooter.setText(nt["latexPost"])
d.setWindowTitle(
without_unicode_isolation(tr.actions_options_for(val=nt["name"]))
)
qconnect(frm.buttonBox.helpRequested, lambda: openHelp(HelpPage.LATEX))
restoreGeom(d, "modelopts")
gui_hooks.models_advanced_will_show(d)
d.exec()
saveGeom(d, "modelopts")
nt["latexsvg"] = frm.latexsvg.isChecked()
nt["latexPre"] = str(frm.latexHeader.toPlainText())
nt["latexPost"] = str(frm.latexFooter.toPlainText())
2021-04-30 08:09:44 +02:00
update_notetype_legacy(parent=self, notetype=nt).success(
self.refresh_list
).run_in_background()
2020-07-30 05:28:52 +02:00
def _tmpNote(self) -> Note:
nt = self.current_notetype()
2020-05-14 12:58:45 +02:00
return Note(self.col, nt)
2012-12-22 00:21:24 +01:00
2020-07-30 05:28:52 +02:00
def onFields(self) -> None:
2012-12-22 00:21:24 +01:00
from aqt.fields import FieldDialog
2019-12-23 01:34:10 +01:00
FieldDialog(self.mw, self.current_notetype(), parent=self)
2012-12-22 00:21:24 +01:00
2020-07-30 05:28:52 +02:00
def onCards(self) -> None:
2012-12-22 00:21:24 +01:00
from aqt.clayout import CardLayout
2019-12-23 01:34:10 +01:00
2012-12-22 00:21:24 +01:00
n = self._tmpNote()
2020-05-14 12:58:45 +02:00
CardLayout(self.mw, n, ord=0, parent=self, fill_empty=True)
2012-12-22 00:21:24 +01:00
# Cleanup
##########################################################################
2020-07-30 05:28:52 +02:00
def reject(self) -> None:
saveGeom(self, "models")
QDialog.reject(self)
2019-12-23 01:34:10 +01:00
class AddModel(QDialog):
model: Optional[NotetypeDict]
2020-08-11 22:56:58 +02:00
def __init__(
self,
mw: AnkiQt,
on_success: Callable[[NotetypeDict], None],
parent: Optional[QWidget] = None,
) -> None:
self.parent_ = parent or mw
self.mw = mw
self.col = mw.col
QDialog.__init__(self, self.parent_, Qt.WindowType.Window)
self.model = None
self.dialog = aqt.forms.addmodel.Ui_Dialog()
self.dialog.setupUi(self)
self.setWindowModality(Qt.WindowModality.ApplicationModal)
disable_help_button(self)
# standard models
self.notetypes: list[
Union[NotetypeDict, Callable[[Collection], NotetypeDict]]
] = []
for name, func in stdmodels.get_stock_notetypes(self.col):
item = QListWidgetItem(tr.notetypes_add(val=name))
self.dialog.models.addItem(item)
self.notetypes.append(func)
# add copies
2013-01-08 02:39:51 +01:00
for m in sorted(self.col.models.all(), key=itemgetter("name")):
item = QListWidgetItem(tr.notetypes_clone(val=m["name"]))
self.dialog.models.addItem(item)
self.notetypes.append(m)
self.dialog.models.setCurrentRow(0)
# the list widget will swallow the enter key
s = QShortcut(QKeySequence("Return"), self)
qconnect(s.activated, self.accept)
# help
qconnect(self.dialog.buttonBox.helpRequested, self.onHelp)
self.on_success = on_success
self.show()
2020-07-30 05:28:52 +02:00
def reject(self) -> None:
QDialog.reject(self)
2020-07-30 05:28:52 +02:00
def accept(self) -> None:
model = self.notetypes[self.dialog.models.currentRow()]
if isinstance(model, dict):
# clone existing
self.model = self.mw.col.models.copy(model, add=False)
else:
self.model = model(self.col)
QDialog.accept(self)
# On mac, we need to allow time for the existing modal to close or
# Qt gets confused.
self.mw.progress.single_shot(100, lambda: self.on_success(self.model), True)
2020-07-30 05:28:52 +02:00
def onHelp(self) -> None:
openHelp(HelpPage.ADDING_A_NOTE_TYPE)