首先说下qt的插件,扫一下盲,很多新人会有点懵逼,它包含几大类:
1、qt内核插件plugin:用来完善qt本身的功能,比如你搞个数据库插件,让别人可以直接用来读写数据库,这个也叫high-level api
2、qtcreator插件:用来扩展qtcreator的功能,比如代码缩进整理
3、qtdesigner插件:用来当作普通控件使用,比如你制定一个绿色button,写完插件后拖到qtdesigner的plugin目录,然后就可以愉快的用鼠标拖拉这个绿色button到你的widget了,可以参考:http://labisart.com/blog/index.php/Home/Index/article/aid/172
4、普通程序插件:官方例子Plug & Paint的解析很好,实际上qtcreator也是基于此方法然后扩展出自己的插件
我们这里说的就是第四种:普通程序插件
创建和调用都比较简单,主要说说插件怎么发信号和槽,主要参考3篇文章:
https://blog.csdn.net/kenfan1647/article/details/107493294
https://blog.csdn.net/Ecrhon/article/details/83622545
https://blog.csdn.net/yizhou2010/article/details/79961554
我们不希望自定义结构体来做转发,所以还是希望用原生的signal/slot机制来通信。当然,插件本身应该是解耦的,如果互相调用又违反了这个规则了,可以思考下互相connect是否合适?
废话说完转入正题.
这种方法就是纯虚函数接口,signal和slots你不要声明,但是要加注释,不然别人用你的接口的时候搞不清哪个跟哪个。
然后在插件类的实现中,指定哪些是signal,哪些是slots即可:
#ifndef MYINTERFACE4_H #define MYINTERFACE4_H #include <QWidget> #include <QObject> class _MyPlugin4 { public: ~_MyPlugin4() = default; virtual QWidget *CreateWidget(QWidget *parent)=0; virtual int add(int a,int b)=0; //signals: virtual void sigAdd(QString txt)=0; //public slots: // 直接当作曹函数 virtual void onNotify(QString txt) =0; }; Q_DECLARE_INTERFACE(_MyPlugin4,"com.company._MyPlugin4/1.0") #endif // MYINTERFACE_H
继承的插件实现定义如下:
class MyPlugin4 :public QObject, public _MyPlugin4, public IPluginBase { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QGenericPluginFactoryInterface4" FILE "MyPlugin4.json") Q_INTERFACES(_MyPlugin4 IPluginBase) public: QWidget *CreateWidget(QWidget *parent) override; int add(int a,int b) override; // signals: void sigAdd(QString txt) override; public slots: void onNotify(QString txt) override; };
connect的时候找到qobject,然后使用传统的connect方法即可连接。
// pb pbMyplugin4Add void MainWindow::on_pbMyplugin4Add_clicked() { QObject *obj = PluginManager::i().plugin("MyPlugin4"); if(obj == nullptr){ T_ERR << "实例为空"; return; } _MyPlugin4 *m2 = qobject_cast<_MyPlugin4*>(obj); if(m2 != nullptr){ // 链接信号和曹,要用object才可链接 connect(obj,SIGNAL(sigAdd(QString)),this,SLOT(onAdd(QString)),Qt::UniqueConnection); T_INF <<"1+2="<<m2->add(1,2); // 发信号给这个插件 connect(this,SIGNAL(notifyPlugin(QString)),obj,SLOT(onNotify(QString)),Qt::UniqueConnection); emit notifyPlugin("mainwindow to plugin"); } }
首先遇到问题是signal不能为虚函数:https://stackoverflow.com/questions/50516359/declaring-signals-in-interface-class-using-qt-plugin-system-with-new-signal-slot
然后你connect的时候,如果用QT5的新语法,他就提示找不到信号函数所在的符号,解决方案2个:
1、把接口建立为dll工程,跟着exe走,也是上面文章说的,这个显然很挫
2、使用connect老方法即可
不推荐用这个,大家用我上面这个靠谱一点,可以留言交流。
另外要注意,插件子类的实现不能把构造函数隐藏起来,即
private: MyClass();
是不行的,moc会以new生成新实例,如果定义为private就没法编译通过了。