CTK框架 - 插件依赖关系 - 给插件加上界面
插件依赖关系
在第一篇文章中吗,我们提到了MANIFEST.MF中有个字段是依赖关系,这次我们来测试一下这个依赖关系:
MANIFEST.MF
之前对资源文件没有做过多介绍,这次来说一下里面的内容
名称 | 作用 |
Plugin-SymbolicName | 插件的符号名,插件的唯一标识 |
Plugin-Copyright | 插件的版权信息 |
Plugin-Description | 插件的简要描述 |
Plugin-Name | 插件的名称,对插件起说明作用,不影响插件功能 |
Plugin-Vendor | 插件的供应商 |
Plugin-Localization | 标识插件的Qt .qm 文件的基本名称:如中文qt_zh.qm,此处写zh |
Require-Plugin | 插件所需的其他插件的符号名称 |
Plugin-Version | 插件的版本号 |
Plugin-ActivationPolicy | 插件的激活策略 |
Plugin-UpdateLocation | 在插件更新操作期间,获取新插件版本的位置 |
插件的激活策略由 Plugin-ActivationPolicy指定,默认值是 lazy
● eager:插件使用 ctkPlugin::START_ACTIVATION_POLICY 选项启动,当框架启动时会立即激活。
● lazy:插件使用 ctkPlugin::START_ACTIVATION_POLICY 选项启动,并在 ctkPlugin::STARTING 状态等待,直到插件的第一类实例化发生。插件将在实例返回给请求者之前被激活
插件依赖的插件由Require-Plugin指定,
resolution用来标识Require-Plugin中的解析类型,默认值是mandatory。包括:
● optional(可选的):表示所需的插件是可选的,并且即使所需的插件没有被解析,该插件也可以被解析。
● mandatory(强制的):表示在解析插件时,所需的插件也必须被解析。如果所需的插件不能被解析,则模块解析失败。
首先将About依赖于Core插件
Plugin-SymbolicName:About Plugin-Version:1.0.0 Require-Plugin:Core; plugin-version="[2.0,5.0)"; resolution:="mandatory"
这个时候加载About插件会打印Failed to resolve required plugin: Core
如果版本号正常或者是resolution:="optional"
则可以正常加载
给插件加上界面
Core插件
我们之前都是使用控制台打印的插件的通信信息或者是其他的信息,这次我们来给我们的插件加上界面。
界面的创建:
1.首先是我们的Core插件,我们给Core加一个mainwindow,并且加一个sendEvent的菜单,并且在mainwindowplugin中使用pop接口将界面弹出来
mainwindow
#ifndef CTKTEST_MAINWINDOW_H #define CTKTEST_MAINWINDOW_H #include <QMainWindow> #include "ctkPluginContext.h" class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(ctkPluginContext *context, QWidget *parent = 0); ~MainWindow() override; protected slots: void actionSendEvent(); private: ctkPluginContext *context_; }; #endif //CTKTEST_MAINWINDOW_H #include "mainwindow.h" #include "service/event/ctkEventAdmin.h" #include <QMenuBar> MainWindow::MainWindow(ctkPluginContext *context, QWidget *parent) : QMainWindow(parent) , context_(context) { QMenuBar *menubar = new QMenuBar(this); setMenuBar(menubar); QAction *action = new QAction("发送事件", this); menubar->addAction(action); connect(action, &QAction::triggered, this, &MainWindow::actionSendEvent); } MainWindow::~MainWindow() { } void MainWindow::actionSendEvent() { //获取事件服务接口 ctkServiceReference ref = context_->getServiceReference<ctkEventAdmin>(); ctkEventAdmin* eventAdmin{nullptr}; if(ref) { eventAdmin = context_->getService<ctkEventAdmin>(ref); context_->ungetService(ref); } //发送事件 ctkDictionary message; ctkDictionary message2; ctkDictionary message3; message.insert("About", "im a message to about plugin"); message2.insert("About", "im AAA message to about plugin"); message3.insert("MMM", "im MMM message to about plugin"); if(eventAdmin) eventAdmin->postEvent(ctkEvent("About", message)); if(eventAdmin) eventAdmin->postEvent(ctkEvent("AAA", message2)); if(eventAdmin) eventAdmin->postEvent(ctkEvent("About", message3)); }
mainwindowplugin
#ifndef CTKTEST_MAINWINDOWPLUGIN_H #define CTKTEST_MAINWINDOWPLUGIN_H #include <QObject> #include "../src/imainwindow.h" #include "ctkPluginContext.h" #include "service/event/ctkEventHandler.h" class MainWindow; class MainWindowPlugin : public QObject, public iMainWindow { Q_OBJECT /* 此宏与Q_DECLARE_INTERFACE宏配合使用。 Q_DECLARE_INTERFACE:声明一个接口类 Q_INTERFACES:当一个类继承这个接口类,表明需要实现这个接口类 */ Q_INTERFACES(iMainWindow) public: explicit MainWindowPlugin(ctkPluginContext *context); void popWindow() override; signals: void sig_send(); private: ctkPluginContext *context_; MainWindow *main_window_; }; #endif //CTKTEST_MAINWINDOWPLUGIN_H #include "mainwindowplugin.h" #include "service/event/ctkEventAdmin.h" #include <iostream> #include "mainwindow.h" MainWindowPlugin::MainWindowPlugin(ctkPluginContext *context) : context_(context) { main_window_ = new MainWindow(context); } void MainWindowPlugin::popWindow() { main_window_->show(); }
这样我们在调用popWindow的时候我们就得到了一个界面,并且点击发送事件的时候也会打印我们之前事件打印的那句话
注意: 不要忘记删除掉imainwindow中的sendEvent接口,因为这个时候它已经没有任何作用了
About插件
接下来我们给About也做一个界面弹出:
aboutdlg
#ifndef CTKTEST_ABOUTDLG_H #define CTKTEST_ABOUTDLG_H #include <QDialog> class AboutDlg : public QDialog { Q_OBJECT public: explicit AboutDlg(QWidget *parent = 0); ~AboutDlg() override; }; #endif //CTKTEST_ABOUTDLG_H #include <iostream> #include "aboutdlg.h" AboutDlg::AboutDlg(QWidget *parent) : QDialog(parent) { } AboutDlg::~AboutDlg() { std::cout << "~AboutDlg" << std::endl; }
aboutplugin
#ifndef CTKTEST_ABOUTPLUGIN_H #define CTKTEST_ABOUTPLUGIN_H #include <QObject> #include "ctkPluginContext.h" #include "service/event/ctkEventAdmin.h" #include "service/event/ctkEventHandler.h" class AboutDlg; class AboutPlugin : public QObject, public ctkEventHandler { Q_OBJECT Q_INTERFACES(ctkEventHandler) public: explicit AboutPlugin(ctkPluginContext *context); signals: void sig_open(); protected slots: void pageOpen(); protected: void handleEvent(const ctkEvent& event) override; private: ctkPluginContext *m_context; QSharedPointer<AboutDlg> about_page_; }; #endif //CTKTEST_ABOUTPLUGIN_H #include "aboutplugin.h" #include "aboutdlg.h" #include <service/event/ctkEventConstants.h> #include <iostream> AboutPlugin::AboutPlugin(ctkPluginContext *context) : m_context( context ) { //注册监听信号"About" ctkDictionary dic; dic.insert(ctkEventConstants::EVENT_TOPIC, "About"); m_context->registerService<ctkEventHandler>(this, dic); connect(this, &AboutPlugin::sig_open, this, &AboutPlugin::pageOpen); about_page_.reset(new AboutDlg()); } void AboutPlugin::handleEvent(const ctkEvent &event) { //接收监听事件接口 if(event.getTopic() == "About") { emit sig_open(); } } void AboutPlugin::pageOpen() { about_page_->exec(); }
这里使用信号打开界面的原因是因为postEvent不在界面线程,所以需要使用信号槽来打开界面。
界面的析构:
1.界面是非智能指针对象: 非智能指针对象记得在stop删除插件的时候把new出的界面对象删除;
2.界面是智能指针对象:如果我们使用QSharedPointer<MainWindow> main_window_;我们则不用关心这个界面的析构,当我们关闭界面的时候会自动调用界面的析构函数。
我这里为了省事也是都给界面换成了智能指针,并且我再析构函数中加了打印,可以看到系统关闭的时候界面的析构函数都被调用了。