question displaying

This commit is contained in:
Damien Elmes 2011-03-22 14:58:59 +09:00
parent 265b598173
commit 46c14b5efc
6 changed files with 131 additions and 207 deletions

View file

@ -46,7 +46,6 @@ defaultConf = {
'proxyPass': '', 'proxyPass': '',
'proxyPort': 8080, 'proxyPort': 8080,
'proxyUser': '', 'proxyUser': '',
'qaDivider': True,
'recentColours': ["#000000", "#0000ff"], 'recentColours': ["#000000", "#0000ff"],
'repeatQuestionAudio': True, 'repeatQuestionAudio': True,
'scrollToAnswer': True, 'scrollToAnswer': True,

View file

@ -2073,11 +2073,7 @@ This deck already exists on your computer. Overwrite the local copy?"""),
def enableCardMenuItems(self): def enableCardMenuItems(self):
self.maybeEnableUndo() self.maybeEnableUndo()
snd = (hasSound(self.currentCard.question) or
(hasSound(self.currentCard.answer) and
self.state != "getQuestion"))
self.form.actionEditLayout.setEnabled(True) self.form.actionEditLayout.setEnabled(True)
self.form.actionRepeatAudio.setEnabled(snd)
self.form.actionMarkCard.setEnabled(True) self.form.actionMarkCard.setEnabled(True)
self.form.actionSuspendCard.setEnabled(True) self.form.actionSuspendCard.setEnabled(True)
self.form.actionDelete.setEnabled(True) self.form.actionDelete.setEnabled(True)

View file

@ -43,8 +43,11 @@ class Overview(object):
evt.accept() evt.accept()
def _linkHandler(self, url): def _linkHandler(self, url):
print "link", url
if url == "studysel": if url == "studysel":
pass self.mw.deck.sched.useGroups = True
self.mw.deck.reset()
self.mw.moveToState("review")
elif url == "opts": elif url == "opts":
pass pass
elif url == "list": elif url == "list":

View file

@ -8,7 +8,7 @@ from PyQt4.QtCore import *
from PyQt4.QtGui import * from PyQt4.QtGui import *
from anki.utils import fmtTimeSpan, stripHTML from anki.utils import fmtTimeSpan, stripHTML
from anki.hooks import addHook, runHook, runFilter from anki.hooks import addHook, runHook, runFilter
from anki.sound import playFromText from anki.sound import playFromText, clearAudioQueue
from aqt.utils import mungeQA, getBase from aqt.utils import mungeQA, getBase
import aqt import aqt
@ -18,130 +18,77 @@ class Reviewer(object):
def __init__(self, mw): def __init__(self, mw):
self.mw = mw self.mw = mw
self.web = mw.web self.web = mw.web
self._state = None self.card = None
self.cardQueue = []
# self.main.connect(self.body, SIGNAL("loadFinished(bool)"), # self.main.connect(self.body, SIGNAL("loadFinished(bool)"),
# self.onLoadFinished) # self.onLoadFinished)
def show(self): def show(self):
self._reset() self._getCard()
# State control # Fetching a card
########################################################################## ##########################################################################
def _reset(self): def _getCard(self):
pass if self.cardQueue:
# a card has been retrieved from undo
c = self.cardQueue.pop()
else:
c = self.mw.deck.sched.getCard()
self.card = c
clearAudioQueue()
if c:
self.mw.enableCardMenuItems()
self._maybeEnableSound()
self._showQuestion()
else:
self.mw.disableCardMenuItems()
if self.mw.deck.cardCount():
self._showCongrats()
else:
self._showEmpty()
def setState(self, state): def _maybeEnableSound(self):
"Change to STATE, and update the display." print "enable sound fixme"
self.oldState = getattr(self, 'state', None) return
self.state = state snd = (hasSound(self.reviewer.card.q()) or
if self.state == "initial": (hasSound(self.reviewer.card.a()) and
return self.state != "getQuestion"))
elif self.state == "deckBrowser": self.form.actionRepeatAudio.setEnabled(snd)
self.clearWindow()
self.drawWelcomeMessage()
self.flush()
return
self.redisplay()
def redisplay(self):
"Idempotently display the current state (prompt for question, etc)" # Showing the question
if self.state == "deckBrowser" or self.state == "studyScreen": ##########################################################################
return
self.buffer = "" def _showQuestion(self):
self.haveTop = self.needFutureWarning() # fixme: timeboxing
self.drawRule = (self.main.config['qaDivider'] and # fixme: q/a separation
self.main.currentCard and # fixme: prevent audio from repeating
not self.main.currentCard.cardModel.questionInAnswer) c = self.card
if not self.main.deck.isEmpty(): # original question with sounds
if self.haveTop: q = c.q()
self.drawTopSection() if (#self.state != self.oldState and not nosound
if self.state == "showQuestion": self.mw.config['autoplaySounds']):
self.setBackground() playFromText(q)
self.drawQuestion() q = mungeQA(q)
if self.drawRule: self.handleTypeAnsQ()
self.write("<hr>") self._renderQA(c, q)
elif self.state == "showAnswer":
self.setBackground() def _renderQA(self, card, text):
if not self.main.currentCard.cardModel.questionInAnswer: self.web.stdHtml(text, card.model().css, bodyClass=card.bgClass())
self.drawQuestion(nosound=True)
if self.drawRule:
self.write("<hr>")
self.drawAnswer()
elif self.state == "deckEmpty":
self.drawWelcomeMessage()
elif self.state == "deckFinished":
self.drawDeckFinishedMessage()
self.flush()
def addStyles(self): def addStyles(self):
# card styles # card styles
s = "<style>\n" s = "<style>\n"
if self.main.deck: if self.main.deck:
s += self.main.deck.css s += self.main.deck.css
s = runFilter("addStyles", s, self.main.currentCard) s = runFilter("addStyles", s, self.card)
s += "</style>" s += "</style>"
return s return s
def clearWindow(self):
self.body.setHtml("")
self.buffer = ""
def setBackground(self): # Q/A support
col = self.main.currentCard.cardModel.lastFontColour ##########################################################################
self.write("<style>html { background: %s;}</style>" % col)
def _getQuestionState(self, oldState):
# stop anything playing
clearAudioQueue()
if self.deck.isEmpty():
return self.moveToState("deckEmpty")
else:
# timeboxing only supported using the standard scheduler
if not self.deck.finishScheduler:
if self.config['showStudyScreen']:
if not self.deck.timeboxStarted():
return self.moveToState("studyScreen")
elif self.deck.timeboxReached():
self.showToolTip(_("Session limit reached."))
self.moveToState("studyScreen")
# switch to timeboxing screen
self.form.tabWidget.setCurrentIndex(2)
return
if not self.currentCard:
self.currentCard = self.deck.getCard()
if self.currentCard:
if self.lastCard:
if self.lastCard.id == self.currentCard.id:
pass
# if self.currentCard.combinedDue > time.time():
# # if the same card is being shown and it's not
# # due yet, give up
# return self.moveToState("deckFinished")
self.enableCardMenuItems()
return self.moveToState("showQuestion")
else:
return self.moveToState("deckFinished")
def _deckEmptyState(self, oldState):
self.switchToWelcomeScreen()
self.disableCardMenuItems()
def _deckFinishedState(self, oldState):
self.currentCard = None
self.deck.db.flush()
self.hideButtons()
self.disableCardMenuItems()
self.switchToCongratsScreen()
self.form.learnMoreButton.setEnabled(
not not self.deck.newAvail)
self.startRefreshTimer()
self.bodyView.setState(state)
# focus finish button
self.form.finishButton.setFocus()
runHook('deckFinished')
def _showQuestionState(self, oldState): def _showQuestionState(self, oldState):
# ensure cwd set to media dir # ensure cwd set to media dir
@ -150,11 +97,46 @@ class Reviewer(object):
self.updateMarkAction() self.updateMarkAction()
runHook('showQuestion') runHook('showQuestion')
# Showing the answer
##########################################################################
# elif self.state == "showAnswer":
# self.setBackground()
# if not self.card.cardModel.questionInAnswer:
# self.drawQuestion(nosound=True)
# if self.drawRule:
# self.write("<hr>")
# self.drawAnswer()
def _showAnswerState(self, oldState): def _showAnswerState(self, oldState):
self.showEaseButtons() self.showEaseButtons()
def drawAnswer(self):
"Show the answer."
a = self.card.htmlAnswer()
a = runFilter("drawAnswer", a, self.card)
if self.card.cardModel.typeAnswer:
try:
cor = stripMedia(stripHTML(self.card.fact[
self.card.cardModel.typeAnswer]))
except KeyError:
self.card.cardModel.typeAnswer = ""
cor = ""
if cor:
given = unicode(self.main.typeAnswerField.text())
res = self.correct(cor, given)
a = res + "<br>" + a
self.write(self.center('<span id=answer />'
+ mungeQA(a)))
if self.state != self.oldState and self.main.config['autoplaySounds']:
playFromText(a)
def onLoadFinished(self, bool):
if self.state == "showAnswer":
if self.main.config['scrollToAnswer']:
mf = self.body.page().mainFrame()
mf.evaluateJavaScript("location.hash = 'answer'")
# Font properties & output # Font properties & output
########################################################################## ##########################################################################
@ -165,7 +147,7 @@ class Reviewer(object):
# hook for user css # hook for user css
runHook("preFlushHook") runHook("preFlushHook")
self.buffer = '''<html><head>%s</head><body>%s</body></html>''' % ( self.buffer = '''<html><head>%s</head><body>%s</body></html>''' % (
getBase(self.main.deck, self.main.currentCard), self.buffer) getBase(self.main.deck, self.card), self.buffer)
#print self.buffer.encode("utf-8") #print self.buffer.encode("utf-8")
b = self.buffer b = self.buffer
# Feeding webkit unicode can result in it not finding images, so on # Feeding webkit unicode can result in it not finding images, so on
@ -183,13 +165,6 @@ class Reviewer(object):
text = unicode(text, "utf-8") text = unicode(text, "utf-8")
self.buffer += text self.buffer += text
# Question and answer
##########################################################################
failedCharColour = "#FF0000"
passedCharColour = "#00FF00"
futureWarningColour = "#FF0000"
def center(self, str, height=40): def center(self, str, height=40):
if not self.main.config['splitQA']: if not self.main.config['splitQA']:
return "<center>" + str + "</center>" return "<center>" + str + "</center>"
@ -198,30 +173,23 @@ class Reviewer(object):
<div style="display: table-cell; vertical-align: middle;">\ <div style="display: table-cell; vertical-align: middle;">\
<div style="">%s</div></div></div></center>''' % (height, str) <div style="">%s</div></div></div></center>''' % (height, str)
def drawQuestion(self, nosound=False): # Type in the answer
"Show the question." ##########################################################################
if not self.main.config['splitQA']:
self.write("<br>") failedCharColour = "#FF0000"
q = self.main.currentCard.htmlQuestion() passedCharColour = "#00FF00"
if self.haveTop: futureWarningColour = "#FF0000"
height = 35
elif self.main.currentCard.cardModel.questionInAnswer: def handleTypeAnsQ(self):
height = 40 return
else: if self.card.cardModel.typeAnswer:
height = 45
q = runFilter("drawQuestion", q, self.main.currentCard)
self.write(self.center(self.mungeQA(self.main.deck, q), height))
if (self.state != self.oldState and not nosound
and self.main.config['autoplaySounds']):
playFromText(q)
if self.main.currentCard.cardModel.typeAnswer:
self.adjustInputFont() self.adjustInputFont()
def getFont(self): def getFont(self):
sz = 20 sz = 20
fn = u"Arial" fn = u"Arial"
for fm in self.main.currentCard.fact.model.fieldModels: for fm in self.card.fact.model.fieldModels:
if fm.name == self.main.currentCard.cardModel.typeAnswer: if fm.name == self.card.cardModel.typeAnswer:
sz = fm.quizFontSize or sz sz = fm.quizFontSize or sz
fn = fm.quizFontFamily or fn fn = fm.quizFontFamily or fn
break break
@ -237,7 +205,6 @@ class Reviewer(object):
self.main.typeAnswerField.setFixedHeight( self.main.typeAnswerField.setFixedHeight(
self.main.typeAnswerField.sizeHint().height() + 10) self.main.typeAnswerField.sizeHint().height() + 10)
def calculateOkBadStyle(self): def calculateOkBadStyle(self):
"Precalculates styles for correct and incorrect part of answer" "Precalculates styles for correct and incorrect part of answer"
(fn, sz) = self.getFont() (fn, sz) = self.getFont()
@ -274,13 +241,10 @@ class Reviewer(object):
"Diff-corrects the typed-in answer." "Diff-corrects the typed-in answer."
if b == "": if b == "":
return ""; return "";
self.calculateOkBadStyle() self.calculateOkBadStyle()
ret = "" ret = ""
lastEqual = "" lastEqual = ""
s = difflib.SequenceMatcher(None, b, a) s = difflib.SequenceMatcher(None, b, a)
for tag, i1, i2, j1, j2 in s.get_opcodes(): for tag, i1, i2, j1, j2 in s.get_opcodes():
if tag == "equal": if tag == "equal":
lastEqual = b[i1:i2] lastEqual = b[i1:i2]
@ -295,71 +259,33 @@ class Reviewer(object):
dashNum = (j2 - j1) if ucd.category(a[j1]) != 'Mn' else ((j2 - j1) - 1) dashNum = (j2 - j1) if ucd.category(a[j1]) != 'Mn' else ((j2 - j1) - 1)
ret += self.applyStyle(a[j1], lastEqual, "-" * dashNum) ret += self.applyStyle(a[j1], lastEqual, "-" * dashNum)
lastEqual = "" lastEqual = ""
return ret + self.ok(lastEqual) return ret + self.ok(lastEqual)
def drawAnswer(self): # Deck finished case
"Show the answer."
a = self.main.currentCard.htmlAnswer()
a = runFilter("drawAnswer", a, self.main.currentCard)
if self.main.currentCard.cardModel.typeAnswer:
try:
cor = stripMedia(stripHTML(self.main.currentCard.fact[
self.main.currentCard.cardModel.typeAnswer]))
except KeyError:
self.main.currentCard.cardModel.typeAnswer = ""
cor = ""
if cor:
given = unicode(self.main.typeAnswerField.text())
res = self.correct(cor, given)
a = res + "<br>" + a
self.write(self.center('<span id=answer />'
+ self.mungeQA(self.main.deck, a)))
if self.state != self.oldState and self.main.config['autoplaySounds']:
playFromText(a)
def mungeQA(self, deck, txt):
txt = mungeQA(deck, txt)
return txt
def onLoadFinished(self, bool):
if self.state == "showAnswer":
if self.main.config['scrollToAnswer']:
mf = self.body.page().mainFrame()
mf.evaluateJavaScript("location.hash = 'answer'")
# Top section
########################################################################## ##########################################################################
def drawTopSection(self): def _showCongrats(self):
"Show previous card, next scheduled time, and stats." self.card = None
self.buffer += "<center>" self.deck.db.flush()
self.drawFutureWarning() self.hideButtons()
self.buffer += "</center>" self.disableCardMenuItems()
self.switchToCongratsScreen()
def needFutureWarning(self): self.form.learnMoreButton.setEnabled(
if not self.main.currentCard: not not self.deck.newAvail)
return self.startRefreshTimer()
if self.main.currentCard.due <= self.main.deck.dueCutoff: self.bodyView.setState(state)
return # focus finish button
if self.main.currentCard.due - time.time() <= self.main.deck.delay0: self.form.finishButton.setFocus()
return runHook('deckFinished')
if self.main.deck.scheduler == "cram":
return
return True
def drawFutureWarning(self):
if not self.needFutureWarning():
return
self.write("<span style='color: %s'>" % futureWarningColour +
_("This card was due in %s.") % fmtTimeSpan(
self.main.currentCard.due - time.time(), after=True) +
"</span>")
# Welcome/empty/finished deck messages
##########################################################################
def drawDeckFinishedMessage(self): def drawDeckFinishedMessage(self):
"Tell the user the deck is finished." "Tell the user the deck is finished."
self.main.mainWin.congratsLabel.setText( self.main.mainWin.congratsLabel.setText(
self.main.deck.deckFinishedMsg()) self.main.deck.deckFinishedMsg())
# Deck empty case
##########################################################################
def _showEmpty(self):
self.switchToWelcomeScreen()
self.disableCardMenuItems()

View file

@ -254,10 +254,10 @@ def restoreHeader(widget, key):
if aqt.mw.config.get(key): if aqt.mw.config.get(key):
widget.restoreState(aqt.mw.config[key]) widget.restoreState(aqt.mw.config[key])
def mungeQA(deck, txt): def mungeQA(txt):
txt = stripSounds(txt) txt = stripSounds(txt)
# osx webkit doesn't understand font weight 600 # osx webkit doesn't understand font weight 600
txt = re.sub("font-weight:.+?;", "font-weight: bold;", txt) #txt = re.sub("font-weight:.+?;", "font-weight: bold;", txt)
return txt return txt
def applyStyles(widget): def applyStyles(widget):

View file

@ -64,13 +64,13 @@ class AnkiWebView(QWebView):
if loadCB: if loadCB:
self._loadFinishedCB = loadCB self._loadFinishedCB = loadCB
QWebView.setHtml(self, html) QWebView.setHtml(self, html)
def stdHtml(self, body, css="", loadCB=None): def stdHtml(self, body, css="", bodyClass="", loadCB=None):
self.setHtml(""" self.setHtml("""
<html><head><style>%s</style> <html><head><style>%s</style>
<script src="qrc:/jquery.min.js"></script> <script src="qrc:/jquery.min.js"></script>
<script src="qrc:/jquery.flot.min.js"></script> <script src="qrc:/jquery.flot.min.js"></script>
</head> </head>
<body>%s</body></html>""" % (css, body), loadCB) <body class="%s">%s</body></html>""" % (css, bodyClass, body), loadCB)
# ensure we're focused # ensure we're focused
self.setFocus() self.setFocus()
def setBridge(self, bridge): def setBridge(self, bridge):