mirror of
https://github.com/ankitects/anki.git
synced 2025-11-13 08:07:11 -05:00
fix sorting, allow sort changing, catch attempts to modify schema
This commit is contained in:
parent
ae43b78faa
commit
1d9f0176a5
5 changed files with 132 additions and 69 deletions
120
aqt/browser.py
120
aqt/browser.py
|
|
@ -15,7 +15,6 @@ from aqt.utils import saveGeom, restoreGeom, saveSplitter, restoreSplitter, \
|
||||||
saveHeader, restoreHeader, saveState, restoreState, applyStyles
|
saveHeader, restoreHeader, saveState, restoreState, applyStyles
|
||||||
from anki.errors import *
|
from anki.errors import *
|
||||||
from anki.db import *
|
from anki.db import *
|
||||||
from anki.stats import CardStats
|
|
||||||
from anki.hooks import runHook, addHook, removeHook
|
from anki.hooks import runHook, addHook, removeHook
|
||||||
|
|
||||||
# fixme: notice added tags?
|
# fixme: notice added tags?
|
||||||
|
|
@ -109,7 +108,7 @@ class DeckModel(QAbstractTableModel):
|
||||||
def showMatching(self, force=True):
|
def showMatching(self, force=True):
|
||||||
self.parent.mw.progress.start()
|
self.parent.mw.progress.start()
|
||||||
t = time.time()
|
t = time.time()
|
||||||
self.cards = self.deck.findCards(self.searchStr.strip(), "factFld")
|
self.cards = self.deck.findCards(self.searchStr.strip())
|
||||||
print "fetch cards in %dms" % ((time.time() - t)*1000)
|
print "fetch cards in %dms" % ((time.time() - t)*1000)
|
||||||
self.parent.mw.progress.finish()
|
self.parent.mw.progress.finish()
|
||||||
# if self.deck.getInt('reverseOrder'):
|
# if self.deck.getInt('reverseOrder'):
|
||||||
|
|
@ -169,7 +168,7 @@ class DeckModel(QAbstractTableModel):
|
||||||
else:
|
else:
|
||||||
return self.nextDue(index)
|
return self.nextDue(index)
|
||||||
|
|
||||||
def updateHeader(self):
|
def onSortChanged(self):
|
||||||
if self.sortKey == "created":
|
if self.sortKey == "created":
|
||||||
k = _("Created")
|
k = _("Created")
|
||||||
elif self.sortKey == "modified":
|
elif self.sortKey == "modified":
|
||||||
|
|
@ -287,7 +286,7 @@ class Browser(QMainWindow):
|
||||||
self.setupHooks()
|
self.setupHooks()
|
||||||
self.setupEditor()
|
self.setupEditor()
|
||||||
self.setupCardInfo()
|
self.setupCardInfo()
|
||||||
self.updateSortOrder()
|
self.updateSortIcon()
|
||||||
self.updateFont()
|
self.updateFont()
|
||||||
self.form.searchEdit.setFocus()
|
self.form.searchEdit.setFocus()
|
||||||
self.updateFilterLabel()
|
self.updateFilterLabel()
|
||||||
|
|
@ -389,13 +388,6 @@ class Browser(QMainWindow):
|
||||||
self.onSearch)
|
self.onSearch)
|
||||||
self.setTabOrder(self.form.searchEdit, self.form.tableView)
|
self.setTabOrder(self.form.searchEdit, self.form.tableView)
|
||||||
|
|
||||||
def reverseOrder(self):
|
|
||||||
self.deck.setVar("reverseOrder", not self.deck.getInt("reverseOrder"))
|
|
||||||
self.model.cards.reverse()
|
|
||||||
self.model.reset()
|
|
||||||
self.focusCard()
|
|
||||||
self.updateSortOrder()
|
|
||||||
|
|
||||||
def onSearch(self, force=True):
|
def onSearch(self, force=True):
|
||||||
# fixme:
|
# fixme:
|
||||||
# if self.mw.inDbHandler:
|
# if self.mw.inDbHandler:
|
||||||
|
|
@ -481,71 +473,41 @@ class Browser(QMainWindow):
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def setupSort(self):
|
def setupSort(self):
|
||||||
self.form.sortBox.setMaxVisibleItems(30)
|
self.sortTypes = [
|
||||||
self.sortIndex = int(self.deck.conf.get("sortIdx", "0"))
|
"factFld",
|
||||||
self.drawSort()
|
"factCrt",
|
||||||
|
"factMod",
|
||||||
|
"cardMod",
|
||||||
|
"cardDue",
|
||||||
|
"cardEase",
|
||||||
|
"cardReps",
|
||||||
|
"cardLapses",
|
||||||
|
]
|
||||||
|
idx = self.sortTypes.index(self.deck.conf['sortType'])
|
||||||
|
self.form.sortBox.setCurrentIndex(idx)
|
||||||
self.connect(self.form.sortBox, SIGNAL("activated(int)"),
|
self.connect(self.form.sortBox, SIGNAL("activated(int)"),
|
||||||
self.sortChanged)
|
self.sortChanged)
|
||||||
self.sortChanged(self.sortIndex, refresh=False)
|
self.sortChanged(idx, refresh=False)
|
||||||
self.connect(self.form.sortOrder, SIGNAL("clicked()"),
|
self.connect(self.form.sortOrder, SIGNAL("clicked()"),
|
||||||
self.reverseOrder)
|
self.toggleSortOrder)
|
||||||
|
|
||||||
def drawSort(self):
|
def toggleSortOrder(self):
|
||||||
self.sortList = [
|
self.deck.conf['sortBackwards'] = not self.deck.conf['sortBackwards']
|
||||||
_("Question"),
|
self.model.cards.reverse()
|
||||||
_("Answer"),
|
self.model.reset()
|
||||||
_("Created"),
|
self.focusCard()
|
||||||
_("Modified"),
|
self.updateSortIcon()
|
||||||
_("Due"),
|
|
||||||
_("Interval"),
|
|
||||||
_("Reps"),
|
|
||||||
_("Ease"),
|
|
||||||
_("Fact Created"),
|
|
||||||
_("Lapses"),
|
|
||||||
_("First Review"),
|
|
||||||
]
|
|
||||||
self.form.sortBox.clear()
|
|
||||||
self.form.sortBox.addItems(QStringList(self.sortList))
|
|
||||||
if self.sortIndex >= len(self.sortList):
|
|
||||||
self.sortIndex = 0
|
|
||||||
self.form.sortBox.setCurrentIndex(self.sortIndex)
|
|
||||||
|
|
||||||
def updateSortOrder(self):
|
def updateSortIcon(self):
|
||||||
if int(self.deck.conf.get("revOrder", "0")):
|
if self.deck.conf['sortBackwards']:
|
||||||
self.form.sortOrder.setIcon(QIcon(":/icons/view-sort-descending.png"))
|
self.form.sortOrder.setIcon(QIcon(":/icons/view-sort-descending.png"))
|
||||||
else:
|
else:
|
||||||
self.form.sortOrder.setIcon(QIcon(":/icons/view-sort-ascending.png"))
|
self.form.sortOrder.setIcon(QIcon(":/icons/view-sort-ascending.png"))
|
||||||
|
|
||||||
def sortChanged(self, idx, refresh=True):
|
def sortChanged(self, idx, refresh=True):
|
||||||
if idx == 0:
|
|
||||||
self.sortKey = "question"
|
|
||||||
elif idx == 1:
|
|
||||||
self.sortKey = "answer"
|
|
||||||
elif idx == 2:
|
|
||||||
self.sortKey = "created"
|
|
||||||
elif idx == 3:
|
|
||||||
self.sortKey = "modified"
|
|
||||||
elif idx == 4:
|
|
||||||
self.sortKey = "combinedDue"
|
|
||||||
elif idx == 5:
|
|
||||||
self.sortKey = "interval"
|
|
||||||
elif idx == 6:
|
|
||||||
self.sortKey = "reps"
|
|
||||||
elif idx == 7:
|
|
||||||
self.sortKey = "factor"
|
|
||||||
elif idx == 8:
|
|
||||||
self.sortKey = "fact"
|
|
||||||
elif idx == 9:
|
|
||||||
self.sortKey = "noCount"
|
|
||||||
elif idx == 10:
|
|
||||||
self.sortKey = "firstAnswered"
|
|
||||||
else:
|
|
||||||
self.sortKey = ("field", self.sortFields[idx-11])
|
|
||||||
#self.rebuildSortIndex(self.sortKey)
|
|
||||||
self.sortIndex = idx
|
self.sortIndex = idx
|
||||||
self.deck.conf['sortIdx'] = idx
|
self.deck.conf['sortType'] = self.sortTypes[idx]
|
||||||
self.model.sortKey = self.sortKey
|
self.model.onSortChanged()
|
||||||
self.model.updateHeader()
|
|
||||||
if refresh:
|
if refresh:
|
||||||
self.model.showMatching()
|
self.model.showMatching()
|
||||||
self.updateFilterLabel()
|
self.updateFilterLabel()
|
||||||
|
|
@ -666,13 +628,37 @@ class Browser(QMainWindow):
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def setupCardInfo(self):
|
def setupCardInfo(self):
|
||||||
|
from anki.stats import CardStats
|
||||||
self.card = None
|
self.card = None
|
||||||
self.cardStats = CardStats(self.deck, None)
|
self.cardStats = CardStats(self.deck, None)
|
||||||
|
self.connect(self.form.cardLabel,
|
||||||
|
SIGNAL("linkActivated(const QString&)"),
|
||||||
|
self.onChangeSortField)
|
||||||
|
|
||||||
def showCardInfo(self, card):
|
def showCardInfo(self, card):
|
||||||
self.cardStats.card = self.card
|
self.cardStats.card = self.card
|
||||||
self.form.cardLabel.setText(
|
rep = self.cardStats.report()
|
||||||
self.cardStats.report())
|
rep = "<style>table * { font-size: 12px; }</style>" + rep
|
||||||
|
m = self.card.model()
|
||||||
|
sortf = m.fields[m.sortIdx()]['name']
|
||||||
|
rep = rep.replace(
|
||||||
|
"</table>",
|
||||||
|
"<tr><td><b>%s</b></td><td>%s</td></tr></table>" % (
|
||||||
|
_("Sort Field"),
|
||||||
|
"<a href=foo>%s</a>" % sortf))
|
||||||
|
self.form.cardLabel.setText(rep)
|
||||||
|
|
||||||
|
def onChangeSortField(self):
|
||||||
|
from aqt.utils import chooseList
|
||||||
|
m = self.card.model()
|
||||||
|
fields = [f['name'] for f in m.fields]
|
||||||
|
idx = chooseList(_("Choose field to sort this model by:"),
|
||||||
|
fields, m.sortIdx())
|
||||||
|
if idx != m.sortIdx():
|
||||||
|
self.mw.progress.start()
|
||||||
|
m.setSortIdx(idx)
|
||||||
|
self.mw.progress.finish()
|
||||||
|
self.onSearch()
|
||||||
|
|
||||||
# Menu helpers
|
# Menu helpers
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ into a bug report:<br>
|
||||||
pluginText = _("""\
|
pluginText = _("""\
|
||||||
An error occurred in a plugin. Please contact the plugin author.<br>
|
An error occurred in a plugin. Please contact the plugin author.<br>
|
||||||
Please do not file a bug report with Anki.<br>""")
|
Please do not file a bug report with Anki.<br>""")
|
||||||
|
self.mw.progress.clear()
|
||||||
|
if "abortSchemaMod" in error:
|
||||||
|
return
|
||||||
if "plugin" in error:
|
if "plugin" in error:
|
||||||
txt = pluginText
|
txt = pluginText
|
||||||
else:
|
else:
|
||||||
|
|
@ -64,4 +67,3 @@ Please do not file a bug report with Anki.<br>""")
|
||||||
# show dialog
|
# show dialog
|
||||||
txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
|
txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
|
||||||
showText(txt, type="html")
|
showText(txt, type="html")
|
||||||
self.mw.progress.clear()
|
|
||||||
|
|
|
||||||
12
aqt/main.py
12
aqt/main.py
|
|
@ -75,6 +75,7 @@ class AnkiQt(QMainWindow):
|
||||||
self.setupVersion()
|
self.setupVersion()
|
||||||
self.setupAutoUpdate()
|
self.setupAutoUpdate()
|
||||||
self.setupCardStats()
|
self.setupCardStats()
|
||||||
|
self.setupSchema()
|
||||||
# screens
|
# screens
|
||||||
self.setupDeckBrowser()
|
self.setupDeckBrowser()
|
||||||
self.setupOverview()
|
self.setupOverview()
|
||||||
|
|
@ -895,6 +896,17 @@ Please choose a new deck name:"""))
|
||||||
if self.state != "showQuestion":
|
if self.state != "showQuestion":
|
||||||
playFromText(self.currentCard.answer)
|
playFromText(self.currentCard.answer)
|
||||||
|
|
||||||
|
# Schema modifications
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
def setupSchema(self):
|
||||||
|
addHook("modSchema", self.onSchemaMod)
|
||||||
|
|
||||||
|
def onSchemaMod(self, arg):
|
||||||
|
return askUser(_("""\
|
||||||
|
This operation can't be merged when syncing, so the next \
|
||||||
|
sync will overwrite any remote changes. Continue?"""))
|
||||||
|
|
||||||
# Media locations
|
# Media locations
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
|
||||||
19
aqt/utils.py
19
aqt/utils.py
|
|
@ -170,6 +170,25 @@ def getOnlyText(*args, **kwargs):
|
||||||
else:
|
else:
|
||||||
return u""
|
return u""
|
||||||
|
|
||||||
|
# fixme: these utilities could be combined into a single base class
|
||||||
|
def chooseList(prompt, choices, startrow=0, parent=None):
|
||||||
|
if not parent:
|
||||||
|
parent = aqt.mw.app.activeWindow()
|
||||||
|
d = QDialog(parent)
|
||||||
|
l = QVBoxLayout()
|
||||||
|
d.setLayout(l)
|
||||||
|
t = QLabel(prompt)
|
||||||
|
l.addWidget(t)
|
||||||
|
c = QListWidget()
|
||||||
|
c.addItems(QStringList(choices))
|
||||||
|
c.setCurrentRow(startrow)
|
||||||
|
l.addWidget(c)
|
||||||
|
bb = QDialogButtonBox(QDialogButtonBox.Ok)
|
||||||
|
bb.connect(bb, SIGNAL("accepted()"), d, SLOT("accept()"))
|
||||||
|
l.addWidget(bb)
|
||||||
|
d.exec_()
|
||||||
|
return c.currentRow()
|
||||||
|
|
||||||
def getTag(parent, deck, question, tags="user", **kwargs):
|
def getTag(parent, deck, question, tags="user", **kwargs):
|
||||||
from aqt.tagedit import TagEdit
|
from aqt.tagedit import TagEdit
|
||||||
te = TagEdit(parent)
|
te = TagEdit(parent)
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,48 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="4">
|
<item row="0" column="4">
|
||||||
<widget class="QComboBox" name="sortBox"/>
|
<widget class="QComboBox" name="sortBox">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Fields</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Creation date</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Edit date</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Review date</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Due date</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Ease factor</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Review count</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>Lapse count</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
|
@ -218,6 +259,9 @@
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue