可插拔的Python程序

2 投票
2 回答
918 浏览
提问于 2025-04-16 03:24

我想做一个支持插件的PyQt4程序。简单来说,我希望用户能够在PyQt4中编写QWidget的子类,并通过一个图形界面(GUI)将这些子类添加到或移除出主应用窗口。我该怎么做,特别是插件的机制部分?

2 个回答

0

你可以先准备一个插件的文件夹,然后为这些插件定义一个接口,接着遍历这个文件夹来导入它们。

我以前在Python中没做过这个,但在C语言中,我是通过定义一些插件需要实现的函数来完成的。基本上,插件里需要有的关键元素包括插件的名称(这样你可以在用户界面上显示出来,让用户可以使用它们)和一个工厂函数来实际创建这些东西。我想在Python中,这些内容可以简单地放在一个字典里,然后从模块中返回。

比如说,可以规定所有插件必须提供一个叫做GetPluginInfo()的函数,这个函数返回一个包含名称和类映射关系的字典。

其实现在想想,你可能只需要导入这个文件,然后查找那些是你关心的类的子类,而不需要强制实现任何特定的API。我虽然没做过很多这种事情,但基本上你可以遍历模块的dir(),检查每个东西是否是QWidget的子类(举个例子)。

6

首先,使用一个叫做 QFileDialog 的工具,或者更方便的是用它的静态函数 QFileDialog.getOpenFileName,让用户选择他们想要“插入”到你的图形界面中的 .py 文件。

接下来,如果你想允许从电脑上的任何地方导入插件(而不是仅仅从之前添加到 sys.path 的特定目录,比如通过环境变量 PYTHONPATH),你可以选择“正确的方法”(这需要不少工作),通过标准Python库中的 imp 模块,或者你可以“走捷径”,如下所示(假设 filepath 是包含文件路径的 QString,并且你使用的文件系统/平台支持Unicode文件名——否则你需要添加适合你平台和文件系统的 .encode)...:

import sys, os

def importfrom(filepath):
    ufp = unicode(filepath)
    thedir, thefile = os.path.split(ufp)
    if sys.path[0] != thedir:
      sys.path.insert(0, thedir)
    themodule, theext = os.path.splitext(thefile)
    return __import__(themodule)

这样做不是线程安全的,因为它可能会对 sys.path 产生副作用,这就是我说它是“走捷径”的原因……不过,做线程安全的导入(以及从“任何地方”进行“干净”的导入)真的很麻烦(如果你需要,可能值得单独提问,因为这和这个问题的核心内容、PyQt等关系不大)。

一旦你得到了模块对象(就是上面 importfrom 的结果),我建议你:

from PyQt4 import QtGui 
import inspect

def is_widget(w):
    return inspect.isclass(w) and issubclass(w, QtGui.QWidget)

def all_widget_classes(amodule):
    return [v for n, v in inspect.getmembers(amodule, is_widget)]

这会返回一个列表,里面包含模块 amodule 中定义的所有小部件 (如果有的话)。

接下来你想做什么就看你自己了。当然,也许你想在列表为空时给出一些错误信息(或者如果列表中有多个项目时?不然你怎么决定使用哪个小部件类呢?),或者实例化这个小部件类(要实例化多少次?放在哪些坐标上?等等——这些问题只有你能回答)并在窗口的合适位置显示出来。

撰写回答