文章目录
- CTK框架 - 将菜单按钮写到插件中
- 代码示例
- 之前关闭的时候崩溃错误
CTK框架 - 将菜单按钮写到插件中
之前我们在的两个插件Core和About,在Core和About中加入了界面,并且Core中插入了一个菜单,点击之后弹出About界面。
我们现在来用之前的知识把菜单改成注册的,并且点击之后弹出About界面。
我这里的思路是使用事件或者是在Core中加注册的服务来注册菜单。之后点击菜单的时候发送事件。
代码示例
- 编写一个menubar 用来管理菜单和处理其他插件注册的menu事件
menubar.h
#ifndef CTKPLUGIN_MENUBAR_H
#define CTKPLUGIN_MENUBAR_H
#include <QMenuBar>
#include "service/event/ctkEventAdmin.h"
#include "service/event/ctkEventHandler.h"
class MenuBar : public QMenuBar, public ctkEventHandler
{Q_OBJECTQ_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 <iostream>
#include <QJsonObject>
#include <QJsonDocument>
#include <QAction>
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 <QJsonObject>
/****** @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
- 在aboutplugin中发送对应的事件
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析构还会报一个线程错误,我们把析构使用信号去处理。
具体修改见项目。这里就不贴优化部分的代码了,思路就是使用信号去处理界面的析构,否则会因为析构代码不在界面线程调用而崩溃。