From f3e5f5d9e1a01012e0b5fd6767c02aee21e7f1e9 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 28 Nov 2008 14:43:26 +0900 Subject: [PATCH] convert editor into main window wip, sort by ease --- ankiqt/ui/cardlist.py | 342 +++++++++++------------------ designer/cardlist.ui | 492 +++++++++++++++++++++--------------------- 2 files changed, 373 insertions(+), 461 deletions(-) diff --git a/ankiqt/ui/cardlist.py b/ankiqt/ui/cardlist.py index a59001d91..15ed2331a 100644 --- a/ankiqt/ui/cardlist.py +++ b/ankiqt/ui/cardlist.py @@ -15,6 +15,7 @@ from anki.utils import fmtTimeSpan, parseTags, findTag, addTags, deleteTags, \ from ankiqt.ui.utils import saveGeom, restoreGeom from anki.errors import * from anki.db import * +from anki.stats import CardStats # Deck editor ########################################################################## @@ -126,14 +127,9 @@ class DeckModel(QAbstractTableModel): ads = " and ".join(ads) if isinstance(self.sortKey, types.StringType): # card property - if self.sortKey == "ease": - sort = ("order by cards.reps = 0, " - "cards.noCount / (cards.reps + 0.001) desc, " - "cards.reps") - else: - sort = "order by cards." + self.sortKey - if self.sortKey in ("question", "answer"): - sort += " collate nocase" + sort = "order by cards." + self.sortKey + if self.sortKey in ("question", "answer"): + sort += " collate nocase" query = ("select id, priority, question, answer, due, " "reps, factId from cards ") if ads: @@ -222,7 +218,7 @@ class StatusDelegate(QItemDelegate): self.drawDisplay(painter, option, option.rect, data.toString()) painter.restore() -class EditDeck(QDialog): +class EditDeck(QMainWindow): def __init__(self, parent): QDialog.__init__(self, parent, Qt.Window) @@ -230,7 +226,7 @@ class EditDeck(QDialog): self.deck = self.parent.deck self.config = parent.config self.origModTime = parent.deck.modified - self.dialog = ankiqt.forms.cardlist.Ui_EditDeck() + self.dialog = ankiqt.forms.cardlist.Ui_MainWindow() self.dialog.setupUi(self) # flush all changes before we load self.deck.s.flush() @@ -242,7 +238,7 @@ class EditDeck(QDialog): self.dialog.tableView.setFont(QFont( self.config['editFontFamily'], self.config['editFontSize'])) - self.setupButtons() + self.setupMenus() self.setupFilter() self.setupSort() self.setupHeaders() @@ -317,7 +313,7 @@ class EditDeck(QDialog): _("Due date"), _("Interval"), _("Answer count"), - _("Difficulty"), + _("Ease"), ] self.sortFields = sorted(self.deck.allFields()) self.sortList.extend([_("Field '%s'") % f for f in self.sortFields]) @@ -341,7 +337,7 @@ class EditDeck(QDialog): elif idx == 6: self.sortKey = "reps" elif idx == 7: - self.sortKey = "ease" + self.sortKey = "factor" else: self.sortKey = ("field", self.sortFields[idx-8]) self.sortIndex = idx @@ -409,71 +405,99 @@ class EditDeck(QDialog): self.dialog.tableView.horizontalHeader().setResizeMode(i, QHeaderView.Stretch) self.dialog.tableView.horizontalHeader().setResizeMode(2, QHeaderView.ResizeToContents) - def setupButtons(self): - # buttons - self.connect(self.dialog.factsButton, - SIGNAL("clicked()"), - self.factsMenu) - self.connect(self.dialog.cardsButton, - SIGNAL("clicked()"), - self.cardsMenu) - # menus - self.connect(self.dialog.action_Delete_card, SIGNAL("triggered()"), self.deleteCards) - self.connect(self.dialog.actionAdd_fact_tag, SIGNAL("triggered()"), self.addFactTags) - self.connect(self.dialog.actionAdd_card_tag, SIGNAL("triggered()"), self.addCardTags) - self.connect(self.dialog.actionDelete_fact_tag, SIGNAL("triggered()"), self.deleteFactTags) - self.connect(self.dialog.actionDelete_card_tag, SIGNAL("triggered()"), self.deleteCardTags) - self.connect(self.dialog.actionAdd_Missing_Cards, SIGNAL("triggered()"), self.addMissingCards) - self.connect(self.dialog.actionDelete_Fact, SIGNAL("triggered()"), self.deleteFacts) - self.connect(self.dialog.actionResetCardProgress, SIGNAL("triggered()"), self.resetCardProgress) - self.connect(self.dialog.actionResetFactProgress, SIGNAL("triggered()"), self.resetFactProgress) - self.parent.runHook('editor.setupButtons', self) + def setupMenus(self): + self.connect(self.dialog.actionDelete, SIGNAL("triggered()"), self.deleteCards) + self.connect(self.dialog.actionAddTag, SIGNAL("triggered()"), self.addTags) + self.connect(self.dialog.actionDeleteTag, SIGNAL("triggered()"), self.deleteTags) + self.connect(self.dialog.actionAddCards, SIGNAL("triggered()"), self.addCards) + self.connect(self.dialog.actionResetProgress, SIGNAL("triggered()"), self.resetProgress) + self.connect(self.dialog.actionSelectAll, SIGNAL("triggered()"), self.selectAll) + self.connect(self.dialog.actionSelectFacts, SIGNAL("triggered()"), self.selectFacts) + self.parent.runHook('editor.setupMenus', self) - def factsMenu(self): - menu = QMenu(self) - menu.addAction(self.dialog.actionAdd_fact_tag) - menu.addAction(self.dialog.actionDelete_fact_tag) - menu.addSeparator() - menu.addAction(self.dialog.actionAdd_Missing_Cards) - menu.addSeparator() - menu.addAction(self.dialog.actionResetFactProgress) - menu.addAction(self.dialog.actionDelete_Fact) - self.parent.runHook('editor.factsMenu', self, menu) - menu.exec_(self.dialog.factsButton.mapToGlobal(QPoint(0,0))) + def onClose(self): + saveGeom(self, "editor") + self.editor.saveFieldsNow() + if not self.factValid: + ui.utils.showInfo(_( + "Some fields are missing or not unique."), + parent=self, help="AddItems#AddError") + return + self.hide() + self.deck.deleteCards(self.model.deleted.keys()) + if len(self.model.deleted): + self.parent.setStatus( + _("%(del)d deleted.") % + {"del": len(self.model.deleted)}) + if self.origModTime != self.deck.modified: + self.parent.reset() + ui.dialogs.close("CardList") + return True - def cardsMenu(self): - menu = QMenu(self) - menu.addAction(self.dialog.actionAdd_card_tag) - menu.addAction(self.dialog.actionDelete_card_tag) - menu.addSeparator() - menu.addAction(self.dialog.actionResetCardProgress) - menu.addAction(self.dialog.action_Delete_card) - self.parent.runHook('editor.cardsMenu', self, menu) - menu.exec_(self.dialog.cardsButton.mapToGlobal(QPoint(0,0))) + def closeEvent(self, evt): + if self.onClose(): + evt.accept() + else: + evt.ignore() - def deleteCards(self): - cards = self.selectedCards() - self.dialog.tableView.selectionModel().blockSignals(True) - self.dialog.tableView.selectionModel().clear() - self.dialog.tableView.selectionModel().blockSignals(False) - for id in cards: - if id in self.model.deleted: - del self.model.deleted[id] - else: - self.model.deleted[id] = True - self.model.emit(SIGNAL("layoutChanged()")) + def keyPressEvent(self, evt): + "Show answer on RET or register answer." + if evt.key() in (Qt.Key_Escape,): + self.close() - def deleteFacts(self): - cardIds = self.selectedFactsAsCards() - self.dialog.tableView.selectionModel().blockSignals(True) - self.dialog.tableView.selectionModel().clear() - self.dialog.tableView.selectionModel().blockSignals(False) - for id in cardIds: - if id in self.model.deleted: - del self.model.deleted[id] - else: - self.model.deleted[id] = True - self.model.emit(SIGNAL("layoutChanged()")) + # Editor + ###################################################################### + + def setupEditor(self): + self.editor = ui.facteditor.FactEditor(self, + self.dialog.fieldsArea, + self.deck) + self.factValid = True + self.editor.onFactValid = self.onFactValid + self.editor.onFactInvalid = self.onFactInvalid + self.connect(self.dialog.tableView.selectionModel(), + SIGNAL("currentRowChanged(QModelIndex, QModelIndex)"), + self.rowChanged) + + def onFactValid(self, fact): + self.factValid = True + self.dialog.tableView.setEnabled(True) + self.dialog.filterEdit.setEnabled(True) + self.dialog.sortBox.setEnabled(True) + self.dialog.tagList.setEnabled(True) + self.dialog.menubar.setEnabled(True) + self.dialog.cardInfoGroup.setEnabled(True) + + def onFactInvalid(self, fact): + self.factValid = False + self.dialog.tableView.setEnabled(False) + self.dialog.filterEdit.setEnabled(False) + self.dialog.sortBox.setEnabled(False) + self.dialog.tagList.setEnabled(False) + self.dialog.menubar.setEnabled(False) + self.dialog.cardInfoGroup.setEnabled(False) + + def rowChanged(self, current, previous): + self.currentRow = current + self.currentCard = self.model.getCard(current) + self.deck.s.flush() + self.deck.s.refresh(self.currentCard) + self.deck.s.refresh(self.currentCard.fact) + fact = self.currentCard.fact + self.editor.setFact(fact, True) + self.showCardInfo(self.currentCard) + + def setupCardInfo(self): + self.currentCard = None + self.cardStats = CardStats(self.deck, None) + + def showCardInfo(self, card): + self.cardStats.card = self.currentCard + self.dialog.cardLabel.setText( + self.cardStats.report()) + + # Menu helpers + ###################################################################### def selectedCards(self): return [self.model.cards[idx.row()][0] for idx in @@ -491,158 +515,42 @@ where id in (%s)""" % ",".join([ "select id from cards where factId in (%s)" % ",".join([str(s) for s in self.selectedFacts()])) - def setupEditor(self): - self.editor = ui.facteditor.FactEditor(self, - self.dialog.fieldsArea, - self.deck) - self.editor.onFactValid = self.onFactValid - self.editor.onFactInvalid = self.onFactInvalid - self.connect(self.dialog.tableView.selectionModel(), - SIGNAL("currentRowChanged(QModelIndex, QModelIndex)"), - self.rowChanged) - - def onFactValid(self, fact): - self.dialog.tableView.setEnabled(True) - self.dialog.searchGroup.setEnabled(True) - self.dialog.sortGroup.setEnabled(True) - self.dialog.actionGroup.setEnabled(True) - self.dialog.cardInfoGroup.setEnabled(True) - - def onFactInvalid(self, fact): - self.dialog.tableView.setEnabled(False) - self.dialog.searchGroup.setEnabled(False) - self.dialog.sortGroup.setEnabled(False) - self.dialog.actionGroup.setEnabled(False) - self.dialog.cardInfoGroup.setEnabled(False) - - def rowChanged(self, current, previous): - self.currentRow = current - self.currentCard = self.model.getCard(current) - self.deck.s.flush() - self.deck.s.refresh(self.currentCard) - self.deck.s.refresh(self.currentCard.fact) - fact = self.currentCard.fact - self.editor.setFact(fact, True) - self.showCardInfo(self.currentCard) - - def setupCardInfo(self): - self.currentCard = None - self.cardInfoGrid = QGridLayout(self.dialog.cardInfoGroup) - self.cardInfoGrid.setMargin(6) - # card - l = QLabel(_("Tags")) - self.cardInfoGrid.addWidget(l, 0, 0) - self.cardStaticTags = QLabel() - self.cardStaticTags.setWordWrap(True) - self.cardInfoGrid.addWidget(self.cardStaticTags, 1, 0) - l = QLabel(_("Card-specific tags")) - self.cardInfoGrid.addWidget(l, 2, 0) - self.cardTags = QLineEdit() - self.connect(self.cardTags, SIGNAL("textChanged(QString)"), self.saveCardInfo) - self.cardInfoGrid.addWidget(self.cardTags, 3, 0) - l = QLabel(_("Statistics")) - self.cardInfoGrid.addWidget(l, 4, 0) - self.cardStats = QLabel() - self.cardInfoGrid.addWidget(self.cardStats, 5, 0) - item = QSpacerItem(20, 20, QSizePolicy.Expanding, - QSizePolicy.Expanding) - self.cardInfoGrid.addItem(item, 6, 0) - - def updateStaticTags(self): - card = self.currentCard - self.cardStaticTags.setText( - ", ".join(parseTags( - card.fact.model.tags + "," + - card.cardModel.name + "," + - card.fact.tags))) - - def showCardInfo(self, card): - self.cardTags.setText(card.tags) - self.updateStaticTags() - # stats - next = time.time() - card.due - if next > 0: - next = "%s ago" % anki.utils.fmtTimeSpan(next) - else: - next = "in %s" % anki.utils.fmtTimeSpan(abs(next)) - self.cardStats.setText( - _("Created: %(c)s ago
" - "Next due: %(n)s
" - "Interval: %(i)0.0f days
" - "Average: %(a)s
" - "Total: %(t)s
" - "Reviews: %(cor)d/%(tot)d
" - "Successive: %(suc)d")% { - "c": fmtTimeSpan(time.time() - card.created), - "n": next, - "i": card.interval, - "a": fmtTimeSpan(card.averageTime), - "t": fmtTimeSpan(card.reviewTime), - "cor": card.yesCount, - "tot": card.reps, - "suc": card.successive, - }) - - def saveCardInfo(self, text): - if self.currentCard: - tags = unicode(text) - if self.currentCard.tags != tags: - self.currentCard.tags = tags - self.currentCard.setModified() - self.deck.setModified() - - def addFactTags(self): - tags = ui.utils.getOnlyText(_("Enter tag(s) to add to each fact:"), self) - if tags: self.deck.addFactTags(self.selectedFacts(), tags) - self.updateAfterCardChange() - - def addCardTags(self): - tags = ui.utils.getOnlyText(_("Enter tag(s) to add to each card:"), self) - if tags: self.deck.addCardTags(self.selectedCards(), tags) - self.updateAfterCardChange() - - def deleteFactTags(self): - tags = ui.utils.getOnlyText(_("Enter tag(s) to delete from each fact:"), self) - if tags: self.deck.deleteFactTags(self.selectedFacts(), tags) - self.updateAfterCardChange() - - def deleteCardTags(self): - tags = ui.utils.getOnlyText(_("Enter tag(s) to delete from each card:"), self) - if tags: self.deck.deleteCardTags(self.selectedCards(), tags) - self.updateAfterCardChange() - def updateAfterCardChange(self, reset=False): "Refresh info like stats on current card" self.rowChanged(self.currentRow, None) if reset: self.updateSearch() - def addMissingCards(self): - for id in self.selectedFacts(): - self.deck.addMissingCards(self.deck.s.query(Fact).get(id)) + # Menu options + ###################################################################### + + def deleteCards(self): + cards = self.selectedCards() + self.deck.deleteCards(cards) self.updateSearch() - def resetCardProgress(self): + def addTags(self): + tags = ui.utils.getOnlyText(_("Enter tag(s) to add:"), self) + if tags: self.deck.addTags(self.selectedFacts(), tags) + self.updateAfterCardChange() + + def deleteTags(self): + tags = ui.utils.getOnlyText(_("Enter tag(s) to delete:"), self) + if tags: self.deck.deleteTags(self.selectedFacts(), tags) + self.updateAfterCardChange() + + def addCards(self): + raise +# for id in self.selectedFacts(): +# self.deck.addMissingCards(self.deck.s.query(Fact).get(id)) +# self.updateSearch() + + def resetProgress(self): self.deck.resetCards(self.selectedCards()) self.updateAfterCardChange(reset=True) - def resetFactProgress(self): - self.deck.resetCards(self.selectedFactsAsCards()) - self.updateAfterCardChange(reset=True) + def selectAll(self): + pass - def accept(self): - self.editor.saveFieldsNow() - self.hide() - self.deck.deleteCards(self.model.deleted.keys()) - if len(self.model.deleted): - self.parent.setStatus( - _("%(del)d deleted.") % - {"del": len(self.model.deleted)}) - if self.origModTime != self.deck.modified: - self.parent.reset() - ui.dialogs.close("CardList") - QDialog.accept(self) - - def reject(self): - saveGeom(self, "editor") - self.accept() + def selectFacts(self): + pass diff --git a/designer/cardlist.ui b/designer/cardlist.ui index 3701f04e7..0941f160b 100644 --- a/designer/cardlist.ui +++ b/designer/cardlist.ui @@ -1,245 +1,241 @@ - EditDeck - - - Qt::NonModal - + MainWindow + 0 0 - 414 - 434 + 599 + 602 - Anki - Edit Items + Edit Items - - - :/icons/view_text.png:/icons/view_text.png - - - - 3 + + + + 0 + 23 + 599 + 559 + - - 3 - - - - - 6 - - - 0 - - - + + + 0 + + + 4 + + + 0 + + + 4 + + + 4 + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 4 + + + + + + + + + + + + + + + + + + + + Qt::Vertical + + - + 0 - 0 + 4 - - &Search + + Qt::ActionsContextMenu - + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + QAbstractItemView::SelectRows + + + + + + 0 + 2 + + + + QFrame::NoFrame + + + QFrame::Raised + + - 3 + 0 - 3 + 0 - - - + + + 0 - - - - + + 6 + + + + + + 7 + 2 + + + + + + + true + + + + + + + Current Card + + + + 0 + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + - - - - - S&ort - - - - 3 - - - 3 - - - - - - - - - - - Change - - - - 3 - - - 3 - - - - - Facts... - - - false - - - - - - - Cards... - - - false - - - - - - - - - - - - Qt::Vertical - - - - - 0 - 4 - - - - Qt::ActionsContextMenu - - - QFrame::NoFrame - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - QAbstractItemView::SelectRows - - - - - 0 - 2 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - - - 0 - - - 6 - - - - - - 2 - 2 - - - - - 0 - 300 - - - - Current Card - - - false - - - - - - - - 7 - 2 - - - - - - - true - - - - - - - - - - - + + + + + + + 0 + 0 + 599 + 23 + + + + + &Edit + + + + + + + &Actions + + + + + + + + + + + + + + + + + + 0 + 582 + 599 + 20 + + + + :/icons/editdelete.png:/icons/editdelete.png - Toggle Delete + Delete + + + Ctrl+Del - + :/icons/Anki_Add_Tag.png:/icons/Anki_Add_Tag.png @@ -248,16 +244,7 @@ Add Tag... - - - - :/icons/Anki_Add_Tag.png:/icons/Anki_Add_Tag.png - - - Add Tag... - - - + :/icons/Anki_Del_Tag.png:/icons/Anki_Del_Tag.png @@ -266,49 +253,66 @@ Delete Tag... - - - - :/icons/Anki_Del_Tag.png:/icons/Anki_Del_Tag.png - - - Delete Tag... - - - + :/icons/Anki_Card.png:/icons/Anki_Card.png - Add Missing Active Cards + Add Cards... - + - :/icons/editdelete.png:/icons/editdelete.png + :/icons/edit-undo.png:/icons/edit-undo.png - - Toggle Delete - - - Reset Progress - + + + + :/icons/fileclose.png:/icons/fileclose.png + - Reset Progress + &Close + + + + + Select &All + + + Ctrl+A + + + + + Select &Facts - - fieldsArea - - + + + actionSelectAll + triggered() + tableView + selectAll() + + + -1 + -1 + + + 299 + 279 + + + +