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()
|
||||
splash.update()
|
||||
# load plugins
|
||||
self.loadPlugins()
|
||||
self.setupPlugins()
|
||||
splash.update()
|
||||
# show main window
|
||||
splash.finish(self)
|
||||
|
@ -334,6 +334,10 @@ counts are %d %d %d
|
|||
if self.appUpdated:
|
||||
self.config['version'] = aqt.appVersion
|
||||
|
||||
def setupPlugins(self):
|
||||
import aqt.plugins
|
||||
self.pluginManager = aqt.plugins.PluginManager(self)
|
||||
|
||||
def setupThreads(self):
|
||||
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.actionLocalizeMedia, s, self.onLocalizeMedia)
|
||||
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.actionDonate, s, self.onDonate)
|
||||
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.")
|
||||
)
|
||||
|
||||
# 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
|
||||
##########################################################################
|
||||
|
||||
|
|
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.QtCore import *
|
||||
|
||||
from anki.sound import playFromText, stripSounds
|
||||
|
||||
import re, os, sys, urllib, time
|
||||
import aqt
|
||||
from anki.sound import playFromText, stripSounds
|
||||
from anki.utils import call
|
||||
|
||||
def openLink(link):
|
||||
QDesktopServices.openUrl(QUrl(link))
|
||||
|
@ -285,3 +284,11 @@ def getBase(deck, card):
|
|||
return '<base href="%s">' % base
|
||||
else:
|
||||
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