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插件
我们之前都是使用控制台打印的插件的通信信息或者是其他的信息,这次我们来给我们的插件加上界面。
界面的创建:
- 首先是我们的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不在界面线程,所以需要使用信号槽来打开界面。
界面的析构:
- 界面是非智能指针对象: 非智能指针对象记得在stop删除插件的时候把new出的界面对象删除;
- 界面是智能指针对象:如果我们使用
QSharedPointer<MainWindow> main_window_;
我们则不用关心这个界面的析构,当我们关闭界面的时候会自动调用界面的析构函数。
我这里为了省事也是都给界面换成了智能指针,并且我再析构函数中加了打印,可以看到系统关闭的时候界面的析构函数都被调用了。