mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
split plugin handling into separate file
This commit is contained in:
parent
e6b153df13
commit
a1e5a13f98
3 changed files with 182 additions and 173 deletions
175
aqt/main.py
175
aqt/main.py
|
@ -36,7 +36,7 @@ class AnkiQt(QMainWindow):
|
||||||
self.setup()
|
self.setup()
|
||||||
splash.update()
|
splash.update()
|
||||||
# load plugins
|
# load plugins
|
||||||
self.loadPlugins()
|
self.setupPlugins()
|
||||||
splash.update()
|
splash.update()
|
||||||
# show main window
|
# show main window
|
||||||
splash.finish(self)
|
splash.finish(self)
|
||||||
|
@ -334,6 +334,10 @@ counts are %d %d %d
|
||||||
if self.appUpdated:
|
if self.appUpdated:
|
||||||
self.config['version'] = aqt.appVersion
|
self.config['version'] = aqt.appVersion
|
||||||
|
|
||||||
|
def setupPlugins(self):
|
||||||
|
import aqt.plugins
|
||||||
|
self.pluginManager = aqt.plugins.PluginManager(self)
|
||||||
|
|
||||||
def setupThreads(self):
|
def setupThreads(self):
|
||||||
self._mainThread = QThread.currentThread()
|
self._mainThread = QThread.currentThread()
|
||||||
|
|
||||||
|
@ -1711,9 +1715,6 @@ This deck already exists on your computer. Overwrite the local copy?"""),
|
||||||
self.connect(m.actionDownloadMissingMedia, s, self.onDownloadMissingMedia)
|
self.connect(m.actionDownloadMissingMedia, s, self.onDownloadMissingMedia)
|
||||||
self.connect(m.actionLocalizeMedia, s, self.onLocalizeMedia)
|
self.connect(m.actionLocalizeMedia, s, self.onLocalizeMedia)
|
||||||
self.connect(m.actionCram, s, self.onCram)
|
self.connect(m.actionCram, s, self.onCram)
|
||||||
self.connect(m.actionOpenPluginFolder, s, self.onOpenPluginFolder)
|
|
||||||
self.connect(m.actionEnableAllPlugins, s, self.onEnableAllPlugins)
|
|
||||||
self.connect(m.actionDisableAllPlugins, s, self.onDisableAllPlugins)
|
|
||||||
self.connect(m.actionStudyOptions, s, self.onStudyOptions)
|
self.connect(m.actionStudyOptions, s, self.onStudyOptions)
|
||||||
self.connect(m.actionDonate, s, self.onDonate)
|
self.connect(m.actionDonate, s, self.onDonate)
|
||||||
self.connect(m.actionRecordNoiseProfile, s, self.onRecordNoiseProfile)
|
self.connect(m.actionRecordNoiseProfile, s, self.onRecordNoiseProfile)
|
||||||
|
@ -1833,172 +1834,6 @@ This deck already exists on your computer. Overwrite the local copy?"""),
|
||||||
_(" Please ensure it is set correctly and then restart Anki.")
|
_(" Please ensure it is set correctly and then restart Anki.")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Plugins
|
|
||||||
##########################################################################
|
|
||||||
|
|
||||||
def pluginsFolder(self):
|
|
||||||
dir = self.config.confDir
|
|
||||||
if sys.platform.startswith("win32"):
|
|
||||||
dir = dir.encode(sys.getfilesystemencoding())
|
|
||||||
return os.path.join(dir, "plugins")
|
|
||||||
|
|
||||||
def loadPlugins(self):
|
|
||||||
if sys.platform.startswith("win32"):
|
|
||||||
self.clearPluginCache()
|
|
||||||
self.disableObsoletePlugins()
|
|
||||||
plugdir = self.pluginsFolder()
|
|
||||||
sys.path.insert(0, plugdir)
|
|
||||||
plugins = self.enabledPlugins()
|
|
||||||
plugins.sort()
|
|
||||||
self.registeredPlugins = {}
|
|
||||||
for plugin in plugins:
|
|
||||||
try:
|
|
||||||
nopy = plugin.replace(".py", "")
|
|
||||||
__import__(nopy)
|
|
||||||
except:
|
|
||||||
print "Error in %s" % plugin
|
|
||||||
traceback.print_exc()
|
|
||||||
self.checkForUpdatedPlugins()
|
|
||||||
self.disableCardMenuItems()
|
|
||||||
self.rebuildPluginsMenu()
|
|
||||||
# run the obsolete init hook
|
|
||||||
try:
|
|
||||||
runHook('init')
|
|
||||||
except:
|
|
||||||
showWarning(
|
|
||||||
_("Broken plugin:\n\n%s") %
|
|
||||||
unicode(traceback.format_exc(), "utf-8", "replace"))
|
|
||||||
|
|
||||||
def clearPluginCache(self):
|
|
||||||
"Clear .pyc files which may cause crashes if Python version updated."
|
|
||||||
dir = self.pluginsFolder()
|
|
||||||
for curdir, dirs, files in os.walk(dir):
|
|
||||||
for f in files:
|
|
||||||
if not f.endswith(".pyc"):
|
|
||||||
continue
|
|
||||||
os.unlink(os.path.join(curdir, f))
|
|
||||||
|
|
||||||
def disableObsoletePlugins(self):
|
|
||||||
dir = self.pluginsFolder()
|
|
||||||
native = _(
|
|
||||||
"The %s plugin has been disabled, as Anki supports "+
|
|
||||||
"this natively now.")
|
|
||||||
plugins = [
|
|
||||||
("Custom Media Directory.py",
|
|
||||||
(native % "custom media folder") + _(""" \
|
|
||||||
Please visit Settings>Preferences.""")),
|
|
||||||
("Regenerate Reading Field.py", _("""\
|
|
||||||
The regenerate reading field plugin has been disabled, as the Japanese \
|
|
||||||
support plugin supports this now. Please download the latest version.""")),
|
|
||||||
("Sync LaTeX with iPhone client.py",
|
|
||||||
native % "sync LaTeX"),
|
|
||||||
("Incremental Reading.py",
|
|
||||||
_("""The incremental reading plugin has been disabled because \
|
|
||||||
it needs updates.""")),
|
|
||||||
("Learn Mode.py", _("""\
|
|
||||||
The learn mode plugin has been disabled because it needs to be rewritten \
|
|
||||||
to work with this version of Anki."""))
|
|
||||||
]
|
|
||||||
for p in plugins:
|
|
||||||
path = os.path.join(dir, p[0])
|
|
||||||
if os.path.exists(path):
|
|
||||||
new = path.replace(".py", ".disabled")
|
|
||||||
if os.path.exists(new):
|
|
||||||
os.unlink(new)
|
|
||||||
os.rename(path, new)
|
|
||||||
showInfo(p[1])
|
|
||||||
|
|
||||||
def rebuildPluginsMenu(self):
|
|
||||||
if getattr(self, "pluginActions", None) is None:
|
|
||||||
self.pluginActions = []
|
|
||||||
for action in self.pluginActions:
|
|
||||||
self.form.menuStartup.removeAction(action)
|
|
||||||
all = self.allPlugins()
|
|
||||||
all.sort()
|
|
||||||
for fname in all:
|
|
||||||
enabled = fname.endswith(".py")
|
|
||||||
p = re.sub("\.py(\.off)?", "", fname)
|
|
||||||
if p+".py" in self.registeredPlugins:
|
|
||||||
p = self.registeredPlugins[p+".py"]['name']
|
|
||||||
a = QAction(p, self)
|
|
||||||
a.setCheckable(True)
|
|
||||||
a.setChecked(enabled)
|
|
||||||
self.connect(a, SIGNAL("triggered()"),
|
|
||||||
lambda fname=fname: self.togglePlugin(fname))
|
|
||||||
self.form.menuStartup.addAction(a)
|
|
||||||
self.pluginActions.append(a)
|
|
||||||
|
|
||||||
def enabledPlugins(self):
|
|
||||||
return [p for p in os.listdir(self.pluginsFolder())
|
|
||||||
if p.endswith(".py")]
|
|
||||||
|
|
||||||
def disabledPlugins(self):
|
|
||||||
return [p for p in os.listdir(self.pluginsFolder())
|
|
||||||
if p.endswith(".py.off")]
|
|
||||||
|
|
||||||
def allPlugins(self):
|
|
||||||
return [p for p in os.listdir(self.pluginsFolder())
|
|
||||||
if p.endswith(".py.off") or p.endswith(".py")]
|
|
||||||
|
|
||||||
def onOpenPluginFolder(self, path=None):
|
|
||||||
if path is None:
|
|
||||||
path = self.pluginsFolder()
|
|
||||||
if sys.platform == "win32":
|
|
||||||
if isinstance(path, unicode):
|
|
||||||
path = path.encode(sys.getfilesystemencoding())
|
|
||||||
anki.utils.call(["explorer", path], wait=False)
|
|
||||||
else:
|
|
||||||
QDesktopServices.openUrl(QUrl("file://" + path))
|
|
||||||
|
|
||||||
def onGetPlugins(self):
|
|
||||||
QDesktopServices.openUrl(QUrl("http://ichi2.net/anki/wiki/Plugins"))
|
|
||||||
|
|
||||||
def enablePlugin(self, p):
|
|
||||||
pd = self.pluginsFolder()
|
|
||||||
old = os.path.join(pd, p)
|
|
||||||
new = os.path.join(pd, p.replace(".off", ""))
|
|
||||||
try:
|
|
||||||
os.unlink(new)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.rename(old, new)
|
|
||||||
|
|
||||||
def disablePlugin(self, p):
|
|
||||||
pd = self.pluginsFolder()
|
|
||||||
old = os.path.join(pd, p)
|
|
||||||
new = os.path.join(pd, p.replace(".py", ".py.off"))
|
|
||||||
try:
|
|
||||||
os.unlink(new)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
os.rename(old, new)
|
|
||||||
|
|
||||||
def onEnableAllPlugins(self):
|
|
||||||
for p in self.disabledPlugins():
|
|
||||||
self.enablePlugin(p)
|
|
||||||
self.rebuildPluginsMenu()
|
|
||||||
|
|
||||||
def onDisableAllPlugins(self):
|
|
||||||
for p in self.enabledPlugins():
|
|
||||||
self.disablePlugin(p)
|
|
||||||
self.rebuildPluginsMenu()
|
|
||||||
|
|
||||||
def togglePlugin(self, plugin):
|
|
||||||
if plugin.endswith(".py"):
|
|
||||||
self.disablePlugin(plugin)
|
|
||||||
else:
|
|
||||||
self.enablePlugin(plugin)
|
|
||||||
self.rebuildPluginsMenu()
|
|
||||||
|
|
||||||
def registerPlugin(self, name, updateId):
|
|
||||||
return
|
|
||||||
# src = os.path.basename(inspect.getfile(inspect.currentframe(1)))
|
|
||||||
# self.registeredPlugins[src] = {'name': name,
|
|
||||||
# 'id': updateId}
|
|
||||||
|
|
||||||
def checkForUpdatedPlugins(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Custom styles
|
# Custom styles
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
|
|
167
aqt/plugins.py
Normal file
167
aqt/plugins.py
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
# Copyright: Damien Elmes <anki@ichi2.net>
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
|
||||||
|
|
||||||
|
import sys, os, re
|
||||||
|
from PyQt4.QtCore import *
|
||||||
|
from PyQt4.QtGui import *
|
||||||
|
from aqt.utils import showInfo, showWarning, openFolder
|
||||||
|
from anki.hooks import runHook
|
||||||
|
|
||||||
|
class PluginManager(object):
|
||||||
|
|
||||||
|
def __init__(self, mw):
|
||||||
|
self.mw = mw
|
||||||
|
f = self.mw.form; s = SIGNAL("triggered()")
|
||||||
|
self.mw.connect(f.actionOpenPluginFolder, s, self.onOpenPluginFolder)
|
||||||
|
self.mw.connect(f.actionEnableAllPlugins, s, self.onEnableAllPlugins)
|
||||||
|
self.mw.connect(f.actionDisableAllPlugins, s, self.onDisableAllPlugins)
|
||||||
|
if sys.platform.startswith("win32"):
|
||||||
|
self.clearPluginCache()
|
||||||
|
self.disableObsoletePlugins()
|
||||||
|
plugdir = self.pluginsFolder()
|
||||||
|
sys.path.insert(0, plugdir)
|
||||||
|
plugins = self.enabledPlugins()
|
||||||
|
plugins.sort()
|
||||||
|
self.registeredPlugins = {}
|
||||||
|
for plugin in plugins:
|
||||||
|
try:
|
||||||
|
nopy = plugin.replace(".py", "")
|
||||||
|
__import__(nopy)
|
||||||
|
except:
|
||||||
|
print "Error in %s" % plugin
|
||||||
|
traceback.print_exc()
|
||||||
|
self.mw.disableCardMenuItems()
|
||||||
|
self.rebuildPluginsMenu()
|
||||||
|
# run the obsolete init hook
|
||||||
|
try:
|
||||||
|
runHook('init')
|
||||||
|
except:
|
||||||
|
showWarning(
|
||||||
|
_("Broken plugin:\n\n%s") %
|
||||||
|
unicode(traceback.format_exc(), "utf-8", "replace"))
|
||||||
|
|
||||||
|
def pluginsFolder(self):
|
||||||
|
dir = self.mw.config.confDir
|
||||||
|
if sys.platform.startswith("win32"):
|
||||||
|
dir = dir.encode(sys.getfilesystemencoding())
|
||||||
|
return os.path.join(dir, "plugins")
|
||||||
|
|
||||||
|
def clearPluginCache(self):
|
||||||
|
"Clear .pyc files which may cause crashes if Python version updated."
|
||||||
|
dir = self.pluginsFolder()
|
||||||
|
for curdir, dirs, files in os.walk(dir):
|
||||||
|
for f in files:
|
||||||
|
if not f.endswith(".pyc"):
|
||||||
|
continue
|
||||||
|
os.unlink(os.path.join(curdir, f))
|
||||||
|
|
||||||
|
def disableObsoletePlugins(self):
|
||||||
|
dir = self.pluginsFolder()
|
||||||
|
native = _(
|
||||||
|
"The %s plugin has been disabled, as Anki supports "+
|
||||||
|
"this natively now.")
|
||||||
|
plugins = [
|
||||||
|
("Custom Media Directory.py",
|
||||||
|
(native % "custom media folder") + _(""" \
|
||||||
|
Please visit Settings>Preferences.""")),
|
||||||
|
("Regenerate Reading Field.py", _("""\
|
||||||
|
The regenerate reading field plugin has been disabled, as the Japanese \
|
||||||
|
support plugin supports this now. Please download the latest version.""")),
|
||||||
|
("Sync LaTeX with iPhone client.py",
|
||||||
|
native % "sync LaTeX"),
|
||||||
|
("Incremental Reading.py",
|
||||||
|
_("""The incremental reading plugin has been disabled because \
|
||||||
|
it needs updates.""")),
|
||||||
|
("Learn Mode.py", _("""\
|
||||||
|
The learn mode plugin has been disabled because it needs to be rewritten \
|
||||||
|
to work with this version of Anki."""))
|
||||||
|
]
|
||||||
|
for p in plugins:
|
||||||
|
path = os.path.join(dir, p[0])
|
||||||
|
if os.path.exists(path):
|
||||||
|
new = path.replace(".py", ".disabled")
|
||||||
|
if os.path.exists(new):
|
||||||
|
os.unlink(new)
|
||||||
|
os.rename(path, new)
|
||||||
|
showInfo(p[1])
|
||||||
|
|
||||||
|
def rebuildPluginsMenu(self):
|
||||||
|
if getattr(self, "pluginActions", None) is None:
|
||||||
|
self.pluginActions = []
|
||||||
|
for action in self.pluginActions:
|
||||||
|
self.mw.form.menuStartup.removeAction(action)
|
||||||
|
all = self.allPlugins()
|
||||||
|
all.sort()
|
||||||
|
for fname in all:
|
||||||
|
enabled = fname.endswith(".py")
|
||||||
|
p = re.sub("\.py(\.off)?", "", fname)
|
||||||
|
if p+".py" in self.registeredPlugins:
|
||||||
|
p = self.registeredPlugins[p+".py"]['name']
|
||||||
|
a = QAction(p, self.mw)
|
||||||
|
a.setCheckable(True)
|
||||||
|
a.setChecked(enabled)
|
||||||
|
self.mw.connect(a, SIGNAL("triggered()"),
|
||||||
|
lambda fname=fname: self.togglePlugin(fname))
|
||||||
|
self.mw.form.menuStartup.addAction(a)
|
||||||
|
self.pluginActions.append(a)
|
||||||
|
|
||||||
|
def enabledPlugins(self):
|
||||||
|
return [p for p in os.listdir(self.pluginsFolder())
|
||||||
|
if p.endswith(".py")]
|
||||||
|
|
||||||
|
def disabledPlugins(self):
|
||||||
|
return [p for p in os.listdir(self.pluginsFolder())
|
||||||
|
if p.endswith(".py.off")]
|
||||||
|
|
||||||
|
def allPlugins(self):
|
||||||
|
return [p for p in os.listdir(self.pluginsFolder())
|
||||||
|
if p.endswith(".py.off") or p.endswith(".py")]
|
||||||
|
|
||||||
|
def onOpenPluginFolder(self, path=None):
|
||||||
|
if path is None:
|
||||||
|
path = self.pluginsFolder()
|
||||||
|
openFolder(path)
|
||||||
|
|
||||||
|
def onGetPlugins(self):
|
||||||
|
QDesktopServices.openUrl(QUrl("http://ichi2.net/anki/wiki/Plugins"))
|
||||||
|
|
||||||
|
def enablePlugin(self, p):
|
||||||
|
pd = self.pluginsFolder()
|
||||||
|
old = os.path.join(pd, p)
|
||||||
|
new = os.path.join(pd, p.replace(".off", ""))
|
||||||
|
try:
|
||||||
|
os.unlink(new)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
os.rename(old, new)
|
||||||
|
|
||||||
|
def disablePlugin(self, p):
|
||||||
|
pd = self.pluginsFolder()
|
||||||
|
old = os.path.join(pd, p)
|
||||||
|
new = os.path.join(pd, p.replace(".py", ".py.off"))
|
||||||
|
try:
|
||||||
|
os.unlink(new)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
os.rename(old, new)
|
||||||
|
|
||||||
|
def onEnableAllPlugins(self):
|
||||||
|
for p in self.disabledPlugins():
|
||||||
|
self.enablePlugin(p)
|
||||||
|
self.rebuildPluginsMenu()
|
||||||
|
|
||||||
|
def onDisableAllPlugins(self):
|
||||||
|
for p in self.enabledPlugins():
|
||||||
|
self.disablePlugin(p)
|
||||||
|
self.rebuildPluginsMenu()
|
||||||
|
|
||||||
|
def togglePlugin(self, plugin):
|
||||||
|
if plugin.endswith(".py"):
|
||||||
|
self.disablePlugin(plugin)
|
||||||
|
else:
|
||||||
|
self.enablePlugin(plugin)
|
||||||
|
self.rebuildPluginsMenu()
|
||||||
|
|
||||||
|
def registerPlugin(self, name, updateId):
|
||||||
|
return
|
13
aqt/utils.py
13
aqt/utils.py
|
@ -3,11 +3,10 @@
|
||||||
|
|
||||||
from PyQt4.QtGui import *
|
from PyQt4.QtGui import *
|
||||||
from PyQt4.QtCore import *
|
from PyQt4.QtCore import *
|
||||||
|
|
||||||
from anki.sound import playFromText, stripSounds
|
|
||||||
|
|
||||||
import re, os, sys, urllib, time
|
import re, os, sys, urllib, time
|
||||||
import aqt
|
import aqt
|
||||||
|
from anki.sound import playFromText, stripSounds
|
||||||
|
from anki.utils import call
|
||||||
|
|
||||||
def openLink(link):
|
def openLink(link):
|
||||||
QDesktopServices.openUrl(QUrl(link))
|
QDesktopServices.openUrl(QUrl(link))
|
||||||
|
@ -285,3 +284,11 @@ def getBase(deck, card):
|
||||||
return '<base href="%s">' % base
|
return '<base href="%s">' % base
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def openFolder(path):
|
||||||
|
if sys.platform == "win32":
|
||||||
|
if isinstance(path, unicode):
|
||||||
|
path = path.encode(sys.getfilesystemencoding())
|
||||||
|
call(["explorer", path], wait=False)
|
||||||
|
else:
|
||||||
|
QDesktopServices.openUrl(QUrl("file://" + path))
|
||||||
|
|
Loading…
Reference in a new issue