- 2020/6/22
Qt应用开发的基本模式(面向对象)
1. 继承QDialog
- 头文件:
#ifndef GK_DIALOG_H
#define GK_DIALOG_H
#include <QtWidgets/QDialog>
#include <QtWidgets/QPushButton>
class GKDialog: public QDialog{
public:
GKDialog(QWidget *parent=0);
~GKDialog();
private:
QPushButton *btn;
};
#endif
- 代码实现:
include "gkdialog.h"
#include <iostream>
GKDialog::GKDialog(QWidget *parent):
QDialog(parent){
std::cout << "对话框初始化" << std::endl;
// 初始化
btn = new QPushButton("确定",this);
btn->move(250, 300);
}
GKDialog::~GKDialog(){
std::cout << "释放" << std::endl;
delete btn;
}
2. QApplication
- 头文件:
#ifndef GK_APP_H
#define GK_APP_H
#include <QtWidgets/QApplication>
#include "gkdialog.h"
class GKApp : public QApplication{
public:
GKApp(int argc, char **argv);
~GKApp();
private:
GKDialog *dlg;
};
#endif
- 代码实现:
#include "gkapp.h"
#include <iostream>
GKApp::GKApp(int argc, char **argv):
QApplication(argc, argv),
dlg(new GKDialog()){ // 构造初始化列表
// dlg = new GKDialog();
dlg->resize(600, 400);
dlg->move(200, 200);
dlg->show();
std::cout << "应用初始化" << std::endl;
}
GKApp::~GKApp(){
std::cout << "应用释放" << std::endl;
delete dlg;
}
3. main.cpp:
#include "gkapp.h"
// #include "gkdialog.h"
int main(int argc, char **argv){
// 创建Qt应用
GKApp app(argc, argv);
// 创建对话框
// GKDialog dlg;
// dlg.resize(600, 400);
// dlg.move(200, 200);
// dlg.show();
// 消息循环
// 返回状态码
return app.exec();
}
4. Makefile编译脚本:
INCLUDES = /I "D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/include"
LIBS = /LIBPATH:"D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/lib" \
/DYNAMICBASE \
"Qt5Widgetsd.lib" \
"Qt5Guid.lib" \
"Qt5Cored.lib"
CL_ARGS = /EHsc \
/MDd \
/source-charset:utf-8 \
/execution-charset:utf-8 \
/nologo
LINK_ARGS = /MACHINE:X64 /NOLOGO
main:main.cpp gkapp.cpp gkdialog.cpp
@cl /c $(CL_ARGS) /Fo:main.obj $(INCLUDES) main.cpp
@cl /c $(CL_ARGS) /Fo:app.obj $(INCLUDES) gkapp.cpp
@cl /c $(CL_ARGS) /Fo:dialog.obj $(INCLUDES) gkdialog.cpp
@link $(LINK_ARGS) $(LIBS) /OUT:main.exe main.obj app.obj dialog.obj
clean:
@del *.exe *.obj *.exp 2>/Nul
三种使用模式的体会:
-
- 在main函数中直接绘制对话框:
即在main.cpp中利用继承好的GKDialog类对象去直接调用相应方法来绘制出对话窗口。该使用模式的逻辑顺序最适应我们的思维方式,故该种模式的优点是使我们更容易接受与理解;但是由于将执行过程直接暴露于main函数中,使得该模式的对象间的管理结构与职能分布不对称,从工程级别的角度来看是较为失败的一种搭建模式。
-
- 在gkapp类中初始化对话框属性:
即在gkapp.h和gkapp.cpp文件的QApplication去加载GKDialog类并初始化出对话框属性,main函数不再起到绘制作用而是调用作用,该使用模式突出了面向对象编程的思维方式,解放了main函数的职能,使得工具类和对话框类对象间具备了包含关系,很好的提高了代码的封装性和可读性;由于对话框属性是在构造器里面进行初始化,使得该使用模式的内存分配较为冗杂,不是最为合理的使用模式。
- 在gkapp类中直接构造对话框的初始化列表:
即在第二种使用模式的基础上,改变对话框初始化方式,直接进行构造初始化列表,该使用模式不仅使代码具有很好的封装性和复用性,而且还减少了对话框初始化分配内存时的寻址过程,使得程序的效率和性能更为突出,相比于以上两种模式,我认为该模式最为高效和专业。
- 在gkapp类中直接构造对话框的初始化列表:
设计UI(Qt Designer)
代码实现:
- 定义成员
- 创建对象
- 设置属性
工具实现
- Qt Designer(Qt设计大师)
步骤1:创建模板,保存为ui文件
拖动组件满足功能
外观的设计
-
命名规范
窗体名与文件名一致:
将匈牙利命名法和驼峰命名法结合在一起。
-
交互设计
- 设计槽(slot)函数
- 组件的信号与槽函数关联
使用UI
- 条件是*.ui文件
-
编译ui文件为.h文件
-
uic [options] [uifile]
-
-o 输出的文件
: 建议文件输出为.h文件
-
-
创建ui对象
使用ui对象的setupUi绑定我们的对话框
ui->setupUi(this);实现ui设计中的槽函数
- 所有定义了槽函数的头文件需要编译成moc的cpp文件。
cpp需要编译与链接
注意事项;定义槽函数或者是信号的类,需要添加一个宏,Q_OBJECT
-
moc [options] [header-file]
- 选项:
-o 输出的文件moc_***.cpp
- 选项:
- 附gkdialog.cpp(初始化对话框的cpp文件)代码实现:
#include "gkdialog.h"
#include <iostream>
#include <QtGui/QPainter>
#include <QtGui/QKeyEvent>
GKDialog::GKDialog(QWidget *parent):
QDialog(parent),
ui(new Ui::gk()){
std::cout << "对话框初始化" << std::endl;
// 初始化
// ui = new Ui::gk();
ui->setupUi(this);
// btn = new QPushButton("确定",this);
// btn->move(200, 200);
}
GKDialog::~GKDialog(){
std::cout << "释放" << std::endl;
// delete btn;
delete ui;
}
void GKDialog::sobel(){
std::cout << "sobel" << std::endl;
}
void GKDialog::laplace(){
std::cout << "laplace" << std::endl;
}
void GKDialog::gauss(){
std::cout << "gauss" << std::endl;
}
void GKDialog::conv(){
std::cout << "conv" << std::endl;
}
- 编译脚本:
INCLUDES = /I "D:\Qt\Qt5.13.0\5.13.0\msvc2017_64\include"
LIBS = /LIBPATH:"D:\Qt\Qt5.13.0\5.13.0\msvc2017_64\lib" \
/DYNAMICBASE \
"Qt5Widgetsd.lib" \
"Qt5Guid.lib" \
"Qt5Cored.lib"
CL_ARGS = /EHsc \
/MDd \
/source-charset:utf-8 \
/execution-charset:utf-8 \
/nologo
LINK_ARGS = /MACHINE:X64 /NOLOGO
main:main.cpp gkapp.cpp gkdialog.cpp
@cl /c $(CL_ARGS) /Fo:main.obj $(INCLUDES) main.cpp
@cl /c $(CL_ARGS) /Fo:app.obj $(INCLUDES) gkapp.cpp
@cl /c $(CL_ARGS) /Fo:dialog.obj $(INCLUDES) gkdialog.cpp
@cl /c $(CL_ARGS) /Fo:moc_dialog.obj $(INCLUDES) moc_gkdialog.cpp
@link $(LINK_ARGS) $(LIBS) /OUT:main.exe main.obj app.obj dialog.obj moc_dialog.obj
clean:
@del *.exe *.obj *.exp 2>/Nul
uic:gk.ui
@uic -o gk.h gk.ui
moc:
@moc -o moc_gkdialog.cpp gkdialog.h
Qt画图
- Qt应用
QApplication
| - QDialog
-
绘制事件
完成绘图编程模式
QPainter
-
键盘事件
- 通过键盘控制=图形的活动
- 附thdialog.cpp(内含绘制和键盘事件跟踪的代码语句):
#include "thdialog.h"
#include <iostream>
#include <QtGui/QPainter>
THDialog::THDialog(QWidget *parent):
QDialog(parent),
fish(new THFish(100, 100)){
}
THDialog::~THDialog(){
}
// override绘制函数
void THDialog::paintEvent(QPaintEvent *e){
// 在这里完成绘制工作
QPainter painter(this);
fish->showFish(&painter);
}
void THDialog::keyPressEvent(QKeyEvent *e){
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_W){
std::cout << "up" << std::endl;
fish->changeDir(90);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_S){
std::cout << "down" << std::endl;
fish->changeDir(270);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_A){
// std::cout << "left" << std::endl;
fish->changeDir(180);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_D){
// std::cout << "right" << std::endl;
fish->changeDir(0);
fish->swim();
}
this->repaint();
}
Qt多线程
使用线程完成数据同步处理
-
通信机制:信号与槽
- 定义信号
- 发送信号
- 绑定信号到目标
- 目标需要有一个slot函数处理信息
绘制刷新实现动画
- 附thdialog.cpp(内含多线程绘制代码语句):
#include "thdialog.h"
#include <iostream>
#include <QtGui/QPainter>
THDialog::THDialog(QWidget *parent):
QDialog(parent),
fish(new THFish(100, 100)),
fish2(new THFish(200, 100)){
QObject::connect(fish, SIGNAL(sign_open()), this, SLOT(repaint()));
QObject::connect(fish2, SIGNAL(sign_open()), this, SLOT(repaint()));
fish->start(); // 线程启动
fish2->start();
}
THDialog::~THDialog(){
}
// override绘制函数
void THDialog::paintEvent(QPaintEvent *e){
// 在这里完成绘制工作
QPainter painter(this);
fish->showFish(&painter);
fish2->showFish(&painter);
}
void THDialog::keyPressEvent(QKeyEvent *e){
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_W){
std::cout << "up" << std::endl;
fish->changeDir(90);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_S){
std::cout << "down" << std::endl;
fish->changeDir(270);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_A){
// std::cout << "left" << std::endl;
fish->changeDir(180);
fish->swim();
}
if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_D){
// std::cout << "right" << std::endl;
fish->changeDir(0);
fish->swim();
}
this->repaint();
}
作业:使用Qt绘制图型
提交作业:
- thapp.h代码:
#ifndef TH_APP_H
#define TH_APP_H
#include <QtWidgets/QApplication>
#include "thdialog.h"
class THApp : public QApplication{
public:
THApp(int argc, char **argv);
~THApp();
private:
THDialog *dlg;
};
#endif
- thapp.cpp代码:
#include "thapp.h"
THApp::THApp(int argc, char **argv):
QApplication(argc, argv),
dlg(new THDialog()){
dlg->resize(600, 400);
// 完成其他初始化
dlg->show();
}
THApp::~THApp(){
}
- thdialog.h代码:
#ifndef TH_DIALOG_H
#define TH_DIALOG_H
#include <QtWidgets/QDialog>
#include <QtGui/QKeyEvent>
class THDialog: public QDialog{
public:
THDialog(QWidget *parent=0);
~THDialog();
public:
virtual void paintEvent(QPaintEvent *e);
};
#endif
- thdialog.cpp代码:
#include "thdialog.h"
#include <iostream>
#include <QtGui/QPainter>
THDialog::THDialog(QWidget *parent):
QDialog(parent){
}
THDialog::~THDialog(){
}
// override绘制函数
void THDialog::paintEvent(QPaintEvent *e){
// 在这里完成绘制工作
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.save();
QPainterPath path;
path.moveTo(80,320);
path.cubicTo(200,80,320,80,480,320);
painter.setPen(QPen(Qt::black,8));
painter.drawPath(path);
painter.restore();
painter.save();
painter.translate(100,0);
QPointF baseline(200,200);
QLinearGradient myGradinet(50,100,300,350);
myGradinet.setColorAt(0.0,Qt::white);
myGradinet.setColorAt(0.2,Qt::green);
myGradinet.setColorAt(1.0,Qt::black);
QPainterPath myPath;
myPath.addText(baseline,QFont("Arial", 25),tr("Qt"));
painter.setPen(QPen(Qt::yellow,1));
painter.setFont(QFont("Arial", 25));
painter.setBrush(myGradinet);
painter.drawPath(myPath);
painter.restore();
}
- 编译脚本:
INCLUDES = /I "D:\Qt\Qt5.13.0\5.13.0\msvc2017_64\include"
LIBS = /LIBPATH:"D:\Qt\Qt5.13.0\5.13.0\msvc2017_64\lib" \
/DYNAMICBASE \
"Qt5Widgetsd.lib" \
"Qt5Guid.lib" \
"Qt5Cored.lib"
CL_ARGS = /EHsc \
/MDd \
/source-charset:utf-8 \
/execution-charset:utf-8 \
/nologo
LINK_ARGS = /MACHINE:X64 /NOLOGO
main:main.cpp thapp.cpp thdialog.cpp
@cl /c $(CL_ARGS) /Fo:main.obj $(INCLUDES) main.cpp
@cl /c $(CL_ARGS) /Fo:app.obj $(INCLUDES) thapp.cpp
@cl /c $(CL_ARGS) /Fo:dialog.obj $(INCLUDES) thdialog.cpp
@link $(LINK_ARGS) $(LIBS) /OUT:main.exe main.obj app.obj dialog.obj
clean:
@del *.exe *.obj *.exp 2>/Nul
-
绘图如下: