# -*- coding: utf-8 -*- # 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, anki.utils from anki.sound import playFromText, stripSounds from anki.latex import renderLatex, stripLatex from anki.utils import stripHTML from anki.hooks import runHook import types, time, re, os, urllib, sys from ankiqt import ui from ankiqt.ui.utils import mungeQA from anki.utils import fmtTimeSpan from PyQt4.QtWebKit import QWebPage, QWebView failedCharColour = "#FF0000" passedCharColour = "#00FF00" futureWarningColour = "#FF0000" # Views - define the way a user is prompted for questions, etc ########################################################################## class View(object): "Handle the main window update as we transition through various states." def __init__(self, parent, body, frame=None): self.main = parent self.body = body self.frame = frame self.main.connect(self.body, SIGNAL("loadFinished(bool)"), self.onLoadFinished) # State control ########################################################################## 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 == "noDeck": self.clearWindow() self.drawWelcomeMessage() self.flush() return self.redisplay() def redisplay(self): "Idempotently display the current state (prompt for question, etc)" if self.state == "noDeck" or self.state == "studyScreen": return self.clearWindow() self.haveTop = (self.main.lastCard and ( self.main.config['showLastCardContent'] or self.main.config['showLastCardInterval'])) or ( self.main.currentCard and self.main.currentCard.due > time.time()) 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.drawQuestion() if self.drawRule: self.write("
") elif self.state == "showAnswer": 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() def addStyles(self): # card styles s = "" return s def clearWindow(self): self.body.setHtml("") self.buffer = "" # Font properties & output ########################################################################## def flush(self): "Write the current HTML buffer to the screen." self.buffer = self.addStyles() + self.buffer # hook for user css runHook("preFlushHook") #print self.buffer.encode("utf-8") self.body.setHtml(self.buffer) def write(self, text): if type(text) != types.UnicodeType: text = unicode(text, "utf-8") self.buffer += text # Question and answer ########################################################################## def center(self, str, height=40): if not self.main.config['splitQA']: return "
" + str + "
" return '''\
\
\
%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 else: height = 45 self.write(self.center(self.mungeQA(self.main.deck, q), height)) if self.state != self.oldState and not nosound: playFromText(q) def drawAnswer(self): "Show the answer." a = self.main.currentCard.htmlAnswer() if self.main.currentCard.cardModel.typeAnswer: cor = stripHTML(self.main.currentCard.answer) given = unicode(self.main.typeAnswerField.text()) res = [] if len(given) < len(cor): given += " " * (len(cor) - len(given)) sz = self.main.currentCard.cardModel.answerFontSize ok = "background: %s; color: #000; font-size: %dpx" % ( passedCharColour, sz) bad = "background: %s; color: #000; font-size: %dpx;" % ( failedCharColour, sz) for (i, c) in enumerate(given): try: yes = c == cor[i] except IndexError: yes = False if yes: res.append( "%s" % (ok, c)) else: res.append("%s" % (bad, c)) a = "".join(res) + "
" + a self.write(self.center('' + self.mungeQA(self.main.deck, a))) if self.state != self.oldState: playFromText(a) def mungeQA(self, deck, txt): txt = mungeQA(deck, txt) # hack to fix thai presentation issues if self.main.config['addZeroSpace']: txt = txt.replace("", "​") return txt def onLoadFinished(self): if self.state == "showAnswer": if self.main.config['scrollToAnswer']: mf = self.body.page().mainFrame() mf.evaluateJavaScript("location.hash = 'answer'") # Top section ########################################################################## def drawTopSection(self): "Show previous card, next scheduled time, and stats." self.buffer += "
" self.drawFutureWarning() self.drawLastCard() self.buffer += "
" def drawFutureWarning(self): if not self.main.currentCard: return if self.main.currentCard.due <= time.time(): return if self.main.currentCard.due - time.time() <= self.main.deck.delay0: return self.write("" % futureWarningColour + _("This card was due in %s.") % fmtTimeSpan( self.main.currentCard.due - time.time()) + "") def drawLastCard(self): "Show the last card if not the current one, and next time." if self.main.lastCard: if self.main.config['showLastCardContent']: if (self.state == "deckFinished" or self.main.currentCard.id != self.main.lastCard.id): q = self.main.lastCard.question.replace("
", " ") q = stripHTML(q) if len(q) > 50: q = q[:50] + "..." a = self.main.lastCard.answer.replace("
", " ") a = stripHTML(a) if len(a) > 50: a = a[:50] + "..." s = "%s
%s" % (q, a) s = stripLatex(s) self.write('%s
' % s) if self.main.config['showLastCardInterval']: if self.main.lastQuality > 1: msg = _("Well done! This card will appear again in " "%(next)s.") % \ {"next":self.main.lastScheduledTime} else: msg = _("This card will appear again later.") self.write(msg) self.write("
") # Welcome/empty/finished deck messages ########################################################################## def drawWelcomeMessage(self): self.main.mainWin.welcomeText.setText(_("""

Welcome to Anki!

Add material

Start adding your own material.

Open Local Deck

Open Online Deck

Open Sample Deck

Get More Decks

""")) def drawDeckFinishedMessage(self): "Tell the user the deck is finished." self.main.mainWin.congratsLabel.setText( self.main.deck.deckFinishedMsg()) class AnkiWebView(QWebView): def __init__(self, *args): QWebView.__init__(self, *args) self.setObjectName("mainText") def keyPressEvent(self, evt): if evt.matches(QKeySequence.Copy): self.triggerPageAction(QWebPage.Copy) evt.accept() evt.ignore() def contextMenuEvent(self, evt): QWebView.contextMenuEvent(self, evt) def dropEvent(self, evt): pass