Qt -初识

博客主页:【夜泉_ly】
本文专栏:【暂无
欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

文章目录

  • 📚 前言
  • 🛠️ 搭建环境
  • 📂 新建项目
  • 📝 初始代码理解
    • main.cpp
    • widget.h
    • widget.cpp
    • widget.ui
    • HelloWorld.pro
    • 🛠️ 中间文件
      • ui_widget.h
      • .exe

📚 前言

本文主要内容:
在这里插入图片描述

嘿嘿,又开了一个新坑。

为什么学 Qt?

当 C++了解的差不多了,
我就想看看现在自己能做些什么实际的应用。
学习新技术也巩固了旧知识。

然后,我便发现了 Qt。

简单来讲,
Qt 能让我们从“面向黑框框编程”中解脱出来。

因为它提供了丰富的图形界面组件和工具,
使得我们可以更加直观地设计和开发具有良好用户体验的应用程序。

🛠️ 搭建环境

学Qt,那就必须有对应的Qt开发环境。
主要分三个部分:

  • C++ 编译器
  • Qt SDK(软件开发工具包)
  • IDE

看起来很麻烦,但实际上,
我们安装一个Qt SDK,其他两个也都有了~~

这里贴个官网下载地址:http://download.qt.io/archive/qt/

很明显,这是个外国的网站。
怎么下载,那就八仙过海,各显神通了~~

我这里下的是 5.14.2

下载完成,双击安装,一路next,就行了?
不对!!先断网,再双击安装。
为什么?因为这样不用注册Qt账号😋。

安装完了大概会看到五个东西:

在这里插入图片描述

  • Assistant ,这是官方文档
  • Designer,这个后面会搭配Qt Creater使用
  • L开头的,可以对国际化进行支持
  • 黑框框,不管
  • Qt Creater,Qt的集成开发工具,
    后面主要就用这个

📂 新建项目

打开Qt Creater,新建文件:
在这里插入图片描述

我们想用Qt写一个GUI,因此:
点Application,再Widgets,再choose:

在这里插入图片描述

一路next到Details这里,选择QWidget

在这里插入图片描述

再next,最后到这个界面:
在这里插入图片描述

可以看到,这里已经有代码了。
Ctrl R运行,甚至可以生成一个窗口,虽然啥也没有:
在这里插入图片描述
这里不得不再提一下Qt是什么了:
在这里插入图片描述
框架。。
这和我们以前从头开始造轮子就有点区别了。
在这里,我们看到一个完整的 Qt 项目框架已经被自动生成,甚至可运行。
比如刚刚我们选择了QWidget,这里就自动帮我们搭好了相关的架子。
而我们要做的,就是在这个框架上面补充内容。

不过,为了避免当一个“框架驾驶员”,
初学时,先来了解一下它提供了什么,加深理解。

📝 初始代码理解

main.cpp

接下来,先仔细看看 main.cpp 的东西:
在这里插入图片描述

#include "widget.h"

首先,包了一个"widget.h"
这个就是我们刚刚选了QWidgets后自动生成的头文件。

#include <QApplication>

然后是<QApplication>,这个是每个Qt应用程序的基础。
这里面有个类叫做 QApplication
作用是管理GUI应用程序的控制流和主要设置:
在这里插入图片描述

int main(int argc, char *argv[]){

main,这个不必多说。
而形参是命令行参数,这个暂时也不用管。

    QApplication a(argc, argv);

创建了一个 QApplication 对象 a
用于管理我们的应用程序。
这个对象是编写 Qt 的图形化界面所必须要有的!
不过不需要我们写,毕竟是框架嘛😋。

    Widget w;w.show();

这个也是选择QWidgets后自动生成的。
Widget w; 创建一个 Widget 对象。
Widget 是我们自定义的窗口类。
w.show(); 显示窗口。
此调用将使 Widget 出现在屏幕上。
把这两句注释掉。。你的窗口就没了。

    return a.exec();
}

exec() 的作用有很多:
在这里插入图片描述
不过我只看得懂第一句话🤣:
用来启动事件循环,直到被明确终止(比如点击右上角的叉)才会返回,然后退出程序。

如果改成return 0; ,那程序闪一下就没了:
请添加图片描述

widget.h

接下来看widget.h,这里面就是 Widget 类的声明
在这里插入图片描述

#ifndef WIDGET_H
#define WIDGET_H

防止头文件被重复包含。
一般更推荐用#pragma once
不过它写都写了,那我也懒得改了😋~~

#include <QWidget>

#include <QWidget>引入 QWidget 类,
这是 Qt 所有用户界面对象的基类。

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

声明 Ui::Widget 类,这个是 Qt Designer 生成的类。
这个类的名字是可以自己改的,
不过要改的地方还挺多的,一般用他默认给的就行。

class Widget : public QWidget {

继承可以让人变得富有😁,类也一样。
QWidget 是创建项目时选择的父类。
不过需要注意,这里的 Widget
和上面的 Ui::Widget不是一个东西!!!
这里的 Widget 是等会我们写代码的地方,通常继承自 QWidget
Ui::Widget 是由 Qt Designer 根据 .ui 文件生成的类。
为什么要用同一个名字?这样不会混吗?
其实不会,通过使用相同的名字,可以更好的体现两者的关联关系
——它们都用来管理和显示我们最终看到的界面。

    Q_OBJECT

这是一个宏,后续使用信号和槽时必须要写这个宏

Ctrl 加 鼠标点击转到定义

#define Q_OBJECT \
public: \QT_WARNING_PUSH \Q_OBJECT_NO_OVERRIDE_WARNING \static const QMetaObject staticMetaObject; \virtual const QMetaObject *metaObject() const; \virtual void *qt_metacast(const char *); \virtual int qt_metacall(QMetaObject::Call, int, void **); \QT_TR_FUNCTIONS \
private: \Q_OBJECT_NO_ATTRIBUTES_WARNING \Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \QT_WARNING_POP \struct QPrivateSignal {}; \QT_ANNOTATE_CLASS(qt_qobject, "")

。。。看不懂,不看了

public:Widget(QWidget *parent = nullptr);

这里的 parent,很明显代表父亲。
但为什么要在构造里面加个 parent 参数呢?
这就和Qt的对象树有关了,这个之后再讲~~

现在简单理解为:
这个参数将子控件挂到树上。

父控件被删除时,所有的子控件也会被自动删除。
这意味着指定父控件后,不必手动 delete 子控件。

    ~Widget();

析构,不必多说。

private:Ui::Widget *ui;
};

uiUser Interface 的缩写,代表用户界面。
Widget 类有了 Ui::widget 类的指针,
就可以通过这个指针来管理和展示 UI 元素。

widget.cpp

接下来看看 widget.cpp
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"

引了两个头文件,
"widget.h" 好说,毕竟刚刚已经看过了。
"ui_widget.h"
这个是 Qt Designer 自动生成的 UI 文件的头文件。
什么意思?
意思是不用我们管。。。
在项目文件管理窗口里都没有这个文件,
这样设计的目的就是避免我们直接修改它。
如果想修改这里面的内容,
正确的做法是打开 .ui 文件,通过图形化界面调整界面,
之后编译的时候会自动更新 ui_widget.h

Widget::Widget(QWidget *parent): QWidget(parent)

Widget 类的构造函数
这里用 QWidget 的指针,
调用了当前对象的父类 QWidget 的构造函数,
并将 parent 参数传过去。
而父类又会调用它的父类,
最终层层向上,来到了 QObject 的构造:
在这里插入图片描述
此时新创建的这个对象将被挂到对象树上,
便于统一的释放,减少内存泄漏的可能。

而对于这块,我简单模拟实现了一下Qt的对象树,
正确性未知,毕竟我没找到 QObject()的实现,只找到了声明🤣:

在这里插入图片描述

下面来试试模拟实现:

#include <iostream>
#include <vector>
using namespace std;
class QObject 
{
public:QObject(QObject* parent = nullptr):_parent(parent){if(parent){parent->addNode(this);cout << "Add a Node" << endl;}}void addNode(QObject* child) { _child.push_back(child); }virtual ~QObject(){cout << "Delete QObject and nodes" << endl;for (auto e : _child) { if(e)delete e;e = nullptr;cout << "Delete a child" << endl;}}
private:QObject* _parent;vector<QObject*> _child;
};
class QWidget : public QObject
{
public:QWidget(QObject* parent = nullptr):QObject(parent){cout << "QWidget()" << endl;}
};
int main()
{QObject a;QWidget* b = new QWidget(&a);QWidget* c = new QWidget(b);return 0;
}

运行结果:

Add a Node
QWidget()
Add a Node
QWidget()
Delete QObject and nodes
Delete QObject and nodes
Delete QObject and nodes
Delete a child
Delete a child

上面的代码仅供参考。。。
如果对象是在栈上创建的,这段会直接报错(多次析构了)。
听说可以用智能指针解决,我这里就不写了。


    , ui(new Ui::Widget)
{ui->setupUi(this);
}

而之后,又 new 了一个 Ui::Widget ,用来设置 ui

在这里插入图片描述
为指定的小部件设置用户界面。

这里没有在Ui::Widget的构造中传入父对象的指针,为什么?
因为Ui::Widget就没有直接的父子管理能力!
对于Ui::Widget,可以理解为它主要是由图形化界面操控的,
所以和其他的QObject对象关系不大,因此没有父子关系。
再进一步讲,在设计原则中,有非常重要的六个字: “先描述,在组织”
在这儿就可以认为Ui::Widget负责描述,
而其他的QObject主要负责组织。
这样的设计模式可以方便我们将看到的界面和其背后应用程序的代码逻辑分离。

与之对应,其他的控件最好在构造时就传入父对象的指针,
不要等创建完了再 set ,不然有可能会导致析构被两次调用。

int main(int argc, char *argv[])
{QApplication a(argc, argv);QPushButton pushButton;Widget w;pushButton.setParent(&w);w.show();return a.exec();
}

当退出时:
在这里插入图片描述

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

这个刚刚提过,ui 并没有在 Qt 的对象树上,因此这里需要手动 delete

widget.ui

再来看看这个 widget.ui
在这里插入图片描述
双击会来到一个图形化的界面:
在这里插入图片描述
在这里我们可以拖动左边的控件到中间的窗口,
然后可以在右边那个黄色的框里面设置对应的属性。
当我们再次编译时,widget.ui会自动生成对应的代码。
而点击左边的编辑,会来到widget.ui文件:
在这里插入图片描述
可以看见,这个文件只能在 设计模式 下更改,也就是刚刚的图形化界面。
然后,我们还能看见这个文件是 xml 格式的,
这说明了什么?
说明了只学过C/C++的我看不懂。。

HelloWorld.pro

最后是 HelloWorld.pro
在这里插入图片描述

QT       += core gui

引入 Qt 的模块,以后可能会改这里。
现在?暂时不管~~

CONFIG += c++11

指定使用 C++11 标准进行编译。
如果没加这句,一些C++11的东西可能就用不了了。

SOURCES += \main.cpp \widget.cppHEADERS += \widget.hFORMS += \widget.ui

SOURCES列出项目中所有的源文件。
HEADERS列出项目中所有的头文件。
FORMS列出项目中的 .ui 文件。
这个地方也是自动生成的,不用我们管。

🛠️ 中间文件

除了这些,Qt项目编译运行后,
还会多出一些中间文件。
一般会在与 项目目录 并列的地方,
多一个名字很像的目录(build-...):
在这里插入图片描述
打开:
在这里插入图片描述
看到了熟悉的东西:Makefile
不过这个也是自动生成的,不用我们管。
ui_widget.h ,这就是自动生成的 widget.ui 的头文件!
因此在刚刚的 widget.ui 中引用的就是这个东西。

ui_widget.h

在这里插入图片描述
前面几句是注释,
比较有意思的是它还提示我们在该文件的任何修改都是无效的:
WARNING! All changes made in this file will be lost when recompiling UI file!

class Ui_Widget {
public:

这个类就是在 widget.h 中的 Ui::Widget
因为在后面的命名空间 Ui 中,
用一个叫做Widget的空类继承了这个 Ui_Widget

void setupUi(QWidget *Widget)
{if (Widget->objectName().isEmpty())Widget->setObjectName(QString::fromUtf8("Widget"));Widget->resize(800, 600);retranslateUi(Widget);QMetaObject::connectSlotsByName(Widget);
} // setupUi

在刚刚 Widget 的构造中,
Widget 对象将它的 this 指针传给了这个函数。

而这个函数的作用,
就是通过图形化界面中的内容,
Widget 对象进行初始化:

  • if语句
    检查 Widget 的对象名是否为空,
    如果是,则设置对象名为 “Widget”。
  • Widget->resize(800, 600);
    调整窗口的大小到 800x600 像素。
  • retranslateUi(Widget);
    翻译?
  • QMetaObject::connectSlotsByName(Widget);
    从名字猜作用:
    connect连接 Slots槽 By通过 Name名字
   void retranslateUi(QWidget *Widget);

这个应该是用来翻译的。

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

这就是刚刚说的命名空间,
它用来将Ui相关的类都封在一个命名空间下,
可能便于理解?
比如我一看到 Ui::Widget ,噢,这是用来界面设计的。
再看到 Widget ,噢,这是用来写代码的。

.exe

最后再看个东西:
在这里插入图片描述
这个 exe ,就是最终生成的可执行程序,
和之前Qt Creater 中运行的效果一致。

在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/65646.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024.12.30(多点通信)

作业&#xff1a; 1、将广播发送和接收端实现一遍&#xff0c;完成一个发送端发送信息&#xff0c;对应多个接收端接收信息实验。 发送端 #include <myhead.h>#define PORT 8888 #define IP "192.168.124.255"int main(int argc, const char *argv[]) {//1、…

点进CSS选择器

CSS 1.直接在标签的style属性进行设置(行内式) //写在数据单元格td标签内的stytle&#xff0c;设置color颜色和font-size字体大小&#xff1b; <td rowspan"3" style"color: red;font-size: 12px;">Web技术与应用</td> 2.写在head标签中的…

UE5材质节点BumpOffset

BumpOffset 凹凸偏移&#xff0c;可以让材质显示视差偏移的效果 Coordinate是UV&#xff0c;Height是凹凸偏移高度&#xff0c;HeightRatioInput用来控制高度比

仙盟系统开发——启动app失败

var 返回 仙盟使者.Cyber_CallApp(VOAPP, 命令, 携带);

LabVIEW声波谐振管自动化测量系统

开发了一种基于LabVIEW的声波谐振管自动化测量系统。该系统利用LabVIEW的强大功能&#xff0c;实现了对声波谐振频率的精确测量&#xff0c;提高了实验数据的采集效率和准确性。系统主要应用于物理教学和科研中&#xff0c;用于研究声波在谐振管中的传播特性。 项目背景 传统的…

学习笔记:使用 pandas 和 Seaborn 绘制柱状图

学习笔记&#xff1a;使用 pandas 和 Seaborn 绘制柱状图 前言 今天在使用 pandas 对数据进行处理并在 Python 中绘制可视化图表时&#xff0c;遇到了一些关于字体设置和 Seaborn 主题覆盖的小问题。这里将学习到的方法和注意事项做个总结&#xff0c;以便之后的项目中可以快…

【Linux】进程间通信-> 共享内存

共享内存原理 在C语言/C中&#xff0c;malloc也可以在物理内存申请空间&#xff0c;将申请的物理内存空间通过页表映射到进程地址空间&#xff0c;将内存空间的起始地址&#xff08;虚拟地址&#xff09;返回&#xff0c;进而进程可以使用虚拟地址通过页表映射到物理内存的方式…

【yolov5】实现FPS游戏人物检测,并定位到矩形框上中部分,实现自瞄

介绍 本人机器学习小白&#xff0c;通过语言大模型百度进行搜索&#xff0c;磕磕绊绊的实现了初步效果&#xff0c;能有一些锁头效果&#xff0c;但识别速度不是非常快&#xff0c;且没有做敌友区分&#xff0c;效果不是非常的理想&#xff0c;但在4399小游戏中爽一下还是可以…

【Maven】Maven打包机制详解

Maven打包的类型&#xff1f; 以下是几种常见的打包形式&#xff1a; 1、jar (Java Archive) 用途&#xff1a;用于包含 Java 类文件和其他资源&#xff08;如属性文件、配置文件等&#xff09;的库项目。特点&#xff1a; 可以被其他项目作为依赖引用。适合创建独立的应用程…

MySQLOCP考试过了,题库很稳,经验分享。

前几天&#xff0c;本人参加了Oracle认证 MySQLOCP工程师认证考试 &#xff0c;先说下考这个证书的初衷&#xff1a; 1、首先本人是从事数据库运维的&#xff0c;今年开始单位逐步要求DBA持证上岗。 2、本人的工作是涉及数据库维护&#xff0c;对这块的内容比较熟悉&#xff…

MySQL数据导出导出的三种办法(1316)

数据导入导出 基本概述 目前常用的有3中数据导入与导出方法&#xff1a; 使用mysqldump工具&#xff1a; 优点&#xff1a; 简单易用&#xff0c;只需一条命令即可完成数据导出。可以导出表结构和数据&#xff0c;方便完整备份。支持过滤条件&#xff0c;可以选择导出部分数据…

Go 协程池 Gopool VS ants 原理解析

写过高并发的都知道&#xff0c;控制协程数量是问题的关键&#xff0c;如何高效利用协程&#xff0c;本文将介绍gopool和ants两个广泛应用的协程池&#xff0c;通过本文你可以了解到&#xff1a; 1. 实现原理 2. 使用方法 3. 区别 背景 虽然通过go func()即可轻量级实现并发&…

无人机无法返航紧急处理方式!

一、检查飞行环境 了解禁飞原因和规定&#xff1a;首先&#xff0c;需要了解所在地区的无人机飞行规定&#xff0c;确认是否存在禁飞区或限飞区。如果处于禁飞区&#xff0c;应遵守相关规定&#xff0c;不要强行飞行。 检查天气情况&#xff1a;恶劣的天气条件&#xff08;如…

NLP论文速读(NeurIPS 2024)|BERT作为生成式上下文学习者BERTs are Generative In-Context Learners

论文速读|BERTs are Generative In-Context Learners 论文信息&#xff1a; 简介&#xff1a; 本文探讨了在自然语言处理&#xff08;NLP&#xff09;领域中&#xff0c;上下文学习&#xff08;in-context learning&#xff09;的能力&#xff0c;这通常与因果语言模型&#x…

vue3<script setup>中使用Swiper

swiper网址 Swiper中文网-轮播图幻灯片js插件,H5页面前端开发 Swiper - The Most Modern Mobile Touch Slider 安装 Swiper npm安装&#xff1a; npm install swiper yarn安装&#xff1a; yarn add swiper 导入带有所有模块&#xff08;捆绑包&#xff09;的 Swiper //…

今日收获(C语言)

一.文件的打开 有这样一个结构体&#xff0c;它内部是文件信息区&#xff0c;文件信息区中的变化可以影响到硬盘中的数据。这个结构体的名字是FILE。我们如果想要写代码对文件进行各种操作&#xff0c;就需要一个指向文件信息区的指针&#xff0c;这个指针的类型是FILE*&#…

node.js卸载并重新安装(超详细图文步骤)

卸载node.js 重新安装nodejs 一、卸载 1、首先进入控制面板卸载程序 2、卸载后 到文件夹中进行进一步的删除 删除上述的几个文件夹 每个人可能不一样&#xff0c;总之是找到自己的nodejs安装路径&#xff0c;下面是我的 ①删除C:UsersAdminAppDataRoaming路径下的npm相关文件…

仓颉编程语言:编程世界的 “文化瑰宝”

我的个人主页 在当今编程领域百花齐放的时代&#xff0c;各种编程语言争奇斗艳&#xff0c;服务于不同的应用场景和开发者群体。然而&#xff0c;有这样一种编程语言&#xff0c;它承载着独特的文化内涵&#xff0c;宛如编程世界里一颗熠熠生辉的“文化瑰宝”&#xff0c;那就…

Android使用JAVA调用JNI原生C++方法

1.native-lib.cpp为要生成so库的源码文件 2.JNI函数声明说明 NewStringUTF函数会返回jstring JNI函数声明规则 3.JAVA中声明及调用JNI函数 声明&#xff1a; 调用

DAY178内网渗透之内网对抗:横向移动篇入口差异切换上线IPC管道ATSC任务Impacket套件UI插件

1.内网横向移动 1、横向移动篇-入口点分析-域内域外打点 2、横向移动篇-IPC利用-连接通讯&计划任务, 3、横向移动篇-IPC利用-命令模式&工具套件 1.1 横向移动入口知识点 收集到域内用户和凭据后&#xff0c;为后续利用各种协议密码喷射通讯上线提供条件&#xff0c;…