之前我们在的两个插件Core和About,在Core和About中加入了界面,并且Core中插入了一个菜单,点击之后弹出About界面。
我们现在来用之前的知识把菜单改成注册的,并且点击之后弹出About界面。
我这里的思路是使用事件或者是在Core中加注册的服务来注册菜单。之后点击菜单的时候发送事件。
menubar.h
#ifndef CTKPLUGIN_MENUBAR_H
#define CTKPLUGIN_MENUBAR_H
#include
#include "service/event/ctkEventAdmin.h"
#include "service/event/ctkEventHandler.h"
class MenuBar : public QMenuBar, public ctkEventHandler
{
Q_OBJECT
Q_INTERFACES(ctkEventHandler)
public:
MenuBar(ctkPluginContext *context, QWidget *parent = nullptr);
~MenuBar() override;
void initMenu();
QMenu *getMenu(const QString &name);
signals:
void sig_insertMenu(const QJsonObject &obj);
protected:
void handleEvent(const ctkEvent& event) override;
protected slots:
void insertMenuAndActions(const QJsonObject &obj);
void actionTriggered();
private:
ctkPluginContext *context_;
QStringList menu_descriptions_;
QMap<QString, QMenu*> menu_map_;
QMap<QString, QList<QAction *>> actions_map_;
};
#endif //CTKPLUGIN_MENUBAR_H
menubar.cpp
#include "menubar.h"
#include "ctkPluginContext.h"
#include "service/event/ctkEventConstants.h"
#include
#include
#include
#include
MenuBar::MenuBar(ctkPluginContext *context, QWidget *parent)
: QMenuBar(parent)
, context_(context)
{
menu_descriptions_ << "File" << "Edit" << "MPI" << "Help" << "Page";
initMenu();
connect(this, &MenuBar::sig_insertMenu, this, &MenuBar::insertMenuAndActions, Qt::QueuedConnection);
// 注册监听信号"mainwindow_action"
ctkDictionary dic;
dic.insert(ctkEventConstants::EVENT_TOPIC, "mainwindow_action");
try {
context_->registerService<ctkEventHandler>(this, dic);
}
catch (ctkException e)
{
std::cout << e.message().toStdString() << std::endl;
}
}
MenuBar::~MenuBar()
{
}
void MenuBar::initMenu()
{
for(const auto &name : menu_descriptions_)
{
QMenu *menu = new QMenu(name,this);
menu_map_[name] = menu;
addMenu(menu);
}
}
void MenuBar::handleEvent(const ctkEvent &event)
{
if(event.getTopic() == "mainwindow_action")
{
QStringList names = event.getPropertyNames();
for(const auto &name : names)
{
QVariant val = event.getProperty(name);
emit sig_insertMenu(val.toJsonObject());
}
}
}
void MenuBar::actionTriggered()
{
QAction *action = dynamic_cast<QAction *>(sender());
QString text = action->text();
std::cout << text.toStdString() << std::endl;
//获取事件服务接口
ctkServiceReference ref;
ctkEventAdmin* eventAdmin{nullptr};
ref = context_->getServiceReference<ctkEventAdmin>();
if(ref)
{
eventAdmin = context_->getService<ctkEventAdmin>(ref);
context_->ungetService(ref);
}
//发送事件
ctkDictionary message;
if(eventAdmin)
eventAdmin->postEvent(ctkEvent(text, message));
}
void MenuBar::insertMenuAndActions(const QJsonObject &obj)
{
if(obj.isEmpty())
{
return;
}
QString key = obj["menu_name"].toString();
if(menu_map_.contains(key))
{
QAction *action = new QAction(obj["action_name"].toString() ,this);
action->setIcon(QIcon(obj["picture"].toString()));
connect(action, &QAction::triggered, this, &MenuBar::actionTriggered);
if(actions_map_.contains(key))
{
actions_map_[key].insert(obj["num"].toInt(), action);
}
else
{
actions_map_.insert(key, QList<QAction*>() << action);
}
}
else
{
QMenu *menu = new QMenu(key, this);
menu_map_.insert(key, menu);
QAction *action = new QAction(obj["action_name"].toString() ,this);
action->setIcon(QIcon(obj["picture"].toString()));
connect(action, &QAction::triggered, this, &MenuBar::actionTriggered);
actions_map_[key].insert(obj["num"].toInt(), action);
if(actions_map_.contains(key))
{
actions_map_[key].insert(obj["num"].toInt(), action);
}
else
{
actions_map_.insert(key, QList<QAction*>() << action);
}
}
menu_map_[key]->clear();
menu_map_[key]->addActions(actions_map_[key]);
}
QMenu *MenuBar::getMenu(const QString &name)
{
return menu_map_[name];
}
actionregister.h
#ifndef CTKPLUGIN_ACTIONREGISTER_H
#define CTKPLUGIN_ACTIONREGISTER_H
#include "ctkDictionary.h"
#include
/*****
* @brief 注册菜单栏
* @param name 是现实在action的菜单menu的名称
* @param id 是该菜单的唯一编号(也是菜单点击后的事件的id, 事件根据此id发送)
* @param num 是排在第几个位置(如果有重复的可能会按照后注册插件的来)
* @param picture 是图标的路径
*/
struct ActionData
{
QString menu_name;
QString action_name;
int num{-1};
QString picture;
};
class ActionDictionary
{
public:
ActionDictionary() = default;
~ActionDictionary() = default;
void registerAction(const ActionData &actionData);
ctkDictionary getDictionary() { return dictionary; }
private:
ctkDictionary dictionary;
};
void ActionDictionary::registerAction(const ActionData &actionData)
{
QJsonObject obj;
obj.insert("menu_name", actionData.menu_name);
obj.insert("action_name", actionData.action_name);
obj.insert("num", actionData.num);
obj.insert("picture", actionData.picture);
dictionary.insert(actionData.action_name, QVariant::fromValue(obj));
}
#endif //CTKPLUGIN_ACTIONREGISTER_H
void AboutPlugin::registerToMainWindow()
{
//获取事件服务接口
ctkServiceReference ref;
ctkEventAdmin* eventAdmin{nullptr};
ref = context_->getServiceReference<ctkEventAdmin>();
if(ref)
{
eventAdmin = context_->getService<ctkEventAdmin>(ref);
context_->ungetService(ref);
}
//发送事件
ActionData data;
data.action_name = "About";
data.menu_name = "Help";
data.picture = "";
data.num = 1;
ActionDictionary a;
a.registerAction(data);
ctkDictionary message = a.getDictionary();
if(eventAdmin)
eventAdmin->postEvent(ctkEvent("mainwindow_action", message));
}
之前的代码关闭的时候mainwindow析构还会报一个线程错误,我们把析构使用信号去处理。
具体修改见项目。这里就不贴优化部分的代码了,思路就是使用信号去处理界面的析构,否则会因为析构代码不在界面线程调用而崩溃。