本文主要在Qt5.9/C++桌面应用项目开发中,总结一个常用的程序架构(或框架),整个工程主要分为三大模块,分别为视图层、控制层、模型层。其中,视图层完全负责界面类的编写和展示;控制层完全负责处理逻辑,将UI界面和模型层的功能有机结合起来;模型层主要负责实现业务逻辑功能。
实现一个业务逻辑功能时,控制层是主要层;有三个主要的信号,分别是启动信号、完成信号、显示结果界面信号,具体的可以参考博主这篇文章:https://blog.csdn.net/naibozhuan3744/article/details/82383683。其中,控制层所有类都是采用单例模型,具有唯一的实例、提供一个全局接口的优点。
实现一个按钮功能步骤如下:(改进思路:控制层controller作为信号产生中转信号,建立一个控制层文件就行)
a1.UI界面点击按钮,发射一个控制器启动信号;
a2.控制层开启一条支线层,执行模型层功能函数;
a3.模型层功能函数执行完后,发射一个完成控制层信号,表明完成了该业务逻辑功能;
a4.控制层停止该支线层,并且发射一个显示完成业务逻辑UI界面信号;
a5.UI界面接收业务逻辑完成信号,显示完成功能界面。
具体的编程实例如下所述。
1.1新建一个widget工程,不要勾选ui界面。然后分别在添加视图层类(QFirsrPageWidget、QSecondPageWidget)、控制器层类(QFirsrPageController、QSecondPageController)、模型层类(QFirsrPageFunction、QSecondPageFunction),如下图所示:
1.2分别在widget.h、widget.cpp;firstpagecontroller.h、firstpagecontroller.cpp;secondpagecontroller.h、secondpagecontroller.cpp;firstpagefunction.h、firstpagefunction.cpp;secondpagefunction.h、secondpagefunction.cpp;firstpagewidget.h、firstpagewidget.cpp;secondpagewidget.h、secondpagewidget.cpp;类中添加如下代码:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QStackedLayout>
#include <QButtonGroup>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
private:
void setupUI();
void initMenu();
void initStackWidget();
QVBoxLayout *mainLayout;
QButtonGroup *buttonGroup;
QStackedLayout *stackLayout;
// QWidget *firstPageWidget;
// QWidget *secondPageWidget;
private slots:
void buttonGroupSlot(int);
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "view/firstpagewidget.h"
#include "view/secondpagewidget.h"
#include <QPushButton>
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
setupUI();
}
Widget::~Widget()
{
}
void Widget::setupUI()
{
mainLayout = new QVBoxLayout;
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
QWidget *mainWidget = new QWidget(this); /*启动QWidget页面需要制定父对象this指针,不然会出现闪屏bug*/
mainWidget->setLayout(mainLayout);
mainWidget->setObjectName("mainWidget");
mainWidget->setStyleSheet("#mainWidget{border-image:url(:/resource/main_bg.jpg)}"); /*设置主界面背景图片*/
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(tr("\n\n\n\n\n第一页"));
firstPageBtn->setObjectName("firstPageBtn");
firstPageBtn->setCheckable(true);
firstPageBtn->setChecked(true);
firstPageBtn->setFixedSize(btnSize);
firstPageBtn->setStyleSheet("QPushButton{border-image: url(:/resource/icon_page1.png) 0 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}"
"QPushButton:hover{border-image: url(:/resource/icon_page1.png) 5 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}"
"QPushButton:checked{border-image: url(:/resource/icon_page1.png) 4 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}");
QPushButton *secondPageBtn = new QPushButton(tr("\n\n\n\n\n第二页"));
secondPageBtn->setObjectName("secondPageBtn");
secondPageBtn->setCheckable(true);
secondPageBtn->setFixedSize(btnSize);
secondPageBtn->setStyleSheet("QPushButton{border-image: url(:/resource/icon_page2.png) 0 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}"
"QPushButton:hover{border-image: url(:/resource/icon_page2.png) 5 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}"
"QPushButton:checked{border-image: url(:/resource/icon_page2.png) 4 0 0 0;background-repeat: no-repeat;background-position:center;border:none;color:rgb(255, 255, 255);}");
/*单选菜单效果*/
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()
{
/*启动菜单栏界面*/
QFirstPageWidget *firstPageWidget = new QFirstPageWidget(this); /*启动QWidget页面需要制定父对象this指针,不然会出现闪屏bug*/
QSecondPageWidget *secondPageWidget = new QSecondPageWidget(this);
stackLayout = new QStackedLayout;
stackLayout->addWidget(firstPageWidget);
stackLayout->addWidget(secondPageWidget);
stackLayout->setCurrentIndex(0);
mainLayout->addLayout(stackLayout);
}
void Widget::buttonGroupSlot(int)
{
QPushButton* checkedButton = qobject_cast<QPushButton*>(buttonGroup->checkedButton());
QString objectNameCheckedBtn = checkedButton->objectName();
if(objectNameCheckedBtn.compare(QStringLiteral("firstPageBtn"))==0)
{
qDebug()<<tr("单击了第一页菜单按钮");
stackLayout->setCurrentIndex(0);
}
else if(objectNameCheckedBtn.compare(QStringLiteral("secondPageBtn"))==0)
{
qDebug()<<tr("单击了第二页菜单按钮");
stackLayout->setCurrentIndex(1);
}
}
firstpagecontroller.h
#ifndef FIRSTPAGECONTROLLER_H
#define FIRSTPAGECONTROLLER_H
#include <QObject>
#include <QThread>
#include "model/firstpagefunction.h"
class QFirstPageController : public QObject
{
Q_OBJECT
public:
static QFirstPageController* GetInstance();
signals:
void startedCount_signal();
void finishedCount_signal();
void showFinishedCountUI_signal();
private:
static QFirstPageController *m_instance;
QFirstPageController();
void initController();
QThread *threadCount;
QFirstPageFunction *firstPageFunction;
private slots:
void startCountSlot();
void quitCountThreadSlot();
void finishedCountThreadSlot();
};
#endif // FIRSTPAGECONTROLLER_H
firstpagecontroller.cpp
#include "firstpagecontroller.h"
#include <QDebug>
QFirstPageController *QFirstPageController::m_instance = new QFirstPageController();
QFirstPageController *QFirstPageController::GetInstance()
{
return m_instance;
}
QFirstPageController::QFirstPageController()
{
/*初始化*/
initController();
}
void QFirstPageController::initController()
{
/*绑定信号与槽函数*/
connect(this,SIGNAL(startedCount_signal()),this,SLOT(startCountSlot()));
}
void QFirstPageController::startCountSlot()
{
/*启动一对一镜像线程*/
threadCount = new QThread;
firstPageFunction = new QFirstPageFunction();
firstPageFunction->moveToThread(threadCount);
connect(threadCount,SIGNAL(finished()),threadCount,SLOT(deleteLater()));
connect(threadCount,SIGNAL(started()),firstPageFunction,SLOT(count_slot())); /*多线程槽函数*/
connect(this,SIGNAL(finishedCount_signal()),this,SLOT(quitCountThreadSlot()));
connect(threadCount,SIGNAL(finished()),this,SLOT(finishedCountThreadSlot()));
threadCount->start();
}
void QFirstPageController::quitCountThreadSlot()
{
threadCount->quit();
threadCount->wait();
}
void QFirstPageController::finishedCountThreadSlot()
{
qDebug()<<tr("完成计算逻辑功能!!!");
emit showFinishedCountUI_signal();
}
secondpagecontroller.h
#ifndef SECONDPAGECONTROLLER_H
#define SECONDPAGECONTROLLER_H
#include <QObject>
class QSecondPageController : public QObject
{
Q_OBJECT
public:
static QSecondPageController* GetInstance();
signals:
void count_signal();
private:
static QSecondPageController *m_instance;
QSecondPageController();
private slots:
};
#endif // SECONDPAGECONTROLLER_H
secondpagecontroller.cpp
#include "secondpagecontroller.h"
QSecondPageController *QSecondPageController::m_instance = new QSecondPageController();
QSecondPageController *QSecondPageController::GetInstance()
{
return m_instance;
}
QSecondPageController::QSecondPageController()
{
}
firstpagefunction.h
#ifndef FIRSTPAGEFUNCTION_H
#define FIRSTPAGEFUNCTION_H
#include <QObject>
class QFirstPageFunction : public QObject
{
Q_OBJECT
public:
explicit QFirstPageFunction(QObject *parent = nullptr);
signals:
public slots:
void count_slot();
};
#endif // FIRSTPAGEFUNCTION_H
firstpagefunction.cpp
#include "firstpagefunction.h"
#include "controller/firstpagecontroller.h"
#include <QDebug>
QFirstPageFunction::QFirstPageFunction(QObject *parent) : QObject(parent)
{
}
void QFirstPageFunction::count_slot()
{
int a = 2;
int b = 3;
QFirstPageController::GetInstance()->finishedCount_signal();
qDebug()<<tr("页面1的逻辑功能计算结果为==%1").arg(a+b);
}
secondpagefunction.h
#ifndef SECONDPAGEFUNCTION_H
#define SECONDPAGEFUNCTION_H
#include <QObject>
class QSecondPageFunction : public QObject
{
Q_OBJECT
public:
explicit QSecondPageFunction(QObject *parent = nullptr);
signals:
public slots:
void count_slot();
};
#endif // SECONDPAGEFUNCTION_H
secondpagefunction.cpp
#include "secondpagefunction.h"
#include <QDebug>
QSecondPageFunction::QSecondPageFunction(QObject *parent) : QObject(parent)
{
}
void QSecondPageFunction::count_slot()
{
int a = 2;
int b = 3;
qDebug()<<tr("页面2的逻辑功能计算结果为==%1").arg(a*b);
}
firstpagewidget.h
#ifndef FIRSTPAGEWIDGET_H
#define FIRSTPAGEWIDGET_H
#include <QWidget>
#include <QVBoxLayout>
class QFirstPageWidget : public QWidget
{
Q_OBJECT
public:
explicit QFirstPageWidget(QWidget *parent = nullptr);
signals:
private:
void setupUI();
QVBoxLayout *mainLayout;
private slots:
void showOneFunctionUISlot();
void showFinishedCountResultUISlot();
};
#endif // FIRSTPAGEWIDGET_H
firstpagewidget.cpp
#include "firstpagewidget.h"
#include "controller/firstpagecontroller.h"
#include "controller/secondpagecontroller.h"
#include <QPushButton>
#include <QDebug>
#include <QMessageBox>
QFirstPageWidget::QFirstPageWidget(QWidget *parent) : QWidget(parent)
{
setupUI();
}
void QFirstPageWidget::setupUI()
{
QPushButton *firstPage = new QPushButton(tr("我是第一页的页面:点击我实现2+3计算功能(业务逻辑功能)"));
connect(firstPage,SIGNAL(clicked(bool)),this,SLOT(showOneFunctionUISlot()));
mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
mainLayout->addWidget(firstPage);
mainLayout->addStretch();
/*注意:这个绑定不能放在按钮那里,否则会出现第n次点击按钮,然后发射n次信号,执行n次槽函数*/
connect(QFirstPageController::GetInstance(),SIGNAL(showFinishedCountUI_signal()),this,SLOT(showFinishedCountResultUISlot()));
}
void QFirstPageWidget::showOneFunctionUISlot()
{
/*摁下按钮,切换到新的界面*/
QFirstPageController::GetInstance()->startedCount_signal();
}
void QFirstPageWidget::showFinishedCountResultUISlot()
{
qDebug()<<tr("显示计算后界面!!!");
QMessageBox::warning(this,QStringLiteral("结果界面"),QStringLiteral("显示计算后界面!!!"),QMessageBox::Yes,QMessageBox::No);
}
secondpagewidget.h
#ifndef SECONDPAGEWIDGET_H
#define SECONDPAGEWIDGET_H
#include <QWidget>
#include <QVBoxLayout>
class QSecondPageWidget : public QWidget
{
Q_OBJECT
public:
explicit QSecondPageWidget(QWidget *parent = nullptr);
signals:
public slots:
private:
void setupUI();
QVBoxLayout *mainLayout;
};
#endif // SECONDPAGEWIDGET_H
secondpagewidget.cpp
#include "secondpagewidget.h"
#include <QPushButton>
QSecondPageWidget::QSecondPageWidget(QWidget *parent) : QWidget(parent)
{
setupUI();
}
void QSecondPageWidget::setupUI()
{
QPushButton *firstPage = new QPushButton(tr("我是第二页的页面:点击我实现2*3计算功能(业务逻辑功能)"));
mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
mainLayout->addWidget(firstPage);
mainLayout->addStretch();
}
main.cpp
#include "widget.h"
#include <QApplication>
#include <QIcon>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget mainWidget;
mainWidget.resize(960,640);
mainWidget.setWindowTitle(QString::fromUtf8("Qt/C++架构工程demo版本V1.0.0"));
mainWidget.setWindowIcon(QIcon(":/resource/main_ico.ico"));
mainWidget.setStyleSheet("font: bold 12px;");
mainWidget.show();
return a.exec();
}
1.2程序构建运行后,效果如下图所示
原文:https://blog.csdn.net/naibozhuan3744/article/details/82493728