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

文章介绍了如何在CTK框架中将菜单按钮功能集成到插件中,通过事件或服务注册实现菜单动态添加。具体实现包括创建menubar类处理菜单事件,使用ctkEventHandler接口监听特定话题,以及在About插件中发送注册事件来添加菜单项。在解决程序关闭时崩溃的问题上,文章提到通过使用信号处理界面析构来避免线程错误。
摘要由CSDN通过智能技术生成

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];
}
  1. 给定注册接口,按照以下的形式去发送注册菜单的事件

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

turbolove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值