Merge branch 'master' of git://ichi2.net/ankiqt

This commit is contained in:
Susanna Björverud 2009-04-30 10:06:57 +02:00
commit 39da9392a6
29 changed files with 8082 additions and 6959 deletions

View file

@ -6,7 +6,7 @@ from PyQt4.QtCore import *
from PyQt4.QtGui import *
appName="Anki"
appVersion="0.9.9.7.6b"
appVersion="0.9.9.7.7b"
appWebsite="http://ichi2.net/anki/download/"
appWiki="http://ichi2.net/anki/wiki/"
appHelpSite="http://ichi2.net/anki/wiki/AnkiWiki"

View file

@ -88,6 +88,7 @@ class Config(dict):
'typeAnswerFontSize': 20,
'showProgress': True,
'recentColours': ["#000000", "#0000ff"],
'preventEditUntilAnswer': False,
}
for (k,v) in fields.items():
if not self.has_key(k):

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -289,6 +289,7 @@ class EditDeck(QMainWindow):
self.forceClose = False
self.origModTime = parent.deck.modified
self.currentRow = None
self.lastFilter = ""
self.dialog = ankiqt.forms.cardlist.Ui_MainWindow()
self.dialog.setupUi(self)
restoreGeom(self, "editor")
@ -310,7 +311,7 @@ class EditDeck(QMainWindow):
self.setupFilter()
self.setupSort()
self.setupHeaders()
self.setupUndo()
self.setupHooks()
self.setupEditor()
self.setupCardInfo()
self.dialog.filterEdit.setFocus()
@ -362,16 +363,51 @@ class EditDeck(QMainWindow):
self.sortChanged(self.sortIndex, refresh=False)
def drawTags(self):
self.dialog.tagList.view().setFixedWidth(200)
self.dialog.tagList.setMaxVisibleItems(30)
tags = self.deck.allTags()
self.alltags = tags
self.alltags.sort()
self.dialog.tagList.setFixedWidth(130)
self.dialog.tagList.clear()
self.dialog.tagList.addItems(QStringList(
[_('<Tag filter>'), _('No tags')] +
[x.replace("_", " ") for x in self.alltags]))
self.dialog.tagList.view().setFixedWidth(200)
alltags = [None, "Marked", "Suspended", None, None]
# system tags
self.dialog.tagList.addItem(_("<Filter>"))
self.dialog.tagList.addItem(QIcon(":/icons/rating.png"),
_('Marked'))
self.dialog.tagList.addItem(QIcon(":/icons/media-playback-pause.png"),
_('Suspended'))
self.dialog.tagList.addItem(QIcon(":/icons/editclear.png"),
_('No fact tags'))
self.dialog.tagList.insertSeparator(
self.dialog.tagList.count())
# model and card templates
for (type, sql, icon) in (
("models", "select tags from models", "contents.png"),
("cms", "select name from cardModels", "Anki_Card.png")):
d = {}
tagss = self.deck.s.column0(sql)
for tags in tagss:
for tag in parseTags(tags):
d[tag] = 1
sortedtags = sorted(d.keys())
alltags.extend(sortedtags)
icon = QIcon(":/icons/" + icon)
for t in sortedtags:
self.dialog.tagList.addItem(icon, t)
alltags.append(None)
self.dialog.tagList.insertSeparator(
self.dialog.tagList.count())
# fact tags
alluser = sorted(self.deck.allTags())
for tag in alltags:
try:
alluser.remove(tag)
except:
pass
icon = QIcon(":/icons/Anki_Fact.png")
for t in alluser:
t = t.replace("_", " ")
self.dialog.tagList.addItem(icon, t)
alltags.extend(alluser)
self.alltags = alltags
def drawSort(self):
self.sortList = [
@ -449,13 +485,18 @@ class EditDeck(QMainWindow):
def tagChanged(self, idx):
if idx == 0:
self.dialog.filterEdit.setText("")
filter = ""
elif idx == 1:
self.dialog.filterEdit.setText("tag:none")
filter = "tag:marked"
elif idx == 2:
filter = "tag:suspended"
elif idx == 3:
filter = "tag:none"
else:
self.dialog.filterEdit.setText("tag:" + self.alltags[idx-2])
filter = "tag:" + self.alltags[idx]
self.lastFilter = filter
self.dialog.filterEdit.setText(filter)
self.showFilterNow()
self.dialog.tagList.setCurrentIndex(0)
def updateFilterLabel(self):
selected = len(self.dialog.tableView.selectionModel().selectedRows())
@ -487,6 +528,10 @@ class EditDeck(QMainWindow):
def filterTextChanged(self):
interval = 300
# update filter dropdown
if (self.lastFilter.lower()
not in unicode(self.dialog.filterEdit.text()).lower()):
self.dialog.tagList.setCurrentIndex(0)
if self.filterTimer:
self.filterTimer.setInterval(interval)
else:
@ -521,6 +566,8 @@ class EditDeck(QMainWindow):
self.dialog.fieldsArea.hide()
self.dialog.tableView.selectRow(row)
self.dialog.tableView.scrollTo(idx, QAbstractItemView.PositionAtCenter)
if not self.model.cards:
self.editor.setFact(None)
def focusCurrentCard(self):
if self.currentCard:
@ -587,7 +634,7 @@ class EditDeck(QMainWindow):
self.hide()
ui.dialogs.close("CardList")
self.parent.moveToState("auto")
self.teardownUndo()
self.teardownHooks()
return True
def closeEvent(self, evt):
@ -837,11 +884,13 @@ where id in %s""" % ids2str(sf))
# Edit: undo/redo
######################################################################
def setupUndo(self):
def setupHooks(self):
addHook("postUndoRedo", self.postUndoRedo)
addHook("currentCardDeleted", self.updateSearch)
def teardownUndo(self):
def teardownHooks(self):
removeHook("postUndoRedo", self.postUndoRedo)
removeHook("currentCardDeleted", self.updateSearch)
def postUndoRedo(self):
self.updateFilterLabel()

View file

@ -11,7 +11,6 @@ from anki.utils import parseTags
from anki.deck import newCardOrderLabels, newCardSchedulingLabels
from anki.deck import revCardOrderLabels
from anki.utils import hexifyID, dehexifyID
from anki.lang import ngettext
import ankiqt
tabs = ("ModelsAndPriorities",

View file

@ -12,7 +12,7 @@ from ankiqt.ui.sound import getAudio
import anki.sound
from ankiqt import ui
import ankiqt
from ankiqt.ui.utils import mungeQA, saveGeom, restoreGeom
from ankiqt.ui.utils import mungeQA, saveGeom, restoreGeom, getBase
from anki.hooks import addHook, removeHook, runHook
from sqlalchemy.exceptions import InvalidRequestError
@ -44,7 +44,7 @@ class FactEditor(object):
def close(self):
removeHook("deckClosed", self.deckClosedHook)
addHook("colourChanged", self.colourChanged)
removeHook("guiReset", self.refresh)
removeHook("colourChanged", self.colourChanged)
def setFact(self, fact, noFocus=False, check=False, scroll=False):
@ -1075,12 +1075,13 @@ class PreviewDialog(QDialog):
def updateCard(self):
c = self.cards[self.currentCard]
self.dialog.webView.setHtml(
('<html><head>%s</head><body>' % getBase(self.deck)) +
"<style>" + self.deck.css +
("\nhtml { background: %s }" % c.cardModel.lastFontColour) +
"\ndiv { white-space: pre-wrap; }</style>" +
mungeQA(self.deck, c.htmlQuestion()) +
"<br><br><hr><br><br>" +
mungeQA(self.deck, c.htmlAnswer()))
mungeQA(self.deck, c.htmlAnswer()) + "</body></html>")
playFromText(c.question)
playFromText(c.answer)

View file

@ -235,7 +235,10 @@ Please do not file a bug report with Anki.<br><br>""")
elif state == "auto":
self.currentCard = None
if self.deck:
return self.moveToState("getQuestion")
if self.state == "studyScreen":
return self.updateStudyStats()
else:
return self.moveToState("getQuestion")
else:
return self.moveToState("noDeck")
# save the new & last state
@ -344,16 +347,21 @@ Please do not file a bug report with Anki.<br><br>""")
return
if self.state == "showQuestion":
if evt.key() in (Qt.Key_Enter,
Qt.Key_Return):
Qt.Key_Return,
Qt.Key_Space):
evt.accept()
return self.moveToState("showAnswer")
return self.mainWin.showAnswerButton.click()
elif self.state == "showAnswer":
key = unicode(evt.text())
if evt.key() == Qt.Key_Space:
key = str(self.defaultEaseButton())
else:
key = unicode(evt.text())
if key and key >= "1" and key <= "4":
# user entered a quality setting
num=int(key)
evt.accept()
return self.cardAnswered(num)
return getattr(self.mainWin, "easeButton%d" %
num).animateClick()
elif self.state == "studyScreen":
if evt.key() in (Qt.Key_Enter,
Qt.Key_Return):
@ -507,6 +515,7 @@ new:
else:
return QLineEdit.keyPressEvent(self, evt)
self.typeAnswerField = QLineEditNoUndo(self)
self.typeAnswerField.setObjectName("typeAnswerField")
self.typeAnswerField.setFixedWidth(351)
f = QFont()
if sys.platform.startswith("darwin"):
@ -533,18 +542,24 @@ new:
self.typeAnswerField.setText("")
else:
self.mainWin.buttonStack.setCurrentIndex(0)
self.mainWin.showAnswerButton.setFocus()
self.mainWin.mainText.setFocus()
self.mainWin.buttonStack.show()
def showEaseButtons(self):
self.updateEaseButtons()
self.mainWin.buttonStack.setCurrentIndex(1)
self.mainWin.buttonStack.show()
if self.currentCard.reps and not self.currentCard.successive:
if self.defaultEaseButton() == 2:
self.mainWin.easeButton2.setFocus()
else:
self.mainWin.easeButton3.setFocus()
def defaultEaseButton(self):
if self.currentCard.reps and not self.currentCard.successive:
return 2
else:
return 3
def updateEaseButtons(self):
nextInts = {}
for i in range(1, 5):
@ -1217,6 +1232,7 @@ session (black)</dd>
self.deck.setFailedCardPolicy(
self.mainWin.failedCardsOption.currentIndex())
self.deck.flushMod()
self.deck.rebuildQueue()
self.deck.startSession()
self.moveToState("getQuestion")
@ -1397,6 +1413,17 @@ session (black)</dd>
self.deck.deleteCard(self.currentCard.id)
self.reset()
self.deck.setUndoEnd(undo)
runHook("currentCardDeleted")
def onBuryFact(self):
undo = _("Bury")
self.deck.setUndoStart(undo)
for card in self.currentCard.fact.cards:
card.priority = -2
card.isDue = 0
self.deck.flushMod()
self.reset()
self.deck.setUndoEnd(undo)
def onUndo(self):
self.deck.undo()
@ -1603,10 +1630,11 @@ it to your friends.
for f in os.listdir(mdir):
zip.write(os.path.join(mdir, f),
str(os.path.join("shared.media/", f)))
os.chdir(pwd)
shutil.rmtree(mdir)
os.chdir(pwd)
self.deck.updateProgress()
zip.close()
os.chdir(pwd)
os.unlink(path)
self.deck.finishProgress()
self.onOpenPluginFolder(dir)
@ -1889,6 +1917,7 @@ Couldn't contact Anki Online. Please check your internet connection."""))
self.connect(m.actionStudyOptions, s, self.onStudyOptions)
self.connect(m.actionDonate, s, self.onDonate)
self.connect(m.actionRecordNoiseProfile, s, self.onRecordNoiseProfile)
self.connect(m.actionBuryFact, s, self.onBuryFact)
def enableDeckMenuItems(self, enabled=True):
"setEnabled deck-related items."
@ -1944,6 +1973,7 @@ Couldn't contact Anki Online. Please check your internet connection."""))
self.mainWin.actionMarkCard.setEnabled(False)
self.mainWin.actionSuspendCard.setEnabled(False)
self.mainWin.actionDelete.setEnabled(False)
self.mainWin.actionBuryFact.setEnabled(False)
self.mainWin.actionRepeatAudio.setEnabled(False)
def enableCardMenuItems(self):
@ -1954,10 +1984,14 @@ Couldn't contact Anki Online. Please check your internet connection."""))
(hasSound(self.currentCard.answer) and
self.state != "getQuestion"))
self.mainWin.actionRepeatAudio.setEnabled(snd)
self.mainWin.actionEditCurrent.setEnabled(True)
self.mainWin.actionMarkCard.setEnabled(True)
self.mainWin.actionSuspendCard.setEnabled(True)
self.mainWin.actionDelete.setEnabled(True)
self.mainWin.actionBuryFact.setEnabled(True)
enableEdits = (not self.config['preventEditUntilAnswer'] or
self.state != "getQuestion")
self.mainWin.actionEditCurrent.setEnabled(enableEdits)
self.mainWin.actionEditdeck.setEnabled(enableEdits)
def maybeShowKanjiStats(self):
if not self.deck:

View file

@ -386,10 +386,10 @@ order by n""", id=card.id)
count = self.deck.cardModelUseCount(card)
if count:
if not ui.utils.askUser(
_("This model is used by %d cards. If you delete it,\n"
_("This template is used by %d cards. If you delete it,\n"
"all the cards will be deleted too. If you just\n"
"want to prevent the creation of future cards with\n"
"this model, please use the 'disable' button\n"
"this template, please use the 'disable' button\n"
"instead.\n\nReally delete these cards?") % count,
parent=self):
return
@ -407,7 +407,7 @@ order by n""", id=card.id)
active += 1
if active < 2 and card.active:
ui.utils.showWarning(
_("Please enable a different model first."),
_("Please enable a different template first."),
parent=self)
return
card.active = not card.active

View file

@ -116,6 +116,7 @@ class Preferences(QDialog):
self.dialog.addZeroSpace.setChecked(self.config['addZeroSpace'])
self.dialog.alternativeTheme.setChecked(self.config['alternativeTheme'])
self.dialog.showProgress.setChecked(self.config['showProgress'])
self.dialog.preventEdits.setChecked(self.config['preventEditUntilAnswer'])
def updateAdvanced(self):
self.config['showTrayIcon'] = self.dialog.showTray.isChecked()
@ -127,6 +128,7 @@ class Preferences(QDialog):
self.config['addZeroSpace'] = self.dialog.addZeroSpace.isChecked()
self.config['alternativeTheme'] = self.dialog.alternativeTheme.isChecked()
self.config['showProgress'] = self.dialog.showProgress.isChecked()
self.config['preventEditUntilAnswer'] = self.dialog.preventEdits.isChecked()
def codeToIndex(self, code):
n = 0

View file

@ -32,6 +32,17 @@ class TagEdit(QLineEdit):
QLineEdit.focusOutEvent(self, evt)
self.emit(SIGNAL("lostFocus"))
def keyPressEvent(self, evt):
if evt.key() in (Qt.Key_Enter, Qt.Key_Return):
evt.accept()
if self.completer.completionCount():
self.setText(
self.completer.pathFromIndex(self.completer.popup().currentIndex()))
else:
self.setText(self.completer.completionPrefix())
return
QLineEdit.keyPressEvent(self, evt)
class TagCompleter(QCompleter):
def __init__(self, model, parent, edit, *args):

View file

@ -189,6 +189,19 @@ def mungeQA(deck, txt):
txt = stripSounds(txt)
return txt
def getBase(deck):
if deck and deck.mediaDir():
if sys.platform.startswith("win32"):
prefix = u"file:///"
else:
prefix = u"file://"
base = prefix + unicode(
urllib.quote(deck.mediaDir().encode("utf-8")),
"utf-8")
return '<base href="%s/">' % base
else:
return ""
class ProgressWin(object):
def __init__(self, parent, max=0, min=0, title=None):

View file

@ -11,7 +11,7 @@ from anki.utils import stripHTML
from anki.hooks import runHook
import types, time, re, os, urllib, sys, difflib
from ankiqt import ui
from ankiqt.ui.utils import mungeQA
from ankiqt.ui.utils import mungeQA, getBase
from anki.utils import fmtTimeSpan
from PyQt4.QtWebKit import QWebPage, QWebView
@ -106,19 +106,8 @@ class View(object):
self.buffer = self.addStyles() + self.buffer
# hook for user css
runHook("preFlushHook")
if self.main.deck and self.main.deck.mediaDir():
if sys.platform.startswith("win32"):
prefix = u"file:///"
else:
prefix = u"file://"
base = prefix + unicode(
urllib.quote(self.main.deck.mediaDir().encode("utf-8")),
"utf-8")
base = '<base href="%s/">' % base
else:
base = ""
self.buffer = '''<html><head>%s</head><body>%s</body></html>''' % (
base, self.buffer)
getBase(self.main.deck), self.buffer)
#print self.buffer.encode("utf-8")
self.body.setHtml(self.buffer)

View file

@ -513,8 +513,11 @@
<height>0</height>
</size>
</property>
<property name="toolTip" >
<string>The &lt;b>number of minutes in a session&lt;/b>. When a session is finished, this screen will be shown again, allowing you to start another session. Choose 0 for no limit.</string>
</property>
<property name="text" >
<string>&lt;b>Limit minutes per session to:&lt;/b></string>
<string>&lt;b>Session limit (minutes):&lt;/b></string>
</property>
<property name="margin" >
<number>4</number>
@ -545,8 +548,11 @@
<height>0</height>
</size>
</property>
<property name="toolTip" >
<string>The maximum number of &lt;b>new cards shown per day&lt;/b>. The default is 20, to ensure you don't get overwhelmed with reviews after a few days.</string>
</property>
<property name="text" >
<string>&lt;b>Limit new cards per day to:&lt;/b></string>
<string>&lt;b>New cards per day:&lt;/b></string>
</property>
<property name="margin" >
<number>4</number>
@ -600,8 +606,11 @@
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label" >
<property name="toolTip" >
<string>The &lt;b>number of questions in a session&lt;/b>. When a session is finished, this screen will be shown again, allowing you to start another session. Choose 0 for no limit.</string>
</property>
<property name="text" >
<string>&lt;b>Limit repetitions per session to:&lt;/b></string>
<string>&lt;b>Session limit (questions):</string>
</property>
<property name="margin" >
<number>4</number>
@ -691,7 +700,7 @@
</size>
</property>
<property name="text" >
<string>More>></string>
<string>More</string>
</property>
<property name="checkable" >
<bool>true</bool>
@ -1103,7 +1112,7 @@
<x>0</x>
<y>0</y>
<width>723</width>
<height>25</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuHelp" >
@ -1118,7 +1127,6 @@
<addaction name="separator" />
<addaction name="actionDonate" />
<addaction name="actionAbout" />
<addaction name="separator" />
</widget>
<widget class="QMenu" name="menuEdit" >
<property name="title" >
@ -1132,6 +1140,7 @@
<addaction name="actionEditdeck" />
<addaction name="separator" />
<addaction name="actionMarkCard" />
<addaction name="actionBuryFact" />
<addaction name="actionSuspendCard" />
<addaction name="actionDelete" />
</widget>
@ -1633,6 +1642,9 @@
<property name="statusTip" >
<string>Stop reviewing this card until it's unsuspended in the browser</string>
</property>
<property name="shortcut" >
<string>Ctrl+Shift+S</string>
</property>
</action>
<action name="actionModelProperties" >
<property name="icon" >
@ -1934,6 +1946,21 @@
<string>Download a plugin to add new features or change Anki's behaviour</string>
</property>
</action>
<action name="actionBuryFact" >
<property name="icon" >
<iconset resource="../icons.qrc" >
<normaloff>:/icons/khtml_kget.png</normaloff>:/icons/khtml_kget.png</iconset>
</property>
<property name="text" >
<string>&amp;Bury Fact</string>
</property>
<property name="statusTip" >
<string>Suspend the current fact until the deck is closed and opened again</string>
</property>
<property name="shortcut" >
<string>Ctrl+Shift+B</string>
</property>
</action>
</widget>
<tabstops>
<tabstop>newPerDay</tabstop>

View file

@ -96,6 +96,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="preventEdits" >
<property name="text" >
<string>Prevent edits until answer shown</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer" >
<property name="orientation" >
@ -415,6 +422,7 @@
<tabstop>splitQA</tabstop>
<tabstop>showEstimates</tabstop>
<tabstop>showProgress</tabstop>
<tabstop>preventEdits</tabstop>
<tabstop>saveWhenClosing</tabstop>
<tabstop>saveAfterEvery</tabstop>
<tabstop>saveAfterEveryNum</tabstop>