一、基本思想
开发 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();
}