many changes - see detailed log below

- don't use psyco
- bump version number
- autosave more often
- no last card period/text by default
- 3 buttons not 5
- default to tray off
- don't show only current card anymore
- simple toolbar
- support for sources
- don't force size of main window
- new button definitions
- remove vertical answer button support
- ditch 'how well did you remember'
- open online deck no longer hidden in new deck dialog
- default to simple model, no more 'what do you want to learn?'
- prompt user for sync username/password if not set
- simplify dialogs, add help buttons, move documentation to wiki
- don't make the timer change colors - distracting, and punishes
  people who practice writing
- remove 'empty deck' message, allow people to add cards from main screen
- remove 'welcome to anki' message. make the initial user
  experience nicer
This commit is contained in:
Damien Elmes 2008-10-10 16:54:30 +09:00
parent e751089c24
commit 8739e164ac
16 changed files with 401 additions and 290 deletions

View file

@ -6,10 +6,10 @@ from PyQt4.QtCore import *
from PyQt4.QtGui import *
appName="Anki"
appVersion="0.9.8.1"
appVersion="0.9.8.2"
appWebsite="http://ichi2.net/anki/download/"
appWiki="http://ichi2.net/anki/wiki/"
appHelpSite="http://ichi2.net/anki/wiki/Documentation"
appHelpSite="http://ichi2.net/anki/wiki/AnkiWiki"
appIssueTracker="http://code.google.com/p/anki/issues/list"
appForum="http://groups.google.com/group/ankisrs/topics"
modDir=os.path.dirname(os.path.abspath(__file__))
@ -87,14 +87,14 @@ def run():
except (IOError, OSError):
pass
import platform
if (platform.processor() != "powerpc" and
platform.architecture()[0] == "32bit"):
try:
import psyco
psyco.profile()
except ImportError:
print "Installing python-psyco is strongly recommended."
# import platform
# if (platform.processor() != "powerpc" and
# platform.architecture()[0] == "32bit"):
# try:
# import psyco
# psyco.profile()
# except ImportError:
# print "Installing python-psyco is strongly recommended."
app.exec_()

View file

@ -38,22 +38,22 @@ class Config(dict):
'showToolbar': True,
'recentDeckPaths': [],
'saveAfterAnswer': True,
'saveAfterAnswerNum': 30,
'saveAfterAnswerNum': 10,
'saveAfterAdding': True,
'saveAfterAddingNum': 10,
'saveAfterAddingNum': 3,
'saveOnClose': True,
'mainWindowSize': QSize(550, 625),
'mainWindowSize': QSize(450, 400),
'mainWindowPos': QPoint(100, 100),
'easeButtonStyle': 'standard',
'easeButtonHeight': 'standard',
'suppressUpdate': False,
'suppressEstimates': False,
'suppressLastCardInterval': False,
'suppressLastCardContent': False,
'showTray': False,
'showLastCardInterval': False,
'showLastCardContent': False,
'showTrayIcon': False,
'showTimer': True,
'editCurrentOnly': True,
'showSuspendedCards': True,
'show3AnswerButtons': True,
'simpleToolbar': True,
}
for (k,v) in fields.items():
if not self.has_key(k):

View file

@ -260,21 +260,15 @@ class EditDeck(QDialog):
def selectLastCard(self):
"Show the row corresponding to the current card."
if self.parent.config['editCurrentOnly']:
if self.parent.currentCard:
self.dialog.filterEdit.setText("<current>")
self.dialog.filterEdit.selectAll()
self.updateSearch()
if not self.parent.config['editCurrentOnly']:
if self.parent.currentCard:
currentCardIndex = self.findCardInDeckModel(
self.model, self.parent.currentCard )
if currentCardIndex >= 0:
self.dialog.tableView.selectRow( currentCardIndex )
self.dialog.tableView.scrollTo(
self.model.index(currentCardIndex,0),
self.dialog.tableView.PositionAtTop )
if self.parent.currentCard:
currentCardIndex = self.findCardInDeckModel(
self.model, self.parent.currentCard )
if currentCardIndex >= 0:
self.dialog.tableView.selectRow( currentCardIndex )
self.dialog.tableView.scrollTo(
self.model.index(currentCardIndex,0),
self.dialog.tableView.PositionAtTop )
def setupFilter(self):
self.filterTimer = None

View file

@ -3,12 +3,13 @@
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys, re
import sys, re, time
import ankiqt.forms
import anki
from ankiqt import ui
from anki.utils import parseTags
from anki.deck import newCardOrderLabels, newCardSchedulingLabels
from anki.utils import hexifyID, dehexifyID
tabs = ("Synchronization",
"Scheduling",
@ -34,6 +35,9 @@ class DeckProperties(QDialog):
self.connect(self.dialog.modelsEdit, SIGNAL("clicked()"), self.onEdit)
self.connect(self.dialog.modelsDelete, SIGNAL("clicked()"), self.onDelete)
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), self.helpRequested)
self.connect(self.dialog.addSource, SIGNAL("clicked()"), self.onAddSource)
self.connect(self.dialog.deleteSource, SIGNAL("clicked()"), self.onDeleteSource)
self.show()
def readData(self):
@ -67,9 +71,37 @@ class DeckProperties(QDialog):
self.dialog.newCardsPerDay.setText(unicode(self.d.newCardsPerDay))
self.dialog.newCardOrder.setCurrentIndex(self.d.newCardOrder)
self.dialog.newCardScheduling.setCurrentIndex(self.d.newCardSpacing)
# sources
self.sources = self.d.s.all("select id, name from sources")
self.sourcesToRemove = []
self.drawSourcesTable()
# models
self.updateModelsList()
def drawSourcesTable(self):
self.dialog.sourcesTable.clear()
self.dialog.sourcesTable.setRowCount(len(self.sources))
self.dialog.sourcesTable.setColumnCount(2)
self.dialog.sourcesTable.setHorizontalHeaderLabels(
QStringList([_("ID"),
_("Name")]))
self.dialog.sourcesTable.horizontalHeader().setResizeMode(
QHeaderView.Stretch)
self.dialog.sourcesTable.verticalHeader().hide()
self.dialog.sourcesTable.setSelectionBehavior(
QAbstractItemView.SelectRows)
self.dialog.sourcesTable.setSelectionMode(
QAbstractItemView.SingleSelection)
self.sourceItems = []
n = 0
for (id, name) in self.sources:
a = QTableWidgetItem(hexifyID(id))
b = QTableWidgetItem(name)
self.sourceItems.append([a, b])
self.dialog.sourcesTable.setItem(n, 0, a)
self.dialog.sourcesTable.setItem(n, 1, b)
n += 1
def updateModelsList(self):
self.dialog.modelsList.clear()
self.models = []
@ -137,6 +169,26 @@ class DeckProperties(QDialog):
"DeckProperties#" +
tabs[idx]))
def onAddSource(self):
(s, ret) = QInputDialog.getText(self, _("Anki"),
_("Source ID:"))
if not s:
return
rc = self.dialog.sourcesTable.rowCount()
self.dialog.sourcesTable.insertRow(rc)
a = QTableWidgetItem(s)
b = QTableWidgetItem("")
self.dialog.sourcesTable.setItem(rc, 0, a)
self.dialog.sourcesTable.setItem(rc, 1, b)
def onDeleteSource(self):
r = self.dialog.sourcesTable.currentRow()
if r == -1:
return
self.dialog.sourcesTable.removeRow(r)
id = self.sources[r][0]
self.sourcesToRemove.append(id)
def reject(self):
# description
self.updateField(self.d, 'description',
@ -194,6 +246,36 @@ class DeckProperties(QDialog):
self.dialog.newCardOrder.currentIndex())
self.updateField(self.d, "newCardSpacing",
self.dialog.newCardScheduling.currentIndex())
# sources
d = {}
d.update(self.sources)
for n in range(self.dialog.sourcesTable.rowCount()):
try:
id = dehexifyID(str(self.dialog.sourcesTable.item(n, 0).text()))
except (ValueError,OverflowError):
continue
name = unicode(self.dialog.sourcesTable.item(n, 1).text())
if id in d:
if d[id] == name:
del d[id]
continue
# name changed
self.d.s.statement(
"update sources set name = :n where id = :id",
id=id, n=name)
else:
self.d.s.statement("""
insert into sources values
(:id, :n, :t, 0, 0)""", id=id, n=name, t=time.time())
self.d.setModified()
try:
del d[id]
except KeyError:
pass
for id in self.sourcesToRemove + d.keys():
self.d.s.statement("delete from sources where id = :id",
id=id)
self.d.setModified()
# mark deck dirty and close
if self.origMod != self.d.modified:
self.parent.reset()

View file

@ -12,7 +12,7 @@ import ankiqt.forms
class HelpArea(object):
helpAreaWidth = 300
minAppWidth = 500
minAppWidth = 400
def __init__(self, helpFrame, config, mainWindow=None, focus=None):
self.helpFrame = helpFrame
@ -27,16 +27,11 @@ class HelpArea(object):
self.anchorClicked)
self.hide()
def getMinAppWidth(self):
if self.config['easeButtonStyle'] == 'compact':
return self.minAppWidth - 150
return self.minAppWidth
def show(self):
"Show the help area."
if self.mainWindow:
self.mainWindow.setMinimumWidth(
self.getMinAppWidth()+self.helpAreaWidth)
self.minAppWidth+self.helpAreaWidth)
self.helpFrame.show()
self.widget.show()
@ -45,14 +40,6 @@ class HelpArea(object):
self.helpFrame.hide()
self.widget.hide()
if self.mainWindow:
self.mainWindow.setMinimumWidth(self.getMinAppWidth())
# force resize
g = self.mainWindow.geometry()
if g.width() < self.getMinAppWidth():
self.mainWindow.setGeometry(QRect(g.left(),
g.top(),
self.getMinAppWidth(),
g.height()))
self.mainWindow.runHook("helpChanged")
def showKey(self, key, noFlush=False, dict=False):

View file

@ -33,6 +33,7 @@ class AnkiQt(QMainWindow):
self.app = app
self.config = config
self.deck = None
self.state = "initial"
self.views = []
self.setLang()
self.setupFonts()
@ -55,9 +56,7 @@ class AnkiQt(QMainWindow):
self.addView(self.statusView)
self.setupButtons()
self.setupAnchors()
if not self.config['showToolbar']:
self.removeToolBar(self.mainWin.toolBar)
self.mainWin.toolBar.hide()
self.setupToolbar()
self.show()
if sys.platform.startswith("darwin"):
self.setUnifiedTitleAndToolBarOnMac(True)
@ -74,7 +73,7 @@ class AnkiQt(QMainWindow):
self.runHook('init')
except:
print _("Error running initHook. Broken plugin?")
print traceback.print_exc()
traceback.print_exc()
# check for updates
self.setupAutoUpdate()
@ -331,76 +330,50 @@ class AnkiQt(QMainWindow):
for i in range(5):
s=self.deck.nextIntervalStr(self.currentCard, i)
nextInts["ease%d" % i] = s
text = (
(_("Completely forgot"), ""),
(_("Made a mistake"), ""),
(_("Difficult"),
_("Next in <b>%(ease2)s</b>")),
(_("About right"),
_("Next in <b>%(ease3)s</b>")),
(_("Easy"),
_("Next in <b>%(ease4)s</b>")))
# button grid
grid = QGridLayout()
grid.setSpacing(3)
if self.config['easeButtonStyle'] == 'standard':
button3 = self.showStandardEaseButtons(grid, nextInts, text)
if self.config['show3AnswerButtons']:
rng = (1, 4)
else:
button3 = self.showCompactEaseButtons(grid, nextInts)
self.buttonBox.addLayout(grid)
rng = (0, 5)
button3 = self.showCompactEaseButtons(grid, nextInts, rng)
hbox = QHBoxLayout()
hbox.addStretch()
hbox.addLayout(grid)
hbox.addStretch()
self.buttonBox.addLayout(hbox)
button3.setFocus()
def showStandardEaseButtons(self, grid, nextInts, text):
# show 'how well?' message
hbox = QHBoxLayout()
hbox.addItem(self.getSpacer(QSizePolicy.Expanding))
label = QLabel(self.withInterfaceFont(
_("<b>How well did you remember?</b>")))
hbox.addWidget(label)
hbox.addItem(self.getSpacer(QSizePolicy.Expanding))
self.buttonBox.addLayout(hbox)
# populate buttons
button3 = None
for i in range(5):
button = QPushButton(str(i))
button.setFixedWidth(100)
button.setFixedHeight(self.easeButtonHeight)
if i == 3:
button3 = button
grid.addItem(self.getSpacer(QSizePolicy.Expanding), i, 0)
grid.addWidget(button, i, 1)
grid.addItem(self.getSpacer(), i, 2)
grid.addWidget(QLabel(self.withInterfaceFont(text[i][0])), i, 3)
grid.addItem(self.getSpacer(), i, 4)
if not self.config['suppressEstimates']:
grid.addWidget(QLabel(self.withInterfaceFont(
text[i][1] % nextInts)), i, 5)
grid.addItem(self.getSpacer(QSizePolicy.Expanding), i, 6)
self.connect(button, SIGNAL("clicked()"),
lambda i=i: self.cardAnswered(i))
return button3
def showCompactEaseButtons(self, grid, nextInts):
def showCompactEaseButtons(self, grid, nextInts, rng):
text = (
(_("<b>%(ease0)s</b>")),
(_("<b>%(ease1)s</b>")),
(_("<b>%(ease2)s</b>")),
(_("<b>%(ease3)s</b>")),
(_("<b>%(ease4)s</b>")))
(_("<b>%(ease0)s</b>"),
_("<b>Reset.</b><br>You've completely forgotten.")),
(_("<b>%(ease1)s</b>"),
_("<b>Too difficult.</b><br>Show this card again soon.")),
(_("<b>%(ease2)s</b>"),
_("<b>Challenging.</b><br>Wait a little longer next time.")),
(_("<b>%(ease3)s</b>"),
_("<b>Comfortable.</b><br>Wait longer next time.")),
(_("<b>%(ease4)s</b>"),
_("<b>Too easy.</b><br>Wait a lot longer next time.")))
button3 = None
for i in range(5):
for i in range(*rng):
if not self.config['suppressEstimates']:
label = QLabel(self.withInterfaceFont(text[i][0] % nextInts))
label.setAlignment(Qt.AlignHCenter)
grid.addWidget(label, 0, (i*2)+1)
button = QPushButton(str(i))
button.setFixedHeight(self.easeButtonHeight)
if rng[0] == 1:
button.setFixedWidth(120)
button.setToolTip(text[i][1])
self.connect(button, SIGNAL("clicked()"),
lambda i=i: self.cardAnswered(i))
#button.setFixedWidth(70)
if i == 3:
button3 = button
grid.addWidget(button, 0, (i*2)+1)
if not self.config['suppressEstimates']:
label = QLabel(self.withInterfaceFont(text[i] % nextInts))
label.setAlignment(Qt.AlignHCenter)
grid.addWidget(label, 1, (i*2)+1)
self.connect(button, SIGNAL("clicked()"),
lambda i=i: self.cardAnswered(i))
grid.addWidget(button, 1, (i*2)+1)
return button3
def withInterfaceFont(self, text):
@ -508,10 +481,8 @@ class AnkiQt(QMainWindow):
f = unicode(args[0], sys.getfilesystemencoding())
return self.loadDeck(f)
except:
sys.stderr.write("Error loading last deck.\n")
sys.stderr.write("Error loading deck path.\n")
traceback.print_exc()
self.deck = None
return self.moveToState("initial")
# try recent deck paths
for path in self.config['recentDeckPaths']:
try:
@ -523,9 +494,7 @@ class AnkiQt(QMainWindow):
except:
sys.stderr.write("Error loading last deck.\n")
traceback.print_exc()
self.deck = None
return self.moveToState("initial")
return self.moveToState("initial")
self.onNew()
def getDefaultDir(self, save=False):
"Try and get default dir from most recently opened file."
@ -650,19 +619,13 @@ class AnkiQt(QMainWindow):
self.saveDeck()
self.moveToState("initial")
def onOpenOnline(self):
if not self.saveAndClose(exit=True): return
self.deck = DeckStorage.Deck()
# ensure all changes come to us
self.deck.syncName = None
self.deck.modified = 0
self.deck.lastLoaded = self.deck.modified
def ensureSyncParams(self):
if not self.config['syncUsername'] or not self.config['syncPassword']:
d = QDialog(self)
vbox = QVBoxLayout()
l = QLabel(_(
'<h1>Open Online Deck</h1>'
'To load a deck from your free <a href="http://anki.ichi2.net/">online account</a>,<br>'
'<h1>Online Account</h1>'
'To use your free <a href="http://anki.ichi2.net/">online account</a>,<br>'
"please enter your details below.<br>"))
l.setOpenExternalLinks(True)
vbox.addWidget(l)
@ -684,6 +647,15 @@ class AnkiQt(QMainWindow):
d.exec_()
self.config['syncUsername'] = unicode(user.text())
self.config['syncPassword'] = unicode(passwd.text())
def onOpenOnline(self):
self.ensureSyncParams()
if not self.saveAndClose(exit=True): return
self.deck = DeckStorage.Deck()
# ensure all changes come to us
self.deck.syncName = None
self.deck.modified = 0
self.deck.lastLoaded = self.deck.modified
if self.config['syncUsername'] and self.config['syncPassword']:
if self.syncDeck(onlyMerge=True):
return
@ -790,6 +762,12 @@ class AnkiQt(QMainWindow):
self.onOpenSamples()
elif str == "open":
self.onOpen()
elif str == "openrem":
self.onOpenOnline()
if str == "addfacts":
if not self.deck:
self.onNew()
self.onAddCard()
def setupAnchors(self):
self.anchorPrefixes = {
@ -810,6 +788,26 @@ class AnkiQt(QMainWindow):
# open in browser
QDesktopServices.openUrl(QUrl(url))
# Toolbar
##########################################################################
def setupToolbar(self):
if not self.config['showToolbar']:
self.removeToolBar(self.mainWin.toolBar)
self.mainWin.toolBar.hide()
if self.config['simpleToolbar']:
mw = self.mainWin
self.removeToolBar(mw.toolBar)
self.mainWin.toolBar.hide()
mw.toolBar = QToolBar(self)
mw.toolBar.addAction(mw.actionAddcards)
mw.toolBar.addAction(mw.actionEditdeck)
mw.toolBar.addAction(mw.actionRepeatAudio)
mw.toolBar.addAction(mw.actionMarkCard)
mw.toolBar.addAction(mw.actionGraphs)
mw.toolBar.addAction(mw.actionDisplayProperties)
self.addToolBar(Qt.TopToolBarArea, mw.toolBar)
# Tools - looking up words in the dictionary
##########################################################################
@ -1017,6 +1015,7 @@ class AnkiQt(QMainWindow):
reload=True, checkSources=True):
"Synchronise a deck with the server."
# vet input
self.ensureSyncParams()
u=self.config['syncUsername']
p=self.config['syncPassword']
if not u or not p:
@ -1138,46 +1137,48 @@ class AnkiQt(QMainWindow):
)
def connectMenuActions(self):
self.connect(self.mainWin.actionNew, SIGNAL("triggered()"), self.onNew)
self.connect(self.mainWin.actionOpenOnline, SIGNAL("triggered()"), self.onOpenOnline)
self.connect(self.mainWin.actionOpen, SIGNAL("triggered()"), self.onOpen)
self.connect(self.mainWin.actionOpenSamples, SIGNAL("triggered()"), self.onOpenSamples)
self.connect(self.mainWin.actionSave, SIGNAL("triggered()"), self.onSave)
self.connect(self.mainWin.actionSaveAs, SIGNAL("triggered()"), self.onSaveAs)
self.connect(self.mainWin.actionClose, SIGNAL("triggered()"), self.saveAndClose)
self.connect(self.mainWin.actionExit, SIGNAL("triggered()"), self, SLOT("close()"))
self.connect(self.mainWin.actionSyncdeck, SIGNAL("triggered()"), self.syncDeck)
self.connect(self.mainWin.actionDeckProperties, SIGNAL("triggered()"), self.onDeckProperties)
self.connect(self.mainWin.actionDisplayProperties, SIGNAL("triggered()"),self.onDisplayProperties)
self.connect(self.mainWin.actionAddcards, SIGNAL("triggered()"), self.onAddCard)
self.connect(self.mainWin.actionEditdeck, SIGNAL("triggered()"), self.onEditDeck)
self.connect(self.mainWin.actionPreferences, SIGNAL("triggered()"), self.onPrefs)
self.connect(self.mainWin.actionLookup_es, SIGNAL("triggered()"), self.onLookupEdictSelection)
self.connect(self.mainWin.actionLookup_esk, SIGNAL("triggered()"), self.onLookupEdictKanjiSelection)
self.connect(self.mainWin.actionLookup_expr, SIGNAL("triggered()"), self.onLookupExpression)
self.connect(self.mainWin.actionLookup_mean, SIGNAL("triggered()"), self.onLookupMeaning)
self.connect(self.mainWin.actionLookup_as, SIGNAL("triggered()"), self.onLookupAlcSelection)
self.connect(self.mainWin.actionDstats, SIGNAL("triggered()"), self.onDeckStats)
self.connect(self.mainWin.actionKstats, SIGNAL("triggered()"), self.onKanjiStats)
self.connect(self.mainWin.actionCstats, SIGNAL("triggered()"), self.onCardStats)
self.connect(self.mainWin.actionGraphs, SIGNAL("triggered()"), self.onShowGraph)
self.connect(self.mainWin.actionAbout, SIGNAL("triggered()"), self.onAbout)
self.connect(self.mainWin.actionReportbug, SIGNAL("triggered()"), self.onReportBug)
self.connect(self.mainWin.actionForum, SIGNAL("triggered()"), self.onForum)
self.connect(self.mainWin.actionStarthere, SIGNAL("triggered()"), self.onStartHere)
self.connect(self.mainWin.actionImport, SIGNAL("triggered()"), self.onImport)
self.connect(self.mainWin.actionExport, SIGNAL("triggered()"), self.onExport)
self.connect(self.mainWin.actionMarkCard, SIGNAL("toggled(bool)"), self.onMark)
self.connect(self.mainWin.actionSuspendCard, SIGNAL("triggered()"), self.onSuspend)
self.connect(self.mainWin.actionModelProperties, SIGNAL("triggered()"), self.onModelProperties)
self.connect(self.mainWin.actionRepeatQuestionAudio, SIGNAL("triggered()"), self.onRepeatQuestion)
self.connect(self.mainWin.actionRepeatAnswerAudio, SIGNAL("triggered()"), self.onRepeatAnswer)
self.connect(self.mainWin.actionRepeatAudio, SIGNAL("triggered()"), self.onRepeatAudio)
self.connect(self.mainWin.actionUndoAnswer, SIGNAL("triggered()"), self.onUndoAnswer)
self.connect(self.mainWin.actionCheckDatabaseIntegrity, SIGNAL("triggered()"), self.onCheckDB)
self.connect(self.mainWin.actionOptimizeDatabase, SIGNAL("triggered()"), self.onOptimizeDB)
self.connect(self.mainWin.actionMergeModels, SIGNAL("triggered()"), self.onMergeModels)
self.connect(self.mainWin.actionCheckMediaDatabase, SIGNAL("triggered()"), self.onCheckMediaDB)
m = self.mainWin
s = SIGNAL("triggered()")
self.connect(m.actionNew, s, self.onNew)
self.connect(m.actionOpenOnline, s, self.onOpenOnline)
self.connect(m.actionOpen, s, self.onOpen)
self.connect(m.actionOpenSamples, s, self.onOpenSamples)
self.connect(m.actionSave, s, self.onSave)
self.connect(m.actionSaveAs, s, self.onSaveAs)
self.connect(m.actionClose, s, self.saveAndClose)
self.connect(m.actionExit, s, self, SLOT("close()"))
self.connect(m.actionSyncdeck, s, self.syncDeck)
self.connect(m.actionDeckProperties, s, self.onDeckProperties)
self.connect(m.actionDisplayProperties, s,self.onDisplayProperties)
self.connect(m.actionAddcards, s, self.onAddCard)
self.connect(m.actionEditdeck, s, self.onEditDeck)
self.connect(m.actionPreferences, s, self.onPrefs)
self.connect(m.actionLookup_es, s, self.onLookupEdictSelection)
self.connect(m.actionLookup_esk, s, self.onLookupEdictKanjiSelection)
self.connect(m.actionLookup_expr, s, self.onLookupExpression)
self.connect(m.actionLookup_mean, s, self.onLookupMeaning)
self.connect(m.actionLookup_as, s, self.onLookupAlcSelection)
self.connect(m.actionDstats, s, self.onDeckStats)
self.connect(m.actionKstats, s, self.onKanjiStats)
self.connect(m.actionCstats, s, self.onCardStats)
self.connect(m.actionGraphs, s, self.onShowGraph)
self.connect(m.actionAbout, s, self.onAbout)
self.connect(m.actionReportbug, s, self.onReportBug)
self.connect(m.actionForum, s, self.onForum)
self.connect(m.actionStarthere, s, self.onStartHere)
self.connect(m.actionImport, s, self.onImport)
self.connect(m.actionExport, s, self.onExport)
self.connect(m.actionMarkCard, SIGNAL("toggled(bool)"), self.onMark)
self.connect(m.actionSuspendCard, s, self.onSuspend)
self.connect(m.actionModelProperties, s, self.onModelProperties)
self.connect(m.actionRepeatQuestionAudio, s, self.onRepeatQuestion)
self.connect(m.actionRepeatAnswerAudio, s, self.onRepeatAnswer)
self.connect(m.actionRepeatAudio, s, self.onRepeatAudio)
self.connect(m.actionUndoAnswer, s, self.onUndoAnswer)
self.connect(m.actionCheckDatabaseIntegrity, s, self.onCheckDB)
self.connect(m.actionOptimizeDatabase, s, self.onOptimizeDB)
self.connect(m.actionMergeModels, s, self.onMergeModels)
self.connect(m.actionCheckMediaDatabase, s, self.onCheckMediaDB)
def enableDeckMenuItems(self, enabled=True):
"setEnabled deck-related items."

View file

@ -192,6 +192,8 @@ class AddModel(QDialog):
# the list widget will swallow the enter key
s = QShortcut(QKeySequence("Return"), self)
self.connect(s, SIGNAL("activated()"), self.accept)
# help
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), self.onHelp)
def getModel(self):
self.exec_()
@ -202,3 +204,6 @@ class AddModel(QDialog):
unicode(self.dialog.models.currentItem().text())]
QDialog.accept(self)
def onHelp(self):
QDesktopServices.openUrl(QUrl(ankiqt.appWiki +
"AddModel"))

View file

@ -9,6 +9,10 @@ import anki
from anki.models import FieldModel, CardModel
from ankiqt import ui
tabs = ("General",
"Fields",
"Cards")
class ModelProperties(QDialog):
def __init__(self, parent, model, main=None, onFinish=None):
@ -22,6 +26,8 @@ class ModelProperties(QDialog):
self.onFinish = onFinish
self.dialog = ankiqt.forms.modelproperties.Ui_ModelProperties()
self.dialog.setupUi(self)
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"),
self.helpRequested)
self.setupFields()
self.setupCards()
self.readData()
@ -431,6 +437,12 @@ class ModelProperties(QDialog):
self.updateCards(row + 1)
self.ignoreCardUpdate = False
def helpRequested(self):
idx = self.dialog.tabWidget.currentIndex()
QDesktopServices.openUrl(QUrl(ankiqt.appWiki +
"ModelProperties#" +
tabs[idx]))
# Cleanup
##########################################################################

View file

@ -11,6 +11,10 @@ from anki.stdmodels import JapaneseModel
from ankiqt import ui
import ankiqt.forms
tabs = ("Display",
"SaveAndSync",
"Advanced")
class Preferences(QDialog):
def __init__(self, parent, config):
@ -30,6 +34,7 @@ class Preferences(QDialog):
(_("Korean"), "ko_KR"),
(_("Spanish"), "es_ES"),
]
self.connect(self.dialog.buttonBox, SIGNAL("helpRequested()"), self.helpRequested)
self.setupLang()
self.setupFont()
self.setupColour()
@ -158,32 +163,29 @@ class Preferences(QDialog):
def setupAdvanced(self):
self.dialog.showToolbar.setChecked(self.config['showToolbar'])
self.dialog.compactEaseButtons.setChecked(
self.config['easeButtonStyle'] != 'standard')
self.config['show3AnswerButtons'])
self.dialog.tallButtons.setChecked(
self.config['easeButtonHeight'] != 'standard')
self.dialog.suppressEstimates.setChecked(self.config['suppressEstimates'])
self.dialog.suppressLastCardInterval.setChecked(self.config['suppressLastCardInterval'])
self.dialog.suppressLastCardContent.setChecked(self.config['suppressLastCardContent'])
self.dialog.showTray.setChecked(self.config['showTray'])
self.dialog.showLastCardInterval.setChecked(self.config['showLastCardInterval'])
self.dialog.showLastCardContent.setChecked(self.config['showLastCardContent'])
self.dialog.showTray.setChecked(self.config['showTrayIcon'])
self.dialog.showTimer.setChecked(self.config['showTimer'])
self.dialog.editCurrentOnly.setChecked(self.config['editCurrentOnly'])
self.dialog.simpleToolbar.setChecked(self.config['simpleToolbar'])
def updateAdvanced(self):
self.config['showToolbar'] = self.dialog.showToolbar.isChecked()
if self.dialog.compactEaseButtons.isChecked():
self.config['easeButtonStyle'] = 'compact'
else:
self.config['easeButtonStyle'] = 'standard'
self.config['show3AnswerButtons'] = self.dialog.compactEaseButtons.isChecked()
if self.dialog.tallButtons.isChecked():
self.config['easeButtonHeight'] = 'tall'
else:
self.config['easeButtonHeight'] = 'standard'
self.config['suppressLastCardInterval'] = self.dialog.suppressLastCardInterval.isChecked()
self.config['suppressLastCardContent'] = self.dialog.suppressLastCardContent.isChecked()
self.config['showTray'] = self.dialog.showTray.isChecked()
self.config['showLastCardInterval'] = self.dialog.showLastCardInterval.isChecked()
self.config['showLastCardContent'] = self.dialog.showLastCardContent.isChecked()
self.config['showTrayIcon'] = self.dialog.showTray.isChecked()
self.config['showTimer'] = self.dialog.showTimer.isChecked()
self.config['suppressEstimates'] = self.dialog.suppressEstimates.isChecked()
self.config['editCurrentOnly'] = self.dialog.editCurrentOnly.isChecked()
self.config['simpleToolbar'] = self.dialog.simpleToolbar.isChecked()
def codeToIndex(self, code):
n = 0
@ -193,3 +195,9 @@ class Preferences(QDialog):
n += 1
# default to english
return self.codeToIndex("en_US")
def helpRequested(self):
idx = self.dialog.tabWidget.currentIndex()
QDesktopServices.openUrl(QUrl(ankiqt.appWiki +
"Preferences#" +
tabs[idx]))

View file

@ -248,13 +248,9 @@ You should aim to answer each question within<br>
if self.main.state in "showQuestion":
t = self.main.currentCard.thinkingTime()
if t < 60:
if t < StatusView.warnTime:
col="#000000"
else:
col="#FF3300"
self.timer.setText('<font color="%s">00:%02d</font>' % (col, t))
self.timer.setText('00:%02d' % t)
else:
self.timer.setText('<font color="#FF0000"><b>01:00</b></font>')
self.timer.setText('01:00')
elif self.main.state == "showAnswer":
pass
else:
@ -262,4 +258,4 @@ You should aim to answer each question within<br>
def onConfigure(self):
self.main.deckProperties = ui.deckproperties.DeckProperties(self.main)
self.main.deckProperties.dialog.qtabwidget.setCurrentIndex(2)
self.main.deckProperties.dialog.qtabwidget.setCurrentIndex(1)

View file

@ -14,7 +14,7 @@ class AnkiTrayIcon( QtCore.QObject ):
self.mw = mw
self.anki_visible = True
if (QtGui.QSystemTrayIcon.isSystemTrayAvailable() and
mw.config['showTray']):
mw.config['showTrayIcon']):
self.ti = QtGui.QSystemTrayIcon( mw )
if self.ti:
self.mw.addHook("quit", self.onQuit)

View file

@ -36,7 +36,7 @@ class View(object):
return
elif self.state == "noDeck":
self.clearWindow()
self.drawNoDeckMessage()
self.drawWelcomeMessage()
self.flush()
return
self.redisplay()
@ -50,8 +50,8 @@ class View(object):
self.maybeHelp()
if self.main.deck.cardCount():
if not self.main.lastCard or (
self.main.config['suppressLastCardContent'] and
self.main.config['suppressLastCardInterval']):
not self.main.config['showLastCardContent'] and
not self.main.config['showLastCardInterval']):
self.buffer += "<br>"
else:
self.drawTopSection()
@ -62,7 +62,7 @@ class View(object):
self.drawQuestion(nosound=True)
self.drawAnswer()
elif self.state == "deckEmpty":
self.drawDeckEmptyMessage()
self.drawWelcomeMessage()
elif self.state == "deckFinished":
self.drawDeckFinishedMessage()
self.flush()
@ -138,7 +138,7 @@ class View(object):
def drawLastCard(self):
"Show the last card if not the current one, and next time."
if self.main.lastCard:
if not self.main.config['suppressLastCardContent']:
if self.main.config['showLastCardContent']:
if (self.state == "deckFinished" or
self.main.currentCard.id != self.main.lastCard.id):
q = self.main.lastCard.question.replace("<br>", " ")
@ -152,7 +152,7 @@ class View(object):
s = "%s<br>%s" % (q, a)
s = stripLatex(s)
self.write('<span class="lastCard">%s</span><br>' % s)
if not self.main.config['suppressLastCardInterval']:
if self.main.config['showLastCardInterval']:
if self.main.lastQuality > 1:
msg = _("Well done! This card will appear again in "
"<b>%(next)s</b>.") % \
@ -162,6 +162,7 @@ class View(object):
"<b>%(next)s</b>.") % \
{"next":self.main.lastScheduledTime}
self.write(msg)
self.write("<br>")
# Help
##########################################################################
@ -189,39 +190,47 @@ class View(object):
# Welcome/empty/finished deck messages
##########################################################################
def drawNoDeckMessage(self):
self.write(_("""<h1>Welcome to Anki!</h1>
def drawWelcomeMessage(self):
self.write(_("""
<h1>Welcome to Anki!</h1>
<p>
<table>
<tr>
<td width=50>
<a href="welcome:addfacts"><img src=":/icons/list-add.png"></a>
</td>
<td valign=middle><h1><a href="welcome:addfacts">Add material</a></h1>
Start adding your own material.</td>
</tr>
</table>
<br>
<table>
<tr>
<td>
<a href="welcome:new"><img src=":/icons/document-new.png"></a>
<a href="welcome:open"><img src=":/icons/document-open.png"></a>
</td>
<td valign=middle>
<h2><a href="welcome:new">Create a new deck</a></h2></td>
<td valign=middle><h2><a href="welcome:open">Open Local Deck</a></h2></td>
</tr>
<tr>
<td>
<a href="welcome:openrem"><img src=":/icons/document-open-remote.png"></a>
</td>
<td valign=middle><h2><a href="welcome:openrem">Open Online Deck</a></h2></td>
</tr>
<tr>
<td width=50>
<a href="welcome:sample"><img src=":/icons/anki.png"></a>
</td>
<td valign=middle><h2><a href="welcome:sample">Open a sample deck</a></h2></td>
<td valign=middle><h2><a href="welcome:sample">Open Sample Deck</a></h2></td>
</tr>
<tr>
<td>
<a href="welcome:open"><img src=":/icons/document-open.png"></a>
</td>
<td valign=middle><h2><a href="welcome:open">Open an existing deck</a></h2></td>
</tr>
</table>
"""))
def drawDeckEmptyMessage(self):
"Tell the user the deck is empty."
self.write(_("""
<h1>Empty deck</h1>The current deck has no cards in it. Please select 'Add
card' from the Edit menu."""))
</table>"""))
def drawDeckFinishedMessage(self):
"Tell the user the deck is finished."

View file

@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>388</width>
<height>456</height>
<width>285</width>
<height>269</height>
</rect>
</property>
<property name="windowTitle" >

View file

@ -29,8 +29,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>413</width>
<height>385</height>
<width>407</width>
<height>373</height>
</rect>
</property>
<attribute name="title" >
@ -72,7 +72,7 @@
</property>
</widget>
</item>
<item row="3" column="0" >
<item row="4" column="0" >
<widget class="QLabel" name="label_28" >
<property name="maximumSize" >
<size>
@ -81,38 +81,48 @@
</size>
</property>
<property name="text" >
<string>&lt;h1>Sources&lt;h1></string>
<string>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Arial'; font-size:8pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:xx-large; font-weight:600;">&lt;span style=" font-size:xx-large;">Sources&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QListWidget" name="listWidget" />
</item>
<item row="5" column="0" >
<item row="6" column="0" >
<layout class="QHBoxLayout" name="horizontalLayout_3" >
<item>
<widget class="QPushButton" name="addSource" >
<property name="text" >
<string>Add Source</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editSource" >
<property name="text" >
<string>Edit Source</string>
<string>&amp;Add Source</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteSource" >
<property name="text" >
<string>Delete Source</string>
<string>&amp;Delete Source</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" >
<widget class="QTableWidget" name="sourcesTable" />
</item>
<item row="3" column="0" >
<spacer name="verticalSpacer_4" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
@ -633,9 +643,8 @@
<tabstop>qtabwidget</tabstop>
<tabstop>doSync</tabstop>
<tabstop>syncName</tabstop>
<tabstop>listWidget</tabstop>
<tabstop>sourcesTable</tabstop>
<tabstop>addSource</tabstop>
<tabstop>editSource</tabstop>
<tabstop>deleteSource</tabstop>
<tabstop>newCardsPerDay</tabstop>
<tabstop>failedCardMax</tabstop>

View file

@ -6,7 +6,7 @@
<x>0</x>
<y>0</y>
<width>543</width>
<height>457</height>
<height>285</height>
</rect>
</property>
<property name="sizePolicy" >
@ -28,7 +28,7 @@
<x>0</x>
<y>53</y>
<width>543</width>
<height>384</height>
<height>212</height>
</rect>
</property>
<property name="sizePolicy" >
@ -194,7 +194,7 @@
<item>
<widget class="QFrame" name="innerHelpFrame" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -285,7 +285,7 @@
<bool>true</bool>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<sizepolicy vsizetype="Expanding" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -472,7 +472,7 @@
<property name="geometry" >
<rect>
<x>0</x>
<y>437</y>
<y>265</y>
<width>543</width>
<height>20</height>
</rect>

View file

@ -5,8 +5,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>532</width>
<height>531</height>
<width>322</width>
<height>417</height>
</rect>
</property>
<property name="windowTitle" >
@ -23,12 +23,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>510</width>
<height>449</height>
<width>306</width>
<height>351</height>
</rect>
</property>
<attribute name="title" >
<string>Language, Fonts and Colours</string>
<string>Display</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
@ -42,7 +42,7 @@
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>&lt;h1>Interface language&lt;/h1>The language for the user interface: dialogs, menus, etc.</string>
<string>&lt;h1>Language&lt;/h1></string>
</property>
<property name="wordWrap" >
<bool>false</bool>
@ -68,7 +68,7 @@
</sizepolicy>
</property>
<property name="text" >
<string>&lt;h1>Standard fonts&lt;/h1>See 'display properties' for deck specific font preferences.</string>
<string>&lt;h1>Fonts&lt;/h1></string>
</property>
<property name="wordWrap" >
<bool>false</bool>
@ -169,7 +169,7 @@
</sizepolicy>
</property>
<property name="text" >
<string>&lt;h1>Standard colours&lt;/h1>These colours are used for all decks.</string>
<string>&lt;h1>Colours&lt;/h1></string>
</property>
<property name="wordWrap" >
<bool>false</bool>
@ -250,12 +250,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>453</width>
<height>443</height>
<width>306</width>
<height>351</height>
</rect>
</property>
<attribute name="title" >
<string>Autosave and Synchronisation</string>
<string>Save &amp;&amp; Sync</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="spacing" >
@ -283,7 +283,7 @@
<item>
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>&lt;h1>Autosaving&lt;/h1>Anki can save your progress automatically.</string>
<string>&lt;h1>Autosaving&lt;/h1></string>
</property>
</widget>
</item>
@ -350,7 +350,7 @@
<item>
<widget class="QLabel" name="label_16" >
<property name="text" >
<string>&lt;h1>Synchronisation&lt;/h1>Synchronisation enables you to access your deck from the web and your mobile phone. You can &lt;a href="http://anki.ichi2.net/">create a free account&lt;/a>.</string>
<string>&lt;h1>Synchronisation&lt;/h1>&lt;a href="http://anki.ichi2.net/">Create a free account&lt;/a>.</string>
</property>
<property name="wordWrap" >
<bool>true</bool>
@ -419,7 +419,7 @@
</layout>
</item>
<item>
<spacer>
<spacer name="verticalSpacer_2" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
@ -438,30 +438,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>453</width>
<height>443</height>
<width>306</width>
<height>351</height>
</rect>
</property>
<attribute name="title" >
<string>Advanced Settings</string>
<string>Advanced</string>
</attribute>
<layout class="QVBoxLayout" >
<item>
<layout class="QGridLayout" >
<item row="1" column="0" >
<widget class="QCheckBox" name="showToolbar" >
<property name="text" >
<string>Show toolbar on startup</string>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="compactEaseButtons" >
<property name="text" >
<string>Use compact answer button style</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_6" >
<property name="text" >
@ -469,55 +455,66 @@
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QCheckBox" name="tallButtons" >
<item row="1" column="0" >
<widget class="QCheckBox" name="compactEaseButtons" >
<property name="text" >
<string>Tall buttons (for touchscreen)</string>
<string>Show 3 answer buttons, not 5</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QCheckBox" name="showTimer" >
<property name="text" >
<string>Show timer</string>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QCheckBox" name="editCurrentOnly" >
<property name="text" >
<string>Start Edit Deck with only current card selected</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QCheckBox" name="suppressEstimates" >
<property name="text" >
<string>Hide next interval when showing answer buttons</string>
</property>
</widget>
</item>
<item row="6" column="0" >
<widget class="QCheckBox" name="suppressLastCardInterval" >
<item row="5" column="0" >
<widget class="QCheckBox" name="showLastCardContent" >
<property name="text" >
<string>Hide interval of last card</string>
<string>Show last card's question/answer</string>
</property>
</widget>
</item>
<item row="6" column="0" >
<widget class="QCheckBox" name="showLastCardInterval" >
<property name="text" >
<string>Show last card's interval</string>
</property>
</widget>
</item>
<item row="7" column="0" >
<widget class="QCheckBox" name="suppressLastCardContent" >
<widget class="QCheckBox" name="showToolbar" >
<property name="text" >
<string>Hide question/answer of last card</string>
<string>Show toolbar on startup</string>
</property>
</widget>
</item>
<item row="8" column="0" >
<widget class="QCheckBox" name="tallButtons" >
<property name="text" >
<string>Tall buttons (for touchscreen)</string>
</property>
</widget>
</item>
<item row="9" column="0" >
<widget class="QCheckBox" name="showTray" >
<property name="text" >
<string>Show tray icon</string>
</property>
</widget>
</item>
<item row="9" column="0" >
<widget class="QCheckBox" name="showTimer" >
<item row="2" column="0" >
<widget class="QCheckBox" name="simpleToolbar" >
<property name="text" >
<string>Show timer</string>
<string>Simple toolbar</string>
</property>
</widget>
</item>
@ -546,11 +543,13 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<zorder>buttonBox</zorder>
<zorder>tabWidget</zorder>
</widget>
<tabstops>
<tabstop>tabWidget</tabstop>
@ -573,6 +572,15 @@
<tabstop>syncPass</tabstop>
<tabstop>syncOnOpen</tabstop>
<tabstop>syncOnClose</tabstop>
<tabstop>compactEaseButtons</tabstop>
<tabstop>simpleToolbar</tabstop>
<tabstop>showTimer</tabstop>
<tabstop>suppressEstimates</tabstop>
<tabstop>showLastCardContent</tabstop>
<tabstop>showLastCardInterval</tabstop>
<tabstop>showToolbar</tabstop>
<tabstop>tallButtons</tabstop>
<tabstop>showTray</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>