QT如何创建和使用Qt Plugins (插件)

本文介绍了如何使用Qt创建和加载插件(plugins),包括搭建插件项目、定义接口及实现,以及在主程序中动态加载和使用插件的过程。

对于一个大型软件系统来说,实现plugin是一件很美妙的事情,一个成功的plugin系统可以使软件增色不少。Plugin最大的功能是在一定程度内提高了软件的灵活度和可扩展性。一个设计精良的server软件plugin系统甚至在server程序不退出的情况下可以调用新加入的plugin,实现不间断服务的升级。那么,Qt是怎样实现它的plugin系统呢?

使用Qt创建plugin和在程序中调用plugin是很简单的事情,Qt提供了很多helper class供大家使用。总体来说,Qt的plugin分为2种,按照Qt文档的说法,一种是高等级的plugin。其实说白了就是已经确定interface的Qt本身的plugin。(大家可能都知道,Qt的很多功能,像数据库驱动、图片格式支持、文字内码等都是通过plugin实现的)举个例子来说,Qt可能本身没有西班牙语(希望这次西班牙夺冠:-)的文字内码,但是程序员可以通过按照codec interface写出西班牙语的codec plugin从而使Qt支持西班牙语。

另一种是低等级的plugin。就是该plugin的interface也需要程序员自己编写。所以如果你知道怎么写一个低等级的plugin并使用它之后,高等级的plugin也就完全掌握了。下面我就重点说说低等级的plugin在Qt里实现的一些要点。

从编程的角度,重点还是OOP。所谓的plugin,其实就是一些按照特定interface写成的子类。该Interface必须是虚基类,且所有函数(除了析构)都是虚函数。而所谓的plugin就是继承该虚基类和QObject的子类。当程序调用该plugin的某个函数时,是通过该plugin的虚基类在运行时动态绑定至子类的vtable执行的。所以Qt实现plugin的基础还是OOP的继承和多态。

举个大家都知道的例子来说明,PhotoShop(可能它并不是这么实现的)的所有滤镜有个统一的interface虚基类,该类提供了一个虚函数doSomeWork用于实现滤镜效果。当用户选择某个特殊滤镜时,程序会调用plugin中该滤镜class的doSomeWork实现函数来执行该操作,从而实现特定的滤镜功能。

那么为什么该plugin类不但要继承interface类,还需要继承QObject类呢?原因是调用plugin时需要该plugin类QObject那部分的meta信息。如果大家看过例子代码,会发现,用QPluginLoader调用plugin的文件后,关键的一步是确定该plugin是什么类型的。简单的另人惊讶,一句qobject_cast就搞定了。刚看到这句我百思不得其解,好在Qt有源代码可看,看了源代码发现qobject_cast类似于标准C++的dynamic_cast,且无需RTTI支持并能跨DLL。在代码中,qobject_cast是通过QObject的metaobject的cast函数来实现的。那么该函数是怎么写的呢?

plugin 是一个实现了一个或多个接口的DLL,下面我们将介绍在QT中如何创建PLUG IN 和如何动态加载PLUGIN

一、创建Plugin(插件)

先新建一个库项目

选择chose进入下一下页面,类型选择Qt Plugin,输入一个名称:我输入的是plugin

再点击下一步到

再点击下一步直到完成

我们在.pro文件里加上

QT       +=widgets

DESTDIR =./Mydll

它的意思是我们把生成的内容放到这个文件夹里,如果没有它会自动生成

点击项目把Shadow build 去掉勾选

这样一个Qt Plugin框架就搭建好了。

1、定义一个接口MyInterface.h


MyInterface.h代码如下

#ifndef MYINTERFACE_H
#define MYINTERFACE_H
#include <QWidget>

class MyInterface {

public:
    virtual QWidget *CreateWidget(QWidget *parent)=0;
    virtual int add(int a,int b)=0;

};

Q_DECLARE_INTERFACE(MyInterface,"sdafasdfsadfsadfsadfsadf");

#endif // MYINTERFACE_H

2、插件类头文件MyPlugin.h

#ifndef MYPLUGIN_H
#define MYPLUGIN_H

#include <QtPlugin>
#include "myinterface.h"


class myPlugin : public QObject,public MyInterface
{
    Q_OBJECT
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QGenericPluginFactoryInterface" FILE "plugin.json")
    Q_INTERFACES(MyInterface);
#endif // QT_VERSION >= 0x050000

public:
    //myPlugin(QObject *parent = 0);
    QWidget *CreateWidget(QWidget *parent);
    int add(int a,int b);
};

这里添加一个MyForm的widget为实现CreateWidget调用

3、Myplugin.cpp

#include "myplugin.h"
#include "myform.h"   //这里我加了一个form




#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(plugin, myPlugin)
#endif // QT_VERSION < 0x050000

QWidget *myPlugin::CreateWidget(QWidget *parent)
{
   return new MyForm(parent);
}

int myPlugin::add(int a, int b)
{
    return a+b;
}

重新构建在mydll里面就生成了plugin.dll

二、如何加载使用plugin插件

使用plugin插件需要接口头文件 Myinterface.h和plugin.dll两个文件

1、新建一个mainwindow的项目文件

    在类里定义一个 QWidget*m_Form;

    加载函数如下

//加载plugin
bool MainWindow::LoadPlugin(QString pluginpath)
{
  QFile file(pluginpath);
  if (!file.exists())
  {
      QMessageBox::warning(this,tr("错误信息"),tr("找不到%1文件").arg(pluginpath));
      return false;
  }

  QPluginLoader loader(pluginpath);
  QObject *instance = loader.instance(); //
  if (instance!= NULL)
      {
           qDebug()<<pluginpath+" is loaded";
           MyInterface *avc = qobject_cast<MyInterface *>(instance);
           m_Form = avc->CreateWidget(this);      
            m_Form->show();         
           ui->lineEdit->setText(QString::number(avc->add(7,8)));
           return true;
      }
  else {
      QMessageBox::information(this,"failed to load plugin",loader.errorString());
  }
       // 需要手动释放  
     delete instance;  

}


创建可在其他项目中使用Qt 插件,主要涉及以下几个关键步骤。这些步骤基于 Qt 提供的插件机制,尤其是使用 `QPluginLoader` 加载插件,并通过定义接口实现插件与主程序之间的通信。 ### 创建插件项目 1. **创建子目录项目** 打开 Qt Creator,选择“文件” -> “新建文件或项目”,选择“其他项目”下的“子目录项目”,输入项目名称路径后完成创建。该子目录项目将作为主程序插件项目的容器。 2. **创建主程序项目** 在子目录项目中添加一个应用程序项目(例如 `MyApp`),这是插件将被加载使用的主程序。 3. **创建插件项目** 在同一个子目录项目中,添加一个库项目(Library),选择“Qt 插件”模板,输入插件名称(例如 `MyPlugin`)。该项目将实现插件的具体功能。 ### 定义插件接口 4. **定义接口类** 在主程序项目中添加一个接口头文件(例如 `MyInterfaces.h`),定义一个抽象类作为插件接口。该接口必须继承自 `QObject`,并使用 `Q_OBJECT` 宏声明。 ```cpp #ifndef MYINTERFACES_H #define MYINTERFACES_H #include <QObject> class MyInterface { public: virtual ~MyInterface() {} virtual QString name() const = 0; virtual void setName(const QString &name) = 0; }; Q_DECLARE_INTERFACE(MyInterface, "com.example.MyInterface/1.0") #endif // MYINTERFACES_H ``` 5. **插件实现接口** 在插件项目中,创建一个类继承自 `QObject` 上述接口类,并实现其虚函数。同时,使用 `Q_OBJECT` `Q_PLUGIN_METADATA` 宏注册插件。 ```cpp #include <QObject> #include "myinterfaces.h" class MyPlugin : public QObject, public MyInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "com.example.MyInterface/1.0") Q_INTERFACES(MyInterface) public: QString name() const override; void setName(const QString &name) override; private: QString m_name; }; ``` ### 加载并使用插件 6. **使用 QPluginLoader 加载插件** 在主程序中,使用 `QPluginLoader` 加载插件库,并通过 `qobject_cast` 转换为接口类指针。 ```cpp #include <QPluginLoader> #include <QDebug> #include "myinterfaces.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); QPluginLoader pluginLoader("path/to/libMyPlugin.so"); // Windows 下为 .dll,macOS 下为 .dylib QObject *pluginInstance = pluginLoader.instance(); if (pluginInstance) { MyInterface *myInterface = qobject_cast<MyInterface *>(pluginInstance); if (myInterface) { myInterface->setName("Qt Plugin"); qDebug() << "Plugin name:" << myInterface->name(); } } else { qDebug() << "Failed to load plugin:" << pluginLoader.errorString(); } return app.exec(); } ``` ### 插件部署与使用环境 7. **插件部署位置** 插件构建完成后,生成的 `.dll`(Windows)、`.so`(Linux)或 `.dylib`(macOS)文件应放置在主程序可访问的目录中。通常的做法是将插件文件放在主程序目录下的 `plugins` 子目录中。 8. **Qt Designer 中使用插件** 如果希望在 Qt Designer 中使用自定义插件,需将插件复制到 Qt 安装目录下的 `plugins/designer` 文件夹中,重启 Qt Designer 即可识别新插件 [^2]。 ### 插件机制的优势 9. **插件机制的优势** Qt 插件机制提供了良好的模块化设计,支持运行时动态加载卸载功能模块,提高应用程序的灵活性可扩展性 [^3]。通过接口抽象,插件与主程序解耦,便于维护更新。 ---
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值