2.QT初识-实现hello world|图形化方式|纯代码方式|对象树|乱码|编辑框|按钮|坐标系(C++)

实现helloworld

图形化方式

在界面上创建出一个控件,显示hello world
双击widge.ui文件,打开图形化界面
![[Pasted image 20250317220920.png]]

左侧底下有一个Label控件
![[Pasted image 20250317221017.png]]

将这个控件拖到主界面中
![[Pasted image 20250317221120.png]]

插入文本hello world
![[Pasted image 20250317221145.png]]

Qt Designer右上角,通过树形结构,显示出了当前界面都有哪些控件
![[Pasted image 20250317221215.png]]

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Widget</class>
 <widget class="QWidget" name="Widget">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Widget</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>280</x>
     <y>210</y>
     <width>201</width>
     <height>121</height>
    </rect>
   </property>
   <property name="text">
    <string>hello world</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

ui文件自动多了helloworld的一个label标签
![[Pasted image 20250317221703.png]]

刚才往界面上拖拽了一个QLabel控件
此时,ui文件的xml中就会多出来这一段代码
进一步,qmake就会在编译项目的时候,基于这个内容生成一段C++代码,通过这个C++代码构建出界面的内容

点击编译,多出hello world的内容
![[Pasted image 20250317223054.png]]

ui_widget.h,就是qmake根据xml文件生成的头文件,完成界面的初始化和创建label

    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(280, 210, 201, 121));
  
        retranslateUi(Widget);
  
        QMetaObject::connectSlotsByName(Widget);
    } // setupUi
纯代码方式

通过编写代码,在界面上创建控件,显示hello world
一般通过代码构造函数的时候,通常会把构造界面的代码放到widget的构造函数中
Qt中每个类都有一个对应的头文件
![[Pasted image 20250317224046.png]]

最早时候。Qt用的是qlabel.h
C++98标准,统一使用cstdio把stdio.h替代

QLabel* label = new QLabel(this);
//QLabel label;

创建对象的时候,可以在堆上创建,也可以在栈上创建,Qt中更推荐堆中创建
this,给当前这个label对象,指定一个“父对象”this,也就是这个构造函数所对应的对象,也就是main函数里的创建的widget对象w

Widget w;

设置控件中,要显示的文本是QString
由于Qt诞生久远,当时C++还没有标准,所以Qt自己搞了一套基础类。虽然与标准库共存。
Qt原生的api中,涉及到的接口,用的是Qt自己的容器。所以相比std::string,还是用Qstring

label->setText("hello world");

在QString中提供了c风格字符串作为参数的构造函数,不显示构造QString,c风格字符串会隐式构造成QString对象
QString对应的头文件,已经被很多Qt内置的其他类给间接包含了,一般不需要显式包含QString头文件
widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QLabel>
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    QLabel* label = new QLabel(this);
    label->setText("hello world");
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250318222338.png]]

QLabel默认是左上角

QLabel* label = new QLabel(this);
对象树

new了对象之后,为什么没有delete,会不会出现内存泄漏
上述代码在Qt中不会出现内存泄漏。label对象会在合适的时候被析构函数,虽然没有手写delete,确实能释放。
之所以能把对象释放掉,主要是因为把这个对象挂到了对象树上。
前端开发也涉及到类似的对象书(DOM),本质上也是一个树形结构(N叉树),通过树形结构把界面上的各种元素组织起来
Qt中也是类似,也是搞了一个对象树,也是N叉树,把界面上的各种元素组织起来了。
![[Pasted image 20250416211653.png]]

通过这个树形结构,把界面上要显示的这些控件对象就组织起来了
使用对象树,把这些内容组织起来,最主要的目的,就是为了能够在合适的时机把这些对象统一进行释放(窗口关闭/销毁的时候)
这里的树上的这些对象,统一销毁是再好不过的。
如果某个对象提前销毁,此时就会导致对应的控件就在界面上不存在了

通过new的方式创建对象,也就是为了把这个对象的生命周期,交给Qt的对象树来统一管理
如果这个对象是按照栈上的变量创建的,就可能会存在提前释放的问题

    QLabel label(this);
    label.setText("hello world");

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

自定义一个Label类来实验析构函数
![[Pasted image 20250416213953.png]]

新建一个类
![[Pasted image 20250416214053.png]]

类名是MyLabel,继承QLabel
![[Pasted image 20250416214157.png]]

左侧多出文件
![[Pasted image 20250416214247.png]]

Qt Creator生成了一些代码,但是没完全生成,头文件没有自动包含,需要自己手动包含

在Qt Creator中,可以通过F4切换头文件和对应的.cpp文件(C++IDE常用功能)
![[Pasted image 20250416215056.png]]

调用父类构造函数,将自己类的对象加入到对象树中

创建自定义的类,最主要的目的,是自定义一个析构函数,在析构函数中,完成打印,方便看到最终的自动销毁对象的效果

![[Pasted image 20250416215653.png]]

写完一个函数的声明以后,按下alt+enter,自动跳转到对应的cpp文件中添加函数的定义
widge.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;
}

使用自己定义的MyLabel代替原来的QLabel,所谓的继承本质上是扩展,保持原有功能不变的基础上,给对象扩展出一个析构函数,通过析构函数,打印一个自定义的内容,方便观察程序运行效果
![[Pasted image 20250416221719.png]]

输出hello world
![[Pasted image 20250416221832.png]]

点击关闭窗口,销毁以后,输出了自定义的内容
说明析构函数是执行了
虽然没有手动delete,但是由于把MyLabel挂到了对象树上,此时窗口被销毁的时候,就会自动销毁对象中的所有对象,MyLabel的析构是执行到了

编码乱码

发现输出的内容有乱码,表示编码方式不匹配

计算机中存储的是二进制数字
英文字母用ASCII码表表示,规定了每个字符都有一个对应的数字表示
只是表示英文,一个字节就够了

中文中日常的常用字有4k个,总数有60k
仍然使用一个大表格,给每个汉字,分配一个数字
字符集,表示汉字的字符集,有很多种,不同的字符集,表示同一个汉字,使用的数字不相同
目前表示汉字字符集,主要有2种

  1. GBK,使用两个字符表示一个汉字,windows默认是GBK
  2. UTF-8 / utf8,变长编码,表示一个符号,使用的字节数有变化,在utf8中,一个汉字,一般是3个字节,Linux默认是utf8

如果字符串本身是utf8编码的,但是终端(控制台)是按照gbk的方式来解析显示的,此时就会出现乱码
拿着utf8这里的数值,去查询gbk的码表,此时就会出现乱码

这里的字符串使用的编码方式,和当前mylabel.cpp文件的编码方式是一致的
![[Pasted image 20250417095743.png]]

选中文件,在explorer中显示
![[Pasted image 20250417095829.png]]

使用记事本打开
![[Pasted image 20250417095902.png]]

点击另存为
![[Pasted image 20250417095936.png]]

下方会显示编码方式,这个文件就是utf8编码
如果是ANSI,说明就是GBK编码

当前表示中文的主流方式,是utf8,支持各种语言的文字
Qt中的QString,可以帮助自动处理编码方式的问题
Qt中还提供了专门用来打印日志的工具,也能自动处理编码方式
qDebug(),借助这个,就可以完成打印日志的过程,很好地处理字符编码

#include "mylabel.h"
#include <iostream>
#include <QDebug>
  
MyLabel::MyLabel(QWidget* parent) : QLabel(parent)
{
  
}
  
MyLabel::~MyLabel()
{
    //std::cout << "MyLabel 被销毁!" << std::endl;
    qDebug() << "MyLabel 被销毁!";
}

qDebug()这个宏,封装了QDebug对象,直接使用qDebug(),可以当作cout使用
![[Pasted image 20250417100856.png]]

再次测试,没有出现乱码
后续如果需要通过打印日志的方式,输出调试信息,都优先使用qDebug,cout对编码的处理不太好,window上容易出现乱码,Linux一般没事,Linux默认编码是utf8

使用qDebug,打印的调试日志,是可以统一进行关闭的,这样程序发布的时候,用户就看不到日志,可以通过编译开关,统一关闭

使用编辑框实现

完成hello world可以使用多种控件来实现
使用编辑框来实现hello world
单行编辑框QLineEdit
多行编辑框QTextEdit
![[Pasted image 20250417105433.png]]

Line Edit
![[Pasted image 20250417105609.png]]

![[Pasted image 20250417105614.png]]

可以在右下角进行编辑
运行
![[Pasted image 20250417105653.png]]

使用纯代码实现上述功能

#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->setText("hello world");
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250417105958.png]]

通过按钮的方式实现

![[Pasted image 20250417110119.png]]

Push Button 普通按钮
![[Pasted image 20250417110205.png]]

运行
![[Pasted image 20250417110223.png]]

Qt中的信号槽机制
本质就是给按钮的点击操作,关联上一个处理函数
当用户点击的时候,就会执行这个处理函数

connect();
Linux网络编程中,学过一个函数叫connect
这个函数用来给TCPsocket建立连接的,写TCP客户端的时候,就需要先建立连接,然后才能读写数据
Qt中的connect是QObject这个类提供的静态函数,这个函数的作用就是连接信号和槽

![[Pasted image 20250417120309.png]]

ui->pushButton:谁发出的信号
访问到form file(ui文件)中创建的控件
![[Pasted image 20250417120500.png]]

在Qt Desinger 中创建一个控件的时候,此时就会给这个控件分配一个objectName属性,这个属性的值,要求是在界面中得是唯一个,不能和别人重复
qmake在预处理.ui文件的时候,就会根据这里的objectName生成对应的 C++代码.
C++代码中该QPushButton对象的变量名字就是这里的objectName.
这个变量就是ui属性中的成员变量
![[Pasted image 20250417121332.png]]

&QPushButton::clicked:发出了啥信号
点击按钮的时候就会自动触发这个信号
this:谁来处理这个信号
&Widget::handleClick:具体怎么处理

#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::handleClick);
}
  
Widget::~Widget()
{
    delete ui;
}

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"
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
}
  
Widget::~Widget()
{
    delete ui;
}
  
void Widget::handleClick()
{
    //当按钮被点击之后,就把按钮中的文本进行切换
    ui->pushButton->setText("hello qt");
}

![[Pasted image 20250417121939.png]]

点击之后切换为hello qt
![[Pasted image 20250417121950.png]]

void Widget::handleClick()
{
    //当按钮被点击之后,就把按钮中的文本进行切换
    if (ui->pushButton->text() == QString("Hello world")){
        ui->pushButton->setText("hello qt");
    }
    else{
        ui->pushButton->setText("Hello world");
    }
  
}

通过添加条件判断实现文本来回切换
![[Pasted image 20250417122455.png]]

ui指的就是widget.h里的ui成员
![[Pasted image 20250417122646.png]]

![[Pasted image 20250417122612.png]]

pushButton就是在build文件夹里的ui_widget.h里的成员
可以修改objectName为myButton,再修改代码里的pushButton为myButton,再编译就会顺利执行
![[Pasted image 20250417122931.png]]

![[Pasted image 20250417122950.png]]

可以运行
![[Pasted image 20250417123009.png]]

此时ui_widget.h里的pushButton变为了myButton
![[Pasted image 20250417123042.png]]

在objectName中,设置成什么值,生成的变量名就叫啥名字,就可以根据这个名字来获取对应的控件的变量

代码实现通过按钮输出hello world

#include "widget.h"
#include "ui_widget.h"
  
#include <QPushButton>
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QPushButton* myButton = new QPushButton(this);
    myButton->setText("hello world");
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250417123718.png]]

目前muButton是Widget的局部变量,handleClick无法访问
改为在widget.h中,加上头文件QPushButton,加上成员变量myButton

#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"
  
#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");
    }
}

![[Pasted image 20250417124254.png]]

图形化创建代码创建

对于纯代码版本,按钮对象是自己new的
为了保证其他函数中能够访问到这个变量,就需要把按钮对象设定为Widget类的成员变量

而通过控件自动生成的按钮对象,不需要咱们自己new。new对象的操作已经是被Qt自动生成了,而且这个按钮对象,已经作为ui对象里的一个成员变量了. 也无需作为Widget的成员

如果当前程序界面,界面内容是比较固定的,此时就会以图形化的方式来构造界面,但是如果程序界面,经常要动态变化 此时就会以代码的方式来构造界面
这两种方式,哪种方便就用哪种,而且这两种方式也可以配合使用

坐标系

坐标体系:以左上⻆为原点(0,0),X向右增加,Y向下增加
![[Pasted image 20250417131725.png]]

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

#include "widget.h"
#include "ui_widget.h"
  
#include <QPushButton>
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250417133122.png]]

默认情况下,按钮在0,0左上角

#include "widget.h"
#include "ui_widget.h"
  
#include <QPushButton>
  
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
  
    QPushButton* button = new QPushButton(this);
    button->setText("按钮");
    button->move(200, 300);
  
    this->move(100, 0);
}
  
Widget::~Widget()
{
    delete ui;
}

![[Pasted image 20250417134935.png]]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值