update fields and models diags

- field changes are now applied when user closes dialog with save
button, in bulk
- models diag now fetches note type and saves it as required, instead
of holding on top a copy that can grow stale as changes are made in
subdialogs
- both dialogs now perform operations in the backend
- note.model() now fetches the note type on the fly, instead of
holding on to a copy that may become stale
This commit is contained in:
Damien Elmes 2020-05-04 21:52:48 +10:00
parent 2308b136fd
commit 25f122bf5c
7 changed files with 105 additions and 85 deletions

View file

@ -360,23 +360,24 @@ class ModelManager:
f["name"] = name
return f
def addField(self, m: NoteType, field: Field) -> None:
def addField(self, m: NoteType, field: Field, save=True) -> None:
if m["id"]:
self.col.modSchema(check=True)
m["flds"].append(field)
if m["id"]:
if m["id"] and save:
self.save(m)
def remField(self, m: NoteType, field: Field) -> None:
def remField(self, m: NoteType, field: Field, save=True) -> None:
self.col.modSchema(check=True)
m["flds"].remove(field)
self.save(m)
if save:
self.save(m)
def moveField(self, m: NoteType, field: Field, idx: int) -> None:
def moveField(self, m: NoteType, field: Field, idx: int, save=True) -> None:
self.col.modSchema(check=True)
oldidx = m["flds"].index(field)
if oldidx == idx:
@ -385,14 +386,16 @@ class ModelManager:
m["flds"].remove(field)
m["flds"].insert(idx, field)
self.save(m)
if save:
self.save(m)
def renameField(self, m: NoteType, field: Field, newName: str) -> None:
def renameField(self, m: NoteType, field: Field, newName: str, save=True) -> None:
assert field in m["flds"]
field["name"] = newName
self.save(m)
if save:
self.save(m)
# Adding & changing templates
##################################################

View file

@ -48,9 +48,7 @@ class Note:
self.usn = n.usn
self.tags = list(n.tags)
self.fields = list(n.fields)
self._model = self.col.models.get(self.mid)
self._fmap = self.col.models.fieldMap(self._model)
self._fmap = self.col.models.fieldMap(self.model())
# fixme: only save tags in list on save
def to_backend_note(self) -> BackendNote:
@ -66,8 +64,7 @@ class Note:
fields=self.fields,
)
def flush(self, mod=None) -> None:
# fixme: mod unused?
def flush(self) -> None:
assert self.id != 0
self.col.backend.update_note(self.to_backend_note())
@ -83,7 +80,9 @@ class Note:
]
def model(self) -> Optional[NoteType]:
return self._model
return self.col.models.get(self.mid)
_model = property(model)
# Dict interface
##################################################

View file

@ -341,7 +341,7 @@ class Editor:
def _onFields(self):
from aqt.fields import FieldDialog
FieldDialog(self.mw, self.note, parent=self.parentWindow)
FieldDialog(self.mw, self.note.model(), parent=self.parentWindow)
def onCardLayout(self):
self.saveNow(self._onCardLayout)

View file

@ -4,20 +4,20 @@
import aqt
from anki.consts import *
from anki.lang import _, ngettext
from anki.models import NoteType
from anki.rsbackend import TemplateError
from aqt import AnkiQt
from aqt.qt import *
from aqt.utils import askUser, getOnlyText, openHelp, showWarning
class FieldDialog(QDialog):
def __init__(self, mw, note, ord=0, parent=None):
QDialog.__init__(self, parent or mw) # , Qt.Window)
self.mw = aqt.mw
self.parent = parent or mw
self.note = note
def __init__(self, mw: AnkiQt, nt: NoteType, parent=None):
QDialog.__init__(self, parent or mw)
self.mw = mw.weakref()
self.col = self.mw.col
self.mm = self.mw.col.models
self.model = note.model()
self.model = nt
self.mw.checkpoint(_("Fields"))
self.form = aqt.forms.fields.Ui_Dialog()
self.form.setupUi(self)
@ -87,7 +87,7 @@ class FieldDialog(QDialog):
name = self._uniqueName(_("New name:"), self.currentIdx, f["name"])
if not name:
return
self.mm.renameField(self.model, f, name)
self.mm.renameField(self.model, f, name, save=False)
self.saveField()
self.fillFields()
self.form.fieldList.setCurrentRow(idx)
@ -97,10 +97,8 @@ class FieldDialog(QDialog):
if not name:
return
self.saveField()
self.mw.progress.start()
f = self.mm.newField(name)
self.mm.addField(self.model, f)
self.mw.progress.finish()
self.mm.addField(self.model, f, save=False)
self.fillFields()
self.form.fieldList.setCurrentRow(len(self.model["flds"]) - 1)
@ -112,9 +110,7 @@ class FieldDialog(QDialog):
if not askUser(_("Delete field from %s?") % c):
return
f = self.model["flds"][self.form.fieldList.currentRow()]
self.mw.progress.start()
self.mm.remField(self.model, f)
self.mw.progress.finish()
self.mm.remField(self.model, f, save=False)
self.fillFields()
self.form.fieldList.setCurrentRow(0)
@ -140,9 +136,7 @@ class FieldDialog(QDialog):
def moveField(self, pos):
self.saveField()
f = self.model["flds"][self.currentIdx]
self.mw.progress.start()
self.mm.moveField(self.model, f, pos - 1)
self.mw.progress.finish()
self.mm.moveField(self.model, f, pos - 1, save=False)
self.fillFields()
self.form.fieldList.setCurrentRow(pos - 1)
@ -174,19 +168,21 @@ class FieldDialog(QDialog):
def accept(self):
self.saveField()
if self.oldSortField != self.model["sortf"]:
self.mw.progress.start()
self.mw.col.updateFieldCache(self.mm.nids(self.model))
self.mw.progress.finish()
try:
self.mm.save(self.model)
except TemplateError as e:
# fixme: i18n
showWarning("Unable to save changes: " + str(e))
return
self.mw.reset()
QDialog.accept(self)
def save():
self.mm.save(self.model)
def on_done(fut):
try:
fut.result()
except TemplateError as e:
# fixme: i18n
showWarning("Unable to save changes: " + str(e))
return
self.mw.reset()
QDialog.accept(self)
self.mw.taskman.with_progress(save, on_done, self)
def onHelp(self):
openHelp("fields")

View file

@ -1419,7 +1419,6 @@ will be lost. Continue?"""
print("\n")
del note.fields
del note._fmap
del note._model
pprint.pprint(note.__dict__)
print("\nCard:")

View file

@ -8,6 +8,7 @@ from typing import List, Optional
import aqt.clayout
from anki import stdmodels
from anki.lang import _, ngettext
from anki.models import NoteType
from anki.rsbackend import pb
from aqt import AnkiQt, gui_hooks
from aqt.qt import *
@ -24,11 +25,11 @@ from aqt.utils import (
class Models(QDialog):
def __init__(self, mw: AnkiQt, parent=None, fromMain=False):
self.mw = mw
self.mw = mw.weakref()
parent = parent or mw
self.fromMain = fromMain
QDialog.__init__(self, parent, Qt.Window)
self.col = mw.col
self.col = mw.col.weakref()
assert self.col
self.mm = self.col.models
self.mw.checkpoint(_("Note Types"))
@ -61,25 +62,39 @@ class Models(QDialog):
qconnect(b.clicked, self.onCards)
b = box.addButton(_("Options..."), t)
qconnect(b.clicked, self.onAdvanced)
qconnect(f.modelsList.currentRowChanged, self.modelChanged)
qconnect(f.modelsList.itemDoubleClicked, self.onRename)
self.updateModelsList()
def on_done(fut):
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(self.col.models.all_use_counts, on_done, self)
f.modelsList.setCurrentRow(0)
maybeHideClose(box)
def onRename(self):
txt = getText(_("New name:"), default=self.model["name"])
nt = self.current_notetype()
txt = getText(_("New name:"), default=nt["name"])
if txt[1] and txt[0]:
self.model["name"] = txt[0]
self.mm.save(self.model, updateReqs=False)
self.updateModelsList()
nt["name"] = txt[0]
self.saveAndRefresh(nt)
def updateModelsList(self):
def saveAndRefresh(self, nt: NoteType) -> None:
def save():
self.mm.save(nt)
return self.col.models.all_use_counts()
def on_done(fut):
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
def updateModelsList(self, notetypes):
row = self.form.modelsList.currentRow()
if row == -1:
row = 0
self.models = self.col.models.all_use_counts()
self.form.modelsList.clear()
self.models = notetypes
for m in self.models:
mUse = m.use_count
mUse = ngettext("%d note", "%d notes", mUse) % mUse
@ -87,11 +102,9 @@ class Models(QDialog):
self.form.modelsList.addItem(item)
self.form.modelsList.setCurrentRow(row)
def modelChanged(self):
if self.model:
self.saveModel()
idx = self.form.modelsList.currentRow()
self.model = self.col.models.get(self.models[idx].id)
def current_notetype(self) -> NoteType:
row = self.form.modelsList.currentRow()
return self.mm.get(self.models[row].id)
def onAdd(self):
m = AddModel(self.mw, self).get()
@ -99,9 +112,7 @@ class Models(QDialog):
txt = getText(_("Name:"), default=m["name"])[0]
if txt:
m["name"] = txt
self.mm.ensureNameUnique(m)
self.mm.save(m)
self.updateModelsList()
self.saveAndRefresh(m)
def onDelete(self):
if len(self.models) < 2:
@ -114,40 +125,50 @@ class Models(QDialog):
msg = _("Delete this unused note type?")
if not askUser(msg, parent=self):
return
self.mm.rem(self.model)
self.model = None
self.updateModelsList()
self.col.modSchema(check=True)
nt = self.current_notetype()
def save():
self.mm.rem(nt)
return self.col.models.all_use_counts()
def on_done(fut):
self.updateModelsList(fut.result())
self.mw.taskman.with_progress(save, on_done, self)
def onAdvanced(self):
nt = self.current_notetype()
d = QDialog(self)
frm = aqt.forms.modelopts.Ui_Dialog()
frm.setupUi(d)
frm.latexsvg.setChecked(self.model.get("latexsvg", False))
frm.latexHeader.setText(self.model["latexPre"])
frm.latexFooter.setText(self.model["latexPost"])
d.setWindowTitle(_("Options for %s") % self.model["name"])
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")
self.model["latexsvg"] = frm.latexsvg.isChecked()
self.model["latexPre"] = str(frm.latexHeader.toPlainText())
self.model["latexPost"] = str(frm.latexFooter.toPlainText())
def saveModel(self):
self.mm.save(self.model, updateReqs=False)
nt["latexsvg"] = frm.latexsvg.isChecked()
nt["latexPre"] = str(frm.latexHeader.toPlainText())
nt["latexPost"] = str(frm.latexFooter.toPlainText())
self.saveAndRefresh(nt)
def _tmpNote(self):
self.mm.setCurrent(self.model)
nt = self.current_notetype()
self.mm.setCurrent(nt)
n = self.col.newNote(forDeck=False)
field_names = list(n.keys())
for name in field_names:
n[name] = "(" + name + ")"
cloze_re = re.compile(r"{{(?:[^}:]*:)*cloze:(?:[^}:]*:)*([^}]+)}}")
q_template = self.model["tmpls"][0]["qfmt"]
a_template = self.model["tmpls"][0]["afmt"]
q_template = nt["tmpls"][0]["qfmt"]
a_template = nt["tmpls"][0]["afmt"]
used_cloze_fields = []
used_cloze_fields.extend(cloze_re.findall(q_template))
@ -161,8 +182,7 @@ class Models(QDialog):
def onFields(self):
from aqt.fields import FieldDialog
n = self._tmpNote()
FieldDialog(self.mw, n, parent=self)
FieldDialog(self.mw, self.current_notetype(), parent=self)
def onCards(self):
from aqt.clayout import CardLayout
@ -176,7 +196,6 @@ class Models(QDialog):
# need to flush model on change or reject
def reject(self):
self.saveModel()
self.mw.reset()
saveGeom(self, "models")
QDialog.reject(self)

View file

@ -8,12 +8,9 @@ warn_redundant_casts = True
warn_unused_configs = True
#check_untyped_defs = true
[mypy-aqt.forms.*]
check_untyped_defs=false
[mypy-aqt.tagedit]
check_untyped_defs=true
[mypy-aqt.mpv]
ignore_errors=true
[mypy-win32file]
ignore_missing_imports = True
[mypy-win32pipe]
@ -54,3 +51,10 @@ ignore_missing_imports = True
ignore_missing_imports = True
[mypy-socks]
ignore_missing_imports = True
[mypy-aqt.forms.*]
check_untyped_defs=false
[mypy-aqt.tagedit]
check_untyped_defs=true
[mypy-aqt.fields]
check_untyped_defs=true