mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 22:42:25 -04:00
273 lines
10 KiB
Python
273 lines
10 KiB
Python
# Copyright: Damien Elmes <anki@ichi2.net>
|
|
# 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
|
|
from anki.hooks import addHook
|
|
|
|
class QClickableLabel(QLabel):
|
|
url = "http://ichi2.net/anki/wiki/TheTimerAndShortQuestions"
|
|
def mouseReleaseEvent(self, evt):
|
|
QDesktopServices.openUrl(QUrl(self.url))
|
|
|
|
class QClickableProgress(QProgressBar):
|
|
url = "http://ichi2.net/anki/wiki/ProgressBars"
|
|
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.timer = None
|
|
self.timerFlashStart = 0
|
|
self.thinkingTimer = QTimer(parent)
|
|
self.thinkingTimer.start(1000)
|
|
parent.connect(self.thinkingTimer, SIGNAL("timeout()"),
|
|
self.drawTimer)
|
|
self.countTimer = QTimer(parent)
|
|
self.countTimer.start(60000)
|
|
parent.connect(self.countTimer, SIGNAL("timeout()"),
|
|
self.updateCount)
|
|
addHook("showQuestion", self.flashTimer)
|
|
|
|
# State control
|
|
##########################################################################
|
|
|
|
def setState(self, state):
|
|
"Change to STATE, and update the display."
|
|
self.state = state
|
|
if self.state == "initial":
|
|
self.showDeckStatus()
|
|
elif self.state == "noDeck":
|
|
self.hideDeckStatus()
|
|
elif self.state in ("showQuestion",
|
|
"deckFinished",
|
|
"deckEmpty",
|
|
"studyScreen"):
|
|
self.redraw()
|
|
self.showOrHideToolbar(self.state)
|
|
|
|
def showOrHideToolbar(self, state):
|
|
if (not self.main.config['showProgress'] and
|
|
state in ("showQuestion", "showAnswer")):
|
|
shown = False
|
|
else:
|
|
shown = True
|
|
self.progressBar.setShown(shown)
|
|
self.etaText.setShown(shown)
|
|
self.remText.setShown(shown)
|
|
self.sep1.setShown(shown)
|
|
self.sep2.setShown(shown)
|
|
# timer has a separate option
|
|
if not self.main.config['showTimer']:
|
|
shown = False
|
|
self.timer.setShown(shown)
|
|
self.sep3.setShown(shown)
|
|
self.statusbar.hideOrShow()
|
|
|
|
# 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, 14)
|
|
# small spacer
|
|
self.initialSpace = QWidget()
|
|
self.addWidget(self.initialSpace, 1)
|
|
# remaining & eta
|
|
self.remText = QLabel()
|
|
self.addWidget(self.remText, 0)
|
|
self.sep1 = self.vertSep()
|
|
self.addWidget(self.sep1, 0)
|
|
self.etaText = QLabel()
|
|
self.etaText.setToolTip(_(
|
|
"<h1>Estimated time</h1>"
|
|
"This is how long it will take to complete the current mode "
|
|
"at your current pace."))
|
|
self.addWidget(self.etaText, 0)
|
|
# progress&retention
|
|
self.sep2 = self.vertSep()
|
|
self.addWidget(self.sep2, 0)
|
|
self.progressBar = QClickableProgress()
|
|
self.progressBar.setFixedSize(*progressBarSize)
|
|
self.progressBar.setMaximum(100)
|
|
self.progressBar.setTextVisible(False)
|
|
if QApplication.instance().style().objectName() != "plastique":
|
|
self.plastiqueStyle = QStyleFactory.create("plastique")
|
|
self.progressBar.setStyle(self.plastiqueStyle)
|
|
self.addWidget(self.progressBar, 0)
|
|
# timer
|
|
self.sep3 = self.vertSep()
|
|
self.addWidget(self.sep3, 0)
|
|
self.timer = QClickableLabel()
|
|
self.timer.setText("00:00")
|
|
self.addWidget(self.timer)
|
|
|
|
def addWidget(self, w, stretch=0):
|
|
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; }")
|
|
|
|
# Updating
|
|
##########################################################################
|
|
|
|
def redraw(self):
|
|
p = QPalette()
|
|
stats = {
|
|
'failed': self.main.deck.failedSoonCount,
|
|
'new': self.main.deck.newCount,
|
|
'rev': self.main.deck.revCount,
|
|
'new2': self.main.deck.newAvail
|
|
}
|
|
remStr = _("Remaining: ")
|
|
if self.state == "deckFinished":
|
|
remStr += "<b>0</b>"
|
|
elif self.state == "deckEmpty":
|
|
remStr += "<b>0</b>"
|
|
else:
|
|
# remaining string, bolded depending on current card
|
|
if sys.platform.startswith("linux"):
|
|
s = " "
|
|
else:
|
|
s = " "
|
|
if not self.main.currentCard:
|
|
remStr += "%(failed1)s" + s + "%(rev1)s" + s + "%(new1)s"
|
|
else:
|
|
t = self.main.deck.cardQueue(self.main.currentCard)
|
|
if t == 0:
|
|
remStr += ("<u>%(failed1)s</u>" + s +
|
|
"%(rev1)s" + s + "%(new1)s")
|
|
elif t == 1:
|
|
remStr += ("%(failed1)s" + s + "<u>%(rev1)s</u>" + s +
|
|
"%(new1)s")
|
|
else:
|
|
remStr += ("%(failed1)s" + s + "%(rev1)s" + s +
|
|
"<u>%(new1)s</u>")
|
|
stats['failed1'] = '<font color=#990000>%s</font>' % stats['failed']
|
|
stats['rev1'] = '<font color=#000000>%s</font>' % stats['rev']
|
|
stats['new1'] = '<font color=#0000ff>%s</font>' % stats['new']
|
|
self.remText.setText(remStr % stats)
|
|
self.remText.setToolTip("<h1>" +_(
|
|
"Remaining cards") + "</h1><p/>" +
|
|
ngettext("There is <b>%d</b> failed card due soon.", \
|
|
"There are <b>%d</b> failed cards due soon.", \
|
|
stats['failed']) % stats['failed'] + "<br>" +
|
|
ngettext("There is <b>%d</b> card awaiting review.",
|
|
"There are <b>%d</b> cards awaiting review.", \
|
|
stats['rev']) % stats['rev'] + "<br>" +
|
|
ngettext("There is <b>%d</b> new card due today.", \
|
|
"There are <b>%d</b> new cards due today.",\
|
|
stats['new']) % stats['new'] + "<br><br>" +
|
|
ngettext("There is <b>%d</b> new card in total.", \
|
|
"There are <b>%d</b> new cards in total.",\
|
|
stats['new2']) % stats['new2'])
|
|
# eta
|
|
print "need eta"
|
|
self.etaText.setText(_("ETA: <b>%(timeLeft)s</b>")) # % stats)
|
|
# retention & progress bars
|
|
p.setColor(QPalette.Base, QColor("black"))
|
|
p.setColor(QPalette.Button, QColor("black"))
|
|
print "need yes total%"
|
|
self.setProgressColour(p, 50) #stats['dYesTotal%'])
|
|
self.progressBar.setPalette(p)
|
|
self.progressBar.setValue(50)
|
|
# tooltips
|
|
self.progressBar.setToolTip(
|
|
_("The percentage of correct answers this session."))
|
|
if self.main.config['showTimer']:
|
|
self.drawTimer()
|
|
self.timer.setToolTip(_("""
|
|
<h1>Time</h1>
|
|
Anki tracks how long you spend looking at a card.<br>
|
|
This time is used to calculate the ETA, but not used<br>
|
|
for scheduling.<br><br>
|
|
You should aim to answer each question within<br>
|
|
10 seconds. Click the timer to learn more."""))
|
|
|
|
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 self.main.inDbHandler:
|
|
return
|
|
if not self.main.config['showTimer']:
|
|
return
|
|
if not self.timer:
|
|
return
|
|
if self.main.deck and self.main.state in ("showQuestion", "showAnswer"):
|
|
if self.main.currentCard:
|
|
if time.time() - self.timerFlashStart < 1:
|
|
return
|
|
if not self.main.config['showCardTimer']:
|
|
return
|
|
t = self.main.currentCard.thinkingTime()
|
|
self.setTimer('%02d:%02d' % (t/60, t%60))
|
|
return
|
|
self.setTimer("00:00")
|
|
|
|
def flashTimer(self):
|
|
if not (self.main.deck.sessionStartTime and
|
|
self.main.deck.sessionTimeLimit): # or self.main.deck.reviewEarly:
|
|
return
|
|
t = time.time() - self.main.deck.sessionStartTime
|
|
t = self.main.deck.sessionTimeLimit - t
|
|
if t < 0:
|
|
t = 0
|
|
self.setTimer('<span style="color:#0000ff">%02d:%02d</span>' %
|
|
(t/60, t%60))
|
|
self.timerFlashStart = time.time()
|
|
|
|
def updateCount(self):
|
|
if self.main.inDbHandler:
|
|
return
|
|
if not self.main.deck:
|
|
return
|
|
if self.state in ("deckFinished", "studyScreen"):
|
|
self.main.deck.updateCutoff()
|
|
self.main.deck.reset()
|
|
self.redraw()
|
|
self.main.updateTitleBar()
|
|
if self.state == "studyScreen":
|
|
self.main.updateStudyStats()
|
|
|
|
def setTimer(self, txt):
|
|
self.timer.setText("<qt>" + txt + " ")
|