diff --git a/ankiqt/config.py b/ankiqt/config.py
index 2b3e6d26e..90f667a32 100644
--- a/ankiqt/config.py
+++ b/ankiqt/config.py
@@ -75,6 +75,7 @@ class Config(dict):
'showStudyOptions': False,
'showStudyStats': True,
'showCardTimer': True,
+ 'standaloneWindows': True,
'extraNewCards': 5,
'randomizeOnCram': True,
'created': time.time(),
diff --git a/ankiqt/ui/addcards.py b/ankiqt/ui/addcards.py
index 31f98e13c..40497f545 100644
--- a/ankiqt/ui/addcards.py
+++ b/ankiqt/ui/addcards.py
@@ -22,7 +22,11 @@ class FocusButton(QPushButton):
class AddCards(QDialog):
def __init__(self, parent):
- QDialog.__init__(self, parent, Qt.Window)
+ if parent.config['standaloneWindows']:
+ windParent = None
+ else:
+ windParent = parent
+ QDialog.__init__(self, windParent, Qt.Window)
self.parent = parent
self.config = parent.config
self.dialog = ankiqt.forms.addcards.Ui_AddCards()
diff --git a/ankiqt/ui/cardlist.py b/ankiqt/ui/cardlist.py
index 31327983a..0486825b9 100644
--- a/ankiqt/ui/cardlist.py
+++ b/ankiqt/ui/cardlist.py
@@ -278,7 +278,11 @@ class StatusDelegate(QItemDelegate):
class EditDeck(QMainWindow):
def __init__(self, parent):
- QMainWindow.__init__(self, parent)
+ if parent.config['standaloneWindows']:
+ windParent = None
+ else:
+ windParent = parent
+ QMainWindow.__init__(self, windParent)
self.parent = parent
self.deck = self.parent.deck
self.config = parent.config
@@ -412,8 +416,7 @@ class EditDeck(QMainWindow):
self.sortKey = ("field", self.sortFields[idx-9])
self.rebuildSortIndex(self.sortKey)
self.sortIndex = idx
- if self.deck.getInt('sortIndex') != idx:
- self.deck.setVar('sortIndex', idx)
+ self.deck.setVar('sortIndex', idx)
self.model.sortKey = self.sortKey
self.model.updateHeader()
if refresh:
@@ -499,6 +502,8 @@ class EditDeck(QMainWindow):
self.updateSearch()
def updateSearch(self, force=True):
+ if self.parent.inDbHandler:
+ return
idx = self.dialog.tableView.currentIndex()
row = idx.row()
self.model.searchStr = unicode(self.dialog.filterEdit.text())
diff --git a/ankiqt/ui/deckproperties.py b/ankiqt/ui/deckproperties.py
index d5cd6ae88..cf779ad22 100644
--- a/ankiqt/ui/deckproperties.py
+++ b/ankiqt/ui/deckproperties.py
@@ -235,7 +235,7 @@ class DeckProperties(QDialog):
v = float(self.dialog.delay2.text())
self.updateField(self.d, 'delay2', v)
v = int(self.dialog.failedCardMax.text())
- self.updateField(self.d, 'failedCardMax', max(v, 5))
+ self.updateField(self.d, 'failedCardMax', v)
except ValueError:
pass
# hour shift
diff --git a/ankiqt/ui/graphs.py b/ankiqt/ui/graphs.py
index dc23a0a3f..66062c0cf 100644
--- a/ankiqt/ui/graphs.py
+++ b/ankiqt/ui/graphs.py
@@ -42,9 +42,10 @@ class AnkiFigureCanvas (FigureCanvas):
class AdjustableFigure(QWidget):
- def __init__(self, config, name, figureFunc, choices=None):
+ def __init__(self, parent, name, figureFunc, choices=None):
QWidget.__init__(self)
- self.config = config
+ self.parent = parent
+ self.config = parent.config
self.name = name
self.vbox = QVBoxLayout()
self.vbox.setSpacing(2)
@@ -75,6 +76,8 @@ class AdjustableFigure(QWidget):
self.vbox.addLayout(self.hbox)
def updateFigure(self):
+ if self.parent.inDbHandler:
+ return
self.updateTimer = None
self.setUpdatesEnabled(False)
idx = self.vbox.indexOf(self.figureCanvas)
@@ -184,43 +187,43 @@ class GraphWindow(object):
self.diag.show()
def setupGraphs(self):
- nextDue = AdjustableFigure(self.parent.config, 'due', self.dg.nextDue, self.range)
+ nextDue = AdjustableFigure(self.parent, 'due', self.dg.nextDue, self.range)
nextDue.addWidget(QLabel(_("
Due
")))
self.vbox.addWidget(nextDue)
self.widgets.append(nextDue)
- workload = AdjustableFigure(self.parent.config, 'reps', self.dg.workDone, self.range)
+ workload = AdjustableFigure(self.parent, 'reps', self.dg.workDone, self.range)
workload.addWidget(QLabel(_("Reps
")))
self.vbox.addWidget(workload)
self.widgets.append(workload)
- times = AdjustableFigure(self.parent.config, 'times', self.dg.timeSpent, self.range)
+ times = AdjustableFigure(self.parent, 'times', self.dg.timeSpent, self.range)
times.addWidget(QLabel(_("Review Time
")))
self.vbox.addWidget(times)
self.widgets.append(times)
- added = AdjustableFigure(self.parent.config, 'added', self.dg.addedRecently, self.range)
+ added = AdjustableFigure(self.parent, 'added', self.dg.addedRecently, self.range)
added.addWidget(QLabel(_("Added
")))
self.vbox.addWidget(added)
self.widgets.append(added)
- answered = AdjustableFigure(self.parent.config, 'answered', lambda *args: apply(
+ answered = AdjustableFigure(self.parent, 'answered', lambda *args: apply(
self.dg.addedRecently, args + ('firstAnswered',)), self.range)
answered.addWidget(QLabel(_("First Answered
")))
self.vbox.addWidget(answered)
self.widgets.append(answered)
- cumDue = AdjustableFigure(self.parent.config, 'cum', self.dg.cumulativeDue, self.range)
+ cumDue = AdjustableFigure(self.parent, 'cum', self.dg.cumulativeDue, self.range)
cumDue.addWidget(QLabel(_("Cumulative Due
")))
self.vbox.addWidget(cumDue)
self.widgets.append(cumDue)
- interval = AdjustableFigure(self.parent.config, 'interval', self.dg.intervalPeriod, self.range)
+ interval = AdjustableFigure(self.parent, 'interval', self.dg.intervalPeriod, self.range)
interval.addWidget(QLabel(_("Intervals
")))
self.vbox.addWidget(interval)
self.widgets.append(interval)
- eases = AdjustableFigure(self.parent.config, 'eases', self.dg.easeBars)
+ eases = AdjustableFigure(self.parent, 'eases', self.dg.easeBars)
eases.addWidget(QLabel(_("Eases
")))
self.vbox.addWidget(eases)
self.widgets.append(eases)
diff --git a/ankiqt/ui/importing.py b/ankiqt/ui/importing.py
index 753b80609..be620426e 100644
--- a/ankiqt/ui/importing.py
+++ b/ankiqt/ui/importing.py
@@ -134,6 +134,8 @@ class ImportDialog(QDialog):
txt = (
_("Importing complete. %(num)d facts imported from %(file)s.\n") %
{"num": self.importer.total, "file": os.path.basename(self.file)})
+ self.dialog.groupBox.setShown(False)
+ self.dialog.buttonBox.button(QDialogButtonBox.Close).setFocus()
if self.importer.log:
txt += _("Log of import:\n") + "\n".join(self.importer.log)
self.dialog.status.setText(txt)
diff --git a/ankiqt/ui/main.py b/ankiqt/ui/main.py
index 4c33536d8..5b03e7c2b 100644
--- a/ankiqt/ui/main.py
+++ b/ankiqt/ui/main.py
@@ -14,7 +14,7 @@ from PyQt4.QtGui import *
from anki import DeckStorage
from anki.errors import *
from anki.sound import hasSound, playFromText, clearAudioQueue
-from anki.utils import addTags, deleteTags, parseTags
+from anki.utils import addTags, deleteTags, parseTags, canonifyTags
from anki.media import rebuildMediaDir
from anki.db import OperationalError
from anki.stdmodels import BasicModel
@@ -685,8 +685,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
def onClose(self):
if self.inMainWindow():
- cramming = self.deck is not None and self.deck.name() == "cram"
- self.saveAndClose(hideWelcome=cramming)
+ self.saveAndClose(hideWelcome=self.isCramming())
if cramming:
self.loadRecent(0)
else:
@@ -830,14 +829,17 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.updateRecentFiles(file)
return True
- def onUnsavedTimer(self):
+ def showToolTip(self, msg):
+ t = QTimer(self)
+ t.setSingleShot(True)
+ t.start(200)
+ self.connect(t, SIGNAL("timeout()"),
+ lambda msg=msg: self._showToolTip(msg))
+
+ def _showToolTip(self, msg):
QToolTip.showText(
- self.mainWin.statusbar.mapToGlobal(QPoint(0, -100)),
- _("""\
-Unsaved Deck
-Careful. You're editing an unsaved Deck.
-Choose File -> Save to start autosaving
-your deck."""))
+ self.mainWin.statusbar.mapToGlobal(QPoint(0, -40)),
+ msg)
def save(self, required=False):
if not self.deck.path:
@@ -845,10 +847,11 @@ your deck."""))
# backed in memory, make sure it's saved
return self.onSaveAs()
else:
- t = QTimer(self)
- t.setSingleShot(True)
- t.start(200)
- self.connect(t, SIGNAL("timeout()"), self.onUnsavedTimer)
+ self.showToolTip(_("""\
+Unsaved Deck
+Careful. You're editing an unsaved Deck.
+Choose File -> Save to start autosaving
+your deck."""))
return
if not self.deck.modifiedSinceSave():
return True
@@ -1036,7 +1039,6 @@ your deck."""))
def updateStudyStats(self):
wasReached = self.deck.sessionLimitReached()
- self.deck.sessionStartTime = 0
sessionColour = '%s'
cardColour = '%s'
if not wasReached:
@@ -1155,8 +1157,21 @@ day = :d""", d=yesterday)
int(self.mainWin.questionLimit.text()))
except (ValueError, OverflowError):
pass
- uf(self.deck, 'newCardOrder',
- self.mainWin.newCardOrder.currentIndex())
+ ncOrd = self.mainWin.newCardOrder.currentIndex()
+ if self.deck.newCardOrder != ncOrd:
+ if self.deck.newCardOrder == 0 and ncOrd != 0:
+ # random to non-random
+ self.deck.startProgress()
+ self.deck.updateProgress(_("Ordering..."))
+ self.deck.orderNewCards()
+ self.deck.finishProgress()
+ elif self.deck.newCardOrder != 0 and ncOrd == 0:
+ # non-random to random
+ self.deck.startProgress()
+ self.deck.updateProgress(_("Randomizing..."))
+ self.deck.randomizeNewCards()
+ self.deck.finishProgress()
+ uf(self.deck, 'newCardOrder', ncOrd)
uf(self.deck, 'newCardSpacing',
self.mainWin.newCardScheduling.currentIndex())
uf(self.deck, 'revCardOrder',
@@ -1315,11 +1330,11 @@ day = :d""", d=yesterday)
def onMark(self, toggled):
if self.currentCard.hasTag("Marked"):
- self.currentCard.fact.tags = deleteTags(
- "Marked", self.currentCard.fact.tags)
+ self.currentCard.fact.tags = canonifyTags(deleteTags(
+ "Marked", self.currentCard.fact.tags))
else:
- self.currentCard.fact.tags = addTags(
- "Marked", self.currentCard.fact.tags)
+ self.currentCard.fact.tags = canonifyTags(addTags(
+ "Marked", self.currentCard.fact.tags))
self.currentCard.fact.setModified(textChanged=True)
self.deck.updateFactTags([self.currentCard.fact.id])
self.deck.setModified()
@@ -1327,7 +1342,8 @@ day = :d""", d=yesterday)
def onSuspend(self):
undo = _("Suspend")
self.deck.setUndoStart(undo)
- self.currentCard.fact.tags = addTags("Suspended", self.currentCard.fact.tags)
+ self.currentCard.fact.tags = canonifyTags(
+ addTags("Suspended", self.currentCard.fact.tags))
self.currentCard.fact.setModified(textChanged=True)
self.deck.updateFactTags([self.currentCard.fact.id])
for card in self.currentCard.fact.cards:
@@ -1358,10 +1374,19 @@ day = :d""", d=yesterday)
##########################################################################
def onAddCard(self):
+ if self.isCramming():
+ ui.utils.showInfo(_("""\
+You are currently cramming. Please close this deck first."""))
+ return
ui.dialogs.get("AddCards", self)
def onEditDeck(self):
ui.dialogs.get("CardList", self)
+ if self.isCramming():
+ self.showToolTip(_("""\
+Cramming
+You are currently cramming. Any edits you make to this deck
+will be lost when you close the deck."""))
def onEditCurrent(self):
self.moveToState("editCurrentFact")
@@ -1397,7 +1422,10 @@ day = :d""", d=yesterday)
##########################################################################
def onImport(self):
- import ui.importing
+ if self.isCramming():
+ ui.utils.showInfo(_("""\
+You are currently cramming. Please close this deck first."""))
+ return
if self.deck is None:
self.onNew()
ui.importing.ImportDialog(self)
@@ -1421,8 +1449,11 @@ day = :d""", d=yesterday)
e.exportInto(path)
return (e, path)
+ def isCramming(self):
+ return self.deck is not None and self.deck.name() == "cram"
+
def onCram(self, cardIds=[]):
- if self.deck.name() == "cram":
+ if self.isCramming():
ui.utils.showInfo(
_("Already cramming. Please close this deck first."))
return
@@ -1442,7 +1473,7 @@ day = :d""", d=yesterday)
ui.utils.showInfo(_("No cards matched the provided tags."))
return
if self.config['randomizeOnCram']:
- n = 5
+ n = 3
else:
n = 2
p = ui.utils.ProgressWin(self, n, 0, _("Cram"))
@@ -1462,22 +1493,11 @@ day = :d""", d=yesterday)
self.deck.easyIntervalMax = 0.25
self.deck.newCardOrder = 0
self.deck.syncName = None
+ p.update()
+ self.deck.updateDynamicIndices()
if self.config['randomizeOnCram']:
p.update(_("Randomizing..."))
- self.deck.s.statement(
- "create temporary table idmap (old, new, primary key (old))")
- self.deck.s.statement(
- "insert into idmap select id, random() from facts")
- self.deck.s.statement(
- "update facts set id = (select new from idmap where old = id)")
- p.update()
- self.deck.s.statement(
- "update cards set factId = (select new from idmap where old = factId)")
- p.update()
- self.deck.s.statement(
- "update fields set factId = (select new from idmap where old = factId)")
- p.update()
- self.deck.updateDynamicIndices()
+ self.deck.randomizeNewCards()
self.reset()
p.finish()
@@ -1645,6 +1665,7 @@ it to your friends.
onlyMerge, self.sourcesToCheck)
self.connect(self.syncThread, SIGNAL("setStatus"), self.setSyncStatus)
self.connect(self.syncThread, SIGNAL("showWarning"), self.showSyncWarning)
+ self.connect(self.syncThread, SIGNAL("noSyncResponse"), self.noSyncResponse)
self.connect(self.syncThread, SIGNAL("moveToState"), self.moveToState)
self.connect(self.syncThread, SIGNAL("noMatchingDeck"), self.selectSyncDeck)
self.connect(self.syncThread, SIGNAL("syncClockOff"), self.syncClockOff)
@@ -1719,6 +1740,11 @@ it to your friends.
ui.utils.showWarning(text, self)
self.setStatus("")
+ def noSyncResponse(self):
+ self.showToolTip(_("""\
+Sync Failed
+Couldn't contact Anki Online. Please check your internet connection."""))
+
def openSyncProgress(self):
self.syncProgressDialog = QProgressDialog(_("Syncing Media..."),
"", 0, 0, self)
diff --git a/ankiqt/ui/status.py b/ankiqt/ui/status.py
index 83b6af118..646df8815 100644
--- a/ankiqt/ui/status.py
+++ b/ankiqt/ui/status.py
@@ -64,13 +64,16 @@ class StatusView(object):
shown = True
self.progressBar.setShown(shown)
self.retentionBar.setShown(shown)
- self.timer.setShown(shown)
self.etaText.setShown(shown)
self.remText.setShown(shown)
self.sep1.setShown(shown)
self.sep2.setShown(shown)
- self.sep3.setShown(shown)
self.statusbar.hideOrShow()
+ # timer has a separate option
+ if not self.main.config['showTimer']:
+ shown = False
+ self.timer.setShown(shown)
+ self.sep3.setShown(shown)
# Setup and teardown
##########################################################################
@@ -130,8 +133,6 @@ class StatusView(object):
self.timer.setText("00:00")
self.addWidget(self.timer)
self.redraw()
- if not self.main.config['showTimer']:
- self.timer.setShown(False)
def addWidget(self, w, stretch=0):
self.statusbar.addWidget(w, stretch)
diff --git a/ankiqt/ui/sync.py b/ankiqt/ui/sync.py
index 6ff62a644..1452bf6c3 100644
--- a/ankiqt/ui/sync.py
+++ b/ankiqt/ui/sync.py
@@ -36,8 +36,11 @@ class Sync(QThread):
self.syncDeck()
def error(self, error):
- error = self.getErrorMessage(error)
- self.emit(SIGNAL("showWarning"), error)
+ if error.data.get('type') == 'noResponse':
+ self.emit(SIGNAL("noSyncResponse"))
+ else:
+ error = self.getErrorMessage(error)
+ self.emit(SIGNAL("showWarning"), error)
if self.onlyMerge:
# new file needs cleaning up
self.emit(SIGNAL("cleanNewDeck"))
@@ -50,11 +53,7 @@ class Sync(QThread):
elif error.data.get('status') == "oldVersion":
msg=_("The sync protocol has changed. Please upgrade.")
else:
- msg=_("""\
-Syncing failed. Please try again in a few minutes.
-If the problem persists, please report it on the forum.
-
-Error: %s""" % `getattr(error, 'data')`)
+ msg=_("Unknown error: %s" % `getattr(error, 'data')`)
return msg
def connect(self, *args):
diff --git a/designer/addcards.ui b/designer/addcards.ui
index f1d6dab55..1bb20da65 100644
--- a/designer/addcards.ui
+++ b/designer/addcards.ui
@@ -12,6 +12,10 @@
Add Items
+
+
+ :/icons/list-add.png:/icons/list-add.png
+
0
@@ -93,8 +97,8 @@
<!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="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></p></body></html>
+</style></head><body style=" font-family:'Sans Serif'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Arial';"></p></body></html>
diff --git a/designer/cardlist.ui b/designer/cardlist.ui
index 4595230cd..4b6f0b41f 100644
--- a/designer/cardlist.ui
+++ b/designer/cardlist.ui
@@ -12,6 +12,10 @@
Browse Items
+
+
+ :/icons/find.png:/icons/find.png
+