Qt入门日记1

news2024/11/30 14:30:50

目录

1.Qt简介和案例

2.第一个Qt程序

3.学会查看帮助文档

4.创建一个按钮

5.对象树简介 

6.Qt的坐标系

7. 信号和槽

7.1自定义信号和槽

7.2信号连接信号

7.3拓展

7.4Qt4版本以前的connect

1.Qt简介和案例

Qt是一个跨平台的C++图形用户界面应用程序框架(就是一个库吧),它是完全面向对象的,允许真正意义上的组件式编程。

Qt是Linux桌面环境KDE的基础,它有商业版和开源版。

Qt的成功案例有很多,例如Linux桌面环境KDE、WPS等等。

它的优点如下:

  • 跨平台,几乎支持所有平台
  • 接口简单
  • 一定程度上简化了内存回收机制(对象树)
  • 可以进行嵌入式开发

2.第一个Qt程序

在创建项目时会提示选择三个基类当中的某一个:

这三个基类之间的关系如下:

QMainWindow和QDialog继承自QWidget:

  • QWidget是一个空白窗口
  • QMainWindow是一个带有菜单栏、工具栏、状态栏等等的一个窗口
  • QDialog是一个提示对话框 

项目创建完成后会有一个".pro"文件,这个文件时当前项目的工程文件,里面有一些很重要的内容,不要不要随意修改".pro"文件的内容甚至不要加注释。

可以看到默认情况下,Qt包含了core模块和gui模块,如果是4版本以上的话还会添加一个widgets模块。原因在于4版本以前widgets模块属于gui模块,4版本以后将widgets独立出来了。 

当前项目自动构建出了一些源文件和头文件,可以很轻松的找到主函数:

其中,应用程序对象有且仅有一个, Widget类继承自QWidget,调用该类的show方法可以单独显示一个窗口:

注意Widget类定义当中的"Q_OBJECT",这是一个宏,它的作用是让该类支持信号和槽机制。还要注意其构造函数,有一个名为parent指向QWidget类的指针,这与对象树有关,稍后做解释。

3.学会查看帮助文档

学习任何技术都需要有帮助文档,Qt提供了这样的帮助文档。选中关键字并且按下"F1"可以弹出帮助文档

还有一种方法是直接打开帮助文档的独立引用程序,它的位置在"Qt安装路径/版本号/mingw_64/bin"下:

 

4.创建一个按钮

Qt当中的按钮类为"QPushButton",通过帮助文档可以查看的到:

 接下来在代码当中创建一个按钮并让其显示:

#include "widget.h"

#include <QApplication>
#include <QPushButton>  // 包含头文件
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton;
    btn->show();// 显示
    w.show();
    return a.exec();
}

可以看到如果使用QPushButton类的show方法,是单独打开一个窗口并显示。很显然这并不是想要的效果。如果想要依附于已存在的窗口显示,只需要为按钮指定一个父亲即可

#include "widget.h"

#include <QApplication>
#include <QPushButton>  // 包含头文件
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton;
    btn->setParent(&w);// 依附于已经存在的窗口
    //btn->show();// 显示
    w.show();
    return a.exec();
}

此时按钮便依附于已存在的窗口了。如果想要设置一下按钮的大小、显示文字、改变位置,可以使用QPushButton类的某些成员函数:

#include "widget.h"

#include <QApplication>
#include <QPushButton>  // 包含头文件
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton;
    btn->setParent(&w);// 依附于已经存在的窗口
    btn->setText("First Button");// 显示文字
    btn->resize(200,200);// 设置一下按钮的大小
    btn->move(200,100);// 移动到指定为止
    //btn->show();// 显示
    w.resize(600,400);// 固定一下窗口的大小
    w.show();
    return a.exec();
}

5.对象树简介 

在没有接触Qt之前,上面的代码对于C++程序员来说是难受的,因为new了一个QPushButton对象却并没有做delete操作。

事实上在Qt当中有些情况下并不需要做delete操作,这就是对象树的作用。

对象树是Qt当中的一种自动回收内存的机制,它可以简化程序员的工作。就如上面的代码,程序员可以不用担心内存泄漏,即不用delete。

想要不delete堆上的内存空间,必须符合两个条件:

  • new出来的对象必须是QObject的亲缘类对象
  • 该对象必须指定一个QObject的亲缘类对象

QObject是最顶层的基类,基本上Qt当中的内置类都继承自于QObect。因此比如QWidget、QMainWindow、QDialog、QPushButton等等,它们的最顶层的基类就是QObject。

当new出来的对象指定了父亲之后,就相当于加入了对象树。这颗对象树的任意节点(对象)析构时,会自动释放其所有子孙节点(对象)。

此时可以做一个实验,即自定义一个类,取名为"MyTest"并继承自QPushButton类,指定它的父亲为Widget类:

#ifndef MYTEST_H
#define MYTEST_H

#include <QWidget>
class MyTest : public QWidget
{
    Q_OBJECT
public:
    explicit MyTest(QWidget *parent = nullptr);
    ~MyTest();
signals:

};

#endif // MYTEST_H
#include "mytest.h"
#include <QDebug>
MyTest::MyTest(QWidget *parent)
    : QWidget{parent}
{

}

MyTest::~MyTest()
{
    qDebug() << "~MyPushButton()";
}

修改一下Wdiget类的析构函数:

#include "widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
}

Widget::~Widget()
{
    qDebug() << "~Widget()";
}

然后在主函数当中new出来MyPushButton类,指定其父亲为Wdiget,运行后关闭窗口观察打印结果:

#include "widget.h"

#include <QApplication>
#include <QPushButton>  // 包含头文件
#include "mytest.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    MyTest *mbtn = new MyTest(&w);
    w.resize(600,400);
    w.show();
    return a.exec();
}

关闭窗口后的输出结果,可以看到mbtn所指向的堆空间并没有手动delete,但是由于对象树的存在,不会造成内存泄漏。 

这里可以用一幅图来解释:

此时可能会有个问题,即刚才说的是对象树当中的某个对象释放时会先析构其所有子孙节点。那么为什么先打印父对象的析构,后打印子孙对象的析构呢?这个原因在于析构函数分成两部分执行,一是执行析构函数函数体内的代码,二是执行函数体之后的清理工作。函数体内的代码是释放当前对象的大部分资源,例如对象内部new出来的空间;函数体之后是清理对象本身。所以对象树可以理解成清理对象本身之前先析构其所有子孙对象。 

6.Qt的坐标系

Qt的窗口是有坐标系的,上面在控制按钮的位置时涉及到坐标系。Qt窗口的坐标系如下图所示:

7. 信号和槽

其实信号和槽可以理解为"信号和信号处理"。熟悉Linux系统编程的话,理解信号和槽不会太难。

信号和槽涉及到两个角色,即信号的发送方和信号的接收方;涉及到两个动作,即信号的发送和信号的处理。

总体而言可以分成两个部分:

connect是一个函数,作用类似于Linux当中的signal()。它的作用我个人理解成"注册一个信号产生时需要做的动作"。 

通过帮助文档可以查看该函数:

原型可以是这样的:

connect(信号的发送发,要发送的信号,信号的接收方,接收到信号后要做的动作) 

做一个小任务,即创建一个按钮,点击该按钮后关闭窗口:

#include "widget.h"

#include <QApplication>
#include <QPushButton>  // 包含头文件

#include "mytest.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    QPushButton *btn = new QPushButton(&w);
    btn->setText("PushButton");
    btn->move(200,100);
    btn->resize(100,100);
    QObject::connect(btn,&QPushButton::clicked,&w,&Widget::close);// 注册信号
    w.show();
    return a.exec();
}

该按钮点击后,窗口就关闭了。 

综上,信号和槽的优点非常明显:

  • 使得编程多样化,可以实现更多丰富的功能,非常自由
  • 低耦合,信号的发送方和接收方是低耦合的,这样设计有利于代码维护,出错的概率降低

7.1自定义信号和槽

如果自定义了一些类,并且想让这些类也能够使用信号和槽机制,Qt是支持的。

创建一个Student类和Teacher类,signals关键字下写自定义信号函数,该函数只声明不实现;public slots关键字(较高版本的Qt可以写到public下和全局下)下写自定义槽函数,该函数要声明和定义。

当前的任务是Teacher发出一个"下课"信号,学生做出"走出教室"动作。

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>

class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    void ClassOver();// 下课,只声明不实现
};

#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>

class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);
    void GoOutOfClass();// 要声明和实现
signals:

};

#endif // STUDENT_H
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent)
    : QObject{parent}
{

}
void Student::GoOutOfClass()
{
    qDebug() << "学生走出教室!";
}

此时在主函数当中完成任务:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Student *stu = new Student;
    Teacher *tec = new Teacher;
    QObject::connect(tec,&Teacher::ClassOver,stu,&Student::GoOutOfClass);
    emit tec->ClassOver();// emit关键字,发送信号
    delete stu;delete tec;
    return a.exec();
}

此时要注意一个关键字,即"emit",它的作用在于触发信号。 

其他方面,无论是信号函数还是槽函数,都是可以带参数的

这里再做一个实验,即让信号函数之间发生重载,槽函数之间发生重载:

#ifndef TEACHER_H
#define TEACHER_H

#include <QObject>
#include <QString>
class Teacher : public QObject
{
    Q_OBJECT
public:
    explicit Teacher(QObject *parent = nullptr);

signals:
    void ClassOver();// 下课,只声明不实现
    void ClassOver(QString str);
};

#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H

#include <QObject>
#include <QString>
class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = nullptr);
    void GoOutOfClass();// 要声明和实现
    void GoOutOfClass(QString str);
signals:

};

#endif // STUDENT_H
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent)
    : QObject{parent}
{

}
void Student::GoOutOfClass()
{
    qDebug() << "学生走出教室!";
}

void Student::GoOutOfClass(QString str)
{
    qDebug() << "老师说:" << str;
}

那么在主函数当中修改一下代码:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Student *stu = new Student;
    Teacher *tec = new Teacher;
    QObject::connect(tec,&Teacher::ClassOver,stu,&Student::GoOutOfClass);
    emit tec->ClassOver("下课啦!");// emit关键字,发送信号
    delete stu;delete tec;
    return a.exec();
}

此时出现了编译报错!原因在于connect函数当中的参数,信号函数和槽函数根本没有明确唯一指向,导致编译器报错。解决方法是利用函数指针来明确唯一指向

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Student *stu = new Student;
    Teacher *tec = new Teacher;
    void (Teacher::*tecSignal)(QString) = &Teacher::ClassOver;
    void (Student::*stuSlot)(QString) = &Student::GoOutOfClass;
    QObject::connect(tec,tecSignal,stu,stuSlot);
    emit tec->ClassOver("下课啦!");// emit关键字,发送信号
    delete stu;delete tec;
    return a.exec();
}

需要注意,在声明和定义槽函数时,它们的返回值都为void

如果需要取消注册的话,可以使用disconnect函数,这里就不做示例了。

7.2信号连接信号

信号不一定非要与槽函数connect在一起,信号和信号之间也可以连接。

现在实现一个需求,即创建一个按钮,按钮点击后老师下课,学生走出教室。此时代码可以修改为:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    Student *stu = new Student(&w);
    Teacher *tec = new Teacher(&w);

    void (Teacher::*tecSignal)() = &Teacher::ClassOver;
    void (Student::*stuSlot)() = &Student::GoOutOfClass;
    QObject::connect(tec,tecSignal,stu,stuSlot);

    QPushButton *btn = new QPushButton(&w);
    btn->setText("PushButton");
    btn->resize(200,100);

    QObject::connect(btn,&QPushButton::clicked,tec,tecSignal);
    w.resize(600,400);
    w.show();
    return a.exec();
}

此时点击按钮会让老师产生下课信号,随后学生走出教室。

但是注意这里有个非常局限的东西,那就是信号连接信号时,作为槽函数的信号函数无法传递参数!这个时候就要配合全局函数或者lambda表达式,即在它们内部触发信号!

7.3拓展

下面的规则是总结和拓展:

  • 一个信号可以连接多个槽函数
  • 多个信号可以连接同一个槽函数
  • 信号和槽函数的参数类型必须一一对应
  • 信号函数的参数个数可以多于槽函数的参数个数

对于规则3和4的解释为,槽函数和可以选择接收和不接受信号传递过来的参数,如果选择接收了信号的参数,那么类型必须一致。以一幅图来理解:

7.4Qt4版本以前的connect

上面介绍的都是Qt4版本之后的connect函数用法,在Qt4版本之前,写法是下面这样的:

QObject::connect(tec,SIGNAL(&Teacher::ClassOver(QString)),stu,SLOT(&Student::GoOutOfClass(QString)));

显然这样的方式是非常直观的,因为可以免除函数指针的繁琐写法。但是这种写法必然存在恶劣的缺点否则较新的Qt版本也不会出现较新的写法。

根本原因在于SIGNAL和SLOT关键字会将括号内容字符串化,并且这些字符串之间还不会做任何的比较,所以导致编译器无法报错,导致后续维护项目困难。 

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

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

相关文章

2024“点点点”测试员如何上岸测试开发岗?附完整学习路线!

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

干货分享 | 一分钟带你了解TSMaster小程序编辑代码智能提示功能

本文给大家带来TSMaster小程序编辑的新功能&#xff0c;其中主要包含&#xff1a;代码编辑智能提示功能、可用外部代码编辑器编辑小程序代码并同步。 本文关键字&#xff1a;C小程序、Python小程序、代码智能提示、外部代码编辑器、Visual Studio 目录/Catalog ● TSMaster的…

11月1日星期三今日早报简报微语报早读

11月1日星期三&#xff0c;农历九月十八&#xff0c;早报微语早读分享。 1、神舟十六号航天员乘组平安抵京&#xff1b; 2、微信/抖音/B站等平台&#xff1a;将推动50万粉以上“自媒体”账号实名信息展示&#xff1b; 3、第三批鼓励仿制药品建议目录公示&#xff0c;包括抗癌…

灰狼优化算法(GWO)python

目录 一、灰狼优化算法的python实现 二、灰狼优化算法与遗传算法的对比分析&#xff08;python&#xff09; 2.1 GWO1.py 2.2 GA1.py 2.3 GWO_vs_GA.py 2.4 运行结果 ​三、基于莱维飞行改进的灰狼优化算法的python实现 一、灰狼优化算法的python实现 import numpy as …

报修软件有什么用?企业如何做好设备管理与维护?

在当今的商业环境中&#xff0c;设备设施的维护和管理已经成为企业运营的重要环节。无论是学校、酒店、物业等大型企事业单位&#xff0c;还是运维集成商、制造工厂等企业单位&#xff0c;都需要对设备设施进行有效的管理。报修软件作为一种智能化的解决方案&#xff0c;为设备…

钉钉会议室无需API开发轻松连接OA、电商、营销、CRM、用户运营、推广、客服等近千款系统

钉钉会议室支持成员管理、主持人权限管理、高级会控、组织内会议全员静音、共享权限控制等会议管理能力&#xff0c;确保会议安全可控的进行。 官网&#xff1a;https://page.dingtalk.com/wow/z/dingtalk/Rax/RoomsIntro 集简云无代码集成平台&#xff0c;轻松连接钉钉会议室…

专业软件测评中心▏App渗透测试的测试流程和注意事项简析

当前&#xff0c;我国数字经济蓬勃发展&#xff0c;为实现高质量发展注入了澎湃动力&#xff0c;App的需求和供给都十分旺盛&#xff0c;因此安全问题逐渐成为用户最为关注的话题之一&#xff0c;而渗透测试至关重要。 一、App渗透测试流程&#xff1a;   1、需求收集&#…

Vray3.6 for SketchUp安装失败错误如何处理?

SketchUp这款软件是一个极受欢迎并且易于使用的3D软件&#xff0c;有很多朋友会通过Sketchup建模或是Sketchup渲染操作。 Sketchup 建模渲染来说&#xff0c;也是较简单的一款软件&#xff0c;今天小编聊的话题不是Sketchup怎样渲染的操作。 而是对于一些新手朋友提到的关于V…

functools模块:让Python编程更高效

​​​​​​​ 概要 不知道小伙伴们在Python编程中&#xff0c;我们经常会遇到一些需要反复使用的代码片段&#xff0c;例如装饰器、高阶函数等。为了提高代码的复用性和可读性&#xff0c;Python提供了functools模块。functools模块包含了许多实用的功能&#xff0c;…

软文推广方案,媒介盒子分享

作为企业宣传的手段&#xff0c;它能用较低的成本获得较好的宣传效果&#xff0c;但有许多企业在进行软文推广时并不起效&#xff0c;这是因为没掌握好方法。今天媒介盒子就来告诉大家&#xff0c;通用的软文推广方案。 一、 明确推广目标以及受众 明确软文推广的目标有助于明…

数字化如何赋能企业降本增效?

在当前高度不确定的市场环境下&#xff0c;降本增效已成为传统企业热议的话题。在这个背景下&#xff0c;企业内部各种“卷”现象层出不穷&#xff0c;各部门都在积极降本、开源节流&#xff0c;同时也在争夺本就不足的企业资源。因此&#xff0c;数字部门在资源受限的情况下&a…

人工智能基础_机器学习016_BGD批量梯度下降求解多元一次方程_使用SGD随机梯度下降计算一元一次方程---人工智能工作笔记0056

然后上面我们用BGD计算了一元一次方程,那么现在我们使用BGD来进行计算多元一次方程 对多元一次方程进行批量梯度下降. import numpy as np X = np.random.rand(100,8) 首先因为是8元一次方程,我们要生成100行8列的X的数据对应x1到x8 w = np.random.randint(1,10,size = (8…

[移动通讯]【Carrier Aggregation-10】【 Radio Resource Control (RRC) Aspects】

前言&#xff1a; 参考《4G/LTE - LTE Advanced》 Carrier Aggregation is a special form of LTE technology that enables UE and Network to use more than one carrier frequencies. Actually this is not a new concept in LTE. You might have used/heard Dual Carrier …

Django3框架-(3)-[使用websocket]:使用channels实现websocket功能;简化的配置和实际使用方式

概述&#xff1a; 对于Django使用channels实现websocket的功能&#xff0c;之前就写了几篇博文了。随着在项目的使用和实际维护来说&#xff0c;重新设置了相关处理方法。 一般来说&#xff0c;前后端都只维护一个全局的连接&#xff0c;通过携带数据来判断具体的操作&#x…

京东协议算法最新版

环境准备 1 com.jingdong.app.mall11.6.4 入口定位 逆向分析&#xff0c;发现 params 里面有一个 sign 以及请求头里面有一个 jdgs 首先我们发现京东的 sign 是 32 位的&#xff0c;猜测其可能是 md5 之类的 hash 算法&#xff0c;既然是 hash 算法&#xff0c;那么就大概率…

程序员不得不知道的三大编程语言,看看你了解吗?

作为一名合格的程序员&#xff0c;不仅要有过硬的技术&#xff0c;还要了解许多基础知识。编程语言可是程序员工作的主力军&#xff0c;但是它是如何产生和发展的&#xff0c;你知道吗&#xff1f;接下来就让我们一起来看看编程语言和它们的发展吧&#xff01;记得点赞加收藏哦…

对 Webpack 的理解

结论先行&#xff1a; Webpack 是目前比较常用的模块打包工具&#xff0c;它能够管理和打包我们开发中所用到的 HTML、 CSS、JS 以及各种静态资源文件。 webpack内部做的事情呢&#xff0c;就是分析出各个模块之间的依赖关系&#xff0c;然后形成资源列表&#xff0c;最终打包…

FL Studio21.2.0.3842中文免费版和谐绿色版本下载

FL Studio21.2.0.3842中文免费版带有 stem 分离和 FL Cloud&#xff0c;这是一项专为 FL Studio 打造的具有里程碑意义的新服务。其他新功能包括 FL Studio Fruity Edition 的 Audio Clips&#xff08;音频剪辑&#xff09;和一个新的模拟建模合成器 Kepler。 为庆祝 FL Studio…

python类如何实例化对象

python类如何实例化对象 1、把类看作是定制的数据类型。既然是类型&#xff0c;只能用来表示数据的类型&#xff0c;不能直接用来保存数据。**要保存数据&#xff0c;首先需要创建一个类似于这类容器的东西&#xff0c;称为对象(或例子)。通过类别产生对象的过程称为例子。 2、…