mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
model changing
This commit is contained in:
parent
8865ca6131
commit
139048ad3f
3 changed files with 147 additions and 115 deletions
207
aqt/browser.py
207
aqt/browser.py
|
@ -13,7 +13,7 @@ from anki.utils import fmtTimeSpan, parseTags, hasTag, addTags, delTags, \
|
||||||
ids2str, stripHTMLMedia
|
ids2str, stripHTMLMedia
|
||||||
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
|
from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
|
||||||
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
|
saveHeader, restoreHeader, saveState, restoreState, applyStyles, getTag, \
|
||||||
showInfo
|
showInfo, askUser
|
||||||
from anki.errors import *
|
from anki.errors import *
|
||||||
from anki.db import *
|
from anki.db import *
|
||||||
from anki.hooks import runHook, addHook, removeHook
|
from anki.hooks import runHook, addHook, removeHook
|
||||||
|
@ -708,6 +708,18 @@ where id in %s""" % ids2str(
|
||||||
"select id from cards where fid in (%s)" %
|
"select id from cards where fid in (%s)" %
|
||||||
",".join([str(s) for s in self.selectedFacts()]))
|
",".join([str(s) for s in self.selectedFacts()]))
|
||||||
|
|
||||||
|
def oneModelFacts(self):
|
||||||
|
sf = self.selectedFacts()
|
||||||
|
if not sf:
|
||||||
|
return
|
||||||
|
mods = self.deck.db.scalar("""
|
||||||
|
select count(distinct mid) from facts
|
||||||
|
where id in %s""" % ids2str(sf))
|
||||||
|
if mods > 1:
|
||||||
|
showInfo(_("Please select cards from only one model."))
|
||||||
|
return
|
||||||
|
return sf
|
||||||
|
|
||||||
def resetDeck(self):
|
def resetDeck(self):
|
||||||
"Signal the queue needs rebuilding."
|
"Signal the queue needs rebuilding."
|
||||||
# for operations that change models we'll need to reset immediately,
|
# for operations that change models we'll need to reset immediately,
|
||||||
|
@ -718,7 +730,14 @@ where id in %s""" % ids2str(
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def genCards(self):
|
def genCards(self):
|
||||||
GenCards(self)
|
fids = self.oneModelFacts()
|
||||||
|
if fids:
|
||||||
|
GenCards(self, fids)
|
||||||
|
|
||||||
|
def onChangeModel(self):
|
||||||
|
fids = self.oneModelFacts()
|
||||||
|
if fids:
|
||||||
|
ChangeModel(self, fids)
|
||||||
|
|
||||||
def cram(self):
|
def cram(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
@ -828,30 +847,6 @@ where id in %s""" % ids2str(
|
||||||
self.deck.setUndoEnd(n)
|
self.deck.setUndoEnd(n)
|
||||||
self.resetDeck()
|
self.resetDeck()
|
||||||
|
|
||||||
# Model changing
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def onChangeModel(self):
|
|
||||||
sf = self.selectedFacts()
|
|
||||||
mods = self.deck.db.column0("""
|
|
||||||
select distinct modelId from facts
|
|
||||||
where id in %s""" % ids2str(sf))
|
|
||||||
if not len(mods) == 1:
|
|
||||||
ui.utils.showInfo(
|
|
||||||
_("Can only change one model at a time."),
|
|
||||||
parent=self)
|
|
||||||
return
|
|
||||||
d = ChangeModelDialog(self, self.card.fact.model,
|
|
||||||
self.card.cardModel)
|
|
||||||
d.exec_()
|
|
||||||
if d.ret:
|
|
||||||
n = _("Change Model")
|
|
||||||
self.deck.setUndoStart(n)
|
|
||||||
self.deck.changeModel(sf, *d.ret)
|
|
||||||
self.deck.setUndoEnd(n)
|
|
||||||
self.onSearch()
|
|
||||||
self.resetDeck()
|
|
||||||
|
|
||||||
# Edit: selection
|
# Edit: selection
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
@ -1106,11 +1101,10 @@ select fm.id, fm.name from fieldmodels fm""")
|
||||||
|
|
||||||
class GenCards(QDialog):
|
class GenCards(QDialog):
|
||||||
|
|
||||||
def __init__(self, browser):
|
def __init__(self, browser, fids):
|
||||||
self.browser = browser
|
|
||||||
if not self.canShow():
|
|
||||||
return
|
|
||||||
QDialog.__init__(self, browser)
|
QDialog.__init__(self, browser)
|
||||||
|
self.browser = browser
|
||||||
|
self.fids = fids
|
||||||
self.form = aqt.forms.addcardmodels.Ui_Dialog()
|
self.form = aqt.forms.addcardmodels.Ui_Dialog()
|
||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
||||||
|
@ -1118,19 +1112,6 @@ class GenCards(QDialog):
|
||||||
restoreGeom(self, "addCardModels")
|
restoreGeom(self, "addCardModels")
|
||||||
self.getSelection()
|
self.getSelection()
|
||||||
|
|
||||||
def canShow(self):
|
|
||||||
b = self.browser
|
|
||||||
self.fids = b.selectedFacts()
|
|
||||||
if not self.fids:
|
|
||||||
return
|
|
||||||
mods = b.deck.db.scalar("""
|
|
||||||
select count(distinct mid) from facts
|
|
||||||
where id in %s""" % ids2str(self.fids))
|
|
||||||
if mods > 1:
|
|
||||||
showInfo(_("Please select cards from only one model."))
|
|
||||||
return
|
|
||||||
return True
|
|
||||||
|
|
||||||
def getSelection(self):
|
def getSelection(self):
|
||||||
# get cards to enable
|
# get cards to enable
|
||||||
f = self.browser.deck.getFact(self.fids[0])
|
f = self.browser.deck.getFact(self.fids[0])
|
||||||
|
@ -1146,7 +1127,7 @@ where id in %s""" % ids2str(self.fids))
|
||||||
else:
|
else:
|
||||||
mode = QItemSelectionModel.Deselect
|
mode = QItemSelectionModel.Deselect
|
||||||
self.form.list.selectionModel().select(idx, mode)
|
self.form.list.selectionModel().select(idx, mode)
|
||||||
self.show()
|
self.exec_()
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
tplates = []
|
tplates = []
|
||||||
|
@ -1176,54 +1157,57 @@ where id in %s""" % ids2str(self.fids))
|
||||||
# Change model dialog
|
# Change model dialog
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
class ChangeModelDialog(QDialog):
|
class ChangeModel(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent, oldModel, oldTemplate):
|
def __init__(self, browser, fids):
|
||||||
QDialog.__init__(self, parent, Qt.Window)
|
QDialog.__init__(self, browser)
|
||||||
self.parent = parent
|
self.browser = browser
|
||||||
self.origModel = self.parent.deck.currentModel
|
self.fids = fids
|
||||||
self.oldModel = oldModel
|
self.oldModel = browser.card.fact().model()
|
||||||
self.oldTemplate = oldTemplate
|
|
||||||
self.form = aqt.forms.changemodel.Ui_Dialog()
|
self.form = aqt.forms.changemodel.Ui_Dialog()
|
||||||
self.form.setupUi(self)
|
self.form.setupUi(self)
|
||||||
|
self.setup()
|
||||||
|
restoreGeom(self, "changeModel")
|
||||||
|
addHook("reset", self.onReset)
|
||||||
|
self.exec_()
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
# maps
|
# maps
|
||||||
self.fieldMapWidget = None
|
self.flayout = QHBoxLayout()
|
||||||
self.fieldMapLayout = QHBoxLayout()
|
self.flayout.setMargin(0)
|
||||||
self.form.fieldMap.setLayout(self.fieldMapLayout)
|
self.fwidg = None
|
||||||
self.templateMapWidget = None
|
self.form.fieldMap.setLayout(self.flayout)
|
||||||
self.templateMapLayout = QHBoxLayout()
|
self.tlayout = QHBoxLayout()
|
||||||
self.form.templateMap.setLayout(self.templateMapLayout)
|
self.tlayout.setMargin(0)
|
||||||
|
self.twidg = None
|
||||||
|
self.form.templateMap.setLayout(self.tlayout)
|
||||||
# model chooser
|
# model chooser
|
||||||
self.parent.deck.currentModel = oldModel
|
import aqt.modelchooser
|
||||||
|
self.oldCurrentModel = self.browser.deck.conf['currentModelId']
|
||||||
|
self.browser.deck.conf['currentModelId'] = self.oldModel.id
|
||||||
self.form.oldModelLabel.setText(self.oldModel.name)
|
self.form.oldModelLabel.setText(self.oldModel.name)
|
||||||
self.modelChooser = ui.modelchooser.ModelChooser(self,
|
self.modelChooser = aqt.modelchooser.ModelChooser(
|
||||||
self.parent,
|
self.browser.mw, self.form.modelChooserWidget, cards=False, label=False)
|
||||||
self.parent.deck,
|
|
||||||
self.modelChanged,
|
|
||||||
cards=False,
|
|
||||||
label=False)
|
|
||||||
self.form.modelChooserWidget.setLayout(self.modelChooser)
|
|
||||||
self.modelChooser.models.setFocus()
|
self.modelChooser.models.setFocus()
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
||||||
self.onHelp)
|
self.onHelp)
|
||||||
restoreGeom(self, "changeModel")
|
|
||||||
self.modelChanged(self.oldModel)
|
self.modelChanged(self.oldModel)
|
||||||
self.ret = None
|
|
||||||
self.pauseUpdate = False
|
self.pauseUpdate = False
|
||||||
|
|
||||||
|
def onReset(self):
|
||||||
|
self.modelChanged(self.browser.deck.currentModel())
|
||||||
|
|
||||||
def modelChanged(self, model):
|
def modelChanged(self, model):
|
||||||
self.targetModel = model
|
self.targetModel = model
|
||||||
# just changing template?
|
|
||||||
self.form.fieldMap.setEnabled(self.targetModel != self.oldModel)
|
|
||||||
self.rebuildTemplateMap()
|
self.rebuildTemplateMap()
|
||||||
self.rebuildFieldMap()
|
self.rebuildFieldMap()
|
||||||
|
|
||||||
def rebuildTemplateMap(self, key=None, attr=None):
|
def rebuildTemplateMap(self, key=None, attr=None):
|
||||||
if not key:
|
if not key:
|
||||||
key = "template"
|
key = "t"
|
||||||
attr = "cardModels"
|
attr = "templates"
|
||||||
map = getattr(self, key + "MapWidget")
|
map = getattr(self, key + "widg")
|
||||||
lay = getattr(self, key + "MapLayout")
|
lay = getattr(self, key + "layout")
|
||||||
src = getattr(self.oldModel, attr)
|
src = getattr(self.oldModel, attr)
|
||||||
dst = getattr(self.targetModel, attr)
|
dst = getattr(self.targetModel, attr)
|
||||||
if map:
|
if map:
|
||||||
|
@ -1233,11 +1217,11 @@ class ChangeModelDialog(QDialog):
|
||||||
map = QWidget()
|
map = QWidget()
|
||||||
l = QGridLayout()
|
l = QGridLayout()
|
||||||
combos = []
|
combos = []
|
||||||
targets = [x.name for x in dst] + [_("Nothing")]
|
targets = [x['name'] for x in dst] + [_("Nothing")]
|
||||||
qtargets = QStringList(targets)
|
qtargets = QStringList(targets)
|
||||||
indices = {}
|
indices = {}
|
||||||
for i, x in enumerate(src):
|
for i, x in enumerate(src):
|
||||||
l.addWidget(QLabel(_("Change %s to:") % x.name), i, 0)
|
l.addWidget(QLabel(_("Change %s to:") % x['name']), i, 0)
|
||||||
cb = QComboBox()
|
cb = QComboBox()
|
||||||
cb.addItems(qtargets)
|
cb.addItems(qtargets)
|
||||||
idx = min(i, len(targets)-1)
|
idx = min(i, len(targets)-1)
|
||||||
|
@ -1249,20 +1233,20 @@ class ChangeModelDialog(QDialog):
|
||||||
l.addWidget(cb, i, 1)
|
l.addWidget(cb, i, 1)
|
||||||
map.setLayout(l)
|
map.setLayout(l)
|
||||||
lay.addWidget(map)
|
lay.addWidget(map)
|
||||||
setattr(self, key + "MapWidget", map)
|
setattr(self, key + "widg", map)
|
||||||
setattr(self, key + "MapLayout", lay)
|
setattr(self, key + "layout", lay)
|
||||||
setattr(self, key + "Combos", combos)
|
setattr(self, key + "combos", combos)
|
||||||
setattr(self, key + "Indices", indices)
|
setattr(self, key + "indices", indices)
|
||||||
|
|
||||||
def rebuildFieldMap(self):
|
def rebuildFieldMap(self):
|
||||||
return self.rebuildTemplateMap(key="field", attr="fieldModels")
|
return self.rebuildTemplateMap(key="f", attr="fields")
|
||||||
|
|
||||||
def onComboChanged(self, i, cb, key):
|
def onComboChanged(self, i, cb, key):
|
||||||
indices = getattr(self, key + "Indices")
|
indices = getattr(self, key + "indices")
|
||||||
if self.pauseUpdate:
|
if self.pauseUpdate:
|
||||||
indices[cb] = i
|
indices[cb] = i
|
||||||
return
|
return
|
||||||
combos = getattr(self, key + "Combos")
|
combos = getattr(self, key + "combos")
|
||||||
if i == cb.count() - 1:
|
if i == cb.count() - 1:
|
||||||
# set to 'nothing'
|
# set to 'nothing'
|
||||||
return
|
return
|
||||||
|
@ -1279,55 +1263,56 @@ class ChangeModelDialog(QDialog):
|
||||||
|
|
||||||
def getTemplateMap(self, old=None, combos=None, new=None):
|
def getTemplateMap(self, old=None, combos=None, new=None):
|
||||||
if not old:
|
if not old:
|
||||||
old = self.oldModel.cardModels
|
old = self.oldModel.templates
|
||||||
combos = self.templateCombos
|
combos = self.tcombos
|
||||||
new = self.targetModel.cardModels
|
new = self.targetModel.templates
|
||||||
map = {}
|
map = {}
|
||||||
for i, f in enumerate(old):
|
for i, f in enumerate(old):
|
||||||
idx = combos[i].currentIndex()
|
idx = combos[i].currentIndex()
|
||||||
if idx == len(new):
|
if idx == len(new):
|
||||||
# ignore
|
# ignore
|
||||||
map[f] = None
|
map[f['ord']] = None
|
||||||
else:
|
else:
|
||||||
f2 = new[idx]
|
f2 = new[idx]
|
||||||
if f2 in map.values():
|
map[f['ord']] = f2['ord']
|
||||||
return None
|
|
||||||
map[f] = f2
|
|
||||||
return map
|
return map
|
||||||
|
|
||||||
def getFieldMap(self):
|
def getFieldMap(self):
|
||||||
return self.getTemplateMap(
|
return self.getTemplateMap(
|
||||||
old=self.oldModel.fieldModels,
|
old=self.oldModel.fields,
|
||||||
combos=self.fieldCombos,
|
combos=self.fcombos,
|
||||||
new=self.targetModel.fieldModels)
|
new=self.targetModel.fields)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
removeHook("reset", self.onReset)
|
||||||
|
self.oldCurrentModel = self.browser.deck.conf['currentModelId']
|
||||||
|
self.modelChooser.cleanup()
|
||||||
|
saveGeom(self, "changeModel")
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
self.parent.deck.currentModel = self.origModel
|
self.cleanup()
|
||||||
self.modelChooser.deinit()
|
|
||||||
return QDialog.reject(self)
|
return QDialog.reject(self)
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
saveGeom(self, "changeModel")
|
|
||||||
self.parent.deck.currentModel = self.origModel
|
|
||||||
# check maps
|
# check maps
|
||||||
fmap = self.getFieldMap()
|
fmap = self.getFieldMap()
|
||||||
cmap = self.getTemplateMap()
|
cmap = self.getTemplateMap()
|
||||||
if not cmap or (self.targetModel != self.oldModel and
|
if any(True for c in cmap.values() if c is None):
|
||||||
not fmap):
|
if not askUser(_("""\
|
||||||
ui.utils.showInfo(
|
Any cards with templates mapped to nothing will be deleted. \
|
||||||
_("Targets must be unique."), parent=self)
|
If a fact has no remaining cards, it will be lost. \
|
||||||
return
|
Are you sure you want to continue?""")):
|
||||||
if [c for c in cmap.values() if not c]:
|
|
||||||
if not ui.utils.askUser(_("""\
|
|
||||||
Any cards with templates mapped to nothing will be deleted.
|
|
||||||
If a fact has no remaining cards, it will be lost.
|
|
||||||
Are you sure you want to continue?"""), parent=self):
|
|
||||||
return
|
return
|
||||||
self.modelChooser.deinit()
|
self.browser.mw.checkpoint(_("Change Model"))
|
||||||
if self.targetModel == self.oldModel:
|
b = self.browser
|
||||||
self.ret = (self.targetModel, None, cmap)
|
b.mw.progress.start()
|
||||||
return QDialog.accept(self)
|
b.model.beginReset()
|
||||||
self.ret = (self.targetModel, fmap, cmap)
|
self.oldModel.changeModel(self.fids, self.targetModel, fmap, cmap)
|
||||||
|
b.onSearch(reset=False)
|
||||||
|
b.model.endReset()
|
||||||
|
b.mw.progress.finish()
|
||||||
|
b.resetDeck()
|
||||||
|
self.cleanup()
|
||||||
return QDialog.accept(self)
|
return QDialog.accept(self)
|
||||||
|
|
||||||
def onHelp(self):
|
def onHelp(self):
|
||||||
|
|
|
@ -444,6 +444,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Change &Model...</string>
|
<string>Change &Model...</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+Shift+M</string>
|
||||||
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionSelectFacts">
|
<action name="actionSelectFacts">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>294</width>
|
<width>362</width>
|
||||||
<height>331</height>
|
<height>391</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="templateMap">
|
<widget class="QGroupBox" name="tgroup">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -75,10 +75,32 @@
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Templates</string>
|
<string>Templates</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea">
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="templateMap">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>330</width>
|
||||||
|
<height>120</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="fieldMap">
|
<widget class="QGroupBox" name="fgroup">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -88,6 +110,28 @@
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Fields</string>
|
<string>Fields</string>
|
||||||
</property>
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QScrollArea" name="scrollArea_2">
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="fieldMap">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>330</width>
|
||||||
|
<height>119</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
Loading…
Reference in a new issue