hello,家人们,我们将进入qt初识的学习,好了,废话不多讲,开干!
[1:Qt实现Hello World(标签实现)](#1:Qt实现Hello World(标签实现))
[3:Qt实现Hello World(按钮实现)](#3:Qt实现Hello World(按钮实现))
[5.2:Qt Creator中的快捷键](#5.2:Qt Creator中的快捷键)
1:Qt实现Hello World(标签实现)
在C语言和C++的学习中,我们都是以实现hello world为入门,那么同理,在Qt中也是如此,只不过
在Qt中有两种方式实现hello world
- 纯代码方式
- 图形化方式
1.1:图形化方式
1.双击:"widget.ui "⽂件

2:拖拽 "标签" ⾄ UI 设计界⾯中,并双击修改标签内容;

3:构建并且运行


1.2:纯代码方式
1.2.1:Widget.h
cpp
#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();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
1.2.1:Widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include "QLabel"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//通过new一个对象
QLabel *label =new QLabel(this);
label->setText("hello world");
}
Widget::~Widget()
{
delete ui;
}
1.2.1:Main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

2:对象树
cpp
#include "widget.h"
#include "ui_widget.h"
#include "QLabel"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//通过new一个对象
QLabel *label =new QLabel(this);
label->setText("hello world");
}
Widget::~Widget()
{
delete ui;
}
在刚刚纯代码实现hello world,我们可以看到,我们new了一个对象,但是没有对其delete,有的uu就会有疑问,这样子不会发生内存泄漏吗
上述代码,在Qt中不会发生内存泄漏,label对象会在合适的时候被析构释放,之所以能够把对象释放掉,主要是因为把这个对象挂在了对象树上.
我们可以看到,在Qt中创建对象的时候,我们为其提供了一个this指针,而这个this指针就是Parent对象指针,那么这个parent是用来干嘛的呢QObject 是以对象树的形式组织起来的.
- 当创建⼀个 QObject 对象时,会看到 QObject 的构造函数接收⼀个 QObject 指针作为参数,这个参数就是 parent,也就是父对象指针。
- 这相当于,在创建 QObject 对象时,可以提供⼀个其⽗对象,我们创建的这个 QObject 对象会⾃动添加到其⽗对象的 children() 列表.
- 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类)
这种机制在 GUI 程序设计中相当有⽤。例如,⼀个按钮有⼀个 QShortcut(快捷键)对象作为其子对象。当删除按钮的时候,这个快捷键理应被删除。这是合理的.
QWidget是能够在屏幕上显⽰的⼀切组件的父类。
QWidget 继承自QObject ,因此也继承了这种对象树关系。⼀个孩⼦⾃动地成为⽗组件的⼀个子组件。因此,它会显⽰在父组件的坐标系统中,被⽗组件的边界剪裁。例如,当⽤⼾关闭⼀个对话框的时候,应⽤程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该⼀起被删除。事实就是如此,因为这些都是对话框的⼦组件.
当然,我们也可以自己删除⼦对象,它们会⾃动从其⽗对象列表中删除 。 ⽐如,当我们删除了⼀个⼯具栏时,其所在的主窗⼝会⾃动将该⼯具栏从其⼦对象列表中删除,并且⾃动调整屏幕显示.
Qt 引⼊对象树的概念,在⼀定程度上解决了内存问题.当⼀个 QObject 对象在堆上创建的时候,Qt 会同时为其创建⼀个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的.
任何对象树中的 QObject 对象 delete 的时候,如果这个对象有 parent,则⾃动将其从 parent 的children() 列表中删除;如果有孩⼦,则⾃动 delete 每⼀个孩⼦。Qt 保证没有 QObject 会被delete 两次,这是由析构顺序决定的。
所以,在Qt中尽量在构造的时候就指定 parent 对象,并且⼤胆在堆上创建。

使用对象树,将这些内容组织起来,最主要的目的,就是为了能够在合适的时机(窗口关闭/销毁),把这些对象统一进行释放.
为了验证label对象会在合适的时候被析构释放,博主将通过下面这段代码来带uu们观察下
2.1:代码示例
1:选中⼯程名,⿏标右键 -------> "add new..."(或 "添加新文件" )


3、选择 "choose...",弹出如下界⾯;

4、点击 "下⼀步",弹出如下对话框;

5、点击 "完成" 之后,手动创建类的头文件以及源文件会自动添加到目标程中;

2.1.1:Mylabel.h
cpp
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QWidget>
#include "QLabel"
//修改继承的父类
class MyLabel : public QLabel
{
public:
//构造函数使用带QWidget * parent,这样子才能确保咱们自己的对象能够加到对象树上.
MyLabel(QWidget * parent);
~MyLabel();
};
#endif // MYLABEL_H
2.1.2:widget.h
cpp
#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();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
2.1.2:widget.cpp
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;
}
2.1.2:Mylabel.cpp
cpp
#include "mylabel.h"
#include <iostream>
//图穷匕见,匕就是这个调用父类构造函数
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
}
MyLabel::~MyLabel()
{
std::cout<<"MyLabel 被销毁了"<<std::endl;
}
2.1.2:main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
我们创建自定义的类,最主要的目的是,是自定义一个析构函数,在析构函数中,完成打印,方便咱们看到最终的自动销毁对象的效果.
6:编写好代码后,编译并运行
7:关闭对话框
- 通过观察日志,我们可以发现,当关闭对话框时,有日志打印,说明析构函数是执行了,虽然没有手动delete,但是由于把Mylabel挂到了对象树上,此时窗口被销毁的时候,就会自动销毁对象树中的所有对象.MyLabel的析构函数是执行到了.
- 但是我们可以观察到,预期打印的是MyLabel被销毁了,但是实际显示的效果,出现了乱码,乱码这个问题出现的原因有且只有一个,那就是编码方式不匹配!!!
2.2:乱码问题的解释
在计算机中,一个汉字占几个字节,针对这个问题,很多uu会回答2个字节,但是是错的,因为只要回答一个具体的数字就一定是错的,因为少了前提条件:当前环境的中文编码使用的是哪个方式.
目前,表示汉字字符集,主要是两种方式.
- GBK:使用2个字节表示一个汉字,Windows简体中文版,默认的字符集就是GBK.
- UTF-8/uft8:变长编码------->表示一个符号,使用的字节数有会变化,2~4,但是在utf8中,一个汉字,一般是3个字节,Linux中默认就是uft-8的编码方式
所以如果你的字符串本身是uft8编码的,但是控制台(终端)是按照gbk的方式来解析显示的,此时就会出现乱码.

我们通过观察发现,mylabel.cpp的文件的编码方式是utf-8,如果显示的是ANSI,说明该文件是GBK编码,既然出现了乱码,那说明此时Qt Creator内置的终端不是utf-8的编码方式来显示字符串,那么我们该如何解决这个问题呢?
2.2.1:乱码问题的解决
Qt中提供了一个qDebug()工具,借助这个,就可以完成打印日志的过程,很好地处理字符编码.(不需要程序员关注了,内部帮我们处理好了)
2.2.1.1:mylabel.cpp
cpp
#include "mylabel.h"
#include <iostream>
#include <QDebug>
//图穷匕见,匕就是这个调用父类构造函数
MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
}
MyLabel::~MyLabel()
{
//这里是一个宏,封装了QDebug对象,直接使用qDebug()这个东西就可以当作cout来使用
qDebug()<<"MyLabel 被销毁了";
}

3:Qt实现Hello World(按钮实现)
3.1:图形化方式
(1)双击:" widget.ui " ⽂件;

(2)拖拽控件⾄ ui 界⾯窗⼝并修改内容;

(3)构建并运⾏,效果如下所⽰:

3.2:纯代码方式
3.2.1:widget.h
cpp
#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();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
3.2.1:widget.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
3.2.1:main.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include "QPushButton"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//创建按钮,传入this指针将其挂在对象树上
QPushButton * Button = new QPushButton(this);
Button->setText("Hello World");
}
Widget::~Widget()
{
delete ui;
}
4:小结
有的uu会好奇,实际开发中,是通过代码的方式构造界面还是通过图形化界面的昂视构造界面为主,其实这两种都很主要,很难区分主次
- 如果你当前的程序界面,界面内容是比较固定的,此时就会以图形化的方式来构造界面.
- 如果你当前的程序界面,界面内容是动态变化的,此时就会以纯代码的方式来构造界面.
因此,这两种方式,哪种方便就用哪种,而且这两种方式也可以搭配使用.
5:Qt编程注意事项
5.1:Qt中的命名规范
- 类名:首字母大写,单词和单词之间首字母大写;
- 函数名及变量名:⾸字母小写,单词和单词之间⾸字母大写;
5.2:Qt Creator中的快捷键
- 注释:ctrl + /
- 运⾏:ctrl + R
- 编译:ctrl + B
- 字体缩放:ctrl + ⿏标滑轮
- 查找:ctrl + F
- 整⾏移动:ctrl + shift + ⬆/⬇
- 帮助⽂档:F1
- ⾃动对⻬:ctrl + i;
- 同名之间的 .h 和 .cpp 的切换:F4
- ⽣成函数声明的对应定义: alt + enter
5.3:使用帮助文档
打开帮助⽂档有三种⽅式. 实际编程中使⽤哪种都可以
- 光标放到要查询的类名/⽅法名上, 直接按 F1

2:Qt Creator 左侧边栏中直接⽤⿏标单击 "帮助" 按钮.


3:找到 Qt Creator 的安装路径,在 "bin" ⽂件夹下找到 assistant.exe,双击打开;

6:Qt中的坐标体系
坐标体系,以左上角为原点(0,0),X向右增加,Y向下增加

对于嵌套窗⼝,其坐标是相对于父窗口来说的

6.1:代码示例
6.1.1:Widget.h
cpp
#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();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
6.1.1:Widget.cpp
cpp
#include "widget.h"
#include "ui_widget.h"
#include "QPushButton"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton * btn1 = new QPushButton(this);
btn1->setText("hello world");
btn1->move(200,300);
QPushButton * btn2 = new QPushButton(this);
btn2->setText("Hello QT");
}
Widget::~Widget()
{
delete ui;
}
6.1.1:Main.cpp
cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}

好啦,uu们,Qt开发初识滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误~


