anki/qt/aqt/models.py

244 lines
7.8 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 operator import itemgetter
2020-08-23 15:52:02 +02:00
from typing import Any, List, Tuple, Callable, 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.backend_pb2 import NoteTypeNameIDUseCount
2019-03-04 02:58:34 +01:00
from anki.lang import _, ngettext
from anki.models import NoteType
2020-05-14 12:58:45 +02:00
from anki.notes import Note
from anki.rsbackend import pb
from aqt import AnkiQt, gui_hooks
2019-12-20 10:19:03 +01:00
from aqt.qt import *
2019-12-23 01:34:10 +01:00
from aqt.utils import (
askUser,
getText,
maybeHideClose,
openHelp,
restoreGeom,
saveGeom,
showInfo,
)
2019-12-20 10:19:03 +01:00
class Models(QDialog):
def __init__(self, mw: AnkiQt, parent=None, fromMain=False):
self.mw = mw
parent = parent or mw
2012-12-22 00:21:24 +01:00
self.fromMain = fromMain
QDialog.__init__(self, parent, Qt.Window)
self.col = mw.col.weakref()
2019-12-23 01:34:10 +01:00
assert self.col
self.mm = self.col.models
self.mw.checkpoint(_("Note Types"))
self.form = aqt.forms.models.Ui_Dialog()
self.form.setupUi(self)
qconnect(self.form.buttonBox.helpRequested, lambda: openHelp("notetypes"))
self.models: List[pb.NoteTypeNameIDUseCount] = []
self.setupModels()
restoreGeom(self, "models")
self.exec_()
# Models
##########################################################################
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 = [
("Add", self.onAdd),
("Rename", self.onRename),
("Delete", self.onDelete),
("Fields...", self.onFields),
("Cards...", self.onCards),
("Options...", self.onAdvanced),
]
2012-12-22 00:21:24 +01:00
if self.fromMain:
2020-08-23 15:52:02 +02:00
default_buttons.extends([
("Fields...", self.onFields),
("Cards...", self.onCards),
])
default_buttons.append(("Options...", self.onAdvanced))
2020-08-23 17:10:56 +02:00
for label, func in gui_hooks.models_did_init_buttons(default_buttons, self):
2020-08-23 15:52:02 +02:00
button = box.addButton(_(label), QDialogButtonBox.ActionRole)
qconnect(button.clicked, func)
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
2020-07-30 05:28:52 +02:00
def on_done(fut) -> None:
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(self.col.models.all_use_counts, on_done, self)
f.modelsList.setCurrentRow(0)
maybeHideClose(box)
2020-07-30 05:28:52 +02:00
def onRename(self) -> None:
nt = self.current_notetype()
txt = getText(_("New name:"), default=nt["name"])
if txt[1] and txt[0]:
nt["name"] = txt[0]
self.saveAndRefresh(nt)
def saveAndRefresh(self, nt: NoteType) -> None:
2020-07-30 05:28:52 +02:00
def save() -> Sequence[pb.NoteTypeNameIDUseCount]:
self.mm.save(nt)
return self.col.models.all_use_counts()
2020-07-30 05:28:52 +02:00
def on_done(fut) -> None:
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
def updateModelsList(self, notetypes: List[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 = ngettext("%d note", "%d notes", m.use_count) % m.use_count
item = QListWidgetItem("%s [%s]" % (m.name, mUse))
self.form.modelsList.addItem(item)
self.form.modelsList.setCurrentRow(row)
def current_notetype(self) -> NoteType:
row = self.form.modelsList.currentRow()
return self.mm.get(self.models[row].id)
2020-07-30 05:28:52 +02:00
def onAdd(self) -> None:
m = AddModel(self.mw, self).get()
if m:
2019-12-23 01:34:10 +01:00
txt = getText(_("Name:"), default=m["name"])[0]
if txt:
2019-12-23 01:34:10 +01:00
m["name"] = txt
self.saveAndRefresh(m)
2020-07-30 05:28:52 +02:00
def onDelete(self) -> None:
if len(self.models) < 2:
2019-12-23 01:34:10 +01:00
showInfo(_("Please add another note type first."), parent=self)
return
idx = self.form.modelsList.currentRow()
if self.models[idx].use_count:
msg = _("Delete this note type and all its cards?")
else:
msg = _("Delete this unused note type?")
if not askUser(msg, parent=self):
return
self.col.modSchema(check=True)
nt = self.current_notetype()
2020-07-30 05:28:52 +02:00
def save() -> Sequence[pb.NoteTypeNameIDUseCount]:
self.mm.rem(nt)
return self.col.models.all_use_counts()
2020-07-30 05:28:52 +02:00
def on_done(fut) -> None:
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
2020-07-30 05:28:52 +02:00
def onAdvanced(self) -> None:
nt = self.current_notetype()
d = QDialog(self)
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(_("Options for %s") % nt["name"])
qconnect(frm.buttonBox.helpRequested, lambda: openHelp("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())
self.saveAndRefresh(nt)
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
##########################################################################
# need to flush model on change or reject
2020-07-30 05:28:52 +02:00
def reject(self) -> None:
self.mw.reset()
saveGeom(self, "models")
QDialog.reject(self)
2019-12-23 01:34:10 +01:00
class AddModel(QDialog):
2020-08-11 22:56:58 +02:00
model: Optional[NoteType]
def __init__(self, mw: AnkiQt, parent: Optional[QWidget] = None):
self.parent_ = parent or mw
self.mw = mw
self.col = mw.col
QDialog.__init__(self, self.parent_, Qt.Window)
self.model = None
self.dialog = aqt.forms.addmodel.Ui_Dialog()
self.dialog.setupUi(self)
# standard models
self.models = []
2020-05-15 08:05:58 +02:00
for (name, func) in stdmodels.get_stock_notetypes(self.col):
item = QListWidgetItem(_("Add: %s") % name)
self.dialog.models.addItem(item)
self.models.append((True, func))
# add copies
2013-01-08 02:39:51 +01:00
for m in sorted(self.col.models.all(), key=itemgetter("name")):
2019-12-23 01:34:10 +01:00
item = QListWidgetItem(_("Clone: %s") % m["name"])
self.dialog.models.addItem(item)
2020-05-04 07:21:03 +02:00
self.models.append((False, m)) # type: ignore
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)
2020-07-30 05:28:52 +02:00
def get(self) -> Any:
self.exec_()
return self.model
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:
(isStd, model) = self.models[self.dialog.models.currentRow()]
if isStd:
# create
self.model = model(self.col)
else:
# add copy to deck
self.model = self.mw.col.models.copy(model)
self.mw.col.models.setCurrent(self.model)
QDialog.accept(self)
2020-07-30 05:28:52 +02:00
def onHelp(self) -> None:
openHelp("notetypes")