modelchooser and card adding

This commit is contained in:
Damien Elmes 2011-04-06 23:14:34 +09:00
parent 5b7daf5060
commit 4d694f0064
7 changed files with 188 additions and 236 deletions

View file

@ -9,16 +9,16 @@ import anki
from anki.facts import Fact
from anki.errors import *
from anki.utils import stripHTML, parseTags
from aqt.utils import saveGeom, restoreGeom
from aqt.utils import saveGeom, restoreGeom, showWarning, askUser
from anki.sound import clearAudioQueue
from anki.hooks import addHook, removeHook
import aqt.editor, aqt.modelchooser
class FocusButton(QPushButton):
def focusInEvent(self, evt):
if evt.reason() == Qt.TabFocusReason:
self.emit(SIGNAL("tabIn"))
QPushButton.focusInEvent(self, evt)
# todo:
# if field.fieldModel.features:
# w.setLayoutDirection(Qt.RightToLeft)
# else:
# w.setLayoutDirection(Qt.LeftToRight)
class AddCards(QDialog):
@ -26,39 +26,37 @@ class AddCards(QDialog):
windParent = None
QDialog.__init__(self, windParent, Qt.Window)
self.mw = mw
self.dialog = aqt.forms.addcards.Ui_Dialog()
self.dialog.setupUi(self)
self.form = aqt.forms.addcards.Ui_Dialog()
self.form.setupUi(self)
self.setWindowTitle(_("Add"))
self.setupEditor()
self.addChooser()
self.addButtons()
self.modelChanged()
self.setupChooser()
self.setupButtons()
self.onReset()
self.addedItems = 0
self.forceClose = False
restoreGeom(self, "add")
addHook('reset', self.onReset)
self.setupNewFact()
self.show()
addHook('guiReset', self.modelChanged)
def setupEditor(self):
self.editor = aqt.editor.Editor(self.mw, self.dialog.fieldsArea)
self.editor = aqt.editor.Editor(self.mw, self.form.fieldsArea)
# get a fact for testing
fact = self.mw.deck.getFact(3951)
self.editor.setFact(fact)
#fact = self.mw.deck.getFact(3951)
#self.editor.setFact(fact)
def addChooser(self):
return
self.modelChooser = aqt.modelchooser.ModelChooser(self,
self.mw,
self.mw.deck,
self.modelChanged)
self.dialog.modelArea.setLayout(self.modelChooser)
def setupChooser(self):
self.modelChooser = aqt.modelchooser.ModelChooser(
self.mw, self.form.modelArea)
# modelChanged func
def helpRequested(self):
aqt.openHelp("AddItems")
def addButtons(self):
def setupButtons(self):
self.addButton = QPushButton(_("Add"))
self.dialog.buttonBox.addButton(self.addButton,
self.form.buttonBox.addButton(self.addButton,
QDialogButtonBox.ActionRole)
self.addButton.setShortcut(_("Ctrl+Return"))
if sys.platform.startswith("darwin"):
@ -70,11 +68,11 @@ class AddCards(QDialog):
self.connect(self.addButton, SIGNAL("clicked()"), self.addCards)
self.closeButton = QPushButton(_("Close"))
self.closeButton.setAutoDefault(False)
self.dialog.buttonBox.addButton(self.closeButton,
self.form.buttonBox.addButton(self.closeButton,
QDialogButtonBox.RejectRole)
self.helpButton = QPushButton(_("Help"))
self.helpButton.setAutoDefault(False)
self.dialog.buttonBox.addButton(self.helpButton,
self.form.buttonBox.addButton(self.helpButton,
QDialogButtonBox.HelpRole)
self.connect(self.helpButton, SIGNAL("clicked()"), self.helpRequested)
@ -84,31 +82,41 @@ class AddCards(QDialog):
browser.updateSearch()
browser.onFact()
def modelChanged(self, model=None):
return
# FIXME: need to make sure to clean up fact on exit
def setupNewFact(self, set=True):
f = self.mw.deck.newFact()
if self.editor.fact:
# keep tags?
f.tags = self.editor.fact.tags
if set:
self.editor.setFact(f)
return f
def onReset(self, model=None):
oldFact = self.editor.fact
# create a new fact
fact = self.mw.deck.newFact()
fact = self.setupNewFact(set=False)
# copy fields from old fact
if oldFact:
n = 0
for field in fact.fields:
self.removeTempFact(oldFact)
for n in range(len(fact._fields)):
try:
field.value = oldFact.fields[n].value
fact._fields[n] = oldFact._fields[n]
except IndexError:
break
n += 1
fact.tags = oldFact.tags
else:
fact.tags = "last" #self.mw.deck.lastTags
# set the new fact
self.editor.setFact(fact, check=True, forceRedraw=True)
self.setTabOrder(self.editor.tags, self.addButton)
self.setTabOrder(self.addButton, self.closeButton)
self.setTabOrder(self.closeButton, self.helpButton)
self.editor.setFact(fact)
#self.setTabOrder(self.editor.tags, self.addButton)
#self.setTabOrder(self.addButton, self.closeButton)
#self.setTabOrder(self.closeButton, self.helpButton)
def removeTempFact(self, fact):
if not fact:
return
# we don't have to worry about cards; just the fact
self.mw.deck._delFacts([fact.id])
def reportAddedFact(self, fact):
self.dialog.status.append(
return
self.form.status.append(
_("Added %(num)d card(s) for <a href=\"%(id)d\">"
"%(str)s</a>.") % {
"num": len(fact.cards),
@ -118,57 +126,31 @@ class AddCards(QDialog):
})
def addFact(self, fact):
try:
fact = self.mw.deck.addFact(fact, False)
except FactInvalidError:
ui.utils.showInfo(_(
if any(fact.problems()):
showWarning(_(
"Some fields are missing or not unique."),
parent=self, help="AddItems#AddError")
help="AddItems#AddError")
return
if not fact:
ui.utils.showWarning(_("""\
cards = self.mw.deck.addFact(fact)
if not cards:
showWarning(_("""\
The input you have provided would make an empty
question or answer on all cards."""), parent=self)
question or answer on all cards."""), help="AddItems")
return
self.reportAddedFact(fact)
# we don't reset() until the add cards dialog is closed
# FIXME: return to overview on add?
return fact
def initializeNewFact(self, old_fact):
f = self.mw.deck.newFact()
f.tags = self.mw.deck.lastTags
return f
def clearOldFact(self, old_fact):
f = self.initializeNewFact(old_fact)
self.editor.setFact(f, check=True, scroll=True)
# let completer know our extra tags
self.editor.tags.addTags(parseTags(self.mw.deck.lastTags))
return f
def addCards(self):
# make sure updated
self.editor.saveFieldsNow()
self.editor.saveNow()
fact = self.editor.fact
n = _("Add")
self.mw.deck.setUndoStart(n)
fact = self.addFact(fact)
if not fact:
return
# stop anything playing
clearAudioQueue()
self.mw.deck.setUndoEnd(n)
self.mw.deck.rebuildCounts()
self.mw.updateTitleBar()
# start a new fact
self.clearOldFact(fact)
self.mw.autosave()
self.setupNewFact()
self.mw.deck.autosave()
def keyPressEvent(self, evt):
"Show answer on RET or register answer."
@ -178,37 +160,20 @@ question or answer on all cards."""), parent=self)
return
return QDialog.keyPressEvent(self, evt)
def closeEvent(self, evt):
if self.onClose():
evt.accept()
else:
evt.ignore()
def reject(self):
# remove dupe
if not self.canClose():
return
clearAudioQueue()
self.removeTempFact(self.editor.fact)
self.editor.setFact(None)
self.modelChooser.cleanup()
removeHook('reset', self.onReset)
saveGeom(self, "add")
aqt.dialogs.close("AddCards")
QDialog.reject(self)
if self.onClose():
self.modelChooser.deinit()
QDialog.reject(self)
def onClose(self):
return
removeHook('guiReset', self.modelChanged)
# stop anything playing
clearAudioQueue()
def canClose(self):
if (self.forceClose or self.editor.fieldsAreBlank() or
ui.utils.askUser(_("Close and lose current input?"),
self)):
self.modelChooser.deinit()
self.editor.close()
ui.dialogs.close("AddCards")
self.mw.deck.db.flush()
self.mw.deck.rebuildCSS()
self.mw.reset()
saveGeom(self, "add")
askUser(_("Close and lose current input?"))):
return True
else:
return False

View file

@ -13,6 +13,8 @@ from aqt.utils import saveGeom, restoreGeom, getBase, mungeQA, \
getText
import aqt.templates
# fixme: replace font substitutions with native comma list
class ResizingTextEdit(QTextEdit):
def sizeHint(self):
return QSize(200, 800)

View file

@ -15,13 +15,6 @@ from aqt.utils import shortcut, showInfo, showWarning, getBase, getFile
import aqt
import anki.js
# todo:
# if field.fieldModel.features:
# w.setLayoutDirection(Qt.RightToLeft)
# else:
# w.setLayoutDirection(Qt.LeftToRight)
pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif")
audio = ("wav", "mp3", "ogg", "flac")
@ -175,6 +168,7 @@ $(function () {
</body></html>
"""
# caller is responsible for resetting fact on reset
class Editor(object):
def __init__(self, mw, widget):
self.widget = widget
@ -184,20 +178,12 @@ class Editor(object):
self._keepButtons = False
# current card, for card layout
self.card = None
addHook("deckClosed", self.deckClosedHook)
addHook("guiReset", self.refresh)
addHook("colourChanged", self.colourChanged)
self.setupOuter()
self.setupButtons()
self.setupWeb()
self.setupTagsAndGroup()
self.setupKeyboard()
def close(self):
removeHook("deckClosed", self.deckClosedHook)
removeHook("guiReset", self.refresh)
removeHook("colourChanged", self.colourChanged)
# Initial setup
############################################################
@ -380,15 +366,6 @@ class Editor(object):
return [(f['font'], f['esize'])
for f in self.fact.model().fields]
def refresh(self):
if self.fact:
self.fact.load()
# fixme: what if fact is deleted?
self.setFact(self.fact)
def deckClosedHook(self):
self.setFact(None)
def saveNow(self):
"Must call this before adding cards, closing dialog, etc."
if not self.fact:
@ -409,6 +386,14 @@ class Editor(object):
cols.append("#ffc")
self.web.eval("setBackgrounds(%s);" % simplejson.dumps(cols))
def fieldsAreBlank(self):
if not self.fact:
return True
for f in self.fact._fields:
if f:
return False
return True
# HTML editing
######################################################################
@ -592,7 +577,7 @@ class Editor(object):
recent.append("#000000")
self.colourDiag.close()
self.onForeground()
runHook("colourChanged")
self.colourChanged()
def onNextColour(self):
self.colourDiag.focusWidget().nextInFocusChain().setFocus()
@ -610,7 +595,7 @@ class Editor(object):
recent.append(colour)
self.web.eval("setFormat('forecolor', '%s')" % colour)
self.colourDiag.close()
runHook("colourChanged")
self.colourChanged()
def onNewColour(self):
new = QColorDialog.getColor(Qt.white, self.widget)
@ -620,7 +605,7 @@ class Editor(object):
txtcol = unicode(new.name())
if txtcol not in recent:
recent.append(txtcol)
runHook("colourChanged")
self.colourChanged()
self.onChooseColour(txtcol)
# Audio/video/images

View file

@ -113,7 +113,12 @@ class AnkiQt(QMainWindow):
def _editCurrentState(self, oldState):
pass
def reset(self):
def factChanged(self, fid):
"Called when a card or fact is edited (but not deleted)."
runHook("factChanged", fid)
def reset(self, type="all", *args):
"Called for non-trivial edits. Rebuilds queue."
self.deck.reset()
runHook("reset")

View file

@ -4,38 +4,42 @@
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from operator import attrgetter
import anki, sys
from anki import stdmodels
from anki.models import *
import aqt.forms
from anki.lang import ngettext
from anki.hooks import addHook, removeHook
from aqt.utils import isMac
class ModelChooser(QHBoxLayout):
def __init__(self, parent, main, deck, onChangeFunc=None, cards=True, label=True):
def __init__(self, mw, widget, cards=True, label=True):
QHBoxLayout.__init__(self)
self.parent = parent
self.main = main
self.deck = deck
self.onChangeFunc = onChangeFunc
self.widget = widget
self.mw = mw
self.deck = mw.deck
self.handleCards = cards
self.label = label
self._ignoreReset = False
self.setMargin(0)
self.setSpacing(4)
self.shortcuts = []
if label:
self.setupModels()
self.setupTemplates()
addHook('reset', self.onReset)
self.widget.setLayout(self)
def setupModels(self):
if self.label:
self.modelLabel = QLabel(_("<b>Model</b>:"))
self.addWidget(self.modelLabel)
# models dropdown
self.models = QComboBox()
s = QShortcut(QKeySequence(_("Shift+Alt+m")), self.parent)
s = QShortcut(QKeySequence(_("Shift+Alt+m")), self.widget)
s.connect(s, SIGNAL("activated()"),
lambda: self.models.showPopup())
self.drawModels()
sizePolicy = QSizePolicy(
QSizePolicy.Policy(7),
QSizePolicy.Policy(0))
self.models.setSizePolicy(sizePolicy)
self.addWidget(self.models)
self.connect(self.models, SIGNAL("activated(int)"), self.onModelChange)
# edit button
self.edit = QPushButton()
if not sys.platform.startswith("darwin"):
if not isMac:
self.edit.setFixedWidth(32)
self.edit.setIcon(QIcon(":/icons/configure.png"))
self.edit.setShortcut(_("Shift+Alt+e"))
@ -43,120 +47,105 @@ class ModelChooser(QHBoxLayout):
self.edit.setAutoDefault(False)
self.addWidget(self.edit)
self.connect(self.edit, SIGNAL("clicked()"), self.onEdit)
self.connect(self.models, SIGNAL("activated(int)"), self.onChange)
self.handleCards = False
if cards:
self.handleCards = True
label = QLabel(_("<b>Cards</b>:"))
self.addWidget(label)
# layout
sizePolicy = QSizePolicy(
QSizePolicy.Policy(7),
QSizePolicy.Policy(0))
self.models.setSizePolicy(sizePolicy)
self.updateModels()
def setupTemplates(self):
self.cardShortcuts = []
if self.handleCards:
self.cards = QPushButton()
self.cards.setAutoDefault(False)
self.connect(self.cards, SIGNAL("clicked()"), self.onCardChange)
self.addWidget(self.cards)
self.drawCardModels()
addHook('guiReset', self.onModelEdited)
self.updateTemplates()
def deinit(self):
removeHook('guiReset', self.onModelEdited)
def cleanup(self):
removeHook('reset', self.onReset)
def onReset(self):
if not self._ignoreReset:
self.updateModels()
self.updateTemplates()
def show(self):
for i in range(self.count()):
self.itemAt(i).widget().show()
self.widget.show()
def hide(self):
for i in range(self.count()):
self.itemAt(i).widget().hide()
self.widget.hide()
def onEdit(self):
ui.deckproperties.DeckProperties(self.parent, self.deck,
onFinish=self.onModelEdited)
import aqt.models
aqt.models.Models(self.mw, self.widget)
def onModelEdited(self):
# hack
from aqt import mw
self.deck = mw.deck
self.drawModels()
self.changed(self.deck.currentModel)
def onChange(self, idx):
def onModelChange(self, idx):
model = self._models[idx]
self.deck.currentModel = model
self.changed(self.deck.currentModel)
self.deck.setModified()
self.deck.conf['currentModelId'] = model.id
self.updateTemplates()
self._ignoreReset = True
self.mw.reset()
self._ignoreReset = False
def changed(self, model):
self.deck.addModel(model)
if self.onChangeFunc:
self.onChangeFunc(model)
self.drawCardModels()
def drawModels(self):
def updateModels(self):
self.models.clear()
self._models = sorted(self.deck.models().values(), key=attrgetter("name"))
self._models = sorted(self.deck.models().values(),
key=attrgetter("name"))
self.models.addItems(QStringList(
[m.name for m in self._models]))
idx = self._models.index(self.deck.currentModel())
self.models.setCurrentIndex(idx)
for c, m in enumerate(self._models):
if m.id == self.deck.conf['currentModelId']:
self.models.setCurrentIndex(c)
break
def drawCardModels(self):
def updateTemplates(self):
if not self.handleCards:
return
# remove any shortcuts
for s in self.shortcuts:
for s in self.cardShortcuts:
s.setEnabled(False)
self.shortcuts = []
m = self.deck.currentModel
txt = ", ".join([c.name for c in m.cardModels if c.active])
if len(txt) > 30:
txt = txt[0:30] + "..."
self.cardShortcuts = []
m = self.deck.currentModel()
ts = m.templates
active = [t['name'] for t in ts if t['actv']]
txt = ngettext("%d card", "%d cards", len(active)) % len(active)
self.cards.setText(txt)
n = 1
for c in m.cardModels:
s = QShortcut(QKeySequence("Ctrl+%d" % n), self.parent)
self.parent.connect(s, SIGNAL("activated()"),
lambda c=c: self.toggleCard(c))
self.shortcuts.append(s)
for t in ts:
s = QShortcut(QKeySequence("Ctrl+%d" % n), self.widget)
self.widget.connect(s, SIGNAL("activated()"),
lambda t=t: self.toggleTemplate(t))
self.cardShortcuts.append(s)
n += 1
def onCardChange(self):
m = QMenu(self.parent)
m.setTitle("menu")
model = self.deck.currentModel
for card in model.cardModels:
action = QAction(self.parent)
m = QMenu(self.widget)
model = self.deck.currentModel()
for template in model.templates:
action = QAction(self.widget)
action.setCheckable(True)
if card.active:
if template['actv']:
action.setChecked(True)
action.setText(card.name)
self.connect(action, SIGNAL("toggled(bool)"),
lambda b, a=action, c=card: \
self.cardChangeTriggered(b,a,c))
action.setText(template['name'])
self.connect(action, SIGNAL("activated()"),
lambda t=template: \
self.toggleTemplate(t))
m.addAction(action)
m.exec_(self.cards.mapToGlobal(QPoint(0,0)))
def cardChangeTriggered(self, bool, action, card):
if bool:
card.active = True
elif self.canDisableModel():
card.active = False
self.drawCardModels()
def canDisableTemplate(self):
return len([t for t in self.deck.currentModel().templates
if t['actv']]) > 1
def canDisableModel(self):
active = 0
model = self.deck.currentModel
for c in model.cardModels:
if c.active:
active += 1
if active > 1:
return True
return False
def toggleCard(self, card):
if not card.active:
card.active = True
elif self.canDisableModel():
card.active = False
self.drawCardModels()
def toggleTemplate(self, card):
if not card['actv']:
card['actv'] = True
elif self.canDisableTemplate():
card['actv'] = False
self.deck.currentModel().flush()
self.updateTemplates()
class AddModel(QDialog):

View file

@ -23,7 +23,6 @@ class Overview(object):
self.refresh()
def refresh(self):
print "refreshing"
self._renderPage()
# Handlers

View file

@ -45,7 +45,14 @@
<number>4</number>
</property>
<item row="0" column="0">
<widget class="QWidget" name="modelArea" native="true"/>
<widget class="QWidget" name="modelArea" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>10</height>
</size>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="Line" name="line">