diff --git a/ankiqt/config.py b/ankiqt/config.py index 2b3e6d26e..90f667a32 100644 --- a/ankiqt/config.py +++ b/ankiqt/config.py @@ -75,6 +75,7 @@ class Config(dict): 'showStudyOptions': False, 'showStudyStats': True, 'showCardTimer': True, + 'standaloneWindows': True, 'extraNewCards': 5, 'randomizeOnCram': True, 'created': time.time(), diff --git a/ankiqt/ui/addcards.py b/ankiqt/ui/addcards.py index 31f98e13c..40497f545 100644 --- a/ankiqt/ui/addcards.py +++ b/ankiqt/ui/addcards.py @@ -22,7 +22,11 @@ class FocusButton(QPushButton): class AddCards(QDialog): def __init__(self, parent): - QDialog.__init__(self, parent, Qt.Window) + if parent.config['standaloneWindows']: + windParent = None + else: + windParent = parent + QDialog.__init__(self, windParent, Qt.Window) self.parent = parent self.config = parent.config self.dialog = ankiqt.forms.addcards.Ui_AddCards() diff --git a/ankiqt/ui/cardlist.py b/ankiqt/ui/cardlist.py index 31327983a..0486825b9 100644 --- a/ankiqt/ui/cardlist.py +++ b/ankiqt/ui/cardlist.py @@ -278,7 +278,11 @@ class StatusDelegate(QItemDelegate): class EditDeck(QMainWindow): def __init__(self, parent): - QMainWindow.__init__(self, parent) + if parent.config['standaloneWindows']: + windParent = None + else: + windParent = parent + QMainWindow.__init__(self, windParent) self.parent = parent self.deck = self.parent.deck self.config = parent.config @@ -412,8 +416,7 @@ class EditDeck(QMainWindow): self.sortKey = ("field", self.sortFields[idx-9]) self.rebuildSortIndex(self.sortKey) self.sortIndex = idx - if self.deck.getInt('sortIndex') != idx: - self.deck.setVar('sortIndex', idx) + self.deck.setVar('sortIndex', idx) self.model.sortKey = self.sortKey self.model.updateHeader() if refresh: @@ -499,6 +502,8 @@ class EditDeck(QMainWindow): self.updateSearch() def updateSearch(self, force=True): + if self.parent.inDbHandler: + return idx = self.dialog.tableView.currentIndex() row = idx.row() self.model.searchStr = unicode(self.dialog.filterEdit.text()) diff --git a/ankiqt/ui/deckproperties.py b/ankiqt/ui/deckproperties.py index d5cd6ae88..cf779ad22 100644 --- a/ankiqt/ui/deckproperties.py +++ b/ankiqt/ui/deckproperties.py @@ -235,7 +235,7 @@ class DeckProperties(QDialog): v = float(self.dialog.delay2.text()) self.updateField(self.d, 'delay2', v) v = int(self.dialog.failedCardMax.text()) - self.updateField(self.d, 'failedCardMax', max(v, 5)) + self.updateField(self.d, 'failedCardMax', v) except ValueError: pass # hour shift diff --git a/ankiqt/ui/graphs.py b/ankiqt/ui/graphs.py index dc23a0a3f..66062c0cf 100644 --- a/ankiqt/ui/graphs.py +++ b/ankiqt/ui/graphs.py @@ -42,9 +42,10 @@ class AnkiFigureCanvas (FigureCanvas): class AdjustableFigure(QWidget): - def __init__(self, config, name, figureFunc, choices=None): + def __init__(self, parent, name, figureFunc, choices=None): QWidget.__init__(self) - self.config = config + self.parent = parent + self.config = parent.config self.name = name self.vbox = QVBoxLayout() self.vbox.setSpacing(2) @@ -75,6 +76,8 @@ class AdjustableFigure(QWidget): self.vbox.addLayout(self.hbox) def updateFigure(self): + if self.parent.inDbHandler: + return self.updateTimer = None self.setUpdatesEnabled(False) idx = self.vbox.indexOf(self.figureCanvas) @@ -184,43 +187,43 @@ class GraphWindow(object): self.diag.show() def setupGraphs(self): - nextDue = AdjustableFigure(self.parent.config, 'due', self.dg.nextDue, self.range) + nextDue = AdjustableFigure(self.parent, 'due', self.dg.nextDue, self.range) nextDue.addWidget(QLabel(_("

Due

"))) self.vbox.addWidget(nextDue) self.widgets.append(nextDue) - workload = AdjustableFigure(self.parent.config, 'reps', self.dg.workDone, self.range) + workload = AdjustableFigure(self.parent, 'reps', self.dg.workDone, self.range) workload.addWidget(QLabel(_("

Reps

"))) self.vbox.addWidget(workload) self.widgets.append(workload) - times = AdjustableFigure(self.parent.config, 'times', self.dg.timeSpent, self.range) + times = AdjustableFigure(self.parent, 'times', self.dg.timeSpent, self.range) times.addWidget(QLabel(_("

Review Time

"))) self.vbox.addWidget(times) self.widgets.append(times) - added = AdjustableFigure(self.parent.config, 'added', self.dg.addedRecently, self.range) + added = AdjustableFigure(self.parent, 'added', self.dg.addedRecently, self.range) added.addWidget(QLabel(_("

Added

"))) self.vbox.addWidget(added) self.widgets.append(added) - answered = AdjustableFigure(self.parent.config, 'answered', lambda *args: apply( + answered = AdjustableFigure(self.parent, 'answered', lambda *args: apply( self.dg.addedRecently, args + ('firstAnswered',)), self.range) answered.addWidget(QLabel(_("

First Answered

"))) self.vbox.addWidget(answered) self.widgets.append(answered) - cumDue = AdjustableFigure(self.parent.config, 'cum', self.dg.cumulativeDue, self.range) + cumDue = AdjustableFigure(self.parent, 'cum', self.dg.cumulativeDue, self.range) cumDue.addWidget(QLabel(_("

Cumulative Due

"))) self.vbox.addWidget(cumDue) self.widgets.append(cumDue) - interval = AdjustableFigure(self.parent.config, 'interval', self.dg.intervalPeriod, self.range) + interval = AdjustableFigure(self.parent, 'interval', self.dg.intervalPeriod, self.range) interval.addWidget(QLabel(_("

Intervals

"))) self.vbox.addWidget(interval) self.widgets.append(interval) - eases = AdjustableFigure(self.parent.config, 'eases', self.dg.easeBars) + eases = AdjustableFigure(self.parent, 'eases', self.dg.easeBars) eases.addWidget(QLabel(_("

Eases

"))) self.vbox.addWidget(eases) self.widgets.append(eases) diff --git a/ankiqt/ui/importing.py b/ankiqt/ui/importing.py index 753b80609..be620426e 100644 --- a/ankiqt/ui/importing.py +++ b/ankiqt/ui/importing.py @@ -134,6 +134,8 @@ class ImportDialog(QDialog): txt = ( _("Importing complete. %(num)d facts imported from %(file)s.\n") % {"num": self.importer.total, "file": os.path.basename(self.file)}) + self.dialog.groupBox.setShown(False) + self.dialog.buttonBox.button(QDialogButtonBox.Close).setFocus() if self.importer.log: txt += _("Log of import:\n") + "\n".join(self.importer.log) self.dialog.status.setText(txt) diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py index 4c33536d8..5b03e7c2b 100644 --- a/ankiqt/ui/main.py +++ b/ankiqt/ui/main.py @@ -14,7 +14,7 @@ from PyQt4.QtGui import * from anki import DeckStorage from anki.errors import * from anki.sound import hasSound, playFromText, clearAudioQueue -from anki.utils import addTags, deleteTags, parseTags +from anki.utils import addTags, deleteTags, parseTags, canonifyTags from anki.media import rebuildMediaDir from anki.db import OperationalError from anki.stdmodels import BasicModel @@ -685,8 +685,7 @@ To upgrade an old deck, download Anki 0.9.8.7.""")) def onClose(self): if self.inMainWindow(): - cramming = self.deck is not None and self.deck.name() == "cram" - self.saveAndClose(hideWelcome=cramming) + self.saveAndClose(hideWelcome=self.isCramming()) if cramming: self.loadRecent(0) else: @@ -830,14 +829,17 @@ To upgrade an old deck, download Anki 0.9.8.7.""")) self.updateRecentFiles(file) return True - def onUnsavedTimer(self): + def showToolTip(self, msg): + t = QTimer(self) + t.setSingleShot(True) + t.start(200) + self.connect(t, SIGNAL("timeout()"), + lambda msg=msg: self._showToolTip(msg)) + + def _showToolTip(self, msg): QToolTip.showText( - self.mainWin.statusbar.mapToGlobal(QPoint(0, -100)), - _("""\ -

Unsaved Deck

-Careful. You're editing an unsaved Deck.
-Choose File -> Save to start autosaving
-your deck.""")) + self.mainWin.statusbar.mapToGlobal(QPoint(0, -40)), + msg) def save(self, required=False): if not self.deck.path: @@ -845,10 +847,11 @@ your deck.""")) # backed in memory, make sure it's saved return self.onSaveAs() else: - t = QTimer(self) - t.setSingleShot(True) - t.start(200) - self.connect(t, SIGNAL("timeout()"), self.onUnsavedTimer) + self.showToolTip(_("""\ +

Unsaved Deck

+Careful. You're editing an unsaved Deck.
+Choose File -> Save to start autosaving
+your deck.""")) return if not self.deck.modifiedSinceSave(): return True @@ -1036,7 +1039,6 @@ your deck.""")) def updateStudyStats(self): wasReached = self.deck.sessionLimitReached() - self.deck.sessionStartTime = 0 sessionColour = '%s' cardColour = '%s' if not wasReached: @@ -1155,8 +1157,21 @@ day = :d""", d=yesterday) int(self.mainWin.questionLimit.text())) except (ValueError, OverflowError): pass - uf(self.deck, 'newCardOrder', - self.mainWin.newCardOrder.currentIndex()) + ncOrd = self.mainWin.newCardOrder.currentIndex() + if self.deck.newCardOrder != ncOrd: + if self.deck.newCardOrder == 0 and ncOrd != 0: + # random to non-random + self.deck.startProgress() + self.deck.updateProgress(_("Ordering...")) + self.deck.orderNewCards() + self.deck.finishProgress() + elif self.deck.newCardOrder != 0 and ncOrd == 0: + # non-random to random + self.deck.startProgress() + self.deck.updateProgress(_("Randomizing...")) + self.deck.randomizeNewCards() + self.deck.finishProgress() + uf(self.deck, 'newCardOrder', ncOrd) uf(self.deck, 'newCardSpacing', self.mainWin.newCardScheduling.currentIndex()) uf(self.deck, 'revCardOrder', @@ -1315,11 +1330,11 @@ day = :d""", d=yesterday) def onMark(self, toggled): if self.currentCard.hasTag("Marked"): - self.currentCard.fact.tags = deleteTags( - "Marked", self.currentCard.fact.tags) + self.currentCard.fact.tags = canonifyTags(deleteTags( + "Marked", self.currentCard.fact.tags)) else: - self.currentCard.fact.tags = addTags( - "Marked", self.currentCard.fact.tags) + self.currentCard.fact.tags = canonifyTags(addTags( + "Marked", self.currentCard.fact.tags)) self.currentCard.fact.setModified(textChanged=True) self.deck.updateFactTags([self.currentCard.fact.id]) self.deck.setModified() @@ -1327,7 +1342,8 @@ day = :d""", d=yesterday) def onSuspend(self): undo = _("Suspend") self.deck.setUndoStart(undo) - self.currentCard.fact.tags = addTags("Suspended", self.currentCard.fact.tags) + self.currentCard.fact.tags = canonifyTags( + addTags("Suspended", self.currentCard.fact.tags)) self.currentCard.fact.setModified(textChanged=True) self.deck.updateFactTags([self.currentCard.fact.id]) for card in self.currentCard.fact.cards: @@ -1358,10 +1374,19 @@ day = :d""", d=yesterday) ########################################################################## def onAddCard(self): + if self.isCramming(): + ui.utils.showInfo(_("""\ +You are currently cramming. Please close this deck first.""")) + return ui.dialogs.get("AddCards", self) def onEditDeck(self): ui.dialogs.get("CardList", self) + if self.isCramming(): + self.showToolTip(_("""\ +

Cramming

+You are currently cramming. Any edits you make to this deck +will be lost when you close the deck.""")) def onEditCurrent(self): self.moveToState("editCurrentFact") @@ -1397,7 +1422,10 @@ day = :d""", d=yesterday) ########################################################################## def onImport(self): - import ui.importing + if self.isCramming(): + ui.utils.showInfo(_("""\ +You are currently cramming. Please close this deck first.""")) + return if self.deck is None: self.onNew() ui.importing.ImportDialog(self) @@ -1421,8 +1449,11 @@ day = :d""", d=yesterday) e.exportInto(path) return (e, path) + def isCramming(self): + return self.deck is not None and self.deck.name() == "cram" + def onCram(self, cardIds=[]): - if self.deck.name() == "cram": + if self.isCramming(): ui.utils.showInfo( _("Already cramming. Please close this deck first.")) return @@ -1442,7 +1473,7 @@ day = :d""", d=yesterday) ui.utils.showInfo(_("No cards matched the provided tags.")) return if self.config['randomizeOnCram']: - n = 5 + n = 3 else: n = 2 p = ui.utils.ProgressWin(self, n, 0, _("Cram")) @@ -1462,22 +1493,11 @@ day = :d""", d=yesterday) self.deck.easyIntervalMax = 0.25 self.deck.newCardOrder = 0 self.deck.syncName = None + p.update() + self.deck.updateDynamicIndices() if self.config['randomizeOnCram']: p.update(_("Randomizing...")) - self.deck.s.statement( - "create temporary table idmap (old, new, primary key (old))") - self.deck.s.statement( - "insert into idmap select id, random() from facts") - self.deck.s.statement( - "update facts set id = (select new from idmap where old = id)") - p.update() - self.deck.s.statement( - "update cards set factId = (select new from idmap where old = factId)") - p.update() - self.deck.s.statement( - "update fields set factId = (select new from idmap where old = factId)") - p.update() - self.deck.updateDynamicIndices() + self.deck.randomizeNewCards() self.reset() p.finish() @@ -1645,6 +1665,7 @@ it to your friends. onlyMerge, self.sourcesToCheck) self.connect(self.syncThread, SIGNAL("setStatus"), self.setSyncStatus) self.connect(self.syncThread, SIGNAL("showWarning"), self.showSyncWarning) + self.connect(self.syncThread, SIGNAL("noSyncResponse"), self.noSyncResponse) self.connect(self.syncThread, SIGNAL("moveToState"), self.moveToState) self.connect(self.syncThread, SIGNAL("noMatchingDeck"), self.selectSyncDeck) self.connect(self.syncThread, SIGNAL("syncClockOff"), self.syncClockOff) @@ -1719,6 +1740,11 @@ it to your friends. ui.utils.showWarning(text, self) self.setStatus("") + def noSyncResponse(self): + self.showToolTip(_("""\ +

Sync Failed

+Couldn't contact Anki Online. Please check your internet connection.""")) + def openSyncProgress(self): self.syncProgressDialog = QProgressDialog(_("Syncing Media..."), "", 0, 0, self) diff --git a/ankiqt/ui/status.py b/ankiqt/ui/status.py index 83b6af118..646df8815 100644 --- a/ankiqt/ui/status.py +++ b/ankiqt/ui/status.py @@ -64,13 +64,16 @@ class StatusView(object): shown = True self.progressBar.setShown(shown) self.retentionBar.setShown(shown) - self.timer.setShown(shown) self.etaText.setShown(shown) self.remText.setShown(shown) self.sep1.setShown(shown) self.sep2.setShown(shown) - self.sep3.setShown(shown) self.statusbar.hideOrShow() + # timer has a separate option + if not self.main.config['showTimer']: + shown = False + self.timer.setShown(shown) + self.sep3.setShown(shown) # Setup and teardown ########################################################################## @@ -130,8 +133,6 @@ class StatusView(object): self.timer.setText("00:00") self.addWidget(self.timer) self.redraw() - if not self.main.config['showTimer']: - self.timer.setShown(False) def addWidget(self, w, stretch=0): self.statusbar.addWidget(w, stretch) diff --git a/ankiqt/ui/sync.py b/ankiqt/ui/sync.py index 6ff62a644..1452bf6c3 100644 --- a/ankiqt/ui/sync.py +++ b/ankiqt/ui/sync.py @@ -36,8 +36,11 @@ class Sync(QThread): self.syncDeck() def error(self, error): - error = self.getErrorMessage(error) - self.emit(SIGNAL("showWarning"), error) + if error.data.get('type') == 'noResponse': + self.emit(SIGNAL("noSyncResponse")) + else: + error = self.getErrorMessage(error) + self.emit(SIGNAL("showWarning"), error) if self.onlyMerge: # new file needs cleaning up self.emit(SIGNAL("cleanNewDeck")) @@ -50,11 +53,7 @@ class Sync(QThread): elif error.data.get('status') == "oldVersion": msg=_("The sync protocol has changed. Please upgrade.") else: - msg=_("""\ -Syncing failed. Please try again in a few minutes. -If the problem persists, please report it on the forum. - -Error: %s""" % `getattr(error, 'data')`) + msg=_("Unknown error: %s" % `getattr(error, 'data')`) return msg def connect(self, *args): diff --git a/designer/addcards.ui b/designer/addcards.ui index f1d6dab55..1bb20da65 100644 --- a/designer/addcards.ui +++ b/designer/addcards.ui @@ -12,6 +12,10 @@ Add Items + + + :/icons/list-add.png:/icons/list-add.png + 0 @@ -93,8 +97,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Arial'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html> +</style></head><body style=" font-family:'Sans Serif'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Arial';"></p></body></html> diff --git a/designer/cardlist.ui b/designer/cardlist.ui index 4595230cd..4b6f0b41f 100644 --- a/designer/cardlist.ui +++ b/designer/cardlist.ui @@ -12,6 +12,10 @@ Browse Items + + + :/icons/find.png:/icons/find.png +