From 8739e164acd47c53d2e7312f055c1a23b4528667 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 10 Oct 2008 16:54:30 +0900 Subject: [PATCH] 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 --- ankiqt/__init__.py | 20 +-- ankiqt/config.py | 16 +-- ankiqt/ui/cardlist.py | 22 ++-- ankiqt/ui/deckproperties.py | 84 ++++++++++++- ankiqt/ui/help.py | 17 +-- ankiqt/ui/main.py | 233 ++++++++++++++++++----------------- ankiqt/ui/modelchooser.py | 5 + ankiqt/ui/modelproperties.py | 12 ++ ankiqt/ui/preferences.py | 34 +++-- ankiqt/ui/status.py | 10 +- ankiqt/ui/tray.py | 2 +- ankiqt/ui/view.py | 61 +++++---- designer/addmodel.ui | 4 +- designer/deckproperties.ui | 47 ++++--- designer/main.ui | 10 +- designer/preferences.ui | 114 +++++++++-------- 16 files changed, 401 insertions(+), 290 deletions(-) diff --git a/ankiqt/__init__.py b/ankiqt/__init__.py index f8117d37e..2b6feea8b 100644 --- a/ankiqt/__init__.py +++ b/ankiqt/__init__.py @@ -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_() diff --git a/ankiqt/config.py b/ankiqt/config.py index cd33539ad..15991834f 100644 --- a/ankiqt/config.py +++ b/ankiqt/config.py @@ -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): diff --git a/ankiqt/ui/cardlist.py b/ankiqt/ui/cardlist.py index 5ed08e262..498859040 100644 --- a/ankiqt/ui/cardlist.py +++ b/ankiqt/ui/cardlist.py @@ -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("") - 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 diff --git a/ankiqt/ui/deckproperties.py b/ankiqt/ui/deckproperties.py index 92a4de058..eca9818e0 100644 --- a/ankiqt/ui/deckproperties.py +++ b/ankiqt/ui/deckproperties.py @@ -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() diff --git a/ankiqt/ui/help.py b/ankiqt/ui/help.py index e1ec71426..cb516cf11 100644 --- a/ankiqt/ui/help.py +++ b/ankiqt/ui/help.py @@ -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): diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py index 679f2d5b5..92554caaa 100644 --- a/ankiqt/ui/main.py +++ b/ankiqt/ui/main.py @@ -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 %(ease2)s")), - (_("About right"), - _("Next in %(ease3)s")), - (_("Easy"), - _("Next in %(ease4)s"))) # 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( - _("How well did you remember?"))) - 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 = ( - (_("%(ease0)s")), - (_("%(ease1)s")), - (_("%(ease2)s")), - (_("%(ease3)s")), - (_("%(ease4)s"))) + (_("%(ease0)s"), + _("Reset.
You've completely forgotten.")), + (_("%(ease1)s"), + _("Too difficult.
Show this card again soon.")), + (_("%(ease2)s"), + _("Challenging.
Wait a little longer next time.")), + (_("%(ease3)s"), + _("Comfortable.
Wait longer next time.")), + (_("%(ease4)s"), + _("Too easy.
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(_( - '

Open Online Deck

' - 'To load a deck from your free online account,
' + '

Online Account

' + 'To use your free online account,
' "please enter your details below.
")) 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." diff --git a/ankiqt/ui/modelchooser.py b/ankiqt/ui/modelchooser.py index d161d83ae..7c7950ac2 100644 --- a/ankiqt/ui/modelchooser.py +++ b/ankiqt/ui/modelchooser.py @@ -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")) diff --git a/ankiqt/ui/modelproperties.py b/ankiqt/ui/modelproperties.py index 10ba678d9..72ddfc42c 100644 --- a/ankiqt/ui/modelproperties.py +++ b/ankiqt/ui/modelproperties.py @@ -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 ########################################################################## diff --git a/ankiqt/ui/preferences.py b/ankiqt/ui/preferences.py index 4e2282612..9ee9e6513 100644 --- a/ankiqt/ui/preferences.py +++ b/ankiqt/ui/preferences.py @@ -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])) diff --git a/ankiqt/ui/status.py b/ankiqt/ui/status.py index 33a511c5a..b538b3294 100644 --- a/ankiqt/ui/status.py +++ b/ankiqt/ui/status.py @@ -248,13 +248,9 @@ You should aim to answer each question within
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('00:%02d' % (col, t)) + self.timer.setText('00:%02d' % t) else: - self.timer.setText('01:00') + self.timer.setText('01:00') elif self.main.state == "showAnswer": pass else: @@ -262,4 +258,4 @@ You should aim to answer each question within
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) diff --git a/ankiqt/ui/tray.py b/ankiqt/ui/tray.py index f1a9969f9..46ddf8942 100644 --- a/ankiqt/ui/tray.py +++ b/ankiqt/ui/tray.py @@ -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) diff --git a/ankiqt/ui/view.py b/ankiqt/ui/view.py index 8c06f00ff..5a119d8b9 100644 --- a/ankiqt/ui/view.py +++ b/ankiqt/ui/view.py @@ -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 += "
" 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("
", " ") @@ -152,7 +152,7 @@ class View(object): s = "%s
%s" % (q, a) s = stripLatex(s) self.write('%s
' % 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 " "%(next)s.") % \ @@ -162,6 +162,7 @@ class View(object): "%(next)s.") % \ {"next":self.main.lastScheduledTime} self.write(msg) + self.write("
") # Help ########################################################################## @@ -189,39 +190,47 @@ class View(object): # Welcome/empty/finished deck messages ########################################################################## - def drawNoDeckMessage(self): - self.write(_("""

Welcome to Anki!

+ def drawWelcomeMessage(self): + self.write(_(""" +

Welcome to Anki!

+ + + + + + +
+ +

Add material

+Start adding your own material.
+ +
+ + - + + + + + + - + - - - - -
- + -

Create a new deck

Open Local Deck

+ +

Open Online Deck

Open a sample deck

Open Sample Deck

- -

Open an existing deck

-""")) - - def drawDeckEmptyMessage(self): - "Tell the user the deck is empty." - self.write(_(""" -

Empty deck

The current deck has no cards in it. Please select 'Add -card' from the Edit menu.""")) +""")) def drawDeckFinishedMessage(self): "Tell the user the deck is finished." diff --git a/designer/addmodel.ui b/designer/addmodel.ui index 61aa561ae..69f7a8bb2 100644 --- a/designer/addmodel.ui +++ b/designer/addmodel.ui @@ -5,8 +5,8 @@ 0 0 - 388 - 456 + 285 + 269 diff --git a/designer/deckproperties.ui b/designer/deckproperties.ui index e5b653b8f..4de7d454c 100644 --- a/designer/deckproperties.ui +++ b/designer/deckproperties.ui @@ -29,8 +29,8 @@ 0 0 - 413 - 385 + 407 + 373 @@ -72,7 +72,7 @@ - + @@ -81,38 +81,48 @@ - <h1>Sources<h1> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Arial'; font-size:8pt; font-weight:400; font-style:normal;"> +<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;"><span style=" font-size:xx-large;">Sources</span></p></body></html> - - - - + - Add Source - - - - - - - Edit Source + &Add Source - Delete Source + &Delete Source + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -633,9 +643,8 @@ qtabwidget doSync syncName - listWidget + sourcesTable addSource - editSource deleteSource newCardsPerDay failedCardMax diff --git a/designer/main.ui b/designer/main.ui index 2bdfff6f2..c81a0ee78 100644 --- a/designer/main.ui +++ b/designer/main.ui @@ -6,7 +6,7 @@ 0 0 543 - 457 + 285 @@ -28,7 +28,7 @@ 0 53 543 - 384 + 212 @@ -194,7 +194,7 @@ - + 0 0 @@ -285,7 +285,7 @@ true - + 0 0 @@ -472,7 +472,7 @@ 0 - 437 + 265 543 20 diff --git a/designer/preferences.ui b/designer/preferences.ui index f7d0fd9cc..01cf564d2 100644 --- a/designer/preferences.ui +++ b/designer/preferences.ui @@ -5,8 +5,8 @@ 0 0 - 532 - 531 + 322 + 417 @@ -23,12 +23,12 @@ 0 0 - 510 - 449 + 306 + 351 - Language, Fonts and Colours + Display @@ -42,7 +42,7 @@ - <h1>Interface language</h1>The language for the user interface: dialogs, menus, etc. + <h1>Language</h1> false @@ -68,7 +68,7 @@ - <h1>Standard fonts</h1>See 'display properties' for deck specific font preferences. + <h1>Fonts</h1> false @@ -169,7 +169,7 @@ - <h1>Standard colours</h1>These colours are used for all decks. + <h1>Colours</h1> false @@ -250,12 +250,12 @@ 0 0 - 453 - 443 + 306 + 351 - Autosave and Synchronisation + Save && Sync @@ -283,7 +283,7 @@ - <h1>Autosaving</h1>Anki can save your progress automatically. + <h1>Autosaving</h1> @@ -350,7 +350,7 @@ - <h1>Synchronisation</h1>Synchronisation enables you to access your deck from the web and your mobile phone. You can <a href="http://anki.ichi2.net/">create a free account</a>. + <h1>Synchronisation</h1><a href="http://anki.ichi2.net/">Create a free account</a>. true @@ -419,7 +419,7 @@ - + Qt::Vertical @@ -438,30 +438,16 @@ 0 0 - 453 - 443 + 306 + 351 - Advanced Settings + Advanced - - - - Show toolbar on startup - - - - - - - Use compact answer button style - - - @@ -469,55 +455,66 @@ - - + + - Tall buttons (for touchscreen) + Show 3 answer buttons, not 5 + + + + + + + Show timer - - - Start Edit Deck with only current card selected - - - true - - - - Hide next interval when showing answer buttons - - + + - Hide interval of last card + Show last card's question/answer + + + + + + + Show last card's interval - + - Hide question/answer of last card + Show toolbar on startup + + + Tall buttons (for touchscreen) + + + + Show tray icon - - + + - Show timer + Simple toolbar @@ -546,11 +543,13 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + buttonBox + tabWidget tabWidget @@ -573,6 +572,15 @@ syncPass syncOnOpen syncOnClose + compactEaseButtons + simpleToolbar + showTimer + suppressEstimates + showLastCardContent + showLastCardInterval + showToolbar + tallButtons + showTray buttonBox