栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Qt 之 基于事件总线的MVC模型

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Qt 之 基于事件总线的MVC模型

文章目录

基于事件总线的MVC模型

简介模块

FrontController

FrontController内的线程 AbstractCommand

GitlEventParam继承AbstractCommand的实际Command GitlView

MainWindowGitlIvkCmdEvtGitlUpdateUIEvt 数据流

基于事件总线的MVC模型

基于Qt的MVC开源框架

简介

与传统的MVC有区别,V和M不是直接关联的采用front controller模式,将不同的请求映射成不同的Command, 以便增量式和迭代式开发 模块 FrontController

单例: GitlFrontController维护 - Table事件队列(MainThreadEvtQue)工作线程事件队列(WorkerThreadEvtQue)事件队列锁(EvtQueMutex)指令锁(CmdExeMutex), ensure one command execution at one time


class GitlFrontController : public GitlModule, public QThread
{
public:
    virtual ~GitlFrontController() {}
    
    
    virtual bool detonate(GitlEvent& rcEvt);

    
    virtual void onCommandRequestArrive(GitlIvkCmdEvt& rcEvt);

    
    bool registerCommand(const QString cCommandName, const QmetaObject *pmetaObject);

    
    void unregisterAllCommand();


    virtual void run();
protected:
    explicit GitlFrontController();

protected:

    /// - table
    ADD_CLASS_FIELD( CONCATE(QHash), cCommandTable, getCommandTable, setCommandTable)

    SINGLETON_PATTERN_DECLARE(GitlFrontController)

    ADD_CLASS_FIELD_PRIVATE(QList, pcMainThreadEvtQue)
    ADD_CLASS_FIELD_PRIVATE(QList, pcWorkerThreadEvtQue)
    ADD_CLASS_FIELD_PRIVATE(QMutex, cEvtQueMutex)
    ADD_CLASS_FIELD_PRIVATE(QWaitCondition, cEvtQueNotEmpty)
    ADD_CLASS_FIELD_PRIVATE(QWaitCondition, cEvtQueNotFull)
    ADD_CLASS_FIELD(int, iMaxEvtInQue, setMaxEvtInQue, getMaxEvtInQue)

    ADD_CLASS_FIELD_PRIVATE(QMutex, cCmdExeMutex)   ///< ensure one command execution at one time

};

#endif // GITLFRONTCONTROLLER_H

FrontController内的线程

FrontController 的父类包含QThread, 重写run( )

// 重写run
void GitlFrontController::run()
{
    forever
    {
        /// get one event from the waiting queue
        m_cEvtQueMutex.lock();
        if( m_pcWorkerThreadEvtQue.empty() )
        {
            m_cEvtQueNotEmpty.wait(&m_cEvtQueMutex);
        }
        GitlEvent* pcEvt = m_pcWorkerThreadEvtQue.front();  //It is equivalent to the first(): returns a reference to the first item in the list.
        m_pcWorkerThreadEvtQue.pop_front();                 //It is equivalent to removeFirst():remove the first item in the list.
        m_cEvtQueMutex.unlock();
        m_cEvtQueNotFull.wakeAll();

        /// execute command
        GitlIvkCmdEvt& rcCmdRequestEvt = dynamic_cast(*pcEvt);
        onCommandRequestArrive(rcCmdRequestEvt);
        delete pcEvt;

    }
}

从WorkerThreadEvtQue中获取事件,将事件转换成GitlIvkCmdEvt类型,然后通过onCommandRequestArrive传递至Command层。

AbstractCommand
typedef GitlEventParam GitlCommandParameter;


class GitlAbstractCommand : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE explicit GitlAbstractCommand(QObject *parent = 0):
        QObject(parent)
    {
        m_bInWorkerThread = false;
    }

    virtual ~GitlAbstractCommand()
    {
    }

    
    virtual bool execute(GitlCommandParameter &rcInputArg, GitlCommandParameter &rcOutputArg)
    {
        Q_UNUSED(rcInputArg)
        Q_UNUSED(rcOutputArg)
        qCritical() << "Please reimplement ";
        return false;
    }

    
    ADD_CLASS_FIELD(bool, bInWorkerThread, getInWorkerThread, setInWorkerThread)

};

#endif // GITLABSTRACTCOMMAND_H

execute

1.所有的子类都需要实现这个接口
2.GitlEventParam 类,在EventBus模块中定义, 是用来设置事件参数的接口, 参考下节GitLEventParam
3.入参: rcInputArg, 从UI(GitlView)而来。
4.出参: rcOutputArg, 返回给UI(GitVIew)

GitlEventParam

这个是事件参数,其实在EventBus模块中定义的一个类

gitleventparam.h

class GitlEventParam
{
public:
    GitlEventParam();

    
    bool hasParameter(QString strParam) const;

    
    QVariant getParameter(const QString& strParam ) const;

    
    bool setParameter(const QString& strParam, const QVariant& rvValue);


    ADD_CLASS_FIELD_PRIVATE( CONCATE(QMap), cParameters)    ///< parameters name-value pair

};

#endif // GITLEVENTPARAM_H

这里的参数存储在m_cParameters成员中,该成员是一个QMap类型
即:

private:
QMap   m_cParameters;

gitleventparam.cpp

GitlEventParam::GitlEventParam()
{
}

bool GitlEventParam::hasParameter(QString strParam) const
{
    return m_cParameters.contains(strParam);
}

QVariant GitlEventParam::getParameter(const QString& strParam ) const
{
    QVariant rvValue;
    if( m_cParameters.contains(strParam) )
    {
        rvValue = m_cParameters[strParam];
    }
    else
    {
        qWarning() << QString("Parameter %1 NOT found.").arg(strParam);
    }
    return rvValue;
}

//设置参数
bool GitlEventParam::setParameter(const QString& strParam, const QVariant& rvValue)
{
    m_cParameters[strParam] = rvValue;
    return true;
}

继承AbstractCommand的实际Command

应用中的Command, 这里以FirParamCommand为例, 操控Model , 并且将结果写入output parameter, 该output parameter会传递到view层。

/// command, it manipulates the model and writes the result to output parameter. The output parameter will
/// be pass to view automatically.
class FirParamCommand : public GitlAbstractCommand
{
    Q_OBJECT
public:
    /// Q_INVOKABLE is necessary for constructor
    Q_INVOKABLE explicit FirParamCommand(QObject *parent = 0):GitlAbstractCommand(parent) 
    {
	
	}
    
    bool execute(GitlCommandParameter &rcInputArg, GitlCommandParameter &rcOutputArg)
    {
        QString strDataToCommand = rcInputArg.getParameter("data_to_command").toString();  //返回data_to_command参数名称的实际内容
        TestModel::getInstance()->setDataInModel(strDataToCommand);     //将该实际值传递给TestModel
        rcOutputArg.setParameter("data_to_view", strDataToCommand);
        return true;
    }
};

#endif // TESTCOMMAND_H

注意到在command的execute中,TestModel是一个实际的Model对象,定义如下:

#ifndef TESTMODEL_H
#define TESTMODEL_H
#include "gitldef.h"
#include "gitlmodel.h"
/// model
class TestModel: public GitlModel
{
    ADD_CLASS_FIELD(QString, strDataInModel, getDataInModel, setDataInModel)

protected:
    TestModel() {}
    friend class GitlModel;
};

#endif // TESTMODEL_H
GitlView

继承GitlModule
这里的GitlView 也是集成GitlModule, 通过Module, 可以获得ModuleDelegate的能力。这样,继承GitlView的控件, 可以利用subscribeToEvtBNyName() 接口将关注的事件和detonate绑定在一起。

listenToParams
监听参数,将该监听参数和回调函数绑定在一起

回调函数表
在listenToParams接口内,将rcParams和rcCallback 存储至列表中

class GitlView : public GitlModule
{
public:
    GitlView(GitlEventBus* pcEventBus = NULL);
    virtual ~GitlView() {}
    bool detonate(GitlEvent& rcEvt);    
    
    bool listenToParams(const QString& rcParams, const UIUpdateCallback& rcCallback);
    bool listenToParams(const QStringList& rcParams, const UIUpdateCallback& rcCallback);
    bool unlistenToParams(const QStringList& rcParams);
    bool isListenToParams(const QStringList& rcParams) const;
    ADD_CLASS_FIELD_PRIVATE( CONCATE(QMap), pafCallbacks )
};

而窗口mainwindow 继承GitlView, 在初始化时,调用listenToParams()

    listenToParams("data_to_view", MAKE_CALLBACK(MainWindow::onUIUpdate));   //将返回的参数名称“data_to_view”和onUIUpdate接口绑定在一起
MainWindow

mainwindow.h

class MainWindow : public QMainWindow, public GitlView
{
    Q_OBJECT
    
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    /// it receives the result from commands
    void onUIUpdate(GitlUpdateUIEvt& rcEvt);
    
private slots:
    void on_mtTestButton_clicked();

private:
    Ui::MainWindow *ui;
};

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    listenToParams("data_to_view", MAKE_CALLBACK(MainWindow::onUIUpdate));
}

MainWindow::~MainWindow()
{
    delete ui;
}

//参数传递到view层后的回调函数
void MainWindow::onUIUpdate(GitlUpdateUIEvt &rcEvt)
{
    QString strDataInView = rcEvt.getParameter("data_to_view").toString();
    ui->mtTestLabel->setText(strDataInView);
    qDebug() << strDataInView;
}

void MainWindow::on_mtTestButton_clicked()
{
    /// event (in real case, this event should be dispatch from user interface, i.e. the views)
    GitlIvkCmdEvt cRequestEvt("show_string_command");              //command_name 参数的value = "show_string_command"
    cRequestEvt.setParameter("data_to_command", "Hello GitlMVC");  //data_to_command 参数的value = "Hello GitlMVC"
    cRequestEvt.dispatch();                                        //将事件传递到GitlBus总线上
}

下图可以看到,在cRequestEvt事件中有两个参数

param nameparam value
command_name“show_string_name”
data_to_command“Hello GitlMVC”

GitlIvkCmdEvt

从mainwindow中点击按钮的槽函数可以看出,当点击Button后,发出自定义的Command Event, 该CommandEvent继承GitlEvent, 并且
GitlEvent的类内,有设置参数- 值(params-value)的接口, 此处params 为 command_name , 而value 为该Command的名字

GitlIvkCmdEvt.h

class GitlIvkCmdEvt : public GitlEvent
{
    /// virtual copy pattern, please add this macro to all the subclass
    CLONABLE(GitlIvkCmdEvt)

public:
    GitlIvkCmdEvt(const QString& strCommandName);

    QString getCommandName();
    void setCommandName(const QString& strCommandName);
};

GitlIvkCmdEvt.cpp

GitlIvkCmdEvt::GitlIvkCmdEvt(const QString& strCommandName) :
    GitlEvent(GITL_EXE_COMMAND_REQUEST_EVENT)
{
    GitlEvent::setParameter("command_name", strCommandName);
}

QString GitlIvkCmdEvt::getCommandName()
{
    return GitlEvent::getParameter("command_name").toString();
}

void GitlIvkCmdEvt::setCommandName(const QString& strCommandName)
{
    GitlEvent::setParameter("command_name", strCommandName);
}

GitlUpdateUIEvt

更新UI 事件消息类

这个类和GitlIvkCmdEvt很相似,一个是命令事件类,一个是更新UI事件类,都继承自GitlEvent

updateUIEvt.h

class GitlUpdateUIEvt : public GitlEvent
{
    /// virtual copy pattern, please add this macro to all the subclass
    CLONABLE(GitlUpdateUIEvt)

public:
    GitlUpdateUIEvt();

    QString getCommandName();
    void setCommandName(const QString& strCommandName);
};

#endif // GITLUPDATEUIEVT_H

updateUIEvt.cpp

GitlUpdateUIEvt::GitlUpdateUIEvt() :
    GitlEvent(GITL_UPDATE_UI_REQUEST_EVENT)
{
}

QString GitlUpdateUIEvt::getCommandName()
{
    return getParameter("command_name").toString();
}

void GitlUpdateUIEvt::setCommandName(const QString& strCommandName)
{
    setParameter("command_name", strCommandName);
}

数据流

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/710975.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号