mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
Merge branch 'master' of git://ichi2.net/ankiqt
This commit is contained in:
commit
4852c42611
11 changed files with 116 additions and 67 deletions
|
@ -75,6 +75,7 @@ class Config(dict):
|
|||
'showStudyOptions': False,
|
||||
'showStudyStats': True,
|
||||
'showCardTimer': True,
|
||||
'standaloneWindows': True,
|
||||
'extraNewCards': 5,
|
||||
'randomizeOnCram': True,
|
||||
'created': time.time(),
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(_("<h1>Due</h1>")))
|
||||
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(_("<h1>Reps</h1>")))
|
||||
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(_("<h1>Review Time</h1>")))
|
||||
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(_("<h1>Added</h1>")))
|
||||
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(_("<h1>First Answered</h1>")))
|
||||
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(_("<h1>Cumulative Due</h1>")))
|
||||
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(_("<h1>Intervals</h1>")))
|
||||
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(_("<h1>Eases</h1>")))
|
||||
self.vbox.addWidget(eases)
|
||||
self.widgets.append(eases)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)),
|
||||
_("""\
|
||||
<h1>Unsaved Deck</h1>
|
||||
Careful. You're editing an unsaved Deck.<br>
|
||||
Choose File -> Save to start autosaving<br>
|
||||
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(_("""\
|
||||
<h1>Unsaved Deck</h1>
|
||||
Careful. You're editing an unsaved Deck.<br>
|
||||
Choose File -> Save to start autosaving<br>
|
||||
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 = '<font color=#0000ff>%s</font>'
|
||||
cardColour = '<font color=#0000ff>%s</font>'
|
||||
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(_("""\
|
||||
<h1>Cramming</h1>
|
||||
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(_("""\
|
||||
<h1>Sync Failed</h1>
|
||||
Couldn't contact Anki Online. Please check your internet connection."""))
|
||||
|
||||
def openSyncProgress(self):
|
||||
self.syncProgressDialog = QProgressDialog(_("Syncing Media..."),
|
||||
"", 0, 0, self)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
<property name="windowTitle" >
|
||||
<string>Add Items</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="../icons.qrc" >
|
||||
<normaloff>:/icons/list-add.png</normaloff>:/icons/list-add.png</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
<property name="spacing" >
|
||||
<number>0</number>
|
||||
|
@ -93,8 +97,8 @@
|
|||
<string><!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></string>
|
||||
</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></string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
<property name="windowTitle" >
|
||||
<string>Browse Items</string>
|
||||
</property>
|
||||
<property name="windowIcon" >
|
||||
<iconset resource="../icons.qrc" >
|
||||
<normaloff>:/icons/find.png</normaloff>:/icons/find.png</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
<property name="spacing" >
|
||||
|
|
Loading…
Reference in a new issue