diff --git a/ankiqt/config.py b/ankiqt/config.py
index 88fb62e40..ee4fc841e 100644
--- a/ankiqt/config.py
+++ b/ankiqt/config.py
@@ -69,8 +69,10 @@ class Config(dict):
'qaDivider': True,
'splitQA': True,
'sortIndex': 0,
- 'addZeroSpace': True,
+ 'addZeroSpace': False,
'alternativeTheme': False,
+ 'showStudyScreen': True,
+ 'showStudyOptions': False,
}
for (k,v) in fields.items():
if not self.has_key(k):
diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py
index db985326f..c2fe000a9 100644
--- a/ankiqt/ui/main.py
+++ b/ankiqt/ui/main.py
@@ -19,6 +19,8 @@ from anki.media import rebuildMediaDir
from anki.db import OperationalError
from anki.stdmodels import BasicModel
from anki.hooks import runHook, addHook, removeHook, _hooks
+from anki.deck import newCardOrderLabels, newCardSchedulingLabels
+from anki.deck import revCardOrderLabels
import anki.latex
import anki.lang
import anki.deck
@@ -50,6 +52,7 @@ class AnkiQt(QMainWindow):
self.restoreGeometry(self.config['mainWindowGeom'])
self.setupViews()
self.setupEditor()
+ self.setupStudyScreen()
self.setupButtons()
self.setupAnchors()
self.setupToolbar()
@@ -195,9 +198,9 @@ Please do not file a bug report with Anki.\n\n""")
self.state = state
self.updateTitleBar()
if 'state' != 'noDeck' and state != 'editCurrentFact':
- self.showReviewScreen()
+ self.switchToReviewScreen()
if state == "noDeck":
- self.showWelcomeScreen()
+ self.switchToWelcomeScreen()
self.help.hide()
self.currentCard = None
self.lastCard = None
@@ -218,12 +221,17 @@ Please do not file a bug report with Anki.\n\n""")
# if the same card is being shown and it's not
# due yet, give up
return self.moveToState("deckFinished")
+ if (self.config['showStudyScreen'] and
+ not self.deck.sessionStartTime):
+ return self.moveToState("studyScreen")
+ if self.deck.sessionLimitReached():
+ return self.moveToState("studyScreen")
self.enableCardMenuItems()
return self.moveToState("showQuestion")
else:
return self.moveToState("deckFinished")
elif state == "deckEmpty":
- self.showWelcomeScreen()
+ self.switchToWelcomeScreen()
self.disableCardMenuItems()
elif state == "deckFinished":
self.deck.s.flush()
@@ -250,6 +258,10 @@ Please do not file a bug report with Anki.\n\n""")
self.deck.s.flush()
self.deck.refresh()
return self.moveToState("auto")
+ elif state == "studyScreen":
+ self.currentCard = None
+ self.disableCardMenuItems()
+ self.showStudyScreen()
self.updateViews(state)
def keyPressEvent(self, evt):
@@ -379,16 +391,19 @@ new:
# Main stack
##########################################################################
- def showWelcomeScreen(self):
+ def switchToWelcomeScreen(self):
self.mainWin.mainStack.setCurrentIndex(1)
self.hideButtons()
- def showEditScreen(self):
+ def switchToEditScreen(self):
self.mainWin.mainStack.setCurrentIndex(2)
- def showReviewScreen(self):
+ def switchToStudyScreen(self):
self.mainWin.mainStack.setCurrentIndex(3)
+ def switchToReviewScreen(self):
+ self.mainWin.mainStack.setCurrentIndex(4)
+
# Buttons
##########################################################################
@@ -772,9 +787,10 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
return self.onSaveAs()
return
if not self.deck.modifiedSinceSave():
- return
+ return True
self.deck.save()
self.updateTitleBar()
+ return True
def onSaveAs(self):
"Prompt for a file name, then save."
@@ -892,7 +908,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
def showEditor(self):
self.showSaveEditorButton()
- self.showEditScreen()
+ self.switchToEditScreen()
self.editor.setFact(self.currentCard.fact)
def onFactValid(self, fact):
@@ -901,6 +917,108 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
def onFactInvalid(self, fact):
self.mainWin.saveEditorButton.setEnabled(False)
+ # Study screen
+ ##########################################################################
+
+ def setupStudyScreen(self):
+ self.mainWin.newCardOrder.insertItems(
+ 0, QStringList(newCardOrderLabels().values()))
+ self.mainWin.newCardScheduling.insertItems(
+ 0, QStringList(newCardSchedulingLabels().values()))
+ self.mainWin.revCardOrder.insertItems(
+ 0, QStringList(revCardOrderLabels().values()))
+ self.connect(self.mainWin.optionsHelpButton,
+ SIGNAL("clicked()"),
+ lambda: QDesktopServices.openUrl(QUrl(
+ ankiqt.appWiki + "StudyOptions")))
+
+ def showStudyScreen(self):
+ self.switchToStudyScreen()
+ self.mainWin.optionsButton.setChecked(self.config['showStudyOptions'])
+ self.mainWin.optionsBox.setShown(self.config['showStudyOptions'])
+ initial = self.deck.sessionStartTime == 0
+ if initial or not self.deck.sessionLimitReached():
+ # deck just opened, or screen triggered manually
+ top = _("
Welcome back!
")
+ else:
+ top = _("Well done!
")
+ # top label
+ h = {}
+ s = self.deck.getStats()
+ h['lapsed'] = '%s' % s['failed']
+ h['ret'] = s['rev']
+ h['new'] = '%s' % s['new']
+ h['repsToday'] = '%s' % s['dTotal']
+ h['repsIn5'] = '%s' % self.deck.s.scalar(
+ "select count(*) from reviewHistory where time > :t",
+ t = time.time() - 300)
+ h['timeToday'] = '%s' % (
+ anki.utils.fmtTimeSpan(s['dReviewTime'], short=True))
+ self.mainWin.optionsLabel.setText(top + _("""\
+
+
+
+
+Reps done today: | %(repsToday)s |
+Reps in last 5 mins: | %(repsIn5)s |
+Total time today: | %(timeToday)s |
+ |
+
+Lapsed due: | %(lapsed)s |
+Retained due: | %(ret)s |
+New due: | %(new)s |
+ |
""") % h)
+ # start reviewing button
+ self.mainWin.buttonStack.setCurrentIndex(3)
+ self.mainWin.buttonStack.show()
+ if initial:
+ self.mainWin.startReviewingButton.setText(_("Start &Reviewing"))
+ else:
+ self.mainWin.startReviewingButton.setText(_("Continue &Reviewing"))
+ self.mainWin.startReviewingButton.setFocus()
+ self.connect(self.mainWin.startReviewingButton,
+ SIGNAL("clicked()"),
+ self.onStartReview)
+ self.setupStudyOptions()
+
+ def setupStudyOptions(self):
+ self.mainWin.newPerDay.setText(str(self.deck.newCardsPerDay))
+ self.mainWin.minuteLimit.setText(str(self.deck.sessionTimeLimit/60.0))
+ self.mainWin.questionLimit.setText(str(self.deck.sessionRepLimit))
+ self.mainWin.newCardOrder.setCurrentIndex(self.deck.newCardOrder)
+ self.mainWin.newCardScheduling.setCurrentIndex(self.deck.newCardSpacing)
+ self.mainWin.revCardOrder.setCurrentIndex(self.deck.revCardOrder)
+ self.mainWin.delayLapsedCards.setChecked(not self.deck.delay0)
+
+ def onStartReview(self):
+ self.config['showStudyOptions'] = self.mainWin.optionsButton.isChecked()
+ try:
+ self.deck.newCardsPerDay = int(self.mainWin.newPerDay.text())
+ self.deck.sessionTimeLimit = float(
+ self.mainWin.minuteLimit.text()) * 60
+ self.deck.sessionRepLimit = int(self.mainWin.questionLimit.text())
+ except (ValueError, OverflowError):
+ pass
+ self.deck.newCardOrder = self.mainWin.newCardOrder.currentIndex()
+ self.deck.newCardSpacing = self.mainWin.newCardScheduling.currentIndex()
+ self.deck.revCardOrder = self.mainWin.revCardOrder.currentIndex()
+ # avoid clobbering the user's settings if they haven't changed
+ if self.deck.delay0 and self.mainWin.delayLapsedCards.isChecked():
+ self.deck.delay0 = 0
+ elif (not self.deck.delay0 and
+ not self.mainWin.delayLapsedCards.isChecked()):
+ self.deck.delay0 = 600
+ if not self.deck.sessionStartTime or self.deck.sessionLimitReached():
+ self.deck.startSession()
+ self.deck.flushMod()
+ self.moveToState("getQuestion")
+
+ def onStudyOptions(self):
+ if self.state == "studyScreen":
+ self.onStartReview()
+ else:
+ self.moveToState("studyScreen")
+
# Toolbar
##########################################################################
@@ -917,8 +1035,9 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
mw.toolBar.addAction(mw.actionAddcards)
mw.toolBar.addAction(mw.actionEditCurrent)
mw.toolBar.addAction(mw.actionEditdeck)
- mw.toolBar.addAction(mw.actionMarkCard)
mw.toolBar.addAction(mw.actionGraphs)
+ mw.toolBar.addAction(mw.actionStudyOptions)
+ mw.toolBar.addAction(mw.actionMarkCard)
mw.toolBar.addAction(mw.actionRepeatAudio)
self.addToolBar(Qt.TopToolBarArea, mw.toolBar)
mw.toolBar.setIconSize(QSize(self.config['iconSize'],
@@ -1248,7 +1367,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.connect(self.syncThread, SIGNAL("updateSyncProgress"), self.updateSyncProgress)
self.connect(self.syncThread, SIGNAL("bulkSyncFailed"), self.bulkSyncFailed)
self.syncThread.start()
- self.showWelcomeScreen()
+ self.switchToWelcomeScreen()
self.setEnabled(False)
while not self.syncThread.isFinished():
self.app.processEvents()
@@ -1353,6 +1472,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
"Kstats",
"Cstats",
"ActiveTags",
+ "StudyOptions",
)
deckRelatedMenus = (
@@ -1413,6 +1533,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.connect(m.actionGetMoreDecks, s, self.onGetMoreDecks)
self.connect(m.actionCacheLatex, s, self.onCacheLatex)
self.connect(m.actionUncacheLatex, s, self.onUncacheLatex)
+ self.connect(m.actionStudyOptions, s, self.onStudyOptions)
def enableDeckMenuItems(self, enabled=True):
"setEnabled deck-related items."
diff --git a/ankiqt/ui/preferences.py b/ankiqt/ui/preferences.py
index 18c6b64f5..56a115733 100644
--- a/ankiqt/ui/preferences.py
+++ b/ankiqt/ui/preferences.py
@@ -165,7 +165,8 @@ class Preferences(QDialog):
self.dialog.showToolbar.setChecked(self.config['showToolbar'])
self.dialog.tallButtons.setChecked(
self.config['easeButtonHeight'] != 'standard')
- self.dialog.suppressEstimates.setChecked(self.config['suppressEstimates'])
+ self.dialog.showEstimates.setChecked(not self.config['suppressEstimates'])
+ self.dialog.showStudyOptions.setChecked(self.config['showStudyScreen'])
self.dialog.showLastCardInterval.setChecked(self.config['showLastCardInterval'])
self.dialog.showLastCardContent.setChecked(self.config['showLastCardContent'])
self.dialog.showTray.setChecked(self.config['showTrayIcon'])
@@ -188,7 +189,8 @@ class Preferences(QDialog):
self.config['showLastCardContent'] = self.dialog.showLastCardContent.isChecked()
self.config['showTrayIcon'] = self.dialog.showTray.isChecked()
self.config['showTimer'] = self.dialog.showTimer.isChecked()
- self.config['suppressEstimates'] = self.dialog.suppressEstimates.isChecked()
+ self.config['suppressEstimates'] = not self.dialog.showEstimates.isChecked()
+ self.config['showStudyScreen'] = self.dialog.showStudyOptions.isChecked()
self.config['simpleToolbar'] = self.dialog.simpleToolbar.isChecked()
self.config['scrollToAnswer'] = self.dialog.scrollToAnswer.isChecked()
self.config['qaDivider'] = self.dialog.showDivider.isChecked()
diff --git a/designer/main.ui b/designer/main.ui
index b28f097f3..bf8b1af18 100644
--- a/designer/main.ui
+++ b/designer/main.ui
@@ -5,8 +5,8 @@
0
0
- 655
- 487
+ 827
+ 648
@@ -27,8 +27,8 @@
0
69
- 655
- 398
+ 827
+ 559
@@ -154,8 +154,8 @@
0
0
- 370
- 343
+ 542
+ 504
@@ -164,8 +164,8 @@
0
0
- 100
- 30
+ 550
+ 413
@@ -186,8 +186,8 @@
0
0
- 100
- 30
+ 550
+ 413
@@ -202,19 +202,261 @@
+
+
+
+ 0
+ 0
+ 542
+ 504
+
+
+
+ -
+
+
-
+
+
-
+
+
+ <h1>Welcome!</h1>
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Preferred
+
+
+
+ 20
+ 10
+
+
+
+
+ -
+
+
-
+
+
-
+
+
+ Review &Options>>
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+
+ -
+
+
+ Help
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
+ 2
+
+
+ 0
+
+
-
+
+
+
+ 90
+ 0
+
+
+
+ Time limit (mins):
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+
+ -
+
+
+ Question limit:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 90
+ 0
+
+
+
+ New per day
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Delay lapsed cards until after reviews
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
-
- 3
+ 4
0
0
- 370
+ 542
49
@@ -249,7 +491,7 @@
0
0
- 370
+ 542
49
@@ -399,8 +641,8 @@
0
0
- 100
- 30
+ 542
+ 49
@@ -434,7 +676,39 @@
0
0
- 370
+ 542
+ 49
+
+
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 13
+
+
+
+
+ -
+
+
+ PushButton
+
+
+
+
+
+
+
+
+ 0
+ 0
+ 542
49
@@ -632,7 +906,7 @@
0
0
- 655
+ 827
23
@@ -761,6 +1035,7 @@
+
@@ -778,8 +1053,8 @@
0
- 467
- 655
+ 628
+ 827
20
@@ -792,7 +1067,7 @@
0
23
- 655
+ 827
46
@@ -827,6 +1102,7 @@
+
@@ -1243,7 +1519,7 @@
- :/icons/chronometer.png:/icons/chronometer.png
+ :/icons/view-pim-calendar.png:/icons/view-pim-calendar.png
C&ram...
@@ -1351,9 +1627,55 @@
Uncache LaTeX
+
+
+
+ :/icons/chronometer.png:/icons/chronometer.png
+
+
+ &Study Options
+
+
+
+ optionsButton
+ optionsHelpButton
+ newPerDay
+ minuteLimit
+ questionLimit
+ newCardOrder
+ newCardScheduling
+ revCardOrder
+ delayLapsedCards
+ easeButton2
+ easeButton3
+ easeButton1
+ easeButton4
+ saveEditorButton
+ help
+ welcomeText
+ startReviewingButton
+ showAnswerButton
+
-
+
+
+ optionsButton
+ toggled(bool)
+ optionsBox
+ setShown(bool)
+
+
+ 98
+ 134
+
+
+ 219
+ 190
+
+
+
+
diff --git a/designer/preferences.ui b/designer/preferences.ui
index 970a13395..59fa87715 100644
--- a/designer/preferences.ui
+++ b/designer/preferences.ui
@@ -177,9 +177,16 @@
-
-
+
- Don't show next time before answer
+ Show next time before answer
+
+
+
+ -
+
+
+ Show study options on startup
@@ -546,7 +553,8 @@
backgroundColour
showDivider
splitQA
- suppressEstimates
+ showEstimates
+ showStudyOptions
saveWhenClosing
saveAfterEvery
saveAfterEveryNum
diff --git a/icons.qrc b/icons.qrc
index d6c0dae44..f2915a5f2 100644
--- a/icons.qrc
+++ b/icons.qrc
@@ -1,5 +1,6 @@
+ icons/view-pim-calendar.png
icons/anki-tag.png
icons/edit-redo.png
icons/text-xml.png
diff --git a/icons/view-pim-calendar.png b/icons/view-pim-calendar.png
new file mode 100644
index 000000000..2e02fc9b1
Binary files /dev/null and b/icons/view-pim-calendar.png differ