deck and plugin sharing

This commit is contained in:
Damien Elmes 2009-03-11 05:19:02 +09:00
parent 804413604c
commit 2646a40edb
9 changed files with 671 additions and 119 deletions

View file

@ -26,6 +26,7 @@ def importAll():
import update import update
import utils import utils
import view import view
import getshared
class DialogManager(object): class DialogManager(object):

View file

@ -4,10 +4,20 @@
from PyQt4.QtGui import * from PyQt4.QtGui import *
from PyQt4.QtCore import * from PyQt4.QtCore import *
import anki, ankiqt import anki, ankiqt
from anki.exporting import exporters from anki.exporting import exporters as exporters_
from anki.utils import parseTags from anki.utils import parseTags
from ankiqt import ui from ankiqt import ui
class PackagedAnkiExporter(object):
def __init__(self, *args):
pass
def exporters():
l = list(exporters_())
l.insert(1, (_("Packaged Anki Deck (*.zip)"),
PackagedAnkiExporter))
return l
class ExportDialog(QDialog): class ExportDialog(QDialog):
def __init__(self, parent): def __init__(self, parent):
@ -35,7 +45,7 @@ class ExportDialog(QDialog):
self.setTabOrder(self.tags, self.setTabOrder(self.tags,
self.dialog.includeScheduling) self.dialog.includeScheduling)
# save button # save button
b = QPushButton(_("Export to...")) b = QPushButton(_("Export..."))
self.dialog.buttonBox.addButton(b, QDialogButtonBox.AcceptRole) self.dialog.buttonBox.addButton(b, QDialogButtonBox.AcceptRole)
def exporterChanged(self, idx): def exporterChanged(self, idx):
@ -50,6 +60,9 @@ class ExportDialog(QDialog):
self.dialog.includeTags.hide() self.dialog.includeTags.hide()
def accept(self): def accept(self):
if isinstance(self.exporter, PackagedAnkiExporter):
self.parent.onShare(parseTags(unicode(self.tags.text())))
return QDialog.accept(self)
file = ui.utils.getSaveFile(self, _("Choose file to export to"), "export", file = ui.utils.getSaveFile(self, _("Choose file to export to"), "export",
self.exporter.key, self.exporter.ext) self.exporter.key, self.exporter.ext)
self.hide() self.hide()

210
ankiqt/ui/getshared.py Normal file
View file

@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from PyQt4.QtNetwork import *
import ankiqt, simplejson, time, cStringIO, zipfile, tempfile, os, re
from ankiqt.ui.utils import saveGeom, restoreGeom, showInfo
from anki.utils import fmtTimeSpan
R_ID = 0
R_USERNAME = 1
R_TITLE = 2
R_DESCRIPTION = 3
R_TAGS = 4
R_VERSION = 5
R_FACTS = 6
R_SIZE = 7
R_COUNT = 8
R_MODIFIED = 9
R_FNAME = 10
class GetShared(QDialog):
def __init__(self, parent, type):
QDialog.__init__(self, parent, Qt.Window)
self.parent = parent
self.form = ankiqt.forms.getshared.Ui_Dialog()
self.form.setupUi(self)
restoreGeom(self, "getshared")
self.setupTable()
self.onChangeType(type)
self.ok = False
if type == 0:
self.setWindowTitle(_("Download Shared Deck"))
else:
self.setWindowTitle(_("Download Shared Plugin"))
self.exec_()
def setupTable(self):
self.connect(
self.form.table, SIGNAL("currentCellChanged(int,int,int,int)"),
self.onCellChanged)
self.form.table.verticalHeader().setDefaultSectionSize(
self.parent.config['editLineSize'])
self.form.search.setText("search not yet implemented")
def fetchData(self):
h = QHttp(self)
h.connect(h, SIGNAL("requestFinished(int,bool)"), self.onReqFin)
h.setHost("anki.ichi2.net")
#h.setHost("localhost", 8001)
self.conId = h.get("/file/search?t=%d" % self.type)
self.http = h
self.parent.setProgressParent(self)
self.parent.startProgress()
def onReqFin(self, id, err):
"List fetched."
if id != self.conId:
return
self.parent.finishProgress()
self.parent.setProgressParent(None)
if err:
showInfo(_("Unable to connect to server."), parent=self)
self.close()
return
data = self.http.readAll()
self.allList = simplejson.loads(unicode(data))
self.typeChanged()
self.limit()
def limit(self):
self.curList = self.allList
self.redraw()
def redraw(self):
self.form.table.setSortingEnabled(False)
self.form.table.setRowCount(len(self.curList))
self.items = {}
if self.type == 0:
cols = (R_TITLE, R_FACTS, R_MODIFIED)
else:
cols = (R_TITLE, R_MODIFIED)
for rc, r in enumerate(self.curList):
for cc, c in enumerate(cols):
if c == R_MODIFIED:
txt = time.strftime("%m/%Y", time.localtime(r[c]))
else:
txt = unicode(r[c])
item = QTableWidgetItem(txt)
self.items[item] = r
self.form.table.setItem(rc, cc, item)
self.form.table.setSortingEnabled(True)
self.form.table.selectRow(0)
def onCellChanged(self, row, col, x, y):
ci = self.form.table.currentItem()
if not ci:
self.form.bottomLabel.setText(_("Nothing selected."))
return
r = self.items[ci]
self.curRow = r
self.form.bottomLabel.setText(_("""\
<b>Title</b>: %(title)s<br>
<b>Tags</b>: %(tags)s<br>
<b>Size</b>: %(size)0.2fKB<br>
<b>Uploader</b>: %(author)s<br>
<b>Downloads</b>: %(count)s<br>
<b>Description</b>:<br>%(description)s""") % {
'title': r[R_TITLE],
'tags': r[R_TAGS],
'size': r[R_SIZE] / 1024.0,
'author': r[R_USERNAME],
'count': r[R_COUNT],
'description': r[R_DESCRIPTION].replace("\n", "<br>"),
})
self.form.scrollAreaWidgetContents.adjustSize()
self.form.scrollArea.setWidget(self.form.scrollAreaWidgetContents)
def onChangeType(self, type):
self.type = type
self.fetchData()
def typeChanged(self):
self.form.table.clear()
if self.type == 0:
self.form.table.setColumnCount(3)
self.form.table.setHorizontalHeaderLabels([
_("Title"), _("Facts"), _("Modified")])
else:
self.form.table.setColumnCount(2)
self.form.table.setHorizontalHeaderLabels([
_("Title"), _("Modified")])
self.form.table.horizontalHeader().setResizeMode(
0, QHeaderView.Stretch)
self.form.table.verticalHeader().hide()
self.form.table.setSelectionBehavior(QAbstractItemView.SelectRows)
def accept(self):
h = QHttp(self)
h.connect(h, SIGNAL("requestFinished(int,bool)"), self.onReqFin2)
h.setHost("anki.ichi2.net")
#h.setHost("localhost", 8001)
self.conId = h.get("/file/get?id=%d" % self.curRow[R_ID])
self.http = h
self.parent.setProgressParent(self)
self.parent.startProgress()
def onReqFin2(self, id, err):
"File fetched."
if id != self.conId:
return
try:
self.parent.finishProgress()
self.parent.setProgressParent(None)
if err:
showInfo(_("Unable to connect to server."), parent=self)
self.close()
return
data = self.http.readAll()
ext = os.path.splitext(self.curRow[R_FNAME])[1]
if ext == ".zip":
f = cStringIO.StringIO()
f.write(data)
z = zipfile.ZipFile(f)
else:
z = None
tit = self.curRow[R_TITLE]
tit = re.sub("[^][A-Za-z0-9 ()\-]", "", tit)
tit = tit[0:40]
if self.type == 0:
# deck
dd = self.parent.documentDir
p = os.path.join(dd, tit + ".anki")
if os.path.exists(p):
tit += "%d" % time.time()
for l in z.namelist():
if l == "shared.anki":
dpath = os.path.join(dd, tit + ".anki")
open(dpath, "w").write(z.read(l))
elif l.startswith("shared.media/"):
try:
os.mkdir(os.path.join(dd, tit + ".media"))
except OSError:
pass
open(os.path.join(dd, tit + ".media",
os.path.basename(l)),"w").write(z.read(l))
self.parent.loadDeck(dpath)
self.ok = True
else:
pd = self.parent.pluginsFolder()
if z:
raise "nyi"
# for l in z.namelist():
# try:
# os.mkdir(os.path.join(pd, os.path.dirname(l)))
# except OSError:
# pass
# open(os.path.join(pd, 2))
else:
open(os.path.join(pd, tit + ext), "w").write(data)
showInfo(_("Plugin downloaded. Please restart Anki."),
parent=self)
self.ok = True
return
finally:
QDialog.accept(self)

View file

@ -6,8 +6,8 @@ from PyQt4.QtGui import *
from PyQt4.QtCore import * from PyQt4.QtCore import *
from PyQt4.QtWebKit import QWebPage from PyQt4.QtWebKit import QWebPage
import os, sys, re, types, gettext, stat, traceback import os, sys, re, types, gettext, stat, traceback, inspect
import shutil, time, glob, tempfile, datetime import shutil, time, glob, tempfile, datetime, zipfile
from PyQt4.QtCore import * from PyQt4.QtCore import *
from PyQt4.QtGui import * from PyQt4.QtGui import *
@ -627,50 +627,10 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
latest = self.config['recentDeckPaths'][0] latest = self.config['recentDeckPaths'][0]
defaultDir = os.path.dirname(latest) defaultDir = os.path.dirname(latest)
else: else:
if save:
defaultDir = unicode(os.path.expanduser("~/"), defaultDir = unicode(os.path.expanduser("~/"),
sys.getfilesystemencoding()) sys.getfilesystemencoding())
else:
samples = self.getSamplesDir()
if samples:
return samples
return defaultDir return defaultDir
def getSamplesDir(self):
path = os.path.join(ankiqt.runningDir, "libanki")
if not os.path.exists(path):
path = os.path.join(
os.path.join(ankiqt.runningDir, ".."), "libanki")
if not os.path.exists(path):
path = ankiqt.runningDir
if sys.platform.startswith("win32"):
path = os.path.split(
os.path.split(ankiqt.runningDir)[0])[0]
elif sys.platform.startswith("darwin"):
path = ankiqt.runningDir + "/../../.."
path = os.path.join(path, "samples")
path = os.path.normpath(path)
if os.path.exists(path):
if sys.platform.startswith("darwin"):
return self.openMacSamplesDir(path)
return path
return ""
def openMacSamplesDir(self, path):
# some versions of macosx don't allow the open dialog to point inside
# a .App file, it seems - so we copy the files onto the desktop.
newDir = os.path.expanduser("~/Documents/Anki 0.9 Sample Decks")
import shutil
if os.path.exists(newDir):
files = os.listdir(path)
for file in files:
loc = os.path.join(path, file)
if not os.path.exists(os.path.join(newDir, file)):
shutil.copy2(loc, newDir)
return newDir
shutil.copytree(path, newDir)
return newDir
def updateRecentFiles(self, path): def updateRecentFiles(self, path):
"Add the current deck to the list of recent files." "Add the current deck to the list of recent files."
path = os.path.normpath(path) path = os.path.normpath(path)
@ -762,6 +722,7 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
return True return True
def inMainWindow(self): def inMainWindow(self):
return True
return self.app.activeWindow() == self return self.app.activeWindow() == self
def onNew(self, initial=False, path=None): def onNew(self, initial=False, path=None):
@ -827,23 +788,27 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.deck = None self.deck = None
self.moveToState("initial") self.moveToState("initial")
def onOpen(self, samples=False): def onGetSharedDeck(self):
if not self.inMainWindow(): return
if not self.saveAndClose(hideWelcome=True): return
s = ui.getshared.GetShared(self, 0)
if not s.ok:
self.deck = None
self.moveToState("initial")
def onGetSharedPlugin(self):
if not self.inMainWindow(): return
ui.getshared.GetShared(self, 1)
def onOpen(self):
if not self.inMainWindow(): return if not self.inMainWindow(): return
key = _("Deck files (*.anki)") key = _("Deck files (*.anki)")
if samples: defaultDir = self.getSamplesDir() defaultDir = self.getDefaultDir()
else: defaultDir = self.getDefaultDir()
file = QFileDialog.getOpenFileName(self, _("Open deck"), file = QFileDialog.getOpenFileName(self, _("Open deck"),
defaultDir, key) defaultDir, key)
file = unicode(file) file = unicode(file)
if not file: if not file:
return False return False
if samples:
# we need to copy into a writeable location
d = unicode(
os.path.join(self.documentDir, os.path.basename(file)))
if not os.path.exists(d):
shutil.copy(file, d)
file = d
ret = self.loadDeck(file, interactive=True) ret = self.loadDeck(file, interactive=True)
if not ret: if not ret:
if ret is None: if ret is None:
@ -854,9 +819,6 @@ To upgrade an old deck, download Anki 0.9.8.7."""))
self.updateRecentFiles(file) self.updateRecentFiles(file)
return True return True
def onOpenSamples(self):
self.onOpen(samples=True)
def onUnsavedTimer(self): def onUnsavedTimer(self):
QToolTip.showText( QToolTip.showText(
self.mainWin.statusbar.mapToGlobal(QPoint(0, -100)), self.mainWin.statusbar.mapToGlobal(QPoint(0, -100)),
@ -952,14 +914,12 @@ your deck."""))
def onWelcomeAnchor(self, str): def onWelcomeAnchor(self, str):
if str == "new": if str == "new":
self.onNew() self.onNew()
elif str == "sample":
self.onOpenSamples()
elif str == "open": elif str == "open":
self.onOpen() self.onOpen()
elif str == "sample":
self.onGetSharedDeck()
elif str == "openrem": elif str == "openrem":
self.onOpenOnline() self.onOpenOnline()
elif str == "more":
self.onGetMoreDecks()
if str == "addfacts": if str == "addfacts":
if not self.deck: if not self.deck:
self.onNew() self.onNew()
@ -1177,7 +1137,6 @@ day = :d""", d=yesterday)
self.deck.newCardSpacing = self.mainWin.newCardScheduling.currentIndex() self.deck.newCardSpacing = self.mainWin.newCardScheduling.currentIndex()
self.deck.revCardOrder = self.mainWin.revCardOrder.currentIndex() self.deck.revCardOrder = self.mainWin.revCardOrder.currentIndex()
self.deck.setFailedCardPolicy(self.mainWin.failedCardsOption.currentIndex()) self.deck.setFailedCardPolicy(self.mainWin.failedCardsOption.currentIndex())
self.deck.updateDynamicIndices()
self.deck.startSession() self.deck.startSession()
self.deck.flushMod() self.deck.flushMod()
self.moveToState("getQuestion") self.moveToState("getQuestion")
@ -1403,9 +1362,6 @@ day = :d""", d=yesterday)
def onActiveTags(self): def onActiveTags(self):
ui.activetags.show(self) ui.activetags.show(self)
def onGetMoreDecks(self):
QDesktopServices.openUrl(QUrl(ankiqt.appMoreDecks))
# Importing & exporting # Importing & exporting
########################################################################## ##########################################################################
@ -1417,9 +1373,20 @@ day = :d""", d=yesterday)
def onExport(self): def onExport(self):
ui.exporting.ExportDialog(self) ui.exporting.ExportDialog(self)
# Cramming # Cramming & Sharing
########################################################################## ##########################################################################
def _copyToTmpDeck(self, name="cram.anki", tags=""):
ndir = tempfile.mkdtemp(prefix="anki")
path = os.path.join(ndir, name)
from anki.exporting import AnkiExporter
e = AnkiExporter(self.deck)
if tags:
e.limitTags = parseTags(tags)
path = unicode(path, sys.getfilesystemencoding())
e.exportInto(path)
return (e, path)
def onCram(self): def onCram(self):
if self.deck.name() == "cram": if self.deck.name() == "cram":
ui.utils.showInfo( ui.utils.showInfo(
@ -1433,14 +1400,7 @@ day = :d""", d=yesterday)
return return
s = unicode(s) s = unicode(s)
# open tmp deck # open tmp deck
ndir = tempfile.mkdtemp(prefix="anki") (e, path) = self._copyToTmpDeck(tags=s)
path = os.path.join(ndir, "cram.anki")
from anki.exporting import AnkiExporter
e = AnkiExporter(self.deck)
if s:
e.limitTags = parseTags(s)
path = unicode(path, sys.getfilesystemencoding())
e.exportInto(path)
if not e.exportedCards: if not e.exportedCards:
ui.utils.showInfo(_("No cards matched the provided tags.")) ui.utils.showInfo(_("No cards matched the provided tags."))
return return
@ -1484,6 +1444,79 @@ day = :d""", d=yesterday)
self.reset() self.reset()
p.finish() p.finish()
def onShare(self, tags):
pwd = os.getcwd()
# open tmp deck
(e, path) = self._copyToTmpDeck(name="shared.anki", tags=tags)
if not e.exportedCards:
ui.utils.showInfo(_("No cards matched the provided tags."))
return
self.deck.startProgress()
self.deck.updateProgress()
d = DeckStorage.Deck(path)
# reset scheduling to defaults
d.newCardsPerDay = 20
d.delay0 = 600
d.delay1 = 600
d.delay2 = 0
d.hardIntervalMin = 0.333
d.hardIntervalMax = 0.5
d.midIntervalMin = 3.0
d.midIntervalMax = 5.0
d.easyIntervalMin = 7.0
d.easyIntervalMax = 9.0
d.syncName = None
d.suspended = u"Suspended"
self.deck.updateProgress()
d.updateAllPriorities()
d.utcOffset = 24
d.flushMod()
d.save()
self.deck.updateProgress()
# remove indices
indices = d.s.column0(
"select name from sqlite_master where type = 'index'")
for i in indices:
d.s.statement("drop index %s" % i)
# and q/a cache
d.s.statement("update cards set question = '', answer = ''")
self.deck.updateProgress()
d.s.statement("vacuum")
self.deck.updateProgress()
nfacts = d.factCount
mdir = d.mediaDir()
d.close()
dir = os.path.dirname(path)
zippath = os.path.join(dir, "shared-%d.zip" % time.time())
# zip it up
zip = zipfile.ZipFile(zippath, "w", zipfile.ZIP_DEFLATED)
zip.writestr("facts", str(nfacts))
readmep = os.path.join(dir, "README.html")
readme = open(readmep, "w")
readme.write('''\
<html><body>
This is an exported packaged deck created by Anki.<p>
To share this deck with other people, upload it to
<a href="http://anki.ichi2.net/file/upload">
http://anki.ichi2.net/file/upload</a>, or email
it to your friends.
</body></html>''')
readme.close()
zip.write(readmep, "README.txt")
zip.write(path, "shared.anki")
if mdir:
for f in os.listdir(mdir):
zip.write(os.path.join(mdir, f),
str(os.path.join("shared.media/", f)))
shutil.rmtree(mdir)
self.deck.updateProgress()
zip.close()
os.chdir(pwd)
os.unlink(path)
self.deck.finishProgress()
self.onOpenPluginFolder(dir)
# Reviewing and learning ahead # Reviewing and learning ahead
########################################################################## ##########################################################################
@ -1700,10 +1733,12 @@ day = :d""", d=yesterday)
s = SIGNAL("triggered()") s = SIGNAL("triggered()")
self.connect(m.actionNew, s, self.onNew) self.connect(m.actionNew, s, self.onNew)
self.connect(m.actionOpenOnline, s, self.onOpenOnline) self.connect(m.actionOpenOnline, s, self.onOpenOnline)
self.connect(m.actionDownloadSharedDeck, s, self.onGetSharedDeck)
self.connect(m.actionDownloadSharedPlugin, s, self.onGetSharedPlugin)
self.connect(m.actionOpen, s, self.onOpen) self.connect(m.actionOpen, s, self.onOpen)
self.connect(m.actionOpenSamples, s, self.onOpenSamples)
self.connect(m.actionSave, s, self.onSave) self.connect(m.actionSave, s, self.onSave)
self.connect(m.actionSaveAs, s, self.onSaveAs) self.connect(m.actionSaveAs, s, self.onSaveAs)
self.connect(m.actionShare, s, self.onShare)
self.connect(m.actionClose, s, self.onClose) self.connect(m.actionClose, s, self.onClose)
self.connect(m.actionExit, s, self, SLOT("close()")) self.connect(m.actionExit, s, self, SLOT("close()"))
self.connect(m.actionSyncdeck, s, self.syncDeck) self.connect(m.actionSyncdeck, s, self.syncDeck)
@ -1744,7 +1779,6 @@ day = :d""", d=yesterday)
self.connect(m.actionDisableAllPlugins, s, self.onDisableAllPlugins) self.connect(m.actionDisableAllPlugins, s, self.onDisableAllPlugins)
self.connect(m.actionActiveTags, s, self.onActiveTags) self.connect(m.actionActiveTags, s, self.onActiveTags)
self.connect(m.actionReleaseNotes, s, self.onReleaseNotes) self.connect(m.actionReleaseNotes, s, self.onReleaseNotes)
self.connect(m.actionGetMoreDecks, s, self.onGetMoreDecks)
self.connect(m.actionCacheLatex, s, self.onCacheLatex) self.connect(m.actionCacheLatex, s, self.onCacheLatex)
self.connect(m.actionUncacheLatex, s, self.onUncacheLatex) self.connect(m.actionUncacheLatex, s, self.onUncacheLatex)
self.connect(m.actionStudyOptions, s, self.onStudyOptions) self.connect(m.actionStudyOptions, s, self.onStudyOptions)
@ -1912,6 +1946,7 @@ day = :d""", d=yesterday)
sys.path.insert(0, plugdir) sys.path.insert(0, plugdir)
plugins = self.enabledPlugins() plugins = self.enabledPlugins()
plugins.sort() plugins.sort()
self.registeredPlugins = {}
for plugin in plugins: for plugin in plugins:
try: try:
nopy = plugin.replace(".py", "") nopy = plugin.replace(".py", "")
@ -1919,6 +1954,7 @@ day = :d""", d=yesterday)
except: except:
print "Error in %s" % plugin print "Error in %s" % plugin
traceback.print_exc() traceback.print_exc()
self.checkForUpdatedPlugins()
def rebuildPluginsMenu(self): def rebuildPluginsMenu(self):
if getattr(self, "pluginActions", None) is None: if getattr(self, "pluginActions", None) is None:
@ -1950,13 +1986,15 @@ day = :d""", d=yesterday)
return [p for p in os.listdir(self.pluginsFolder()) return [p for p in os.listdir(self.pluginsFolder())
if p.endswith(".py.off") or p.endswith(".py")] if p.endswith(".py.off") or p.endswith(".py")]
def onOpenPluginFolder(self): def onOpenPluginFolder(self, path=None):
if path is None:
path = self.pluginsFolder()
if sys.platform == "win32": if sys.platform == "win32":
# reuse our process handling code from latex # reuse our process handling code from latex
anki.latex.call(["explorer", self.pluginsFolder().encode( anki.latex.call(["explorer", path.encode(
sys.getfilesystemencoding())]) sys.getfilesystemencoding())])
else: else:
QDesktopServices.openUrl(QUrl("file://" + self.pluginsFolder())) QDesktopServices.openUrl(QUrl("file://" + path))
def onGetPlugins(self): def onGetPlugins(self):
QDesktopServices.openUrl(QUrl("http://ichi2.net/anki/wiki/Plugins")) QDesktopServices.openUrl(QUrl("http://ichi2.net/anki/wiki/Plugins"))
@ -1988,6 +2026,14 @@ day = :d""", d=yesterday)
self.enablePlugin(plugin) self.enablePlugin(plugin)
self.rebuildPluginsMenu() self.rebuildPluginsMenu()
def registerPlugin(self, name, updateId):
src = os.path.basename(inspect.getfile(inspect.currentframe(1)))
self.registeredPlugins[src] = {'name': name,
'id': updateId}
def checkForUpdatedPlugins(self):
pass
# Font localisation # Font localisation
########################################################################## ##########################################################################
@ -2019,9 +2065,9 @@ day = :d""", d=yesterday)
########################################################################## ##########################################################################
def setupProgressInfo(self): def setupProgressInfo(self):
addHook("startProgress", self.onStartProgress) addHook("startProgress", self.startProgress)
addHook("updateProgress", self.onUpdateProgress) addHook("updateProgress", self.updateProgress)
addHook("finishProgress", self.onFinishProgress) addHook("finishProgress", self.finishProgress)
addHook("dbProgress", self.onDbProgress) addHook("dbProgress", self.onDbProgress)
addHook("dbFinished", self.onDbFinished) addHook("dbFinished", self.onDbFinished)
self.progressParent = None self.progressParent = None
@ -2032,7 +2078,7 @@ day = :d""", d=yesterday)
def setProgressParent(self, parent): def setProgressParent(self, parent):
self.progressParent = parent self.progressParent = parent
def onStartProgress(self, max=100, min=0, title=None): def startProgress(self, max=0, min=0, title=None):
if self.mainThread != QThread.currentThread(): if self.mainThread != QThread.currentThread():
return return
self.setBusy() self.setBusy()
@ -2042,14 +2088,14 @@ day = :d""", d=yesterday)
p = ui.utils.ProgressWin(parent, max, min, title) p = ui.utils.ProgressWin(parent, max, min, title)
self.progressWins.append(p) self.progressWins.append(p)
def onUpdateProgress(self, label=None, value=None): def updateProgress(self, label=None, value=None):
if self.mainThread != QThread.currentThread(): if self.mainThread != QThread.currentThread():
return return
if self.progressWins: if self.progressWins:
self.progressWins[-1].update(label, value) self.progressWins[-1].update(label, value)
self.app.processEvents() self.app.processEvents()
def onFinishProgress(self): def finishProgress(self):
if self.mainThread != QThread.currentThread(): if self.mainThread != QThread.currentThread():
return return
if self.progressWins: if self.progressWins:

View file

@ -272,25 +272,18 @@ Start adding your own material.</td>
<td valign=middle><h2><a href="welcome:open">Open Local Deck</a></h2></td> <td valign=middle><h2><a href="welcome:open">Open Local Deck</a></h2></td>
</tr> </tr>
<tr>
<td>
<a href="welcome:openrem"><img src=":/icons/document-open-remote.png"></a>
</td>
<td valign=middle><h2><a href="welcome:openrem">Open Online Deck</a></h2></td>
</tr>
<tr> <tr>
<td width=50> <td width=50>
<a href="welcome:sample"><img src=":/icons/anki.png"></a> <a href="welcome:sample"><img src=":/icons/anki.png"></a>
</td> </td>
<td valign=middle><h2><a href="welcome:sample">Open Sample Deck</a></h2></td> <td valign=middle><h2><a href="welcome:sample">Download Shared Deck</a></h2></td>
</tr> </tr>
<tr> <tr>
<td width=50> <td>
<a href="welcome:more"><img src=":/icons/khtml_kget.png"></a> <a href="welcome:openrem"><img src=":/icons/document-open-remote.png"></a>
</td> </td>
<td valign=middle><h2><a href="welcome:more">Get More Decks</a></h2></td> <td valign=middle><h2><a href="welcome:openrem">Download Personal Deck</a></h2></td>
</tr> </tr>
</table>""")) </table>"""))

View file

@ -58,6 +58,19 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<spacer name="verticalSpacer" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox" > <widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" > <property name="orientation" >

121
designer/getshared.ui Normal file
View file

@ -0,0 +1,121 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>517</width>
<height>411</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QLabel" name="searchLabel" >
<property name="text" >
<string>Search:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="search" />
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<widget class="QTableWidget" name="table" />
<widget class="QScrollArea" name="scrollArea" >
<property name="frameShape" >
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy" >
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>494</width>
<height>54</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<widget class="QLabel" name="bottomLabel" >
<property name="text" >
<string>Loading...</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</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>
</widget>
<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>

View file

@ -1174,7 +1174,6 @@
<addaction name="actionReportbug" /> <addaction name="actionReportbug" />
<addaction name="actionForum" /> <addaction name="actionForum" />
<addaction name="actionReleaseNotes" /> <addaction name="actionReleaseNotes" />
<addaction name="actionGetMoreDecks" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="actionDonate" /> <addaction name="actionDonate" />
<addaction name="actionAbout" /> <addaction name="actionAbout" />
@ -1208,10 +1207,23 @@
<normaloff>:/icons/document-open-recent.png</normaloff>:/icons/document-open-recent.png</iconset> <normaloff>:/icons/document-open-recent.png</normaloff>:/icons/document-open-recent.png</iconset>
</property> </property>
</widget> </widget>
<widget class="QMenu" name="menuDownload" >
<property name="title" >
<string>&amp;Download...</string>
</property>
<property name="icon" >
<iconset resource="../icons.qrc" >
<normaloff>:/icons/document-open-remote.png</normaloff>:/icons/document-open-remote.png</iconset>
</property>
<addaction name="actionOpenOnline" />
<addaction name="separator" />
<addaction name="actionDownloadSharedDeck" />
<addaction name="actionDownloadSharedPlugin" />
</widget>
<addaction name="actionNew" /> <addaction name="actionNew" />
<addaction name="actionOpen" /> <addaction name="actionOpen" />
<addaction name="menuOpenRecent" /> <addaction name="menuOpenRecent" />
<addaction name="actionOpenOnline" /> <addaction name="menuDownload" />
<addaction name="actionImport" /> <addaction name="actionImport" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="actionSave" /> <addaction name="actionSave" />
@ -1288,7 +1300,6 @@
<addaction name="actionDisableAllPlugins" /> <addaction name="actionDisableAllPlugins" />
<addaction name="separator" /> <addaction name="separator" />
</widget> </widget>
<addaction name="actionGetPlugins" />
<addaction name="actionOpenPluginFolder" /> <addaction name="actionOpenPluginFolder" />
<addaction name="separator" /> <addaction name="separator" />
<addaction name="menuStartup" /> <addaction name="menuStartup" />
@ -1762,15 +1773,6 @@
<string>Check Media Database...</string> <string>Check Media Database...</string>
</property> </property>
</action> </action>
<action name="actionOpenOnline" >
<property name="icon" >
<iconset resource="../icons.qrc" >
<normaloff>:/icons/document-open-remote.png</normaloff>:/icons/document-open-remote.png</iconset>
</property>
<property name="text" >
<string>Open On&amp;line...</string>
</property>
</action>
<action name="actionCram" > <action name="actionCram" >
<property name="icon" > <property name="icon" >
<iconset resource="../icons.qrc" > <iconset resource="../icons.qrc" >
@ -1823,15 +1825,6 @@
<string>Active &amp;Tags...</string> <string>Active &amp;Tags...</string>
</property> </property>
</action> </action>
<action name="actionGetMoreDecks" >
<property name="icon" >
<iconset resource="../icons.qrc" >
<normaloff>:/icons/download.png</normaloff>:/icons/download.png</iconset>
</property>
<property name="text" >
<string>&amp;Get More Decks...</string>
</property>
</action>
<action name="actionEditCurrent" > <action name="actionEditCurrent" >
<property name="icon" > <property name="icon" >
<iconset resource="../icons.qrc" > <iconset resource="../icons.qrc" >
@ -1904,6 +1897,34 @@
<string>&amp;Record Noise Profile...</string> <string>&amp;Record Noise Profile...</string>
</property> </property>
</action> </action>
<action name="actionGetShared" >
<property name="text" >
<string>Get Shared...</string>
</property>
<property name="statusTip" >
<string>Open a pre-made deck or plugin</string>
</property>
</action>
<action name="actionShare" >
<property name="text" >
<string>Share...</string>
</property>
</action>
<action name="actionOpenOnline" >
<property name="text" >
<string>Personal Deck</string>
</property>
</action>
<action name="actionDownloadSharedDeck" >
<property name="text" >
<string>Shared Deck</string>
</property>
</action>
<action name="actionDownloadSharedPlugin" >
<property name="text" >
<string>Shared Plugin</string>
</property>
</action>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>newPerDay</tabstop> <tabstop>newPerDay</tabstop>

134
designer/share.ui Normal file
View file

@ -0,0 +1,134 @@
<ui version="4.0" >
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>517</width>
<height>411</height>
</rect>
</property>
<property name="windowTitle" >
<string>Get Shared Decks/Plugins</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QLabel" name="searchLabel" >
<property name="text" >
<string>Search:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="search" />
</item>
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Type:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="type" />
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<widget class="QTableWidget" name="table" />
<widget class="QScrollArea" name="scrollArea" >
<property name="frameShape" >
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<property name="verticalScrollBarPolicy" >
<enum>Qt::ScrollBarAsNeeded</enum>
</property>
<property name="horizontalScrollBarPolicy" >
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>494</width>
<height>54</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<widget class="QLabel" name="bottomLabel" >
<property name="text" >
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</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>
</widget>
<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>