Qt入门(C++)

Jaxsen_Huang 2024-09-07 08:35:02 阅读 80

创建项目基类的选择

在这里插入图片描述

对于基类的选择有三个选项,分别是QMainWindow、QWidget、QDialog

基类 说明
QMainWindow 主窗⼝类,⼀般⽤于较为复杂的应⽤程序,除了中央客⼾区界⾯,还包括菜单栏、⼯具栏、状态栏以及多个可停靠的⼯具对话框等
QWidget 最简单、最基本的窗体程序,⾥⾯可以放置多个控件实现程序功能
QDialog 基于对话框的程序,对话框⼀般⽤于弹窗,也可以⽤于主界⾯显⽰。对话框是从QWidget继承⽽来的,并丰富了⼀些功能,如模态显⽰和返回值等

项目结构

在这里插入图片描述

main.cpp

主函数

在这里插入图片描述

1.mian函数的形参是命令行参数,具体在Linux中会讲到

2.Qapplication 编写QT程序自带的类

3.创建一个控件对象并且显示出来

4.返回Qapplicaion类的对象a中的exec函数的返回值,此exec是用于让程序执行起来的

widget.h

在这里插入图片描述

1.这里的<code>#ifndef 和 #define 是header gurad,保证头文件只被包含一次,更推荐#pragma once,因为不知道在大工程项目中会不会有与文件名重名的现象

2.class Widget: public Qwidget 创建项目时,选择的父类 ps:qt中的类和头文件名字一样.

3.QT_OBJECT是qt内置的宏,本质上是文本替换,如果某个类要使用信号和槽就需要写入这个

4.Widget(Qwidget *parent = nullptr) QT中引入了“对象树”机制,创建Qt的对象,就可以把这个对象挂到对象树上,需要通过制定父节点的方式去挂。

5.Ui:: Widget *ui是用于与form file交互的,form file 是指Forms文件夹中的文件

widget.cpp

在这里插入图片描述

1.<code>#include "widget.h"创建项目时的头文件

2.#include "ui_widget.h" form file被qmake生成的头文件

3.后面的ui(new Ui::Widget)ui->setupUi(this)用于将form file生成的界面和当前的widget关联起来

widget.ui(form flie)

用于设计界面。

在这里插入图片描述

在代码层面用的是xml文件的形式编写。

在这里插入图片描述

xml的标签含义是由程序员自定义的,这里看到的标签是qt开发者制定的,类似于linux网络原理中的自定义应用层协议。

.pro(类似于linux的Makefile文件)

在这里插入图片描述

QT项目的工程文件,也是qmake工具构建的依据(qmake:一种元编程)

<code>QT += core gui:要引入qt的模块,后面学习到一些内容的时候可能要回修改这里

CONFIG += c++11:编译选项

SOURCES += \

main.cpp \

widget.cpp

HEADERS += \

widget.h

FORMS += \

widget.ui

这里描述了当前项目中,参与构建的文件都有什么。会自动生成

编译过程中生成的中间文件

在这里插入图片描述

在运行一次程序之后,就会在项目目录并列的地方,多出来一个“build-xxx”目录,这个目录里面就是该项目运行过程中,生成的一些临时文件。

在这里插入图片描述

ui_widget.h

创建项目时候自动生成的,里面包含了一个 ui_widget.h的类,在widget.h和widget.cpp中都用使用到,也是用于生成界面的

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在widget.cpp中widget类的构造方法使用setipUi去生成界面

.exe文件

最终生成的可执行的程序,如果直接运行,效果就是和之前的在Qt Creater中运行时相同的效果

对象树

在这里插入图片描述

问:我把代码改成这样还能够正常显示吗?答:不能

这里就不得不提出对象树这样的概念。

假设我们有现在这样的一个界面:

在这里插入图片描述

对应的对象树就是这个样子(他是一个n叉树)

在这里插入图片描述

用label实现hello world

在这里插入图片描述

代码实现:

在这里插入图片描述

问:这里的代码会不会导致内存泄露?答:不会

因为:label对象会在合适的时候被析构函数释放

在qt中用n叉树来管理控件对象,方便于在窗口关闭的时候统一释放。所以在这句代码中<code>QLabel* label = new QLabel(this); 在new调用构造传参传this就是交给qt的对象树去管理。这里也解释了为什么上述代码会不能显示hello world,因为这里的label的作用域是在栈上的,生命周期只在构造函数中,出了构造函数就会自动销毁,所以,在窗口关闭之前就销毁了。我们就看不见。

我们可以利用以下这个小例子去证实,我们自己去新建一个mylabel类去继承Qlabel类,然后在mylabel类中实现析构函数去输出日志信息,观看是否自动调用析构函数(在关闭窗口的时候)

mylabel.h

#ifndef MYLABEL_H

#define MYLABEL_H

#include <QLabel>

#include <iostream>

class mylabel : public QLabel

{

public:

mylabel(QWidget* parent);

~mylabel();

};

#endif // MYLABEL_H

这里继承了 QLabel,在构造函数中加入传参QWidget* parent这个参数,参数用于将自己新建的对象挂到qt的对象树上,统一管理析构

mylabel.cpp

#include "mylabel.h"

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>

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

mylabel* label = new mylabel(this);//传入widget的this作为parent,统一析构

label->setText("hello world");

}

Widget::~Widget()

{

delete ui;

}

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

面向对象“继承”,本质上是对现有代码进行的“扩展”

乱码解决问题

但是这里出现一个问题就是在应用程序输出的文字出现了乱码

在qt中打印日志一般都不会用cout,而是用qDebug()工具

在这里插入图片描述

后续再 Qt 中,如果想通过打印日志的方式, 输出一些调试信息,都优先使用 qDebug.虽然使用 cout 也行,但是 cout 对于编码的处理不太好,在windows 上容易出现乱码(如果是 Linux 使用 Qt Creator,一般就没事了,Linux 默认的编码一般都是 utf8)

qDebug()还可以统一关闭,因为qDebug()是一个宏封装了QDebug()对象,所以可以通过编译开关,来实现一键式关闭。

在 Qt 中打印日志,作为调试信息,使用 cout 固然可以, 但是并不是上策 (字符编码处理的不好,也不方便统一进行关闭)Qt 中推荐使用 qDebug()完成日志的打印.

初步认识connet函数

用Push button实现hello world

widget.h

<code>#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);

void handleClick();

~Widget();

private:

Ui::Widget *ui;

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"

#include "ui_widget.h"

#include <mylabel.h>

#include <QLineEdit>

#include <QLabel>

Widget::Widget(QWidget *parent)

: QWidget(parent)

, ui(new Ui::Widget)

{

ui->setupUi(this);

connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);

}

void Widget::handleClick()

{

//当按钮被点击后,改变按钮文本

if(ui->pushButton->text()== QString("hello world"))

{

ui->pushButton->setText("hello qt");

}

else

{

ui->pushButton->setText("hello world");

}

}

Widget::~Widget()

{

delete ui;

}

如何让按钮点击后有效果呢?使用qt中的信号槽机制(本质上就是给按钮点击操作,关联上一个处理函数,当用户点击的时候,就会执行这个处理函数)

使用QT中的connect函数来实现按钮点击逻辑(connet是QObject这个类提供的静态函数.这个函数的作用就是“连接信号”和“槽”)

connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);

第一个参数的作用:谁发出的信号(对象)

第二个参数的作用:发出了个什么信号(这个对象的成员函数)

第三个参数的作用:谁来处理这个信号(处理信号的对象)

第四个参数的作用:具体怎么处理(处理信号对象的成员函数)

ui->pushButton代码解释:可以访问到 form fil(ui文件中的控件),在Qt designer 中创建一个控件的时候,此时就会给这个控件分配一个objectName属性,这个属性的值,要求是在界面中得到是唯一的。qmake在预处理.ui文件的时候,就会根据这里的objectName生成对应的C++代码。C++代码中改QPushButton对象的变量名字就是这里的objectName.

问:这里的objectName是怎么变成ui里面的属性(为什么ui可以->pushButtom)

在工程文件中找到该工程生成的中间文件夹里面的ui_widget.h,这个文件就是根据我们的widget.ui生成的文件,里面是一个类,而我们在UI界面中添加控件,就在这里类里面添加成员变量(成员变量的名字就是该控件的Objectname)这里值得注意的是这里的ui_widget.h中的类ui_widget被一个widget类继承了(仅仅是封装而已,并没有做出扩展,在ui_widget.h后面看到),所以我一开始看项目中的widget类时,不禁迷惑为什么是同一个名字,但是其实是两个不同类ui_widget.h的那个类被ui namespace包住了。

ui_widget.h

/********************************************************************************

** Form generated from reading UI file 'widget.ui'

**

** Created by: Qt User Interface Compiler version 5.14.2

**

** 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/QLineEdit>

#include <QtWidgets/QPushButton>

#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_Widget

{

public:

QLabel *label;

QLineEdit *lineEdit;

QPushButton *pushButton;

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(360, 230, 181, 161));

label->setLayoutDirection(Qt::LeftToRight);

label->setAlignment(Qt::AlignCenter);

lineEdit = new QLineEdit(Widget);

lineEdit->setObjectName(QString::fromUtf8("lineEdit"));

lineEdit->setGeometry(QRect(230, 160, 151, 61));

lineEdit->setAlignment(Qt::AlignCenter);

pushButton = new QPushButton(Widget);

pushButton->setObjectName(QString::fromUtf8("pushButton"));

pushButton->setGeometry(QRect(150, 320, 141, 71));

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));

lineEdit->setText(QCoreApplication::translate("Widget", "hello world", nullptr));

pushButton->setText(QCoreApplication::translate("Widget", "hello world", nullptr));

} // retranslateUi

};

namespace Ui {

class Widget: public Ui_Widget { };

} // namespace Ui

QT_END_NAMESPACE

#endif // UI_WIDGET_H

初步认识Qt窗口坐标体系

在这里插入图片描述

规则一:坐标系的原点(0,0)就是屏幕的左上角/窗口的左上角

规则二:给Qt的某个控件,设置位置,就需要指定坐标,对于这个控件来说,坐标系原点就是相对于父窗口/控件的。如果该控件上根节点那么它的坐标系原点就是相对于整个显示器桌面。

在这里插入图片描述

代码练习:

widget.h

<code>#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);

}

Widget::~Widget()

{

delete ui;

}

总结:通过控件的成员函数中move成员函数来确定控件位置,move(水平方向偏移(向右),垂直方向偏移(向下))单位是像素。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。