У вас должна быть директория плагинов, которую ваше приложение сканирует во время выполнения (или позже) для импорта соответствующего кода. Вот пример, который должен работать с обычным кодом .py или .pyc, который работает даже с плагинами, хранящимися в zip-файлах (так что пользователи могут просто вставить someplugin.zip в каталог 'plugins' и заставить его работать волшебным образом):
import re, os, sys
class Plugin(object):
"""
The base class from which all plugins are derived. It is used by the
plugin loading functions to find all the installed plugins.
"""
def __init__(self, foo):
self.foo = foo
# Any useful base plugin methods would go in here.
def get_plugins(plugin_dir):
"""Adds plugins to sys.path and returns them as a list"""
registered_plugins = []
#check to see if a plugins directory exists and add any found plugins
# (even if they're zipped)
if os.path.exists(plugin_dir):
plugins = os.listdir(plugin_dir)
pattern = ".py$"
for plugin in plugins:
plugin_path = os.path.join(plugin_dir, plugin)
if os.path.splitext(plugin)[1] == ".zip":
sys.path.append(plugin_path)
(plugin, ext) = os.path.splitext(plugin) # Get rid of the .zip extension
registered_plugins.append(plugin)
elif plugin != "__init__.py":
if re.search(pattern, plugin):
(shortname, ext) = os.path.splitext(plugin)
registered_plugins.append(shortname)
if os.path.isdir(plugin_path):
plugins = os.listdir(plugin_path)
for plugin in plugins:
if plugin != "__init__.py":
if re.search(pattern, plugin):
(shortname, ext) = os.path.splitext(plugin)
sys.path.append(plugin_path)
registered_plugins.append(shortname)
return registered_plugins
def init_plugin_system(cfg):
"""
Initializes the plugin system by appending all plugins into sys.path and
then using load_plugins() to import them.
cfg - A dictionary with two keys:
plugin_path - path to the plugin directory (e.g. 'plugins')
plugins - List of plugin names to import (e.g. ['foo', 'bar'])
"""
if not cfg['plugin_path'] in sys.path:
sys.path.insert(0, cfg['plugin_path'])
load_plugins(cfg['plugins'])
def load_plugins(plugins):
"""
Imports all plugins given a list.
Note: Assumes they're all in sys.path.
"""
for plugin in plugins:
__import__(plugin, None, None, [''])
if plugin not in Plugin.__subclasses__():
# This takes care of importing zipped plugins:
__import__(plugin, None, None, [plugin])
Допустим, у меня есть плагин с именем "foo.py" в каталоге с именем "плагины" (который находится в базовом каталоге моего приложения), который добавит новую возможность в мое приложение. Содержимое может выглядеть так:
from plugin_stuff import Plugin
class Foo(Plugin):
"""An example plugin."""
self.menu_entry = {'Tools': {'Foo': self.bar}}
def bar(self):
return "foo plugin!"
Я мог бы инициализировать свои плагины при запуске приложения следующим образом:
plugin_dir = "%s/plugins" % os.getcwd()
plugin_list = get_plugins(plugin_dir)
init_plugin_system({'plugin_path': plugin_dir, 'plugins': plugin_list})
plugins = find_plugins()
plugin_menu_entries = []
for plugin in plugins:
print "Enabling plugin: %s" % plugin.__name__
plugin_menu_entries.append(plugin.menu_entry))
add_menu_entries(plugin_menu_entries) # This is an imaginary function
Это должно работать, пока плагин представляет собой файл .py или .pyc (при условии, что он скомпилирован для рассматриваемой платформы байтово). Это может быть автономный файл или внутри каталога с init .py или внутри zip-файла с такими же правилами.
Откуда я знаю, что это работает? Так я реализовал плагины в PyCI . PyCI - это веб-приложение, но нет никаких причин, почему этот метод не будет работать для обычного старого GUI. В приведенном выше примере я решил использовать воображаемую функцию add_menu_entries () в сочетании с переменной объекта Plugin, которую можно использовать для добавления методов плагина в меню вашего графического интерфейса.
Надеюсь, этот ответ поможет вам построить собственную систему плагинов. Если вы хотите точно увидеть, как это реализовано, я рекомендую вам загрузить исходный код PyCI и посмотреть plugin_utils.py и плагин Example в каталоге plugins_enabled.