mirror of
https://github.com/ankitects/anki.git
synced 2025-11-11 07:07:13 -05:00
merge deck stats and graphs together
This commit is contained in:
parent
0ab698154f
commit
7ab89d2637
4 changed files with 209 additions and 121 deletions
15
aqt/main.py
15
aqt/main.py
|
|
@ -526,7 +526,7 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors")
|
||||||
tb.addAction(frm.actionEditLayout)
|
tb.addAction(frm.actionEditLayout)
|
||||||
tb.addAction(frm.actionEditdeck)
|
tb.addAction(frm.actionEditdeck)
|
||||||
tb.addAction(frm.actionOverview)
|
tb.addAction(frm.actionOverview)
|
||||||
tb.addAction(frm.actionGraphs)
|
tb.addAction(frm.actionStats)
|
||||||
tb.addAction(frm.actionMarkCard)
|
tb.addAction(frm.actionMarkCard)
|
||||||
tb.addAction(frm.actionRepeatAudio)
|
tb.addAction(frm.actionRepeatAudio)
|
||||||
tb.addAction(frm.actionClose)
|
tb.addAction(frm.actionClose)
|
||||||
|
|
@ -651,11 +651,8 @@ Debug info:\n%s""") % traceback.format_exc(), help="DeckErrors")
|
||||||
def onCardStats(self):
|
def onCardStats(self):
|
||||||
self.cardStats.show()
|
self.cardStats.show()
|
||||||
|
|
||||||
def onDeckStats(self):
|
def onStats(self):
|
||||||
aqt.stats.deckStats(self)
|
aqt.stats.DeckStats(self)
|
||||||
|
|
||||||
def onGraphs(self):
|
|
||||||
aqt.stats.graphs(self)
|
|
||||||
|
|
||||||
def onCardLayout(self):
|
def onCardLayout(self):
|
||||||
aqt.clayout.CardLayout(self, 0, self.currentCard.fact,
|
aqt.clayout.CardLayout(self, 0, self.currentCard.fact,
|
||||||
|
|
@ -740,8 +737,7 @@ Please choose a new deck name:"""))
|
||||||
"DeckProperties",
|
"DeckProperties",
|
||||||
"Undo",
|
"Undo",
|
||||||
"Export",
|
"Export",
|
||||||
"Graphs",
|
"Stats",
|
||||||
"Dstats",
|
|
||||||
"Cstats",
|
"Cstats",
|
||||||
"StudyOptions",
|
"StudyOptions",
|
||||||
"Overview",
|
"Overview",
|
||||||
|
|
@ -765,9 +761,8 @@ Please choose a new deck name:"""))
|
||||||
self.connect(m.actionEditdeck, s, self.onEditDeck)
|
self.connect(m.actionEditdeck, s, self.onEditDeck)
|
||||||
self.connect(m.actionEditCurrent, s, self.onEditCurrent)
|
self.connect(m.actionEditCurrent, s, self.onEditCurrent)
|
||||||
self.connect(m.actionPreferences, s, self.onPrefs)
|
self.connect(m.actionPreferences, s, self.onPrefs)
|
||||||
self.connect(m.actionDstats, s, self.onDeckStats)
|
self.connect(m.actionStats, s, self.onStats)
|
||||||
self.connect(m.actionCstats, s, self.onCardStats)
|
self.connect(m.actionCstats, s, self.onCardStats)
|
||||||
self.connect(m.actionGraphs, s, self.onGraphs)
|
|
||||||
self.connect(m.actionEditLayout, s, self.onCardLayout)
|
self.connect(m.actionEditLayout, s, self.onCardLayout)
|
||||||
self.connect(m.actionAbout, s, self.onAbout)
|
self.connect(m.actionAbout, s, self.onAbout)
|
||||||
self.connect(m.actionImport, s, self.onImport)
|
self.connect(m.actionImport, s, self.onImport)
|
||||||
|
|
|
||||||
124
aqt/stats.py
124
aqt/stats.py
|
|
@ -8,6 +8,7 @@ import os, tempfile
|
||||||
from aqt.webview import AnkiWebView
|
from aqt.webview import AnkiWebView
|
||||||
from aqt.utils import saveGeom, restoreGeom
|
from aqt.utils import saveGeom, restoreGeom
|
||||||
from anki.hooks import addHook
|
from anki.hooks import addHook
|
||||||
|
import aqt
|
||||||
|
|
||||||
# Card stats
|
# Card stats
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
@ -58,51 +59,44 @@ class CardStats(object):
|
||||||
<style>table { font-size: 12px; } h1 { font-size: 14px; }</style>
|
<style>table { font-size: 12px; } h1 { font-size: 14px; }</style>
|
||||||
</head><body><center>%s</center></body></html>"""%txt)
|
</head><body><center>%s</center></body></html>"""%txt)
|
||||||
|
|
||||||
# Modal dialog that supports dumping to browser (for printing, etc)
|
# Deck Stats
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
class PrintableReport(QDialog):
|
class DeckStats(QDialog):
|
||||||
|
|
||||||
def __init__(self, mw, type, title, func, css):
|
def __init__(self, mw):
|
||||||
self.mw = mw
|
|
||||||
QDialog.__init__(self, mw)
|
QDialog.__init__(self, mw)
|
||||||
restoreGeom(self, type)
|
self.mw = mw
|
||||||
self.type = type
|
self.name = "deckStats"
|
||||||
self.setWindowTitle(title)
|
self.period = 0
|
||||||
self.setModal(True)
|
self.sel = True
|
||||||
self.mw.progress.start()
|
self.form = aqt.forms.stats.Ui_Dialog()
|
||||||
self.web = AnkiWebView(self)
|
f = self.form
|
||||||
l = QVBoxLayout(self)
|
f.setupUi(self)
|
||||||
l.setContentsMargins(0,0,0,0)
|
restoreGeom(self, self.name)
|
||||||
l.addWidget(self.web)
|
b = f.buttonBox.addButton(_("Save Image"),
|
||||||
self.css = css
|
QDialogButtonBox.ActionRole)
|
||||||
if func:
|
|
||||||
self.report = func()
|
|
||||||
self.web.stdHtml(self.report, css=css)
|
|
||||||
self.box = QDialogButtonBox(QDialogButtonBox.Close)
|
|
||||||
b = self.box.addButton(_("Save Image"), QDialogButtonBox.ActionRole)
|
|
||||||
b.connect(b, SIGNAL("clicked()"), self.browser)
|
b.connect(b, SIGNAL("clicked()"), self.browser)
|
||||||
b.setAutoDefault(False)
|
b.setAutoDefault(False)
|
||||||
self.layout = QHBoxLayout()
|
c = self.connect
|
||||||
self.layout.setContentsMargins(0,0,0,0)
|
s = SIGNAL("clicked()")
|
||||||
self.layout.addWidget(self.box)
|
c(f.groups, s, lambda: self.changeSel(True))
|
||||||
l.addLayout(self.layout)
|
c(f.all, s, lambda: self.changeSel(False))
|
||||||
self.setLayout(l)
|
c(f.month, s, lambda: self.changePeriod(0))
|
||||||
self.connect(self.box, SIGNAL("rejected()"), self, SLOT("reject()"))
|
c(f.year, s, lambda: self.changePeriod(1))
|
||||||
self.mw.progress.finish()
|
c(f.life, s, lambda: self.changePeriod(2))
|
||||||
|
self.refresh()
|
||||||
def run(self):
|
|
||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
def reject(self):
|
def reject(self):
|
||||||
saveGeom(self, self.type)
|
saveGeom(self, self.name)
|
||||||
QDialog.reject(self)
|
QDialog.reject(self)
|
||||||
|
|
||||||
def browser(self):
|
def browser(self):
|
||||||
# dump to a temporary file
|
# dump to a temporary file
|
||||||
tmpdir = tempfile.mkdtemp(prefix="anki")
|
tmpdir = tempfile.mkdtemp(prefix="anki")
|
||||||
path = os.path.join(tmpdir, "report.png")
|
path = os.path.join(tmpdir, "report.png")
|
||||||
p = self.web.page()
|
p = self.form.web.page()
|
||||||
oldsize = p.viewportSize()
|
oldsize = p.viewportSize()
|
||||||
p.setViewportSize(p.mainFrame().contentsSize())
|
p.setViewportSize(p.mainFrame().contentsSize())
|
||||||
image = QImage(p.viewportSize(), QImage.Format_ARGB32)
|
image = QImage(p.viewportSize(), QImage.Format_ARGB32)
|
||||||
|
|
@ -113,73 +107,17 @@ class PrintableReport(QDialog):
|
||||||
p.setViewportSize(oldsize)
|
p.setViewportSize(oldsize)
|
||||||
QDesktopServices.openUrl(QUrl("file://" + path))
|
QDesktopServices.openUrl(QUrl("file://" + path))
|
||||||
|
|
||||||
# Deck stats
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def deckStats(mw):
|
|
||||||
css=mw.sharedCSS+"""
|
|
||||||
body { margin: 2em; font-family: arial; }
|
|
||||||
h1 { font-size: 18px; border-bottom: 1px solid #000; margin-top: 1em;
|
|
||||||
clear: both; margin-bottom: 0.5em; }
|
|
||||||
.info {float:right; padding: 10px; max-width: 300px; border-radius: 5px;
|
|
||||||
background: #ddd; font-size: 14px; }
|
|
||||||
"""
|
|
||||||
return PrintableReport(
|
|
||||||
mw,
|
|
||||||
"deckstats",
|
|
||||||
_("Deck Statistics"),
|
|
||||||
mw.deck.deckStats,
|
|
||||||
css).run()
|
|
||||||
|
|
||||||
# Graphs
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
class Graphs(PrintableReport):
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
self.period = 0
|
|
||||||
self.periods = [
|
|
||||||
_("1 month"),
|
|
||||||
_("1 year"),
|
|
||||||
_("deck life")]
|
|
||||||
PrintableReport.__init__(self, *args)
|
|
||||||
grp = QGroupBox()
|
|
||||||
l = QHBoxLayout()
|
|
||||||
l.setContentsMargins(6,6,6,6)
|
|
||||||
chk = False
|
|
||||||
for c, p in enumerate(self.periods):
|
|
||||||
b = QRadioButton(p)
|
|
||||||
if not chk:
|
|
||||||
b.setChecked(True)
|
|
||||||
chk = True
|
|
||||||
b.connect(b, SIGNAL("clicked()"), lambda n=c: self.changePeriod(n))
|
|
||||||
l.addWidget(b)
|
|
||||||
grp.setLayout(l)
|
|
||||||
self.layout.insertWidget(0, grp)
|
|
||||||
self.layout.insertStretch(0, 10)
|
|
||||||
self.refresh()
|
|
||||||
|
|
||||||
def changePeriod(self, n):
|
def changePeriod(self, n):
|
||||||
self.period = n
|
self.period = n
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
|
def changeSel(self, sel):
|
||||||
|
self.sel = sel
|
||||||
|
self.refresh()
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.mw.progress.start(immediate=True)
|
self.mw.progress.start(immediate=True)
|
||||||
self.report = self.mw.deck.graphs().report(type=self.period)
|
self.report = self.mw.deck.graphs().report(
|
||||||
self.web.stdHtml(self.report, css=self.css)
|
type=self.period, selective=self.sel)
|
||||||
|
self.form.web.setHtml(self.report)
|
||||||
self.mw.progress.finish()
|
self.mw.progress.finish()
|
||||||
|
|
||||||
def graphs(mw):
|
|
||||||
css=mw.sharedCSS+"""
|
|
||||||
body { margin: 2em; font-family: arial; background: #eee; }
|
|
||||||
h1 { font-size: 18px; border-bottom: 1px solid #000; margin-top: 1em;
|
|
||||||
clear: both; margin-bottom: 0.5em; }
|
|
||||||
.info {float:right; padding: 10px; max-width: 300px; border-radius: 5px;
|
|
||||||
background: #ddd; font-size: 14px; }
|
|
||||||
"""
|
|
||||||
return Graphs(
|
|
||||||
mw,
|
|
||||||
"graphs",
|
|
||||||
_("Graphs"),
|
|
||||||
None,
|
|
||||||
css).run()
|
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,7 @@
|
||||||
<addaction name="actionLocalizeMedia"/>
|
<addaction name="actionLocalizeMedia"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
</widget>
|
</widget>
|
||||||
<addaction name="actionGraphs"/>
|
<addaction name="actionStats"/>
|
||||||
<addaction name="actionDstats"/>
|
|
||||||
<addaction name="actionCstats"/>
|
<addaction name="actionCstats"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionRepeatAudio"/>
|
<addaction name="actionRepeatAudio"/>
|
||||||
|
|
@ -321,18 +320,6 @@
|
||||||
<enum>QAction::PreferencesRole</enum>
|
<enum>QAction::PreferencesRole</enum>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionDstats">
|
|
||||||
<property name="icon">
|
|
||||||
<iconset resource="icons.qrc">
|
|
||||||
<normaloff>:/icons/spreadsheet.png</normaloff>:/icons/spreadsheet.png</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Deck Statistics</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>Shift+D</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionAbout">
|
<action name="actionAbout">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="icons.qrc">
|
||||||
|
|
@ -351,7 +338,7 @@
|
||||||
<normaloff>:/icons/package_games_card.png</normaloff>:/icons/package_games_card.png</iconset>
|
<normaloff>:/icons/package_games_card.png</normaloff>:/icons/package_games_card.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Card Statistics</string>
|
<string>&Card Info</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="statusTip">
|
<property name="statusTip">
|
||||||
<string>Show statistics about the current card and last card</string>
|
<string>Show statistics about the current card and last card</string>
|
||||||
|
|
@ -387,19 +374,19 @@
|
||||||
<string>Ctrl+I</string>
|
<string>Ctrl+I</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionGraphs">
|
<action name="actionStats">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset resource="icons.qrc">
|
<iconset resource="icons.qrc">
|
||||||
<normaloff>:/icons/view-statistics.png</normaloff>:/icons/view-statistics.png</iconset>
|
<normaloff>:/icons/view-statistics.png</normaloff>:/icons/view-statistics.png</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Graphs...</string>
|
<string>&Statistics...</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="statusTip">
|
<property name="statusTip">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="shortcut">
|
<property name="shortcut">
|
||||||
<string>Shift+G</string>
|
<string>Shift+S</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionExport">
|
<action name="actionExport">
|
||||||
|
|
|
||||||
168
designer/stats.ui
Normal file
168
designer/stats.ui
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Dialog</class>
|
||||||
|
<widget class="QDialog" name="Dialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>607</width>
|
||||||
|
<height>556</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Statistics</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWebView" name="web">
|
||||||
|
<property name="url">
|
||||||
|
<url>
|
||||||
|
<string>about:blank</string>
|
||||||
|
</url>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox_2">
|
||||||
|
<property name="title">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="groups">
|
||||||
|
<property name="text">
|
||||||
|
<string>groups</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="all">
|
||||||
|
<property name="text">
|
||||||
|
<string>all</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="month">
|
||||||
|
<property name="text">
|
||||||
|
<string>1 month</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="year">
|
||||||
|
<property name="text">
|
||||||
|
<string>1 year</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="life">
|
||||||
|
<property name="text">
|
||||||
|
<string>deck life</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>QWebView</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>QtWebKit/QWebView</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Dialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
||||||
Loading…
Reference in a new issue