mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
update columns code and add support for multiple columns
This commit is contained in:
parent
1d9f0176a5
commit
07c2591be0
2 changed files with 108 additions and 187 deletions
252
aqt/browser.py
252
aqt/browser.py
|
@ -31,22 +31,21 @@ COLOUR_MARKED2 = "#aaaaff"
|
||||||
|
|
||||||
class DeckModel(QAbstractTableModel):
|
class DeckModel(QAbstractTableModel):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, browser):
|
||||||
QAbstractTableModel.__init__(self)
|
QAbstractTableModel.__init__(self)
|
||||||
self.parent = parent
|
self.browser = browser
|
||||||
self.deck = parent.deck
|
self.deck = browser.deck
|
||||||
self.filterTag = None
|
|
||||||
self.sortKey = None
|
self.sortKey = None
|
||||||
# column title, display accessor, sort attr
|
self.columns = ["question", "answer", "sortField", "cardDue", "cardEase"]
|
||||||
self.columns = [(_("Question"), self.currentQuestion),
|
|
||||||
(_("Answer"), self.currentAnswer),
|
|
||||||
[_("Due"), self.thirdColumn],
|
|
||||||
]
|
|
||||||
self.searchStr = ""
|
|
||||||
self.lastSearch = ""
|
|
||||||
self.cards = []
|
self.cards = []
|
||||||
self.cardObjs = {}
|
self.cardObjs = {}
|
||||||
|
|
||||||
|
def getCard(self, index):
|
||||||
|
id = self.cards[index.row()]
|
||||||
|
if not id in self.cardObjs:
|
||||||
|
self.cardObjs[id] = self.deck.getCard(id)
|
||||||
|
return self.cardObjs[id]
|
||||||
|
|
||||||
# Model interface
|
# Model interface
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
@ -54,43 +53,40 @@ class DeckModel(QAbstractTableModel):
|
||||||
return len(self.cards)
|
return len(self.cards)
|
||||||
|
|
||||||
def columnCount(self, index):
|
def columnCount(self, index):
|
||||||
return 3
|
return len(self.columns)
|
||||||
|
|
||||||
def data(self, index, role):
|
def data(self, index, role):
|
||||||
if not index.isValid():
|
if not index.isValid():
|
||||||
return QVariant()
|
return QVariant()
|
||||||
if role == Qt.FontRole:
|
if role == Qt.FontRole:
|
||||||
f = QFont()
|
f = QFont()
|
||||||
f.setPixelSize(self.parent.mw.config['editFontSize'])
|
f.setPixelSize(self.browser.mw.config['editFontSize'])
|
||||||
return QVariant(f)
|
return QVariant(f)
|
||||||
if role == Qt.TextAlignmentRole and index.column() == 2:
|
if role == Qt.TextAlignmentRole:
|
||||||
return QVariant(Qt.AlignHCenter)
|
align = Qt.AlignVCenter
|
||||||
|
if index.column() > 1:
|
||||||
|
align |= Qt.AlignHCenter
|
||||||
|
return QVariant(align)
|
||||||
elif role == Qt.DisplayRole or role == Qt.EditRole:
|
elif role == Qt.DisplayRole or role == Qt.EditRole:
|
||||||
c = self.getCard(index)
|
return QVariant(self.columnData(index))
|
||||||
s = self.columns[index.column()][1](index)
|
|
||||||
s = self.limitContent(s)
|
|
||||||
s = s.replace("<br>", u" ")
|
|
||||||
s = s.replace("<br />", u" ")
|
|
||||||
s = s.replace("\n", u" ")
|
|
||||||
s = re.sub("\[sound:[^]]+\]", "", s)
|
|
||||||
s = stripHTMLMedia(s)
|
|
||||||
s = s.strip()
|
|
||||||
return QVariant(s)
|
|
||||||
else:
|
else:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
def limitContent(self, txt):
|
|
||||||
if "<c>" in txt:
|
|
||||||
matches = re.findall("(?s)<c>(.*?)</c>", txt)
|
|
||||||
return " ".join(matches)
|
|
||||||
else:
|
|
||||||
return txt
|
|
||||||
|
|
||||||
def headerData(self, section, orientation, role):
|
def headerData(self, section, orientation, role):
|
||||||
if orientation == Qt.Vertical:
|
if orientation == Qt.Vertical:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
elif role == Qt.DisplayRole:
|
elif role == Qt.DisplayRole:
|
||||||
return QVariant(self.columns[section][0])
|
type = self.columnType(section)
|
||||||
|
if type == "question":
|
||||||
|
txt = _("Question")
|
||||||
|
elif type == "answer":
|
||||||
|
txt = _("Answer")
|
||||||
|
else:
|
||||||
|
for stype, name in self.browser.sortTypes:
|
||||||
|
if type == stype:
|
||||||
|
txt = name
|
||||||
|
break
|
||||||
|
return QVariant(txt)
|
||||||
elif role == Qt.FontRole:
|
elif role == Qt.FontRole:
|
||||||
f = QFont()
|
f = QFont()
|
||||||
f.setPixelSize(10)
|
f.setPixelSize(10)
|
||||||
|
@ -106,128 +102,91 @@ class DeckModel(QAbstractTableModel):
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def showMatching(self, force=True):
|
def showMatching(self, force=True):
|
||||||
self.parent.mw.progress.start()
|
self.browser.mw.progress.start()
|
||||||
t = time.time()
|
t = time.time()
|
||||||
self.cards = self.deck.findCards(self.searchStr.strip())
|
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.browser.mw.progress.finish()
|
||||||
# if self.deck.getInt('reverseOrder'):
|
|
||||||
# self.cards.reverse()
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.cardObjs = {}
|
self.cardObjs = {}
|
||||||
self.emit(SIGNAL("layoutChanged()"))
|
self.emit(SIGNAL("layoutChanged()"))
|
||||||
|
|
||||||
# Tools
|
# Column data
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def getCard(self, index):
|
def columnType(self, column):
|
||||||
id = self.cards[index.row()]
|
type = self.columns[column]
|
||||||
if not id in self.cardObjs:
|
if type == "sortField":
|
||||||
self.cardObjs[id] = self.deck.getCard(id)
|
type = self.deck.conf['sortType']
|
||||||
return self.cardObjs[id]
|
if type == "factFld":
|
||||||
|
type = "cardDue"
|
||||||
|
return type
|
||||||
|
|
||||||
def currentQuestion(self, index):
|
def columnData(self, index):
|
||||||
return self.getCard(index).q()
|
row = index.row()
|
||||||
|
col = index.column()
|
||||||
def currentAnswer(self, index):
|
type = self.columnType(col)
|
||||||
return self.getCard(index).a()
|
|
||||||
|
|
||||||
def nextDue(self, index):
|
|
||||||
c = self.getCard(index)
|
c = self.getCard(index)
|
||||||
|
if type == "question":
|
||||||
|
return self.formatQA(c.q())
|
||||||
|
elif type == "answer":
|
||||||
|
return self.formatQA(c.a())
|
||||||
|
elif type == "factFld" or type == "cardDue":
|
||||||
|
return self.nextDue(c, index)
|
||||||
|
elif type == "factCrt":
|
||||||
|
return time.strftime("%Y-%m-%d", time.localtime(c.fact().crt))
|
||||||
|
elif type == "factMod":
|
||||||
|
return time.strftime("%Y-%m-%d", time.localtime(c.fact().mod))
|
||||||
|
elif type == "cardMod":
|
||||||
|
return time.strftime("%Y-%m-%d", time.localtime(c.mod))
|
||||||
|
elif type == "cardReps":
|
||||||
|
return str(c.reps)
|
||||||
|
elif type == "cardLapses":
|
||||||
|
return str(c.lapses)
|
||||||
|
elif type == "cardIvl":
|
||||||
|
return fmtTimeSpan(c.ivl*86400)
|
||||||
|
elif type == "cardEase":
|
||||||
|
return "%d%%" % (c.factor/10)
|
||||||
|
|
||||||
|
# def intervalColumn(self, index):
|
||||||
|
# return fmtTimeSpan(
|
||||||
|
# self.cards[index.row()][CARD_INTERVAL]*86400)
|
||||||
|
|
||||||
|
# def limitContent(self, txt):
|
||||||
|
# if "<c>" in txt:
|
||||||
|
# matches = re.findall("(?s)<c>(.*?)</c>", txt)
|
||||||
|
# return " ".join(matches)
|
||||||
|
# else:
|
||||||
|
# return txt
|
||||||
|
|
||||||
|
def formatQA(self, txt):
|
||||||
|
#s = self.limitContent(s)
|
||||||
|
s = txt.replace("<br>", u" ")
|
||||||
|
s = s.replace("<br />", u" ")
|
||||||
|
s = s.replace("\n", u" ")
|
||||||
|
s = re.sub("\[sound:[^]]+\]", "", s)
|
||||||
|
s = stripHTMLMedia(s)
|
||||||
|
s = s.strip()
|
||||||
|
return s
|
||||||
|
|
||||||
|
def nextDue(self, c, index):
|
||||||
if c.type == 0:
|
if c.type == 0:
|
||||||
return _("(new card)")
|
return _("(new card)")
|
||||||
elif c.type == 1:
|
elif c.type == 1:
|
||||||
diff = c.due - time.time()
|
date = c.due
|
||||||
elif c.type == 2:
|
elif c.type == 2:
|
||||||
diff = (c.due - self.deck.sched.today)*86400
|
date = time.time() + ((c.due - self.deck.sched.today)*86400)
|
||||||
if diff <= 0:
|
return time.strftime("%Y-%m-%d", time.localtime(date))
|
||||||
return _("%s ago") % fmtTimeSpan(abs(diff), pad=0)
|
|
||||||
else:
|
|
||||||
return _("in %s") % fmtTimeSpan(diff, pad=0)
|
|
||||||
|
|
||||||
def thirdColumn(self, index):
|
|
||||||
self.sortKey = "aoeu"
|
|
||||||
if self.sortKey == "created":
|
|
||||||
return self.createdColumn(index)
|
|
||||||
elif self.sortKey == "modified":
|
|
||||||
return self.modifiedColumn(index)
|
|
||||||
elif self.sortKey == "interval":
|
|
||||||
return self.intervalColumn(index)
|
|
||||||
elif self.sortKey == "reps":
|
|
||||||
return self.repsColumn(index)
|
|
||||||
elif self.sortKey == "factor":
|
|
||||||
return self.easeColumn(index)
|
|
||||||
elif self.sortKey == "noCount":
|
|
||||||
return self.noColumn(index)
|
|
||||||
elif self.sortKey == "fact":
|
|
||||||
return self.factCreatedColumn(index)
|
|
||||||
elif self.sortKey == "firstAnswered":
|
|
||||||
return self.firstAnsweredColumn(index)
|
|
||||||
else:
|
|
||||||
return self.nextDue(index)
|
|
||||||
|
|
||||||
def onSortChanged(self):
|
|
||||||
if self.sortKey == "created":
|
|
||||||
k = _("Created")
|
|
||||||
elif self.sortKey == "modified":
|
|
||||||
k = _("Modified")
|
|
||||||
elif self.sortKey == "interval":
|
|
||||||
k = _("Interval")
|
|
||||||
elif self.sortKey == "reps":
|
|
||||||
k = _("Reps")
|
|
||||||
elif self.sortKey == "factor":
|
|
||||||
k = _("Ease")
|
|
||||||
elif self.sortKey == "noCount":
|
|
||||||
k = _("Lapses")
|
|
||||||
elif self.sortKey == "firstAnswered":
|
|
||||||
k = _("First Answered")
|
|
||||||
elif self.sortKey == "fact":
|
|
||||||
k = _("Fact Created")
|
|
||||||
else:
|
|
||||||
k = _("Due")
|
|
||||||
self.columns[-1][0] = k
|
|
||||||
|
|
||||||
def createdColumn(self, index):
|
|
||||||
return time.strftime("%Y-%m-%d", time.localtime(
|
|
||||||
self.cards[index.row()][CARD_CREATED]))
|
|
||||||
|
|
||||||
def factCreatedColumn(self, index):
|
|
||||||
return time.strftime("%Y-%m-%d", time.localtime(
|
|
||||||
self.cards[index.row()][CARD_FACTCREATED]))
|
|
||||||
|
|
||||||
def modifiedColumn(self, index):
|
|
||||||
return time.strftime("%Y-%m-%d", time.localtime(
|
|
||||||
self.cards[index.row()][CARD_MODIFIED]))
|
|
||||||
|
|
||||||
def intervalColumn(self, index):
|
|
||||||
return fmtTimeSpan(
|
|
||||||
self.cards[index.row()][CARD_INTERVAL]*86400)
|
|
||||||
|
|
||||||
def repsColumn(self, index):
|
|
||||||
return str(self.cards[index.row()][CARD_REPS])
|
|
||||||
|
|
||||||
def easeColumn(self, index):
|
|
||||||
return "%0.2f" % self.cards[index.row()][CARD_EASE]
|
|
||||||
|
|
||||||
def noColumn(self, index):
|
|
||||||
return "%d" % self.cards[index.row()][CARD_NO]
|
|
||||||
|
|
||||||
def firstAnsweredColumn(self, index):
|
|
||||||
firstAnswered = self.cards[index.row()][CARD_FIRSTANSWERED]
|
|
||||||
if firstAnswered == 0:
|
|
||||||
return _("(new card)")
|
|
||||||
else:
|
|
||||||
return time.strftime("%Y-%m-%d", time.localtime(firstAnswered))
|
|
||||||
|
|
||||||
# Line painter
|
# Line painter
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
class StatusDelegate(QItemDelegate):
|
class StatusDelegate(QItemDelegate):
|
||||||
|
|
||||||
def __init__(self, parent, model):
|
def __init__(self, browser, model):
|
||||||
QItemDelegate.__init__(self, parent)
|
QItemDelegate.__init__(self, browser)
|
||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
|
@ -276,11 +235,11 @@ class Browser(QMainWindow):
|
||||||
self.form.splitter.setChildrenCollapsible(False)
|
self.form.splitter.setChildrenCollapsible(False)
|
||||||
self.form.splitter_2.setChildrenCollapsible(False)
|
self.form.splitter_2.setChildrenCollapsible(False)
|
||||||
self.form.splitter_3.setChildrenCollapsible(False)
|
self.form.splitter_3.setChildrenCollapsible(False)
|
||||||
|
self.setupSort()
|
||||||
self.setupToolbar()
|
self.setupToolbar()
|
||||||
self.setupTable()
|
self.setupTable()
|
||||||
self.setupMenus()
|
self.setupMenus()
|
||||||
self.setupSearch()
|
self.setupSearch()
|
||||||
self.setupSort()
|
|
||||||
self.setupTree()
|
self.setupTree()
|
||||||
self.setupHeaders()
|
self.setupHeaders()
|
||||||
self.setupHooks()
|
self.setupHooks()
|
||||||
|
@ -474,16 +433,20 @@ class Browser(QMainWindow):
|
||||||
|
|
||||||
def setupSort(self):
|
def setupSort(self):
|
||||||
self.sortTypes = [
|
self.sortTypes = [
|
||||||
"factFld",
|
('factFld', _("Fields")),
|
||||||
"factCrt",
|
('factCrt', _("Creation date")),
|
||||||
"factMod",
|
('factMod', _("Edit date")),
|
||||||
"cardMod",
|
('cardMod', _("Review date")),
|
||||||
"cardDue",
|
('cardDue', _("Due date")),
|
||||||
"cardEase",
|
('cardIvl', _("Card interval")),
|
||||||
"cardReps",
|
('cardEase', _("Ease factor")),
|
||||||
"cardLapses",
|
('cardReps', _("Review count")),
|
||||||
|
('cardLapses', _("Lapse count")),
|
||||||
]
|
]
|
||||||
idx = self.sortTypes.index(self.deck.conf['sortType'])
|
for c, (type, name) in enumerate(self.sortTypes):
|
||||||
|
self.form.sortBox.addItem(name)
|
||||||
|
if type == self.deck.conf['sortType']:
|
||||||
|
idx = c
|
||||||
self.form.sortBox.setCurrentIndex(idx)
|
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)
|
||||||
|
@ -505,11 +468,10 @@ class Browser(QMainWindow):
|
||||||
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):
|
||||||
self.sortIndex = idx
|
self.deck.conf['sortType'] = self.sortTypes[idx][0]
|
||||||
self.deck.conf['sortType'] = self.sortTypes[idx]
|
|
||||||
self.model.onSortChanged()
|
|
||||||
if refresh:
|
if refresh:
|
||||||
self.model.showMatching()
|
self.model.showMatching()
|
||||||
|
# fixme: we do this in various locations
|
||||||
self.updateFilterLabel()
|
self.updateFilterLabel()
|
||||||
self.focusCard()
|
self.focusCard()
|
||||||
|
|
||||||
|
|
|
@ -119,48 +119,7 @@
|
||||||
</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>
|
||||||
|
|
Loading…
Reference in a new issue