1. Qt项目创建
模板介绍:
选择路径:
路径不要带中文!!!
中文会影响项目构建
构建系统:
通过Qt写的程序,设计一系列源编程技术,通过代码来生成代码
qmake
是Qt老牌的构建工具
CMake
并非Qt专属,很多开源项目会采用CMake
Qbs
是新一代的Qt构建工具,但实际用的人非常少,Qt官方没怎么维护了
Details
:
Qt Creator创建项目的时候,会自动生成代码,生成的代码,就包含一个类,此处选择自动生产的类的父类
Widget
就是自动生成的类名,QWidget
是生成类继承的父类
Qt中内置的类,都是以
Q
为前缀开头的
这里生成的文件名和类名是关联的,虽然不是强制要求,但还是建议弄成一样的
Qt中创建图形化界面程序的方式有2种:
- 直接通过C++代码创建
- 通过
form file
以图形化的方式生成界面
勾选生成form file
文件,就可以用Qt Designer 或者Qt Creator来编辑这个ui
文件了,从而以图形化的方式快速生成图形界面
选择翻译文件:
此时不关注,这个是和国际化相关的
选择编译器的Qt SDK构建代码:
下载时只勾选了MinGW,就只有一个选项
功能管理工具:
这里可选择是否加入到版本控制器里面,想加就加上即可
2. 认识项目代码
main.cpp
C++的程序需要有main
函数作为入口函数,此处的main函数是自动生成的。
widget.h
这个文件里面就是Widget
类的声明
widget.cpp
widget.ui
双击这个ui
文件,此时Qt Creator就会调用Qt Designer,打开ui文件,图形化界面编辑器
再点击左侧的“编辑”按钮,此时显式的内容,就是ui
文件本体了
这个文件格式叫
xml
格式
xml
格式这里的标签,有哪些标签,表示什么含义,是由程序员自己定义的这里的标签具体含义,我们不需要太关注,只需知道
ui
文件本质上是xml
即可
Qt中使用xml
文件就是描述程序界面是什么样的,进一步的qmake
会调用相关工具,依据这个xml
文件生成一些C++代码,从而把界面完整构造出来
.pro
.pro
文件是Qt项目的工程文件,也是qmake
构建的重要依据
qmake
搭配.pro
起到的作用和Linux当中的makefile作用类似,Qt Creator把这个过程都封装好了,不需要过多关注,只需点击安装按钮即可
构建过程生成的中间文件
运行程序一次之后,在项目目录并列的地方,会多出来一个目录,这个目录里面就是该项目运行过程中生成的一些临时文件
3. Hello World程序
有2种方式实现显式hello world
:
- 图形化的方式,在页面创建控件
- 纯代码方式创建
图形化方式创建
按照上面流程,重新创建一个名为HelloWorld
的项目
双击widget.ui
,进入Designer
这里拖拽Label
控件到显示页面,然后可以修改里面的内容
点左下角运行之后,就显示出了一个hello world
的页面
这里main.cpp
、widget.h
这些文件并不会发生变化,发生变化的是widget.ui
查看中间文件里的ui_widget.h
文件:
void setupUi(QWidget *Widget)
{
if (Widget->objectName().isEmpty())
Widget->setObjectName(QString::fromUtf8("Widget"));
Widget->resize(800, 600);
label = new QLabel(Widget);
label->setObjectName(QString::fromUtf8("label"));
label->setGeometry(QRect(340, 330, 161, 91));
retranslateUi(Widget);
QMetaObject::connectSlotsByName(Widget);
} // setupUi
这里就会多了一段创建label
对象的代码
代码方式创建
再次创建一个工程,名为HelloWorld_2
通过代码创建界面的时候,一般会把构造函数界面代码放到Widget
的构造函数当中
QLabel* label = new QLabel();
这里报错,找不到QLabel
的定义,这是因为没有包含这个类的头文件
Qt当中每个类都有一个对于的同名的头文件
Label
是界面上显示内容的字符串控件
在创建对象的时候,可以通过new的方式在堆上创建,也可以直接在栈上,一般推荐在堆上创建的方式
这里建议在创建的时候,给构造添一个this
参数,这表明给当前创建的对象,指定一个父对象
QLabel* label = new QLabel(this);
然后再调用setText()
方法往里面插入内容即可
void setText(const QString &);
这里参数类型是
QString
,这是因为Qt出的比C++早,早年间C++还没有自己的标准,Qt自己造了一套轮子,来支持Qt开发,里面包括但不限于:Qstring QVector QList QMap
之后C++标准出了,这些引入的Qt容器类,也没删,和现有的STL容器共存
可以显式写为:
label->setText(QString("hello world");
也可以直接写:
label->setText("hello world");
QString
中提供了C风格字符串参数作为构造函数不显式构造
QString
,C风格字符串也会隐式构造QString
对象
内存泄漏问题
这里new
之后,并没有delete
,在C++当中,内存泄漏是需要特别关注的事情
但是上面的代码,在Qt中不会引起内存泄漏,这是因为我们将其挂在了对象树上
编辑框方式创建
编辑框分为2种:
- 单行编辑框:
QLineEdit
- 多行编辑框:
QTextEdit
这里采用QLineEdit
作为演示:
运行之后,这个输入框也可以进行编辑:
也可以采用代码的方式:
#include "widget.h"
#include "ui_widget.h"
#include<QLineEdit>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QLineEdit* edit = new QLineEdit(this);
edit->text("hello world");
}
Widget::~Widget()
{
delete ui;
}
按钮方式创建
这个按钮是可以点击的,这涉及Qt的信号槽机制
本质就是给按钮的点击操作,关联上一个处理函数,用户点击的时候,就会执行这个处理函数
widget.h
:#ifndef WIDGET_H #define WIDGET_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); void handleClick(); private: Ui::Widget *ui; }; #endif // WIDGET_H
widget.cpp
:#include "widget.h" #include "ui_widget.h" #include<QLineEdit> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); //QLineEdit* edit = new QLineEdit(this); //edit->text("hello world"); // 访问到form file(ui文件)创建的控件 connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick); } Widget::~Widget() { delete ui; } void Widget::handleClick() { //点击之后,切换文本 if(ui->pushButton->text() == QString("Hello World")) { ui->pushButton->setText("Hello Qt"); } else { ui->pushButton->setText("Hello World"); } }
这个
connect
是QObject
这个类提供的静态函数,这个函数的作用就是连接信号和槽
ui->pushButton
:访问form file
(ui文件)中创建的控件
这个值会自动生成,也可以手动修改,但是要求在界面中是唯一的
qmake
在处理ui
文件的时候,就会根据这里的objectName
生成对应的C++代码
&QPushButton::clicked
:点击按钮pushButton
的时候,就会触发信号
this
:谁来处理这个信号
&Widget::handleClick
:具体信号怎么处理
这里
ui
能够找到objectName
这个属性,是因为qmake
编译的时候会自动生成,objectName
设置成什么值,变量名就叫什么:
通过代码创建:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QPushButton>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleClick();
private:
Ui::Widget *ui;
QPushButton* myButton;
};
#endif // WIDGET_H
widget.cpp
:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
myButton = new QPushButton(this);
myButton->setText("hello world");
connect(myButton, &QPushButton::clicked, this, &Widget::handleClick);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClick()
{
if(myButton->text() == QString("hello world"))
{
myButton->setText("hello qt");
}
else
{
myButton->setText("hello world");
}
}
对于纯代码版本,按钮对象是自己
new
的,而且为了保证其他函数能够访问到这个变量,就需要把这个按钮对象设定为widget
类的成员变量;对于图形化,不需要自己
new
,new
对象的操作被Qt
自动生成了,而且这个按钮对象,已经作为ui
对象里的成员变量了
4. 对象树
引入对象树原因
Qt中有一个对象树(N叉树),将界面的各种元素组织起来
通过这个树形结构,就将这些控件对象组织起来了,最主要的目的就是为了能够在合适的时间,将这些对象统一释放
如何理解“合适的时间”?
当窗口关闭/销毁的时候,这写对象就会被统一消耗,如果某个对象提前销毁,就会导致控件在界面上不存在
这就是为什么要通过
new
的方式创建对象,将对象的生命周期交给Qt的对象树进行统一管理
这里将对象在栈上创建,运行程序,
hello world
并没有显示,这是因为label
对象随着构造函数的结束就销毁了
对象树自动释放对象实验演示
上面的QLabel
是内置的一个对象,并不会显示输出析构信息。
我们可以自定义一个label
类,然后显式输出析构信息
mylabel.h
:
#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
class MyLabel : public QLabel
{
public:
MyLabel(QWidget* parent); //引入父元素
~MyLabel();
};
#endif // MYLABEL_H
mylabel.cpp
:
#include "mylabel.h"
#include<iostream>
#include<qDebug>
MyLabel::MyLabel(QWidget* parent) : QLabel(parent)
{
}
MyLabel::~MyLabel()
{
std::cout << "MyLabel 销毁" << std::endl;
}
widget.cpp
:
#include "widget.h"
#include "ui_widget.h"
#include"mylabel.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
MyLabel* label = new MyLabel(this);
label->setText("hello world");
}
Widget::~Widget()
{
delete ui;
}
运行之后发现,虽然没有手动delete,但是由于将MyLabel
挂到了对象树当中,销毁窗口的时候,就会自动销毁对象树中的所以对象
这里有一点,虽然输出了析构信息,但是中文部分是乱码的
5. 乱码问题
乱码问题的原因,有且只有一个:编码方式不匹配
计算机中,一个汉族占几个字节?
回答这个问题,先得知道当前中文编码采用的字符集。
英文采用
ASCII
码表,而汉族的字符集,有很多种,最主流的是两种:
GBK(中国大陆较多),使用2个字节表示一个汉字
Windows简体中文版采用的就是默认GBK
UTF-8/utf8,是一种边长编码,表示一个符合使用的字节数有变化,中文一般是3字节
查看编码网站:查看字符编码(UTF-8) (mytju.com)
我们查看当前文件的编码方式为utf8
Qt Creator出现乱码,就表明终端并不是utf8方式编码,但是也不好修改文件和终端的编码方式
要解决这个乱码问题,可以使用Qt内置的qDebug
工具,这可以很好的处理字符编码问题
后续想在Qt中通过打印日志的方式输出调试信息,优先使用qDebug
qDebug
可以通过编译开关,来实现一键式打开/关闭