【Qt】6.Qt对象树
文章目录
- 1.自定义析构函数验证对象树
- 2. 字符编码问题
- 3. 使用输入框实现helloworld
- 4. 使用按钮实现helloworld
- 5. 使用纯代码方式实现按钮helloworld
1.自定义析构函数验证对象树
创建文件:mylabel.cpp
点击下一步,下一步。
可以看出Qt
没有把头文件主动包含,我们要手动来添加。
在Qt Creator中,可以通过F4切换头文件和对应的.cpp
文件。
创建自定义的类,最主要的目的,是自定义一个析构函数,在析构函数中,完成打印,方便咱们看到最终的自动销毁对象的效果。
我们可以光标放到~Mylabel();
上,然后点击alt+enter
,双击在mylabel.cpp添加定义
,就会自定义跳转到cpp
文件并生成对应基本的析构函数。
mylable.h
#ifndef MYLABEL_H
#define MYLABEL_H
#include <QLabel>class Mylabel : public QLabel
{
public://构造函数使用带QWidget*版本的,这样才能确保咱们自己的对象能加到对象树上Mylabel(QWidget* parent);~Mylabel();
};#endif // MYLABEL_H
mylable.cpp
#include "mylabel.h"
#include <iostream>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);//使用自己定义的Mylabell代替原来的QLabel,所谓的继承本质上是扩展,保持原有功能不变的基础上,给对象扩展出一个析构函数//通过这个析构函数,打印一个自定义的日志,方便咱们观察程序运行效果Mylabel* label=new Mylabel(this);label->setText("hello world");
}Widget::~Widget()
{delete ui;
}
运行:
关闭:
日志,有。
说明析构函数是执行了。虽然没有手动
delete
,但是由于把MyLabel
挂到了对象树上。此时窗口被销毁的时候,就会自动销毁对象树中的所有对象
MyLabel
的析构是执行到了。
2. 字符编码问题
预期打印的是“被销毁”三个中文,但是实际的显示效果,出现了乱码。
乱码问题出现的原因,有且只有一个(不局限于C++)就是编码方式不匹配。
如果你字符串本身是
utf8
编码的,但是终端(控制台)是按照gbk
的方式来解析显示的,此时就会出现乱码。(拿着
utf8
这里的数值,去查询gbk
的码表),此时就会出现乱码了。
那么怎么看文件编码呢?
我们右键点击在Explorer中查看
使用记事本打开
记事本右下角有编码,就算没有也可以点击文件->另存为
当前表示中文,主流的方式,还得是
utf8
(支持各种语言文字)
Qt
中有一个东西,QString
,是可以帮助我们自动的处理编码方式的不止是
QString
,Qt
也提供了专门用来打印日志的工具,也能自动处理编码方式
std::cout<<"MyLabel被销毁!"<<std::endl;
Qt
中提供了一个qDebug()
工具,借助这个,就可以完成打印日志的过程,很好的处理字符编码(不需要程序员关注了,内部帮咱们搞好了)
运行,关闭:
后续再
Qt
中,如果想通过打印日志的方式,输出一些调试信息,都优先使用qDebug
。虽然使用cout
也行,但是cout
对于编码的处理不太好,在windows
上容易出现乱码(如果是Linux
使用Qt Creator
,一般就没事了,Linux
默认的编码一般都是utf8
)
使用
qDebug
,还有一个好处:打印的调试日志,是可以统一进行关闭的。输出的日志,是开发阶段,调试程序的时候,使用的。如果你的程序发布给用户,不希望用户看到这些日志的,
qDebug
可以通过编译开关,来实现一键式关闭。
那我之前调试程序,都是用调试器
VS/gdb
,为啥要打印日志调试呢?调试器很多时候是有局限性的,是无法使用的。
假设当前
bug
是一个概率性的bug
,出现的概率是1%
甚至更小,要想调试,无法使用调试器了。使用日志,就可以很好的解决这种问题。
无论是哪种方式,本质上都是观察程序执行的中间过程和中间结果。
3. 使用输入框实现helloworld
4. 使用按钮实现helloworld
使用普通按钮
把文字替换,运行
不过现在点击没有变化,想要有变化可以依赖后面的
信号槽
。本质就是给按钮的点击操作,关联上一个处理函数。当用户点击的时候,就会执行这个处理函数。
Linux
网络编程中,也学过一个函数,叫做connect
。这个函数用来给
TCPsocket
建立连接的,写TCP
客户端的时候,就需要先建立连接,然后才能读写数据。
Qt
中的connect
是QObject
这个类提供的静态函数,作用是“连接信号和槽”。和TCP
的建立连接操作没有任何关系!!
widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handleClick();private:Ui::Widget *ui;
};
#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);connect(ui->pushButton, // 信号发送者:界面上的那个按钮(谁发出了信号)&QPushButton::clicked, // 发送的信号:按钮被“点击”这个动作(发出了什么信号)this, // 信号接收者:当前的Widget窗口对象(谁来处理这个信号)&Widget::handleClick); // 接收信号后要执行的槽函数:我们自己写的handleClick函数(怎么处理这个信号)
}Widget::~Widget()
{// 释放ui指针所指向的内存// 因为在构造函数里用了 new Ui::Widget,所以这里需要手动delete来避免内存泄漏delete ui;
}void Widget::handleClick()
{//当按钮被点击之后,就把按钮中的文本进行切换if(ui->pushButton->text()==QString("hello world")){ui->pushButton->setText("hello qt");}else{ui->pushButton->setText("hello world");}}
按钮点击一次切换一次
可以打开编译后生成文件夹里的.ui
文件:
ui_widget.h
中自动生成的代码里就会包含一个pushButton
的成员变量,这个变量的名字,就是根据objectName
的值来确定的。
我们可以验证一下:
先改一下 里面的数值为mybutton
此时打开之前的.ui
文件
5. 使用纯代码方式实现按钮helloworld
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QPushButton>#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic: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"
#include <QPushButton>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
一个myButton
,而且为了保证其他函数中能够访问到这个变量,就需要把按钮对象设定为Widget
类的成员变量.图形化实现:此时按钮对象,不需要咱们自己
new
。new
对象的操作已经是被Qt
自动生成了。而且这个按钮对象,已经作为ui
对象里的一个成员变量了。也无需作为Widget
的成员。
实际开发中,是通过代码的方式构造界面,为主,还是通过图形化界面的方式构造界面为主??
这两种,都很主要,难分主次。
- 如果你当前程序界面,界面内容是比较固定的,此时就会以图形化的方式来构造界面
- 但是如果你的程序界面,经常要动态变化,此时就会以代码的方式来构造界面
这两种方式,哪种方便就用哪种,而且这两种方式也可以配合使用。