QT --- 初识QT

一、通过代码构建helloworld界面

一般通过代码来构造界面的时候,通常会把构造界面的代码放到Widget/MainWindow的构造函数中。

Qt中每个类都有对应同名的头文件

上古时期,Qt用的是这种风格的文件。1998年之后,C++标准成立了,C++98标准。规定,包含头文件,统一使用#include<cstdio>代替原有的#include<stdio.h>

Lable叫做标签,界面上一个用来显示内容字符串的控件,创建对象的时候,可以直接堆上创建,也可以在栈上创建。跟推荐在堆上创建对象。

this,给当前这个label对象,指定一个"父对象" this 就是Widget w。给这个对象里面设置一个文本,设置控件中,要显示的文本是啥。

Qt诞生于1991年,C++还没有形成标准,C++更没有标准库这样的改变了。当时,如何表示一个字符串,可以使用C风格字符串,也可以使用C++的string。Qt为了让自己的开发能变得的顺畅,就自己发明了一套轮子,搞了一系列的基础类,来支持Qt的开发,

很多年之后,上述这些容器等内容,已经打磨的很好了,形成了C++标准。

很显然,这些已经引入的Qt自己包装好的这些容器类,也不可能删了。就只能和现有的标准库中的容器类共存了。后续的代码中,还会经常见到QString这样的一些东西,而很少见到std::string Qstring和std::string之间也能很方便的相互转换。

在QString中也提供了C风格字符串作为参数的构造函数,不显示构造QString,上述代码中,C风格字符串也会隐式构造程QString对象。QString对应的头文件,已经被很多Qt内置的其他类都包含了。所以我们不需要显式包含QString头文件。通过代码创建QLable默认式在左上角。如果想放到其他的位置,也是可以的。

代码:

二、内存泄露问题的讨论。

这个代码,new了对象之后,咋没有delete呢?不delete不就出现内存泄露了吗?

关注内存泄漏,是要融入到DNA中的事情,内存泄露是一个非常可怕的事情。

三、对象树

上面的代码在QT中不会产生内存泄漏,lable对象会适合的时候被析构释放 ~~ 虽然没有手动写delete,确实能释放。之所以能够把对象释放掉,主要是因为把这个对象挂到了对象树上。前端开发也涉及到了类似的对象树(DOM),本质上也是一个树形结构(N叉树),通过树形结构把界面上的各种元素组织起来。Qt中也有一个对象书树也是N叉树,把界面上的各种元素组织起来了。

通过这个树形结构,就把界面上要显示的这些控件对象都组织起来了。使用对象树,把这些内容组织起来,最主要的目的,就是为了能够在合适的时机,把这些对象统一进行释放。

如果树上的这些对象,统一销毁时最好不过的,如果某个对象提前销毁,此时就会导致对应的控件就在界面上不存在了。此处通过new的方式创建对象,也就是为了把这个对象生命周期,交给Qt的对象树来统一管理。

当把对象改成在栈上创建,此时就可以看到,运行起来的程序无法显示出hello world此时lable对象随着构造函数的结束就销毁了。

我们来验证一下对象树的存在性。可以用自己编写的类来实现验证。创建自定义类,最主要的目的,是自定义一个析构函数,在析构函数中,完成打印,方便我们看到最终的自动销毁对象的结果!!

cpp 复制代码
#include <QLabel>
class MyLable : public QLabel
{
public:
    MyLable(QWidget* parent);
    ~MyLable();
};

#endif // MYLABLE_H
cpp 复制代码
#include "mylable.h"
#include <iostream>
MyLable::MyLable(QWidget* parent)
    :QLabel(parent)
{}

MyLable::~MyLable()
{
    std::cout << "对象被销毁" << std::endl;
}

日志有:说明析构函数是执行了,虽然没有手动delete,但是由于把MyLable挂到了对象树上。此时窗口被销毁的时候,就会自动销毁对象树中的所有对象。MyLable的析构时执行到了的。

预期打印的时被销毁三个中文,但是实际的显示效果,出现了乱码。原因就是编码方式不匹配。

关于乱码问题的解释:

在计算机中,一个汉字,占多少个字节?

针对这个问题,只要你回答出具体的数字,就一定时错的!。前提条件:当前中文编码使用的是哪种方式(字符集)。计算机种存储的都是二进制码。英文字母是通过ASCII码表,规定了每个字符,都有一个对应的数字来表示。只是表示英文,一个字节足够了,毕竟英文字母数目非常有限。

中文一共多少个汉字呢?日常的常用字,大概是4000多个。算上各种生僻字,总数差不多6W多个。我们仍然使用一个大表格,给每个汉字,分配一个整数即可。那么具体这个表格是什么样子。具体每个数字都使用哪个数字表示这个事情就不一定了,字符集表示汉字的字符集,其实是有很多种的不同的字符集,表示同一个汉字,使用的数字并不相同!

目前表示汉字字符集,主要两种方式

1、GBK中国大陆 使用2个字节表示一个汉字。

2、UTF-8变长编码,表示一个符号,使用的字节数有变化。2 - 4字节,但是在utf-8中,一个汉字,一般是3个字节。Linux中默认就是utf-8。

如果你字符串本身是utf-8编码的,但是终端是按照GBK的方式解析的显示的,此时就会出现乱码,(拿着utf-8这里的数值,去查询gbk的码表)此时就会出现乱码了。

Qt Creator内置的终端是utf-8的方式来显示字符串吗?

这个终端好像不能设置字符编码,既然出现乱码了,这里不是utf-8了。当前表示中文,主流的方式,还得是utf-8,它支持各种语言文字。Qt中有一个东西,QString,是可以帮助我们自动的处理编码方式的,不只是QString,Qt也提供了专门用来打印日志的工具,也能自动处理编码方式。Qt中提供了QDebug工具,借助这个工具,就可以完成打印日志的过程,很好的处理字符编码了。

QDebug是Qt中的类,又不会直接使用这个类。

这个宏,封装了QDebug对象,直接使用qDebug()这个东西就可以当作cout来使用。后续在Qt中,如果想通过打印日志的方式,输出一些调试信息,都有限使用qDebug,使用qDebug还有一个好处,打印的调试日志,是可以统一进行关闭的。如果你的程序发布给用户,不希望用户看到这些日志的!qDebug可以通过编译开关,来实现一键式关闭。

小结:

1、认识了QLabel类,能够在界面上显示字符串。

通过setText来设置的,参数QString。这是一个历史原因

2、内存泄漏/文件资源泄漏。

3、对象树,Qt中通过对象树,来统一的释放界面的控件对象。Qt还是推荐使用new的方式在堆上创建对象,通过对象树,统一释放资源。创建对象的时候,在构造函数中,指定父对象此时才会挂到对象树上。如果你的对象没有挂到对象树上,就必须记得手动释放。

4、通过继承自Qt内置的类,就可以达到对现有控件进行功能扩展的效果。Qt内置的QLabel,没法看到销毁过程,为了看清楚我们就创建类MyLable,继承QLabel重写析构函数。在析构函数,加上日志,直观的观察到对象释放的过程了。也可以重写控件中的任何功能,不仅仅是构造,达到功能扩展的目的,面向对象继承本质上就对现有的代码进行扩展。我们要学习,也要思考需要悟道。

5、乱码问题和字符集的问题。在MySQL中很多地方都涉及到。

6、如何在Qt中打印日志,作为调试信息。使用Qt中推荐的qDebug()完成日志的打印。

四、使用输入框实现helloworld

完成一个helloworld可以通过很多种控件来实现。

使用编辑框来完成hello world

单行编辑框 QLineEdit

多行编辑框 QTextEdit

代码:

使用纯代码的方式实现:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QLineEdit>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QLineEdit* qline = new QLineEdit(this);
    qline->setText("hello world");
}

Widget::~Widget()
{
    delete ui;
}

五、使用按钮实现helloworld

使用pushbutton就是一个普通的按钮。按钮可以设置一个点击效果。Qt中的信号槽机制。本质就是给按钮的点击操作关联上一个处理函数,当用户点击的时候,就会执行这个处理函数。、

需要使用connect(),Linux网络编程也学过这个函数,这个函数用来建立连接,然后才能读写数据。Qt中的connect是QObject这个类提供的静态函数,这个函数的作用就是连接信号和槽。和TCP的建立连接操作没有任何的关系。第一个参数谁发的信号,ui->pushbutton,访问到form file(ui文件)中创建的控件!此时就会给这个控件分配一个objectName属性。这个属性的值,要求是在界面上唯一的。不能和别人重复。会自动生成一个,也可以手动修改换成别的。

第二个参数传入,关联到父类中,然后

点击按钮的时候就会自动触发这个信号。

this 谁来处理这个信号,第四个参数具体怎么处理。

代码:

cpp 复制代码
#include "ui_widget.h"
#include <QObject>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton,&QPushButton::clicked,this,&Widget::Handler);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::Handler()
{
    if(ui->pushButton->text() == "hello world")
        ui->pushButton->setText("hello qt");
    else
        ui->pushButton->setText("hello world");
}

使用纯代码的方式实现。

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QObject>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //使用纯代码的方式实现helloworld,信号槽机制
    //button我们定义为了Widget类中的私有成员。
    button = new QPushButton(this);
    button->setText("helloworld");
    connect(button,&QPushButton::clicked,this,&Widget::Handler);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::Handler()
{
    if(button->text() == "helloworld")
    {
        button->setText("hello qt");
    }
    else
    {
        button->setText("helloworld");
    }
}

左边为纯代码创建,右边为ui创建

对于纯代码版本,按钮对象是咱们自己new的。为了保证其他函数中能够访问这个变量,就需要把按钮对象设定为Widget类的成员变量。右边的按钮对象不需要咱们自己new,new对象的操作已经被Qt自动生成了。而且这个按钮对象,已经作为ui对象里的一个成员变量了。也无需作为Widget的成员。实际开发中,这两种都很主要,难分主次!如果当前程序界面,界面内容是比较固定的,此时就会以图形化的方式来构造界面。但是如果你的程序界面,经常要动态变化,此时就会以代码的方式来构造界面。这两种方式,哪种方便就用哪种。而且两种方式也可以配合使用,后面这两种方式都会涉及到。

六、Qt中的命名规范

给变量/函数/文件/类 起名字,是非常有讲究的。

1、起的名字要有描述性,不要使用abc,xyz这种比较无规律的名字来描述。

2、如果名字比较长,有多个单词构成,就需要使用适当的方式来进行区分不同的单词。Qt中偏好使用大写字母来进行单词分割的。这种命名法叫做驼峰命名法。一种是小驼峰给变量命名,一种是大驼峰给类命名。我们入乡随俗。你公司中使用的项目,使用的驼峰/蛇形/其他。

七、认识Qt窗口坐标体系

坐标体系:以左上角为原点(0,0),右边为x,向下为y。平面直角坐标系(笛卡尔坐标系)

给Qt的某个控件,设置位置,就需要指定坐标,对于这个控件来说坐标系原点就是相对于父窗口的

代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QObject>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //使用纯代码的方式实现helloworld,信号槽机制
    //button我们定义为了Widget类中的私有成员。
    button = new QPushButton(this);
    button->setText("helloworld");
    connect(button,&QPushButton::clicked,this,&Widget::Handler);
    button->move(200,300);

}

可以使用move这个函数来移动控件的位置。坐标背后的单位:像素。

相关推荐
用户805533698035 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner5 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz10 天前
QML Hello World 入门示例
qt
xcyxiner13 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner14 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript