# Copyright: Damien Elmes # License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html from PyQt4.QtGui import * from PyQt4.QtCore import * import anki import sys, time from ankiqt import ui class QClickableLabel(QLabel): url = "http://ichi2.net/anki/wiki/TheTimerAndShortQuestions" def mouseReleaseEvent(self, evt): QDesktopServices.openUrl(QUrl(self.url)) # Status bar ########################################################################## class StatusView(object): "Manage the status bar as we transition through various states." warnTime = 10 def __init__(self, parent): self.main = parent self.statusbar = parent.mainWin.statusbar self.shown = [] self.hideBorders() self.setState("noDeck") self.thinkingTimer = QTimer(parent) self.thinkingTimer.start(1000) self.timer = None parent.connect(self.thinkingTimer, SIGNAL("timeout()"), self.drawTimer) # State control ########################################################################## def setState(self, state): "Change to STATE, and update the display." self.state = state if self.state == "initial": self.showDeckStatus() self.updateProgressGoal() elif self.state == "noDeck": self.hideDeckStatus() elif self.state in ("showQuestion", "deckFinished", "deckEmpty"): self.redraw() # Setup and teardown ########################################################################## def vertSep(self): spacer = QFrame() spacer.setFrameStyle(QFrame.VLine) spacer.setFrameShadow(QFrame.Plain) return spacer def showDeckStatus(self): if self.shown: return progressBarSize = (50, 8) # small spacer self.initialSpace = QWidget() self.addWidget(self.initialSpace, 1) # remaining & eta self.remText = QLabel() self.addWidget(self.remText, 0) self.addWidget(self.vertSep(), 0) self.etaText = QLabel() self.etaText.setToolTip(_( "

Estimated time

" "This is how long it will take to complete the current mode " "at your current pace.")) self.addWidget(self.etaText, 0) # progress&retention self.addWidget(self.vertSep(), 0) vbox = QVBoxLayout() vbox.setSpacing(0) vbox.setContentsMargins(0,0,0,0) self.progressBar = QProgressBar() self.progressBar.setFixedSize(*progressBarSize) self.progressBar.setMaximum(100) self.progressBar.setTextVisible(False) vbox.addWidget(self.progressBar, 0) self.retentionBar = QProgressBar() self.retentionBar.setFixedSize(*progressBarSize) self.retentionBar.setMaximum(100) self.retentionBar.setTextVisible(False) vbox.addWidget(self.retentionBar, 0) self.combinedBar = QWidget() self.combinedBar.setLayout(vbox) self.addWidget(self.combinedBar, 0) # timer self.addWidget(self.vertSep(), 0) self.timer = QClickableLabel() self.timer.setText("00:00") if sys.platform.startswith("darwin"): self.timer.setFixedWidth(40) self.addWidget(self.timer) self.plastiqueStyle = QStyleFactory.create("plastique") self.progressBar.setStyle(self.plastiqueStyle) self.retentionBar.setStyle(self.plastiqueStyle) self.optionsButton = QPushButton() self.optionsButton.setIcon(QIcon(":/icons/configure.png")) self.optionsButton.setFixedSize(20, 20) self.optionsButton.setFocusPolicy(Qt.NoFocus) self.optionsButton.setToolTip(_( "Click this button to customize\n" "the way Anki shows you cards.")) self.main.connect(self.optionsButton, SIGNAL("clicked()"), self.onConfigure) self.addWidget(self.optionsButton) self.redraw() def addWidget(self, w, stretch=0, perm=True): if perm: self.statusbar.addPermanentWidget(w, stretch) else: self.statusbar.addWidget(w, stretch) self.shown.append(w) def hideDeckStatus(self): for w in self.shown: self.statusbar.removeWidget(w) w.setParent(None) self.shown = [] def hideBorders(self): "Remove the ugly borders QT places on status bar widgets." self.statusbar.setStyleSheet("::item { border: 0; }") def updateProgressGoal(self): return stats = self.main.deck.sched.getStats() self.totalPending = stats['pending'] # Updating ########################################################################## def redraw(self): p = QPalette() stats = self.main.deck.getStats() remStr = _("Remaining: ") if self.state == "deckFinished": remStr += "0" elif self.state == "deckEmpty": remStr += "0" else: # remaining string, bolded depending on current card if not self.main.currentCard: remStr += "%(failed1)s + %(successive1)s + %(new1)s" else: q = self.main.deck.queueForCard(self.main.currentCard) if q == "failed": remStr += "%(failed1)s  %(successive1)s  %(new1)s" elif q == "rev": remStr += "%(failed1)s  %(successive1)s  %(new1)s" else: remStr += "%(failed1)s  %(successive1)s  %(new1)s" stats['failed1'] = '%s' % stats['failed'] stats['successive1'] = '%s' % stats['successive'] stats['new1'] = '%s' % stats['new'] self.remText.setText(remStr % stats) stats['suspended'] = self.main.deck.suspendedCardCount() stats['spaced'] = self.main.deck.spacedCardCount() stats['new2'] = self.main.deck.newCardCount() self.remText.setToolTip(_( "

Remaining cards

" "

There are %(failed)d failed cards due soon.
" "There are %(successive)d cards awaiting review.
" "There are %(new)d new cards due today.

" "There are %(new2)d new cards in total.
" "There are %(spaced)d spaced cards.
" "There are %(suspended)d suspended cards.") % stats) # eta self.etaText.setText(_("ETA: %(timeLeft)s") % stats) # retention & progress bars p.setColor(QPalette.Base, QColor("black")) p.setColor(QPalette.Button, QColor("black")) self.setProgressColour(p, stats['gMatureYes%']) self.retentionBar.setPalette(p) self.retentionBar.setValue(stats['gMatureYes%']) self.setProgressColour(p, stats['dYesTotal%']) self.progressBar.setPalette(p) self.progressBar.setValue(stats['dYesTotal%']) # tooltips stats['avgTime'] = anki.utils.fmtTimeSpan(stats['dAverageTime'], point=2) stats['revTime'] = anki.utils.fmtTimeSpan(stats['dReviewTime'], point=2) tip = _("""

Performance

The top bar shows your performance today. The bottom bar shows your
performance on cards scheduled for 21 days or more. The bottom bar should
generally be between 80-95%% - lower and you're forgetting mature cards
too often, higher and you're spending too much time reviewing.

Reviews today

Correct today: %(dYesTotal%)0.1f%% (%(dYesTotal)d of %(dTotal)d)
Average time per answer: %(avgTime)s
Total review time: %(revTime)s""") % stats stats['avgTime'] = anki.utils.fmtTimeSpan(stats['gAverageTime'], point=2) stats['revTime'] = anki.utils.fmtTimeSpan(stats['gReviewTime'], point=2) tip += _("""

All Reviews

Correct over a month: %(gMatureYes%)0.1f%% (%(gMatureYes)d of %(gMatureTotal)d)
Average time per answer: %(avgTime)s
Total review time: %(revTime)s
Correct under a month: %(gYoungYes%)0.1f%% (%(gYoungYes)d of %(gYoungTotal)d)
Correct first time: %(gNewYes%)0.1f%% (%(gNewYes)d of %(gNewTotal)d)
Total correct: %(gYesTotal%)0.1f%% (%(gYesTotal)d of %(gTotal)d)""") % stats self.combinedBar.setToolTip(tip) if self.main.config['showTimer']: self.timer.setVisible(True) self.drawTimer() self.timer.setToolTip(_("""

Time

Anki tracks how long you spend looking at a card.
This time is used to calculate the ETA, but not used
for scheduling.

You should aim to answer each question within
10 seconds. Click the timer to learn more.""")) else: self.timer.setVisible(False) def setProgressColour(self, palette, perc): if perc == 0: palette.setColor(QPalette.Highlight, QColor("black")) elif perc < 50: palette.setColor(QPalette.Highlight, QColor("#ee0000")) elif perc < 65: palette.setColor(QPalette.Highlight, QColor("#ee7700")) elif perc < 75: palette.setColor(QPalette.Highlight, QColor("#eeee00")) else: palette.setColor(QPalette.Highlight, QColor("#00ee00")) def drawTimer(self): if not self.main.config['showTimer']: return if not self.timer: return if self.main.state in "showQuestion": t = self.main.currentCard.thinkingTime() if t < 60: self.timer.setText('00:%02d' % t) else: self.timer.setText('01:00') elif self.main.state == "showAnswer": pass else: self.timer.setText("00:00") def onConfigure(self): self.main.deckProperties = ui.deckproperties.DeckProperties(self.main) self.main.deckProperties.dialog.qtabwidget.setCurrentIndex(1)