diff --git a/aqt/config.py b/aqt/config.py index e22821084..3d3f65a29 100644 --- a/aqt/config.py +++ b/aqt/config.py @@ -46,7 +46,6 @@ defaultConf = { 'proxyPass': '', 'proxyPort': 8080, 'proxyUser': '', - 'qaDivider': True, 'recentColours': ["#000000", "#0000ff"], 'repeatQuestionAudio': True, 'scrollToAnswer': True, diff --git a/aqt/main.py b/aqt/main.py index c0e3cb81a..f18c6bf32 100755 --- a/aqt/main.py +++ b/aqt/main.py @@ -2073,11 +2073,7 @@ This deck already exists on your computer. Overwrite the local copy?"""), def enableCardMenuItems(self): self.maybeEnableUndo() - snd = (hasSound(self.currentCard.question) or - (hasSound(self.currentCard.answer) and - self.state != "getQuestion")) self.form.actionEditLayout.setEnabled(True) - self.form.actionRepeatAudio.setEnabled(snd) self.form.actionMarkCard.setEnabled(True) self.form.actionSuspendCard.setEnabled(True) self.form.actionDelete.setEnabled(True) diff --git a/aqt/overview.py b/aqt/overview.py index 5e514f24b..685ba0c9e 100644 --- a/aqt/overview.py +++ b/aqt/overview.py @@ -43,8 +43,11 @@ class Overview(object): evt.accept() def _linkHandler(self, url): + print "link", url if url == "studysel": - pass + self.mw.deck.sched.useGroups = True + self.mw.deck.reset() + self.mw.moveToState("review") elif url == "opts": pass elif url == "list": diff --git a/aqt/reviewer.py b/aqt/reviewer.py index 7899fe0a8..8f7a9aeee 100644 --- a/aqt/reviewer.py +++ b/aqt/reviewer.py @@ -8,7 +8,7 @@ from PyQt4.QtCore import * from PyQt4.QtGui import * from anki.utils import fmtTimeSpan, stripHTML from anki.hooks import addHook, runHook, runFilter -from anki.sound import playFromText +from anki.sound import playFromText, clearAudioQueue from aqt.utils import mungeQA, getBase import aqt @@ -18,130 +18,77 @@ class Reviewer(object): def __init__(self, mw): self.mw = mw self.web = mw.web - self._state = None + self.card = None + self.cardQueue = [] # self.main.connect(self.body, SIGNAL("loadFinished(bool)"), # self.onLoadFinished) def show(self): - self._reset() + self._getCard() - # State control + # Fetching a card ########################################################################## - def _reset(self): - pass + def _getCard(self): + 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): - "Change to STATE, and update the display." - self.oldState = getattr(self, 'state', None) - self.state = state - if self.state == "initial": - return - elif self.state == "deckBrowser": - self.clearWindow() - self.drawWelcomeMessage() - self.flush() - return - self.redisplay() + def _maybeEnableSound(self): + print "enable sound fixme" + return + snd = (hasSound(self.reviewer.card.q()) or + (hasSound(self.reviewer.card.a()) and + self.state != "getQuestion")) + self.form.actionRepeatAudio.setEnabled(snd) - def redisplay(self): - "Idempotently display the current state (prompt for question, etc)" - if self.state == "deckBrowser" or self.state == "studyScreen": - return - self.buffer = "" - self.haveTop = self.needFutureWarning() - self.drawRule = (self.main.config['qaDivider'] and - self.main.currentCard and - not self.main.currentCard.cardModel.questionInAnswer) - if not self.main.deck.isEmpty(): - if self.haveTop: - self.drawTopSection() - if self.state == "showQuestion": - self.setBackground() - self.drawQuestion() - if self.drawRule: - self.write("
") - elif self.state == "showAnswer": - self.setBackground() - if not self.main.currentCard.cardModel.questionInAnswer: - self.drawQuestion(nosound=True) - if self.drawRule: - self.write("
") - self.drawAnswer() - elif self.state == "deckEmpty": - self.drawWelcomeMessage() - elif self.state == "deckFinished": - self.drawDeckFinishedMessage() - self.flush() + + # Showing the question + ########################################################################## + + def _showQuestion(self): + # fixme: timeboxing + # fixme: q/a separation + # fixme: prevent audio from repeating + c = self.card + # original question with sounds + q = c.q() + if (#self.state != self.oldState and not nosound + self.mw.config['autoplaySounds']): + playFromText(q) + q = mungeQA(q) + self.handleTypeAnsQ() + self._renderQA(c, q) + + def _renderQA(self, card, text): + self.web.stdHtml(text, card.model().css, bodyClass=card.bgClass()) def addStyles(self): # card styles s = "" return s - def clearWindow(self): - self.body.setHtml("") - self.buffer = "" - def setBackground(self): - col = self.main.currentCard.cardModel.lastFontColour - self.write("" % 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') + # Q/A support + ########################################################################## def _showQuestionState(self, oldState): # ensure cwd set to media dir @@ -150,11 +97,46 @@ class Reviewer(object): self.updateMarkAction() 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("
") + # self.drawAnswer() + + def _showAnswerState(self, oldState): 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 + "
" + a + self.write(self.center('' + + 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 ########################################################################## @@ -165,7 +147,7 @@ class Reviewer(object): # hook for user css runHook("preFlushHook") self.buffer = '''%s%s''' % ( - getBase(self.main.deck, self.main.currentCard), self.buffer) + getBase(self.main.deck, self.card), self.buffer) #print self.buffer.encode("utf-8") b = self.buffer # Feeding webkit unicode can result in it not finding images, so on @@ -183,13 +165,6 @@ class Reviewer(object): text = unicode(text, "utf-8") self.buffer += text - # Question and answer - ########################################################################## - - failedCharColour = "#FF0000" - passedCharColour = "#00FF00" - futureWarningColour = "#FF0000" - def center(self, str, height=40): if not self.main.config['splitQA']: return "
" + str + "
" @@ -198,30 +173,23 @@ class Reviewer(object):
\
%s
''' % (height, str) - def drawQuestion(self, nosound=False): - "Show the question." - if not self.main.config['splitQA']: - self.write("
") - q = self.main.currentCard.htmlQuestion() - if self.haveTop: - height = 35 - elif self.main.currentCard.cardModel.questionInAnswer: - height = 40 - else: - 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: + # Type in the answer + ########################################################################## + + failedCharColour = "#FF0000" + passedCharColour = "#00FF00" + futureWarningColour = "#FF0000" + + def handleTypeAnsQ(self): + return + if self.card.cardModel.typeAnswer: self.adjustInputFont() def getFont(self): sz = 20 fn = u"Arial" - for fm in self.main.currentCard.fact.model.fieldModels: - if fm.name == self.main.currentCard.cardModel.typeAnswer: + for fm in self.card.fact.model.fieldModels: + if fm.name == self.card.cardModel.typeAnswer: sz = fm.quizFontSize or sz fn = fm.quizFontFamily or fn break @@ -237,7 +205,6 @@ class Reviewer(object): self.main.typeAnswerField.setFixedHeight( self.main.typeAnswerField.sizeHint().height() + 10) - def calculateOkBadStyle(self): "Precalculates styles for correct and incorrect part of answer" (fn, sz) = self.getFont() @@ -274,13 +241,10 @@ class Reviewer(object): "Diff-corrects the typed-in answer." if b == "": return ""; - self.calculateOkBadStyle() - ret = "" lastEqual = "" s = difflib.SequenceMatcher(None, b, a) - for tag, i1, i2, j1, j2 in s.get_opcodes(): if tag == "equal": lastEqual = b[i1:i2] @@ -295,71 +259,33 @@ class Reviewer(object): dashNum = (j2 - j1) if ucd.category(a[j1]) != 'Mn' else ((j2 - j1) - 1) ret += self.applyStyle(a[j1], lastEqual, "-" * dashNum) lastEqual = "" - return ret + self.ok(lastEqual) - def drawAnswer(self): - "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 + "
" + a - self.write(self.center('' - + 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 + # Deck finished case ########################################################################## - def drawTopSection(self): - "Show previous card, next scheduled time, and stats." - self.buffer += "
" - self.drawFutureWarning() - self.buffer += "
" - - def needFutureWarning(self): - if not self.main.currentCard: - return - if self.main.currentCard.due <= self.main.deck.dueCutoff: - return - if self.main.currentCard.due - time.time() <= self.main.deck.delay0: - return - if self.main.deck.scheduler == "cram": - return - return True - - def drawFutureWarning(self): - if not self.needFutureWarning(): - return - self.write("" % futureWarningColour + - _("This card was due in %s.") % fmtTimeSpan( - self.main.currentCard.due - time.time(), after=True) + - "") - - # Welcome/empty/finished deck messages - ########################################################################## + def _showCongrats(self): + self.card = 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 drawDeckFinishedMessage(self): "Tell the user the deck is finished." self.main.mainWin.congratsLabel.setText( self.main.deck.deckFinishedMsg()) + + # Deck empty case + ########################################################################## + + def _showEmpty(self): + self.switchToWelcomeScreen() + self.disableCardMenuItems() diff --git a/aqt/utils.py b/aqt/utils.py index a1d99bf7f..84efbd4de 100644 --- a/aqt/utils.py +++ b/aqt/utils.py @@ -254,10 +254,10 @@ def restoreHeader(widget, key): if aqt.mw.config.get(key): widget.restoreState(aqt.mw.config[key]) -def mungeQA(deck, txt): +def mungeQA(txt): txt = stripSounds(txt) # 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 def applyStyles(widget): diff --git a/aqt/webview.py b/aqt/webview.py index 063eb6e2e..af8f70b9b 100644 --- a/aqt/webview.py +++ b/aqt/webview.py @@ -64,13 +64,13 @@ class AnkiWebView(QWebView): if loadCB: self._loadFinishedCB = loadCB QWebView.setHtml(self, html) - def stdHtml(self, body, css="", loadCB=None): + def stdHtml(self, body, css="", bodyClass="", loadCB=None): self.setHtml(""" -%s""" % (css, body), loadCB) +%s""" % (css, bodyClass, body), loadCB) # ensure we're focused self.setFocus() def setBridge(self, bridge):