四十六、QT应用开发之MVC架构(附案例)

一、基本思想

  开发 Qt 应用程序的一个基本原则就是:UI界面和业务逻辑需要尽可能的分离。
  判断一个结构的解耦程度,一个简单的办法是离开了UI界面,业务逻辑是否可以正常调用和运行,如果可以,说明这个架构是比较成功的。同时,对UI界面和业务逻辑功能的每个模块,是否能够被替换,而不影响整个项目的功能,这点也是判断架构解耦性的一个指标。

二、设计思路

在这里插入图片描述

UI界面层(view)——>控制器层:当用户操作UI界面时,发射一个控制器层信号;

控制器层(controller)——>模型层:控制层调用模型层功能函数,实现对应业务逻辑功能;

模型层(model)——>控制器层:模型功能层,完成业务逻辑后,再发射一个控制器层信号,声明完成了该业务逻辑功能。

控制器层(controller)——>UI界面层:控制器层接收到该支线程完成了对应的业务逻辑,开启槽函数结束该支线程,然后发射一个控制器层完成业务逻辑信号到UI界面层。UI界面层收到信号,显示对应的结果UI界面。

注意:整个过程控制器有三个关键信号,启动业务逻辑功能信号,退出该业务逻辑线程信号,完成业务逻辑信号。而model层功能函数是用支线程来执行。也就是说,所有的模型功能函数都是用支线程完成,这样可以保证UI界面的流畅度。

三、案例

(一)项目基本结构

在这里插入图片描述

(二)代码

1、单例类

singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

#include <QMutex>
#include <QScopedPointer>

template <typename T>
class Singleton
{
public:
    static T &getInstance();
    Singleton(const Singleton &other) = default;
    Singleton<T> &operator=(const Singleton &other) = default;

private:
    static QMutex mutex;
    static QScopedPointer<T> instance;
};

template <typename T> QMutex Singleton<T>::mutex;
template <typename T> QScopedPointer<T> Singleton<T>::instance;

template<typename T>
T &Singleton<T>::getInstance()
{
    if (instance.isNull()) {
        mutex.lock();
        if (instance.isNull()) {
            instance.reset(new T());
        }
        mutex.unlock();
    }
    return *instance.data();
}

#define SINGLETON(Class) \
    private: \
    Class(); \
    ~Class(); \
    Class(const Class &other); \
    Class &operator =(const Class &other); \
    friend class Singleton<Class>; \
    friend struct QScopedPointerDeleter<Class>;


#endif // SINGLETON_H

2、View层
firstpageview.h
#ifndef FIRSTPAGEVIEW_H
#define FIRSTPAGEVIEW_H

#include <QObject>
#include <QWidget>
#include <QVBoxLayout>

class FirstPageView : public QWidget
{
    Q_OBJECT
public:
    explicit FirstPageView(QWidget *parent = nullptr);

private:
    void setUi();
    QVBoxLayout *mainLayout;

private slots:
    void showUiSlot();
    void showFinishedCountResultUISlot();
};

#endif // FIRSTPAGEVIEW_H

firstpageview.cpp
#include "firstpageview.h"

#include <QPushButton>
#include "singleton.h"
#include "controller/firstpagecontroller.h"
#include <QMessageBox>
#include <QDebug>

FirstPageView::FirstPageView(QWidget *parent) : QWidget(parent)
{
    setUi();
}

void FirstPageView::setUi()
{
    QPushButton *firstPage = new QPushButton(tr("我是第一页的页面:点击我实现2+3计算功能(业务逻辑功能)"));
    connect(firstPage, SIGNAL(clicked(bool)), this, SLOT(showUiSlot()));
    mainLayout = new QVBoxLayout(this);
    mainLayout->setMargin(0);
    mainLayout->setSpacing(0);
    mainLayout->addWidget(firstPage);
    mainLayout->addStretch();

    connect(&Singleton<FirstPageController>::getInstance(), SIGNAL(showFinishedCountUiSignal()), this, SLOT(showFinishedCountResultUISlot()));

}

void FirstPageView::showUiSlot()
{
    emit Singleton<FirstPageController>::getInstance().startedCountSignal();
}

void FirstPageView::showFinishedCountResultUISlot()
{
    qDebug()<<tr("显示计算后界面!!!");
        QMessageBox::warning(this,QStringLiteral("结果界面"),QStringLiteral("显示计算后界面!!!"),QMessageBox::Yes,QMessageBox::No);
}

3、controller层
firstpagecontroller.h
#ifndef FIRSTPAGECONTROLLER_H
#define FIRSTPAGECONTROLLER_H

#include <QObject>
#include <QThread>
#include "model/firstpagemodel.h"
#include "singleton.h"

class FirstPageController : public QObject
{
    SINGLETON(FirstPageController)
    Q_OBJECT

signals:
    void startedCountSignal();
    void finishedCountSignal();
    void showFinishedCountUiSignal();

private:
    void initController();
    QThread *threadCount;
    FirstPageModel *firstPageModel;

private slots:
    void startCountSlot();
    void quitCountThreadSlot();
    void finishedCountThreadSlot();


};

#endif // FIRSTPAGECONTROLLER_H

firstpagecontroller.cpp
#include "firstpagecontroller.h"
#include <QDebug>

FirstPageController::FirstPageController()
{
    initController();
}

FirstPageController::~FirstPageController()
{

}

void FirstPageController::initController()
{
    connect(this, SIGNAL(startedCountSignal()), this, SLOT(startCountSlot()));
}

void FirstPageController::startCountSlot()
{
    threadCount = new QThread;
    firstPageModel = new FirstPageModel();
    firstPageModel->moveToThread(threadCount);
    connect(threadCount, SIGNAL(started()), firstPageModel, SLOT(countSlot()));
    connect(threadCount, SIGNAL(finished()), threadCount, SLOT(deleteLater()));
    //保证线程先结束,再去更新UI,可以防止UI卡死
    connect(threadCount, SIGNAL(finished()), this, SLOT(finishedCountThreadSlot()));
    connect(this, SIGNAL(finishedCountSignal()), this, SLOT(quitCountThreadSlot()));
    threadCount->start();
}

void FirstPageController::quitCountThreadSlot()
{
    threadCount->quit();
    threadCount->wait();
}

void FirstPageController::finishedCountThreadSlot()
{
    qDebug() << "完成计算逻辑功能!!!";
    emit showFinishedCountUiSignal();
}

4、model层
firstpagemodel.h
#ifndef FIRSTPAGEMODEL_H
#define FIRSTPAGEMODEL_H

#include <QObject>

class FirstPageModel : public QObject
{
    Q_OBJECT
public:
    explicit FirstPageModel(QObject *parent = nullptr);

signals:

public slots:
    void countSlot();
};

#endif // FIRSTPAGEMODEL_H

firstpagemodel.cpp
#include "firstpagemodel.h"
#include "controller/firstpagecontroller.h"
#include "singleton.h"

#include <QDebug>

FirstPageModel::FirstPageModel(QObject *parent) : QObject(parent)
{

}

void FirstPageModel::countSlot()
{
    int a = 2;
    int b = 3;
    emit Singleton<FirstPageController>::getInstance().finishedCountSignal();
    qDebug() << QString("页面1的逻辑功能计算结果为==%1").arg(a + b);
}

5、其它
widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QVBoxLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QStackedLayout>
#include <QButtonGroup>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();

private:
    void setUi();
    void initMenu();
    void initStackWidget();

    QVBoxLayout *mainLayout;
    QButtonGroup *buttonGroup;
    QStackedLayout *stackLayout;

private slots:
    void buttonGroupSlot(int);
};

#endif // WIDGET_H

widget.cpp
#include "widget.h"

#include "view/firstpageview.h"
#include <QPushButton>
#include <QDebug>
#include <QString>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    setUi();
}

Widget::~Widget()
{

}

void Widget::setUi()
{
    mainLayout = new QVBoxLayout;
    mainLayout->setMargin(0);
    mainLayout->setSpacing(0);
    //启动QWidget页面需要制定父对象this指针,不然会出现闪屏bug
    QWidget *mainWidget = new QWidget(this);
    mainWidget->setLayout(mainLayout);
    mainWidget->setObjectName("mainWidget");
    QVBoxLayout *layout = new QVBoxLayout;
    layout->setMargin(0);
    layout->setSpacing(0);
    layout->addWidget(mainWidget);
    this->setLayout(layout);

    initMenu();
    initStackWidget();
}

void Widget::initMenu()
{
    const QSize btnSize(120, 90);
    QPushButton *firstPageBtn = new QPushButton("\n\n\n\n\n第一页");
    firstPageBtn->setObjectName("firstPageBtn");
    firstPageBtn->setCheckable(true);
    firstPageBtn->setChecked(true);
    firstPageBtn->setFixedSize(btnSize);

    QPushButton *secondPageBtn = new QPushButton("\n\n\n\n\n第二页");
    secondPageBtn->setObjectName("secondPageBtn");
    secondPageBtn->setCheckable(true);
    secondPageBtn->setChecked(true);
    secondPageBtn->setFixedSize(btnSize);

    buttonGroup = new QButtonGroup();
    buttonGroup->setObjectName("buttonGroup");
    buttonGroup->addButton(firstPageBtn);
    buttonGroup->addButton(secondPageBtn);
    //只有一个按钮能被选中
    buttonGroup->setExclusive(true);
    connect(buttonGroup, SIGNAL(buttonClicked(int)), this, SLOT(buttonGroupSlot(int)));

    QHBoxLayout *menuLayout = new QHBoxLayout();
    menuLayout->setMargin(0);
    menuLayout->setSpacing(0);
    menuLayout->addWidget(firstPageBtn);
    menuLayout->addWidget(secondPageBtn);
    menuLayout->addStretch();

    mainLayout->addLayout(menuLayout);

}

void Widget::initStackWidget()
{
    //启动菜单栏界面
    FirstPageView *firstPageView = new FirstPageView(this);
    stackLayout = new QStackedLayout;
    stackLayout->addWidget(firstPageView);
    stackLayout->setCurrentIndex(0);
    mainLayout->addLayout(stackLayout);
}

void Widget::buttonGroupSlot(int)
{
    QPushButton *checkedButton = qobject_cast<QPushButton*>(buttonGroup->checkedButton());
    QString checkedButtonName = checkedButton->objectName();
    if (checkedButtonName.compare(QStringLiteral("firstPageBtn")) == 0) {
        qDebug() << "单击了第一页菜单按钮";
        stackLayout->setCurrentIndex(0);
    } else if (checkedButtonName.compare(QStringLiteral("secondPageBtn")) == 0) {
        qDebug() << "单击了第二页菜单按钮";
        stackLayout->setCurrentIndex(1);
    }
}

main.cpp
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;

    w.resize(960, 640);
    w.setWindowTitle(QString::fromUtf8("Qt/C++架构工程demo版本V1.0.0"));
    w.setStyleSheet("font: bold 12px;");


    w.show();

    return a.exec();
}

  • 31
    点赞
  • 273
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值