From daaf4b6ba8fbddd81553e0076385220ef1a7f327 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Sun, 19 Feb 2006 20:35:46 +0000 Subject: [PATCH] new plugin interface --- doc/PLUGINS | 70 ++++++++++++++ lib/python/Components/PluginComponent.py | 103 +++++++++------------ lib/python/Components/PluginList.py | 20 ++-- lib/python/Plugins/Makefile.am | 5 +- lib/python/Plugins/__init__.py | 1 + lib/python/Plugins/example.py | 0 lib/python/Plugins/test/plugin.py | 9 +- lib/python/Plugins/tuxboxplugins/plugin.py | 1 + lib/python/Plugins/update/plugin.py | 24 +---- lib/python/Plugins/web/plugin.py | 18 +--- lib/python/Screens/PluginBrowser.py | 25 ++--- mytest.py | 5 +- 12 files changed, 155 insertions(+), 126 deletions(-) create mode 100644 doc/PLUGINS delete mode 100644 lib/python/Plugins/example.py diff --git a/doc/PLUGINS b/doc/PLUGINS new file mode 100644 index 00000000..fab701cb --- /dev/null +++ b/doc/PLUGINS @@ -0,0 +1,70 @@ +enigma2 plugins +=============== + +Enigma2 plugins are always written in python. If you really have to call +C/C++ functions from your code, you can supply a python module with it, +implementing your functions. + +Let's write a plugin. We call it "ourSmallTest", and it should be a test +plugin. + +The simplest plugin looks like the following: + +Plugins/ourSmallTest/plugin.py: + +"from Plugins.Plugin import PluginDescriptor + +def main(session): + print "Hello world!" + +def Plugins(): + return PluginDescriptor( + name="Our Small Test", + description="plugin to test some capabilities", + where = PluginDescriptor.WHERE_PLUGINMENU, + fnc=main)" + +Basically, you're writing a "python module", which is called +Plugins.ourSmallTest.plugin. This corresponds to the +Plugins/ourSmallTest/plugin.py file. + +This module must define a single function called "Plugins". The functions is +called for every Plugin, and should return (a list of) +PluginDescriptor-Objects. A PluginDescriptor is a simple object, holding the +Plugin's name, description, picture etc., and an entry point. + +In the first line, we import that class. It's contained in a module called +Plugins.Plugin. + +At the end, we define the "Plugins"-Functions. As said, it returns a +constructed PluginDescriptor-object (in fact it can return either one or a +list of descriptors, here it returns exactly one). We use keyword arguments +to supply the Plugin's information, like the name, the descripttion etc. + +We also supply an entry point, called "fnc". It's set to the "main" +function, which is defined before. Our entry point is called with a number +of arguments, depending on where the plugin was launched from. In this case, +it's a "session" argument. You need the session argument if you want to do +graphical output. A session basically connects to "user". There is always +one sessions which corresponds to the main screen output, but there can also +be other sessions, which yet have to be implemented. (A possible example is a +networked remote session.) If you don't need that argument, just ignore it. + +A plugin can decide where it wants to be displayed. A possible example is +the plugin menu out of the main menu. In the "where" argument to the +descriptor, you can supply one (or a list of) "WHERE_"-identifiers. We use +WHERE_PLUGINMENU. There will be other ones, for example for the blue button, +or specific other menus. + +Now, if you copy this plugin in-place, it should be listed in the plugin +browser in the main menu. You can press "ok" on the plugin, and the "main" +function, which was specified as the plugin's entry point, is executed. + +If you want to open a graphical screen, you might want the entry point to +look like: + +def main(session): + session.open(MyScreen) + +with MyScreen being a GUI screen. + diff --git a/lib/python/Components/PluginComponent.py b/lib/python/Components/PluginComponent.py index adfc98a0..cfdbc4d0 100644 --- a/lib/python/Components/PluginComponent.py +++ b/lib/python/Components/PluginComponent.py @@ -1,76 +1,59 @@ import os from Tools.Directories import * -from Screens.Menu import menuupdater + +def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod class PluginComponent: def __init__(self): - self.plugins = [] + self.plugins = {} self.setPluginPrefix("Plugins.") - self.menuEntries = [] def setPluginPrefix(self, prefix): self.prefix = prefix - def getPluginList(self, runAutostartPlugins=False, runAutoendPlugins=False): - list = [] - dir = os.listdir(resolveFilename(SCOPE_PLUGINS)) - self.menuDelete() - self.menuEntries = [] + def readPluginList(self, runAutostartPlugins=False, runAutoendPlugins=False): + """enumerates plugins""" - for x in dir: + directories = os.listdir(resolveFilename(SCOPE_PLUGINS)) + + for x in directories: path = resolveFilename(SCOPE_PLUGINS, x) + "/" - try: - if os.path.exists(path): - if fileExists(path + "plugin.py"): - pluginmodule = self.prefix + x + ".plugin" - print "trying to import " + pluginmodule - exec "import " + pluginmodule - plugin = eval(pluginmodule) - plugins = plugin.getPlugins() - try: picturepaths = plugin.getPicturePaths() - except: - picturepaths = [] - for p in plugins: - picturepaths.append("") - try: - for menuEntry in plugin.getMenuRegistrationList(): - self.menuEntries.append([menuEntry, pluginmodule]) - except: - pass - - for y in range(len(plugins)): - if len(plugins[y]) < 5: - list.append((path + picturepaths[y], plugins[y][0] , x, plugins[y][2], plugins[y][3], None, plugins[y][1])) - else: - list.append((path + picturepaths[y], plugins[y][0] , x, plugins[y][2], plugins[y][3], plugins[y][4], plugins[y][1])) - if runAutostartPlugins: - try: plugin.autostart() - except: pass - if runAutoendPlugins: - try: plugin.autoend() - except: pass - except: - print "Directory", path, "contains a faulty plugin" - self.menuUpdate() - return list - - def menuDelete(self): - for menuEntry in self.menuEntries: - menuupdater.delMenuItem(menuEntry[0][0], menuEntry[0][1], menuEntry[0][2], menuEntry[1], menuEntry[0][3]) + if os.path.exists(path): + if fileExists(path + "plugin.py"): + plugin = my_import('.'.join(("Plugins", x, "plugin"))) + + if not plugin.__dict__.has_key("Plugins"): + print "Plugin %s doesn't have 'Plugin'-call." % (x) + continue + + print "plugin", plugin + plugins = plugin.Plugins() + + # allow single entry not to be a list + if type(plugins) is not list: + plugins = [ plugins ] + + for p in plugins: + print "imported plugin %s" % (p.name) + + for x in p.where: + self.plugins.setdefault(x, []).append(p) - def menuUpdate(self): - for menuEntry in self.menuEntries: - menuupdater.addMenuItem(menuEntry[0][0], menuEntry[0][1], menuEntry[0][2], menuEntry[1], menuEntry[0][3]) - - def runPlugin(self, plugin, session): - try: - exec("import " + self.prefix + plugin[2] + ".plugin") - if plugin[3] == "screen": - session.open(eval(self.prefix + plugin[2] + ".plugin." + plugin[4]), plugin[5]) - elif plugin[3] == "function": - eval(self.prefix + plugin[2] + ".plugin." + plugin[4])(session, plugin[5]) - except: - print "exec of plugin failed!" + def getPlugins(self, where): + """Get list of plugins in a specific category""" + + if type(where) is not list: + where = [ where ] + res = [ ] + for x in where: + for p in self.plugins.get(x, [ ]): + res.append(p) + return res plugins = PluginComponent() diff --git a/lib/python/Components/PluginList.py b/lib/python/Components/PluginList.py index 22db1ad2..a8493497 100644 --- a/lib/python/Components/PluginList.py +++ b/lib/python/Components/PluginList.py @@ -16,18 +16,20 @@ RT_VALIGN_TOP = 0 RT_VALIGN_CENTER = 8 RT_VALIGN_BOTTOM = 16 -def PluginEntryComponent(picture, name, desc = "Plugin"): - res = [ None ] - res.append((eListboxPythonMultiContent.TYPE_TEXT, 80, 5, 300, 25, 0, RT_HALIGN_LEFT , name)) - res.append((eListboxPythonMultiContent.TYPE_TEXT, 80, 26, 300, 17, 1, RT_HALIGN_LEFT , desc)) - png = loadPNG(picture) - if png == None: - png = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "/plugin.png")) +def PluginEntryComponent(plugin): + res = [ plugin ] + + res.append((eListboxPythonMultiContent.TYPE_TEXT, 80, 5, 300, 25, 0, RT_HALIGN_LEFT, plugin.name)) + res.append((eListboxPythonMultiContent.TYPE_TEXT, 80, 26, 300, 17, 1, RT_HALIGN_LEFT, plugin.description)) + +# png = loadPNG(picture) +# if png == None: + + png = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "/plugin.png")) res.append((eListboxPythonMultiContent.TYPE_PIXMAP, 10, 5, 60, 40, png)) return res - class PluginList(HTMLComponent, GUIComponent, MenuList): def __init__(self, list): GUIComponent.__init__(self) @@ -40,4 +42,4 @@ class PluginList(HTMLComponent, GUIComponent, MenuList): def GUIcreate(self, parent): self.instance = eListbox(parent) self.instance.setContent(self.l) - self.instance.setItemHeight(50) \ No newline at end of file + self.instance.setItemHeight(50) diff --git a/lib/python/Plugins/Makefile.am b/lib/python/Plugins/Makefile.am index 01f21d4e..ec43b903 100644 --- a/lib/python/Plugins/Makefile.am +++ b/lib/python/Plugins/Makefile.am @@ -1,6 +1,7 @@ installdir = $(LIBDIR)/enigma2/python/Plugins -SUBDIRS = update tuxboxplugins web +SUBDIRS = update tuxboxplugins web test install_PYTHON = \ - __init__.py + __init__.py Plugin.py + diff --git a/lib/python/Plugins/__init__.py b/lib/python/Plugins/__init__.py index e69de29b..2a64fc37 100644 --- a/lib/python/Plugins/__init__.py +++ b/lib/python/Plugins/__init__.py @@ -0,0 +1 @@ +#__all__ = [ "Plugin" ] diff --git a/lib/python/Plugins/example.py b/lib/python/Plugins/example.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/python/Plugins/test/plugin.py b/lib/python/Plugins/test/plugin.py index 90c45e6e..1500dd8d 100644 --- a/lib/python/Plugins/test/plugin.py +++ b/lib/python/Plugins/test/plugin.py @@ -5,6 +5,7 @@ from Components.ActionMap import NumberActionMap from Components.Label import Label from Components.Input import Input from Components.GUIComponent import * +from Plugins.Plugin import PluginDescriptor import os @@ -48,8 +49,8 @@ class Test(Screen): print "pressed", number self["text"].number(number) -def getPicturePaths(): - return [ "" ] +def main(session): + session.open(Test) -def getPlugins(): - return [("Test", "plugin to test some capabilities", "screen", "Test")] +def Plugins(): + return PluginDescriptor(name="Test", description="plugin to test some capabilities", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main) diff --git a/lib/python/Plugins/tuxboxplugins/plugin.py b/lib/python/Plugins/tuxboxplugins/plugin.py index bf789465..01d57456 100644 --- a/lib/python/Plugins/tuxboxplugins/plugin.py +++ b/lib/python/Plugins/tuxboxplugins/plugin.py @@ -1,3 +1,4 @@ +# must be fixed for the new plugin interface from enigma import * from Screens.Screen import Screen from Screens.MessageBox import MessageBox diff --git a/lib/python/Plugins/update/plugin.py b/lib/python/Plugins/update/plugin.py index fb332a5e..070c199c 100644 --- a/lib/python/Plugins/update/plugin.py +++ b/lib/python/Plugins/update/plugin.py @@ -4,6 +4,7 @@ from Screens.MessageBox import MessageBox from Components.ActionMap import ActionMap from Components.ScrollLabel import ScrollLabel from Components.GUIComponent import * +from Plugins.Plugin import PluginDescriptor import os @@ -161,23 +162,8 @@ class Ipkg(Screen): else: self.close() +def main(session): + session.open(Upgrade) - -def autostart(): - return - os.popen("ipkg update", "r") -# -#def autoend(): - #print "**************************** AUTOEND" - -def getPicturePaths(): - return ["update.png", "update.png"] - -def getPlugins(): - return [("Softwareupdate", "Updates your receiver's software", "screen", "Upgrade"), - ("IPKG", "Updates your receiver's software", "screen", "Ipkg")] - -def getMenuRegistrationList(): - list = [] - list.append(("setup", 2, "Softwareupdate", "Upgrade")) - return list \ No newline at end of file +def Plugins(): + return PluginDescriptor(name="Softwareupdate", description="Updates your receiver's software", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main) diff --git a/lib/python/Plugins/web/plugin.py b/lib/python/Plugins/web/plugin.py index b450b8cd..e4c099bb 100644 --- a/lib/python/Plugins/web/plugin.py +++ b/lib/python/Plugins/web/plugin.py @@ -1,23 +1,13 @@ -from enigma import * - from twisted.internet import reactor from twisted.web2 import server, http, static -def autostart(): +# this is currently not working +def startWebserver(): print "Web startup" toplevel = static.File("/hdd") site = server.Site(toplevel) reactor.listenTCP(80, http.HTTPFactory(site)) -def autoend(): - pass - -def getPicturePaths(): - return [] - -def getPlugins(): - return [] - -def getMenuRegistrationList(): - return [] +def Plugins(): + return [ ] diff --git a/lib/python/Screens/PluginBrowser.py b/lib/python/Screens/PluginBrowser.py index 6d3ce66a..1c909746 100644 --- a/lib/python/Screens/PluginBrowser.py +++ b/lib/python/Screens/PluginBrowser.py @@ -5,7 +5,7 @@ from Components.ActionMap import ActionMap from Components.PluginComponent import plugins from Components.PluginList import * from Components.config import config - +from Plugins.Plugin import PluginDescriptor class PluginBrowser(Screen): def __init__(self, session): @@ -19,28 +19,21 @@ class PluginBrowser(Screen): { "ok": self.save, "back": self.close, - "up": self.up, - "down": self.down - }, -1) + }) def save(self): #self.close() self.run() def run(self): - plugin = self.pluginlist[self["list"].l.getCurrentSelectionIndex()] - plugins.runPlugin(plugin, self.session) + plugin = self["list"].l.getCurrentSelection()[0] + + plugin(session=self.session) def updateList(self): - self.list = [] - self.pluginlist = plugins.getPluginList() - for x in self.pluginlist: - self.list.append(PluginEntryComponent(x[0], x[1], x[6])) + self.list = [ ] + self.pluginlist = plugins.getPlugins(PluginDescriptor.WHERE_PLUGINMENU) + for plugin in self.pluginlist: + self.list.append(PluginEntryComponent(plugin)) self["list"].l.setList(self.list) - - def up(self): - self["list"].instance.moveSelection(self["list"].instance.moveUp) - - def down(self): - self["list"].instance.moveSelection(self["list"].instance.moveDown) diff --git a/mytest.py b/mytest.py index d71f457e..dda2c87a 100644 --- a/mytest.py +++ b/mytest.py @@ -35,7 +35,8 @@ except: # initialize autorun plugins and plugin menu entries from Components.PluginComponent import plugins -plugins.getPluginList(runAutostartPlugins=True) +plugins.readPluginList(runAutostartPlugins=True) + from Screens.Wizard import wizardManager from Screens.StartWizard import * from Screens.TutorialWizard import * @@ -330,7 +331,7 @@ import Components.NimManager # first, setup a screen try: runScreenTest() - plugins.getPluginList(runAutoendPlugins=True) +# plugins.getPluginList(runAutoendPlugins=True) except: print 'EXCEPTION IN PYTHON STARTUP CODE:' print '-'*60 -- 2.30.2