CTK框架 - 将菜单按钮写到插件中

简介: 之前我们在的两个插件Core和About,在Core和About中加入了界面,并且Core中插入了一个菜单,点击之后弹出About界面。我们现在来用之前的知识把菜单改成注册的,并且点击之后弹出About界面。我这里的思路是使用事件或者是在Core中加注册的服务来注册菜单。之后点击菜单的时候发送事件。

CTK框架 - 将菜单按钮写到插件中

之前我们在的两个插件Core和About,在Core和About中加入了界面,并且Core中插入了一个菜单,点击之后弹出About界面。


我们现在来用之前的知识把菜单改成注册的,并且点击之后弹出About界面。


我这里的思路是使用事件或者是在Core中加注册的服务来注册菜单。之后点击菜单的时候发送事件。


代码示例

1.编写一个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_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 <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];
}

2.给定注册接口,按照以下的形式去发送注册菜单的事件


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

3.在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析构还会报一个线程错误,我们把析构使用信号去处理。


具体修改见项目。这里就不贴优化部分的代码了,思路就是使用信号去处理界面的析构,否则会因为析构代码不在界面线程调用而崩溃。

目录
相关文章
|
缓存 资源调度 区块链
Electron打包图标不显示解决方案
Electron打包图标不显示解决方案
1572 0
|
4月前
|
UED 开发者
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
Flutter笔记:Widgets Easier组件库(13)- 使用底部弹窗
92 2
|
人工智能 自然语言处理 语音技术
关于element ui中引入官方的NavMenu时出现点击某个子菜单时,所有的子菜单全都展开问题
关于element ui中引入官方的NavMenu时出现点击某个子菜单时,所有的子菜单全都展开问题
267 0
Avalonia使用默认弹窗
Avalonia使用默认弹窗
219 0
Avalonia使用默认弹窗
|
前端开发
Ant Design pro路由菜单标签页插件使用
Ant Design pro路由菜单标签页插件使用
246 0
avalonia自定义弹窗
avalonia自定义弹窗
336 0
|
JavaScript 前端开发 中间件
我点击页面元素,VSCode 乖乖打开了组件?原理揭秘。
在大型项目开发中,经常会遇到这样一个场景,QA 丢给你一个出问题的链接,但是你完全不知道这个页面 & 组件对应的文件位置。 react-dev-inspector就是应此需求而生。
|
存储 自然语言处理 安全
DevEco Studio项目构建讲解、编写页面、布局介绍、页面跳转
DevEco Studio项目构建讲解、编写页面、布局介绍、页面跳转
1017 0
DevEco Studio项目构建讲解、编写页面、布局介绍、页面跳转
jira项目笔记9-封装输入框和下拉框组件(不用ant之前)
jira项目笔记9-封装输入框和下拉框组件(不用ant之前)
128 0
jira项目笔记9-封装输入框和下拉框组件(不用ant之前)