Qt——实现”Hello World“、认识对象树与Qt坐标系

发布于:2025-08-12 ⋅ 阅读:(16) ⋅ 点赞:(0)

在创建项目时,使用的基类Base ClassQWidget

1. 使用图形化界面的方式实现“Hello World”

  1. 双击文件:widget.ui,进入designer模式:

在这里插入图片描述

  1. 在“控件盒子”的“Display Widgets”中找到“Label”,并拖放到白板中

    在这里插入图片描述

  2. 双击刚刚拖放到Label,输入"Hello World"

    在这里插入图片描述

  3. 最后直接运行即可:

    在这里插入图片描述

此时我们回过头来看widget.ui和编译生成的ui_widget.h文件,都发生了变化:

// widget.ui
<?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>230</x>
     <y>110</y>
     <width>81</width>
     <height>51</height>
    </rect>
   </property>
   <property name="text">
    <string>&quot;Hello World&quot;</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>


// ui_widget.h
/********************************************************************************
** Form generated from reading UI file 'widget.ui'
**
** Created by: Qt User Interface Compiler version 6.7.3
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef UI_WIDGET_H
#define UI_WIDGET_H

#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Widget
{
public:
    QLabel *label;

    void setupUi(QWidget *Widget)
    {
        if (Widget->objectName().isEmpty())
            Widget->setObjectName("Widget");
        Widget->resize(800, 600);
        label = new QLabel(Widget);
        label->setObjectName("label");
        label->setGeometry(QRect(230, 110, 81, 51));

        retranslateUi(Widget);

        QMetaObject::connectSlotsByName(Widget);
    } // setupUi

    void retranslateUi(QWidget *Widget)
    {
        Widget->setWindowTitle(QCoreApplication::translate("Widget", "Widget", nullptr));
        label->setText(QCoreApplication::translate("Widget", "\"Hello World\"", nullptr));
    } // retranslateUi

};

namespace Ui {
    class Widget: public Ui_Widget {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_WIDGET_H

2. 用代码的方式实现“Hello World”

一般来说,在实现图形化界面时,一般在继承类的构造函数中进行编写,如这里的widget.h

实现如下:

#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;
}
  • Qt中每个类都有其同名的头文件,要使用类QLabel,就需要包含头文件<QLabel>

  • 在定义QLabel对象时,一般建议在堆上创建,同时建议将这个对象挂到对象树上,其父节点就是this

  • 方法label->setText的形参实际上是类型QString

    • Qt的诞生时间比C++标准形成的时间要早,因此,对于一些常见的数据结构,Qt自己造了一些轮子
    • 例如:QStringQVectorQListQMap
    • C++标准库中的容器也可以很方便的和Qt中的容器类相互转换

最后运行结果如图:

在这里插入图片描述

内存泄露问题

可以注意到,为了使用QLable对象,我们只用了new语句:QLabel* label = new QLabel(this);

我们在后面没有delete掉,不就内存泄露了吗?

这里可以告诉大家结论:

  • 上述代码在Qt中,不会产生内存泄露, 及在合适的时候 **,**​label会被自动释放
  • 原因就是,label对象被挂到对象树上了

对象树

引入对象树的主要目的,就是将诸如QLable这样的空间进行统一管理,这样就可以统一在合适的时候进行释放

  • 所谓合适的时候,就是图形界面窗口关闭/销毁的时候
  • 而如果不适用对象树,就可能导致一些资源的提前释放,具体到图形化界面中就可能导致一些控件元素不会正常显示
  • 在上面的代码中,将QLabel对象开辟在堆空间上,就是为了将其生命周期交给对象树进行管理

而如果开辟在栈上,就可能引发问题:

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

    QLabel label(this);
    label.setText("\"Hello World\"");
}

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

运行:

在这里插入图片描述

发现预期的"Hello World"不见了

为了验证如果一个对象被挂到对象树上,会被自动清理,我么可以新建一个类myQLabel,其继承于QLabel

  1. 新建文件

在这里插入图片描述

  1. 选择C++中的C++ class

  2. 填写自定义类的信息

    在这里插入图片描述

代码如下:

// myQLabel.h
#ifndef MYQLABEL_H
#define MYQLABEL_H

#include <QLabel>

class myQLabel : public QLabel
{
public:
    myQLabel(QWidget* parent);
    ~myQLabel();
};

#endif // MYQLABEL_H

// myQlabel.cpp
#include "myqlabel.h"

#include <iostream>

myQLabel::myQLabel(QWidget* parent): QLabel(parent) {}

myQLabel::~myQLabel()
{
    std::cout << "MyQLabel 被释放" << std::endl;
}

// widget.cpp
#include "widget.h"
#include "./ui_widget.h"
// #include <QLabel>
#include "myqlabel.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    myQLabel* label = new myQLabel(this);
    label->setText("\"Hello World\"");
}

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

可以看到,在关闭图形化窗口时,会输出:

在这里插入图片描述

乱码问题

从上面的输出我们可以看到,本来应该被打印的汉字“被释放”,输出竟然是乱码,这是为什么?

首先要说明结论:乱码之所以出现,其最主要的原因就是编码方式不一致

  • 源代码文件的编码格式
  • 终端(控制台)的显示编码格式
  • 字符串本身的编码处理方式

接下来,我们同样用该清楚,一个汉字到底占几个字节?

  • 对于这个问题,我们首先要区分汉字是采用哪种编码方式进行编码的——GBK,UTF-8
  • 如果采用GBK方式编码,那一个汉字就占2个字节
  • 如果采用UTF-8方式编码,那一个汉字就占2~4个字节,一般为3字节

我们先来查看输出汉字的文件myqlable.cpp是采用哪种方式进行编码:

在这里插入图片描述

  • 可以看到,编码方式为UTF-8
  • 而如果显示为ANSI,那么编码方式就是GBK

既然myqlabel.cpp的编码方式为utf8,但输出到控制台却出现乱码,说明终端控制台的编码方式就不是UTF-8

qDebug

为了解决这一问题,我们不是用C++标准的标准输出std::cout,而采用Qt提供的QDebug类:

#include "myqlabel.h"

#include <QDebug>

myQLabel::myQLabel(QWidget* parent): QLabel(parent) {}

myQLabel::~myQLabel()
{
    qDebug() << "MyQLabel 被释放";
}
  • QDebug这个类重载了移位运算符<<,不直接使用类QDebug;而qdebug是一个宏,封装了QDebug对象,我们可以像使用std::cout一样来使用它
  • 使用qDebug()还有另一个好处,我们可以使用开关来对日志输出进行关闭, 防止日志信息对用户的干扰

3. 使用输入框实现 “Hello World”

3.1 使用图形化界面的方式

  1. 双击文件widget.ui,进入designer模式后找到控件:Line Edit

    在这里插入图片描述

  2. 将其拖放到白板中,双击控件,输入"Hello World"

    在这里插入图片描述

  3. 保存并运行即可

    在这里插入图片描述

3.2 使用代码的方式

QLabel的操作方式一样,将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* line_edit = new QLineEdit(this);
    line_edit->setText("\"Hello World\"");
}

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

运行:

在这里插入图片描述

4. 使用按钮的方式实现“Hello World”

4.1 使用图形化界面的方式

  1. 双击文件widget.ui,进入designer模式后找到控件:Push Button

在这里插入图片描述

  1. 将其拖放到白板中,双击控件,输入"Hello World"

    在这里插入图片描述

    同时可以看到,在QObject中出现了objectName和对应的值:

    • Qt Designer创建一个控件的时候,此时会给这个控件分配一个objectName属性,这个属性的值要求在界面中是唯一的。我么也可以自己指定这个值

    • CMake在预处理.ui文件的时候,就会根据objectName来生成同名的类。例如上面的控件pushButton被生成的类名就是myPushButton

    • 我们可以在生成的ui_widget.h中看到这样的代码:

      在这里插入图片描述

  2. 进入编辑界面,修改widget.cpp如下:

// 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 clickHandler();

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->myPushButton, &QPushButton::clicked, this, &Widget::clickHandler);
}

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

// 当控件内容为“Hello World”,点击按钮后,就会变为“Hello Qt”
// 当空间内容为“Hello Qt”,点击按钮后,就会变为“Hello World“
void Widget::clickHandler()
{
    if (ui->myPushButton->text() == "\"Hello World\"") {
        ui->myPushButton->setText("\"Hello Qt\"");
    } else {
        ui->myPushButton->setText("\"Hello World\"");
    }
}

上述出现的connect是类QObject提供的静态函数,其功能是连接“信号”和“槽”,其4个参数的作用分别为:

在这里插入图片描述

在这里插入图片描述

4.2 使用代码的方式

代码如下:

// 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 clickHandler();

private:
    Ui::Widget *ui;

    QPushButton* myPushButton_ = nullptr;
};
#endif // WIDGET_H


// widget.cpp
#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , myPushButton_(new QPushButton(this))
{
    ui->setupUi(this);

    myPushButton_->setText("\"Hello World\"");

    connect(myPushButton_, &QPushButton::clicked, this, &Widget::clickHandler);
}

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

void Widget::clickHandler()
{
    if (myPushButton_->text() == "\"Hello World\"") {
        myPushButton_->setText("\"Hello Qt\"");
    } else {
        myPushButton_->setText("\"Hello World\"");
    }
}

5. 图形化方式开发和代码方式开发

实际开发中,图形化方式开发界面和以纯代码方式代码界面都是很常用的方法

  • 如果界面内容是比较固定的,此时就会使用图形化界面来构造
  • 如果界面内容经常要动态变化,就需要以代码的方式进行开发
  • 此外,这两种方式也可以配合使用

6. Qt 坐标系

Qt的坐标系是 平面直角坐标系,其X轴从左往右增长,Y轴从上往下增长

在这里插入图片描述

坐标系的原点就是屏幕的左上角/窗口的左上角

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

在这里插入图片描述

而如果要指定控件或窗口的位置,就需要用到对应的move成员函数:

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , myPushButton_(new QPushButton(this))
{
    ui->setupUi(this);

    myPushButton_->setText("\"Hello World\"");
    myPushButton_->move(200, 300);

    this->move(100, 200);
}
  • move的第一个参数为水平移动距离,单位为像素px
  • move的第二个参数为竖直移动距离,单位为像素px

运行如图:

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到