mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
browser tweaks
This commit is contained in:
parent
23411c2872
commit
10234be4e9
6 changed files with 38 additions and 149 deletions
|
@ -31,7 +31,7 @@ De Pooter, Susanna Björverud, Tacutu, Timm Preetz, Timo Paulssen, Ursus, Victor
|
||||||
Suba, and Xtru.
|
Suba, and Xtru.
|
||||||
<p>
|
<p>
|
||||||
Anki icon by Alex Fraser (CC GNU GPL)<br>
|
Anki icon by Alex Fraser (CC GNU GPL)<br>
|
||||||
Deck icon by Laurent Baumann (CC BY-NC-SA 3.0)<br>
|
Deck icon: Be Box Icons (free for non-commercial use)<br>
|
||||||
Deck browser icons from:<br>
|
Deck browser icons from:<br>
|
||||||
http://led24.de/iconset<br>
|
http://led24.de/iconset<br>
|
||||||
http://p.yusukekamiyamane.com/<br>
|
http://p.yusukekamiyamane.com/<br>
|
||||||
|
|
127
aqt/browser.py
127
aqt/browser.py
|
@ -327,7 +327,7 @@ class Browser(QMainWindow):
|
||||||
self.onUndoState(self.mw.form.actionUndo.isEnabled())
|
self.onUndoState(self.mw.form.actionUndo.isEnabled())
|
||||||
self.form.searchEdit.setFocus()
|
self.form.searchEdit.setFocus()
|
||||||
self.show()
|
self.show()
|
||||||
self.form.searchEdit.setText("is:recent")
|
self.form.searchEdit.setText("deck:current is:recent")
|
||||||
self.form.searchEdit.selectAll()
|
self.form.searchEdit.selectAll()
|
||||||
self.onSearch()
|
self.onSearch()
|
||||||
|
|
||||||
|
@ -465,7 +465,7 @@ class Browser(QMainWindow):
|
||||||
cur) % {
|
cur) % {
|
||||||
"cur": cur,
|
"cur": cur,
|
||||||
"sel": ngettext("%d selected", "%d selected", selected) % selected
|
"sel": ngettext("%d selected", "%d selected", selected) % selected
|
||||||
} + " - " + self.col.name())
|
})
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
def onReset(self):
|
def onReset(self):
|
||||||
|
@ -613,8 +613,7 @@ class Browser(QMainWindow):
|
||||||
self.form.tree.clear()
|
self.form.tree.clear()
|
||||||
root = self.form.tree.invisibleRootItem()
|
root = self.form.tree.invisibleRootItem()
|
||||||
self._systemTagTree(root)
|
self._systemTagTree(root)
|
||||||
self._modelTree(root)
|
self._decksTree(root)
|
||||||
self._groupTree(root)
|
|
||||||
self._userTagTree(root)
|
self._userTagTree(root)
|
||||||
self.form.tree.expandToDepth(0)
|
self.form.tree.expandToDepth(0)
|
||||||
self.form.tree.setIndentation(15)
|
self.form.tree.setIndentation(15)
|
||||||
|
@ -646,49 +645,21 @@ class Browser(QMainWindow):
|
||||||
self.form.searchEdit.setText(txt)
|
self.form.searchEdit.setText(txt)
|
||||||
self.onSearch()
|
self.onSearch()
|
||||||
|
|
||||||
def _modelTree(self, root):
|
|
||||||
for m in sorted(self.col.models.all(), key=itemgetter("name")):
|
|
||||||
mitem = self.CallbackItem(
|
|
||||||
m['name'], lambda m=m: self.setFilter("model", m['name']))
|
|
||||||
mitem.setIcon(0, QIcon(":/icons/product_design.png"))
|
|
||||||
root.addChild(mitem)
|
|
||||||
for t in m['tmpls']:
|
|
||||||
titem = self.CallbackItem(
|
|
||||||
t['name'], lambda m=m, t=t: self.setFilter(
|
|
||||||
"model", m['name'], "card", t['name']))
|
|
||||||
titem.setIcon(0, QIcon(":/icons/stock_new_template.png"))
|
|
||||||
mitem.addChild(titem)
|
|
||||||
|
|
||||||
def _groupTree(self, root):
|
|
||||||
grps = self.col.sched.deckDueTree()
|
|
||||||
def fillGroups(root, grps, head=""):
|
|
||||||
for g in grps:
|
|
||||||
item = self.CallbackItem(
|
|
||||||
g[0], lambda g=g: self.setFilter(
|
|
||||||
"group", head+g[0]))
|
|
||||||
item.setIcon(0, QIcon(":/icons/stock_group.png"))
|
|
||||||
root.addChild(item)
|
|
||||||
fillGroups(item, g[4], g[0]+"::")
|
|
||||||
fillGroups(root, grps)
|
|
||||||
|
|
||||||
def _systemTagTree(self, root):
|
def _systemTagTree(self, root):
|
||||||
tags = (
|
tags = (
|
||||||
(_("All cards"), "stock_new_template", ""),
|
(_("All cards"), "stock_new_template", ""),
|
||||||
(_("New"), "stock_new_template_blue.png", "is:new"),
|
(_("Current Deck"), "stock_new_template", "deck:current"),
|
||||||
(_("Learning"), "stock_new_template_red.png", "is:lrn"),
|
(_("New"), "plus-circle.png", "is:new"),
|
||||||
(_("Review"), "stock_new_template_green.png", "is:rev"),
|
(_("Learning"), "stock_new_template_red.png", "is:learn"),
|
||||||
|
(_("Review"), "clock-icon.png", "is:review"),
|
||||||
(_("Marked"), "rating.png", "tag:marked"),
|
(_("Marked"), "rating.png", "tag:marked"),
|
||||||
(_("Suspended"), "media-playback-pause.png", "is:suspended"),
|
(_("Suspended"), "media-playback-pause.png", "is:suspended"),
|
||||||
(_("Leech"), "emblem-important.png", "tag:leech"),
|
(_("Leech"), "emblem-important.png", "tag:leech"))
|
||||||
(_("Due"), "stock_new_template_green.png", "is:due"))
|
|
||||||
for name, icon, cmd in tags:
|
for name, icon, cmd in tags:
|
||||||
item = self.CallbackItem(
|
item = self.CallbackItem(
|
||||||
name, lambda c=cmd: self.setFilter(c))
|
name, lambda c=cmd: self.setFilter(c))
|
||||||
item.setIcon(0, QIcon(":/icons/" + icon))
|
item.setIcon(0, QIcon(":/icons/" + icon))
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
item = self.CallbackItem(_("Refresh list"), self.buildTree)
|
|
||||||
item.setIcon(0, QIcon(":/icons/multisynk.png"))
|
|
||||||
root.addChild(item)
|
|
||||||
return root
|
return root
|
||||||
|
|
||||||
def _userTagTree(self, root):
|
def _userTagTree(self, root):
|
||||||
|
@ -698,6 +669,18 @@ class Browser(QMainWindow):
|
||||||
item.setIcon(0, QIcon(":/icons/anki-tag.png"))
|
item.setIcon(0, QIcon(":/icons/anki-tag.png"))
|
||||||
root.addChild(item)
|
root.addChild(item)
|
||||||
|
|
||||||
|
def _decksTree(self, root):
|
||||||
|
grps = self.col.sched.deckDueTree()
|
||||||
|
def fillGroups(root, grps, head=""):
|
||||||
|
for g in grps:
|
||||||
|
item = self.CallbackItem(
|
||||||
|
g[0], lambda g=g: self.setFilter(
|
||||||
|
"deck", head+g[0]))
|
||||||
|
item.setIcon(0, QIcon(":/icons/stock_group.png"))
|
||||||
|
root.addChild(item)
|
||||||
|
fillGroups(item, g[4], g[0]+"::")
|
||||||
|
fillGroups(root, grps)
|
||||||
|
|
||||||
# Card info
|
# Card info
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
@ -1243,76 +1226,6 @@ select fm.id, fm.name from fieldmodels fm""")
|
||||||
def onCardList(self):
|
def onCardList(self):
|
||||||
self.form.tableView.setFocus()
|
self.form.tableView.setFocus()
|
||||||
|
|
||||||
# Generate cards
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
class GenCards(QDialog):
|
|
||||||
|
|
||||||
def __init__(self, browser, nids):
|
|
||||||
QDialog.__init__(self, browser)
|
|
||||||
self.browser = browser
|
|
||||||
self.nids = nids
|
|
||||||
self.form = aqt.forms.gencards.Ui_Dialog()
|
|
||||||
self.form.setupUi(self)
|
|
||||||
self.setWindowModality(Qt.WindowModal)
|
|
||||||
self.connect(self.form.buttonBox, SIGNAL("helpRequested()"),
|
|
||||||
self.onHelp)
|
|
||||||
restoreGeom(self, "addCardModels")
|
|
||||||
self.getSelection()
|
|
||||||
|
|
||||||
def getSelection(self):
|
|
||||||
# get cards to enable
|
|
||||||
f = self.browser.col.getNote(self.nids[0])
|
|
||||||
self.model = f.model()
|
|
||||||
self.items = []
|
|
||||||
for t in self.model.templates:
|
|
||||||
item = QListWidgetItem(t['name'], self.form.list)
|
|
||||||
self.form.list.addItem(item)
|
|
||||||
self.items.append(item)
|
|
||||||
idx = self.form.list.indexFromItem(item)
|
|
||||||
if t['actv']:
|
|
||||||
mode = QItemSelectionModel.Select
|
|
||||||
else:
|
|
||||||
mode = QItemSelectionModel.Deselect
|
|
||||||
self.form.list.selectionModel().select(idx, mode)
|
|
||||||
self.exec_()
|
|
||||||
|
|
||||||
def accept(self):
|
|
||||||
tplates = []
|
|
||||||
unused = []
|
|
||||||
for i, item in enumerate(self.items):
|
|
||||||
idx = self.form.list.indexFromItem(item)
|
|
||||||
if self.form.list.selectionModel().isSelected(idx):
|
|
||||||
tplates.append(self.model.templates[i])
|
|
||||||
else:
|
|
||||||
unused.append(self.model.templates[i]['ord'])
|
|
||||||
if not self.form.deleteUnsel.isChecked():
|
|
||||||
unused = None
|
|
||||||
self.genCards(tplates, unused)
|
|
||||||
saveGeom(self, "addCardModels")
|
|
||||||
QDialog.accept(self)
|
|
||||||
|
|
||||||
def genCards(self, tplates, unused):
|
|
||||||
mw = self.browser.mw
|
|
||||||
mw.checkpoint(_("Generate Cards"))
|
|
||||||
mw.progress.start()
|
|
||||||
for c, nid in enumerate(self.nids):
|
|
||||||
f = mw.col.getNote(nid)
|
|
||||||
mw.col.genCards(f, tplates)
|
|
||||||
if c % 100 == 0:
|
|
||||||
mw.progress.update()
|
|
||||||
if unused:
|
|
||||||
cids = mw.col.db.list("""
|
|
||||||
select id from cards where nid in %s and ord in %s""" % (
|
|
||||||
ids2str(self.nids), ids2str(unused)))
|
|
||||||
mw.col.remCards(cids)
|
|
||||||
mw.progress.finish()
|
|
||||||
mw.requireReset()
|
|
||||||
self.browser.onSearch()
|
|
||||||
|
|
||||||
def onHelp(self):
|
|
||||||
openHelp("Browser#GenerateCards")
|
|
||||||
|
|
||||||
# Change model dialog
|
# Change model dialog
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import anki.js
|
||||||
# is focused, which is not good when the user is tabbing through the dialog
|
# is focused, which is not good when the user is tabbing through the dialog
|
||||||
# fixme: set rtl in div css
|
# fixme: set rtl in div css
|
||||||
|
|
||||||
# fixme: commit from tag/group area causes error
|
# fixme: commit from tag area causes error
|
||||||
|
|
||||||
pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif")
|
pics = ("jpg", "jpeg", "png", "tif", "tiff", "gif")
|
||||||
audio = ("wav", "mp3", "ogg", "flac")
|
audio = ("wav", "mp3", "ogg", "flac")
|
||||||
|
@ -29,6 +29,7 @@ _html = """
|
||||||
border: 1px solid #aaa; background:#fff; color:#000; padding: 5px;
|
border: 1px solid #aaa; background:#fff; color:#000; padding: 5px;
|
||||||
}
|
}
|
||||||
.fname { font-size: 12px; vertical-align: middle; padding-right: 5px; }
|
.fname { font-size: 12px; vertical-align: middle; padding-right: 5px; }
|
||||||
|
#dupes { font-size: 12px; }
|
||||||
img { max-width: 150; max-height: 150; }
|
img { max-width: 150; max-height: 150; }
|
||||||
body { margin: 5px; }
|
body { margin: 5px; }
|
||||||
</style><script>
|
</style><script>
|
||||||
|
@ -204,7 +205,7 @@ class Editor(object):
|
||||||
self.setupOuter()
|
self.setupOuter()
|
||||||
self.setupButtons()
|
self.setupButtons()
|
||||||
self.setupWeb()
|
self.setupWeb()
|
||||||
self.setupTagsAndGroup()
|
self.setupTags()
|
||||||
self.setupKeyboard()
|
self.setupKeyboard()
|
||||||
|
|
||||||
# Initial setup
|
# Initial setup
|
||||||
|
@ -375,7 +376,7 @@ class Editor(object):
|
||||||
self.web.setHtml(_html % (getBase(self.mw.col), anki.js.all,
|
self.web.setHtml(_html % (getBase(self.mw.col), anki.js.all,
|
||||||
_("Show Duplicates")),
|
_("Show Duplicates")),
|
||||||
loadCB=self._loadFinished)
|
loadCB=self._loadFinished)
|
||||||
self.updateTagsAndGroup()
|
self.updateTags()
|
||||||
self.updateKeyboard()
|
self.updateKeyboard()
|
||||||
elif hide:
|
elif hide:
|
||||||
self.widget.hide()
|
self.widget.hide()
|
||||||
|
@ -407,7 +408,7 @@ class Editor(object):
|
||||||
self._keepButtons = True
|
self._keepButtons = True
|
||||||
self.web.eval("saveField('blur');")
|
self.web.eval("saveField('blur');")
|
||||||
self._keepButtons = False
|
self._keepButtons = False
|
||||||
self.saveTagsAndGroup()
|
self.saveTags()
|
||||||
|
|
||||||
def checkValid(self):
|
def checkValid(self):
|
||||||
cols = []
|
cols = []
|
||||||
|
@ -424,10 +425,11 @@ class Editor(object):
|
||||||
self.web.eval("hideDupes();")
|
self.web.eval("hideDupes();")
|
||||||
|
|
||||||
def showDupes(self):
|
def showDupes(self):
|
||||||
contents = self.note.fields[self.dupe]
|
contents = self.note.fields[0]
|
||||||
|
print "conts", `self.note.fields`
|
||||||
browser = aqt.dialogs.open("Browser", self.mw)
|
browser = aqt.dialogs.open("Browser", self.mw)
|
||||||
browser.form.searchEdit.setText(
|
browser.form.searchEdit.setText(
|
||||||
"'model:%s' '%s'" % (self.note.model().name, contents))
|
"'model:%s' '%s'" % (self.note.model()['name'], contents))
|
||||||
browser.onSearch()
|
browser.onSearch()
|
||||||
|
|
||||||
def fieldsAreBlank(self):
|
def fieldsAreBlank(self):
|
||||||
|
@ -455,71 +457,43 @@ class Editor(object):
|
||||||
form.textEdit.toPlainText())
|
form.textEdit.toPlainText())
|
||||||
self.loadNote(self.currentField)
|
self.loadNote(self.currentField)
|
||||||
|
|
||||||
# Tag and group handling
|
# Tag handling
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def setupTagsAndGroup(self):
|
def setupTags(self):
|
||||||
import aqt.tagedit
|
import aqt.tagedit
|
||||||
g = QGroupBox(self.widget)
|
g = QGroupBox(self.widget)
|
||||||
g.setFlat(True)
|
g.setFlat(True)
|
||||||
tb = QGridLayout()
|
tb = QGridLayout()
|
||||||
tb.setSpacing(12)
|
tb.setSpacing(12)
|
||||||
tb.setMargin(6)
|
tb.setMargin(6)
|
||||||
# group
|
|
||||||
l = QLabel(_("Initial Group"))
|
|
||||||
tb.addWidget(l, 0, 0)
|
|
||||||
if not self.addMode:
|
|
||||||
self.group = QPushButton()
|
|
||||||
self.group.connect(self.group, SIGNAL("clicked()"),
|
|
||||||
self.changeGroup)
|
|
||||||
else:
|
|
||||||
self.group = aqt.tagedit.TagEdit(self.widget, type=1)
|
|
||||||
self.group.connect(self.group, SIGNAL("lostFocus"),
|
|
||||||
self.saveTagsAndGroup)
|
|
||||||
tb.addWidget(self.group, 0, 1)
|
|
||||||
# tags
|
# tags
|
||||||
l = QLabel(_("Tags"))
|
l = QLabel(_("Tags"))
|
||||||
tb.addWidget(l, 0, 2)
|
tb.addWidget(l, 0, 2)
|
||||||
self.tags = aqt.tagedit.TagEdit(self.widget)
|
self.tags = aqt.tagedit.TagEdit(self.widget)
|
||||||
self.tags.connect(self.tags, SIGNAL("lostFocus"),
|
self.tags.connect(self.tags, SIGNAL("lostFocus"),
|
||||||
self.saveTagsAndGroup)
|
self.saveTags)
|
||||||
tb.addWidget(self.tags, 0, 3)
|
tb.addWidget(self.tags, 0, 3)
|
||||||
g.setLayout(tb)
|
g.setLayout(tb)
|
||||||
self.outerLayout.addWidget(g)
|
self.outerLayout.addWidget(g)
|
||||||
|
|
||||||
def updateTagsAndGroup(self):
|
def updateTags(self):
|
||||||
if self.tags.col != self.mw.col:
|
if self.tags.col != self.mw.col:
|
||||||
self.tags.setCol(self.mw.col)
|
self.tags.setCol(self.mw.col)
|
||||||
if self.addMode:
|
|
||||||
self.group.setCol(self.mw.col)
|
|
||||||
self.tags.setText(self.note.stringTags().strip())
|
self.tags.setText(self.note.stringTags().strip())
|
||||||
if getattr(self.note, 'did', None):
|
|
||||||
did = self.note.did
|
|
||||||
else:
|
|
||||||
did = self.note.model()['did']
|
|
||||||
self.group.setText(self.mw.col.decks.name(did))
|
|
||||||
|
|
||||||
def saveTagsAndGroup(self):
|
def saveTags(self):
|
||||||
if not self.note:
|
if not self.note:
|
||||||
return
|
return
|
||||||
self.note.tags = self.mw.col.tags.split(unicode(self.tags.text()))
|
self.note.tags = self.mw.col.tags.split(unicode(self.tags.text()))
|
||||||
if self.addMode:
|
if self.addMode:
|
||||||
# save group and tags to model
|
# save group and tags to model
|
||||||
self.note.did = self.mw.col.decks.id(unicode(self.group.text()))
|
|
||||||
m = self.note.model()
|
m = self.note.model()
|
||||||
m['did'] = self.note.did
|
m['did'] = self.note.did
|
||||||
m['tags'] = self.note.tags
|
m['tags'] = self.note.tags
|
||||||
self.mw.col.models.save(m)
|
self.mw.col.models.save(m)
|
||||||
self.note.flush()
|
self.note.flush()
|
||||||
runHook("tagsAndGroupUpdated", self.note)
|
runHook("tagsUpdated", self.note)
|
||||||
|
|
||||||
def changeGroup(self):
|
|
||||||
id = self.note.id
|
|
||||||
runHook("closeEditCurrent")
|
|
||||||
browser = aqt.dialogs.open("Browser", self.mw)
|
|
||||||
browser.form.searchEdit.setText("nid:%d" % id)
|
|
||||||
browser.onSearch()
|
|
||||||
browser.setGroup(True)
|
|
||||||
|
|
||||||
# Format buttons
|
# Format buttons
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
|
@ -162,6 +162,8 @@ $(".ansbut").focus();
|
||||||
# fixme: timer
|
# fixme: timer
|
||||||
self.state = "question"
|
self.state = "question"
|
||||||
c = self.card
|
c = self.card
|
||||||
|
# mod the card so it shows up in the recently modified list
|
||||||
|
self.card.flush()
|
||||||
q = c.q()
|
q = c.q()
|
||||||
a = c.a()
|
a = c.a()
|
||||||
if self.mw.pm.profile['autoplay']:
|
if self.mw.pm.profile['autoplay']:
|
||||||
|
|
|
@ -561,7 +561,7 @@
|
||||||
<action name="actionSetDeck">
|
<action name="actionSetDeck">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="icons.qrc">
|
||||||
<normaloff>:/icons/graphite_smooth_folder_noncommercial.png</normaloff>:/icons/graphite_smooth_folder_noncommercial.png</iconset>
|
<normaloff>:/icons/stock_group.png</normaloff>:/icons/stock_group.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Move to Deck...</string>
|
<string>Move to Deck...</string>
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 4.4 KiB |
Loading…
Reference in a new issue