编写Qtopia插件
l 简介
l 书写功能性
l 子类化接口
l 接口的实现
l 建立实例
l 装载插件
Qtopia下的插件是通过COM-like层来实现的,写一个Qtopia插件一般步骤如下:
1、写出插件将提供的功能;
2、子类化你需要写的插件的接口;
3、提供接口方法的实现;
4、建立一个插件的实例。
下面是写的一个插件必须遵守的规则:
1、由于moc的实现细节,在你的插件里任何QObject的子类都必须拥有一个唯一的名字,以防止两个插件之间发生冲突,这是非常重要的一点。为所有的类(包括内在类)加上一个唯一的前缀,可以很好的实现这一规则。举个例子,如果你的插件"foo"有一个QObject的子类helper,你需要将它命名为FooHelper,这样比Helper更安全。
2、插件不能泄露内存。一个插件可以在任何时候卸载,所以,在插件接口析构的时候,一定要清空所有使用过的内存。
假定如下一个插件接口:
//{05E 0A 4AB-DDC5-4449 -85A 9-828100DE 00A 9}
#ifndef IID_WidgetPlugin
#define IID_WidgetPlugin QUuid( 0x05e 0a 4ab, 0xddc5, 0x4449, 0x85, 0xa9, 0x82, 0x81, 0x00, 0xde, 0x00, 0xa9)
#endif
struct WidgetPluginInterface : public QUnknownInterface
{
virtual QWidget *widget( QWidget *parent ) = 0;
virtual QString name() const = 0;
};
这只是一个提供了插件名和父窗口组件的简单插件。
IID_WidgetPlugin给这个接口定义了一个唯一的ID。
我们写的这个插件提供了一个组件用于在它的中央画一个椭圆,下面这是这个插件提供功能的实现代码。
class EllipseWidget : public QWidget
{
Q_OBJECT
public:
EllipseWidget( QWidget *parent=0 ) : QWidget( parent, "Ellipse" )
{
}
protected:
void paintEvent( QPaintEvent * )
{
QPainter p( this );
p.drawEllipse( rect() );
}
};
现在你可以子类化WidgetPluginInterface:
struct CirclePlugin : public WidgetPluginInterface
{
public:
virtual QWidget *widget( QWidget *parent );
virtual QString name() const;
QRESULT queryInterface( const QUuid&, QUnknownInterface** );
Q_REFCOUNT
protected:
CircleWidget *w;
ulong ref;
};
在CirclePlugin结构中有两点我们必须注意的地方:
1、queryInterface()函数允许插件加载器请求插件实现的接口。
2、Q_REFCOUNT是一个引用计数的宏,这个宏用提供的ulong型的ref变量进行计数。
ref必须在构造函数中用0进行初始化。
CirclePlugin::CirclePlugin()
: w(0), ref(0)
{
}
CirclePlugin::~CirclePlugin()
{
delete w;
}
queryInterface()函数可以用下面的示例代码实现:
QRESULT CirclePlugin::queryInterface( const QUuid &uuid, QUnknownInterface **iface )
{
*iface = 0;
if ( uuid == IID_QUnknown )
*iface = this;
else if ( uuid == IID_WidgetPlugin )
*iface = this;
else
return QS_FALSE;
(*iface)->addRef();
return QS_OK;
}
一个插件可以提供多个接口,但所有的插件都至少要提供QUnknowInterface接口。.
wiget()函数返回一个组件。
QWidget *CirclePlugin::widget( QWidget *parent )
{
if ( !w )
w = new CircleWidget( parent );
return w;
}
name()函数返回插件名。
QString CirclePlugin::name()
{
return qApp->translate( "WidgetPlugin", "Circle" );
}
你也需要用下面的示例代码实例化这个组件插件:
Q_EXPORT_INTERFACE()
{
Q_CREATE_INSTANCE( CirclePlugin )
}
推荐使用PluginLoader类来进行加载插件,PluginLoader类简单化了插件的列举和安装,并且它也给安全模式下启动的系统提供了安全的case。
PluginLoader loader( "Widgets" );
QStringList list = pluginLoader.list();
QStringList::Iterator it;
QValueList<WidgetPluginInterface*> widgetsList;
for ( it = list.begin(); it != list.end(); ++it ) {
WidgetPluginInterface *iface = 0;
if ( pluginLoader.queryInterface( *it, IID_WidgetPlugin, (QUnknownInterface**)&iface ) == QS_OK && iface ) {
widgetsList.append( iface );
}
}
PluginLoader类一般认为插件安装到$QPEDIR/plugins/type目录下。