当前位置: 首页 > news >正文

QT 学习笔记摘要(一)

第一节 QT介绍

 1. QT概述

        简单来说,QT就是一个跨平台的客户端技术,HTML画网页一样,而QT就是画客户端的,它不仅可以绘制界面而且可以做单机应用开发,还可以做网络程序的客户端界面开发

        更专业的说法是:Qt 是⼀个 跨平台的 C++ 图形⽤⼾界⾯应⽤程序框架 ,它拥有完备的C++ 图形库和集成了一系列代码模块简化难度,开发者可以通过简单的拖拽组合来实现复杂的应⽤程序,而且Qt 支持C++PythonQMLJavascript 等多种语言,适合多种技术、开发方式。同时Qt 也拥有一套完整的设计、开发工具以及丰富的文档和例程,其开源社区非常活跃,这些能明显降低开发难度和缩短开发时间

        其实除了QT是客户端技术,还有VB,C++、win32API 的 MFCWinform ,C# .net framework,Java swing/javaFx ,C++ duilib,Objective-c/swift cocoa等等

        但他们不是做出来的客户端界面丑,无法跨平台,没有活跃的社区,就是没有维护了,所以一般公司做客户端界面都是用的QT;

        而Qt是很多客户端跨平台的首选,因为开源、UI 库和各种功能的类库非常丰富,但这里只推荐做pc的客户端,移动端的客户端界面还是不推荐使用Qt

2. QT框架

        一句话:One framework. One codebase. Any platform,这是Qt 官网的一句话,很好的概括了什么是Qt,

口号说明:

  • One framework(一个框架):只需要使用一个开发框架
  • One codebase(一套代码):只需要维护一份代码,而不是针对不同平台写不同的代码
  • Any platform(任何平台):这套代码可以运行在多个平台上,比如 Windows、macOS、Linux,甚至移动设备(iOS/Android)或 Web 端

3. QT发展(了解)

  • Qt最早是1991年由挪威的Eirik Chambe-Eng和Haavard Nord开发的,1994年3月4日成立奇趣科技公司(Trolltech);

  • 2000年奇趣科技公司为开源社区发布了遵循 GPL(GNU General Public License)许可证的开源版本;

  • 2008年诺基亚公司收购了奇趣科技公司,并增加了 LGPL(GNU Lesser General Public License)的授权模式;

  • 2011年3月Qt 商业授权业务出售给了芬兰IT服务公司Digia

  • 2013年7月3日发布Qt5,2020年6月12日**Qt5.15** LTS 正式发布,目前已经推出Qt6。

        经过多年的发展,市面上也有很多基于Qt开发的应用程序:WPS、YY语音、豆瓣电台、虾米音乐、淘宝助理、千牛、暴雪的战网客户端、VirtualBox、Google地图、Photoshop等等

4. QT优点

  • 跨平台,⼏乎⽀持所有的平台;

  • 接⼝简单,容易上⼿,学习 QT 框架对学习其他框架有参考意义。

  • ⼀定程度上简化了内存回收机制

  • 开发效率⾼,能够快速的构建应⽤程序。

  • 有很好的社区氛围,市场份额在缓慢上升。

  • 可以进⾏嵌⼊式开发

5. Qt 的应⽤场景

  • 主要: 桌⾯应⽤程序, 嵌⼊式系统

  • 次要: 移动应⽤程序

        额外多说一下:嵌入式系统指的是日常使用的: 冰箱,洗衣机,路由器,投影仪...之类的,这些设备里面就使用了嵌入式系统

第二节 QT Creator

1. 下载

   https://download.qt.io/archive/qt/5.12/5.12.11/

虽然目前最新的QT版本是QT6,但是我这里使用的是QT5  

  • 我使用的是5.12.11版的qt,大家可以根据自己的实际情况进行下载,
  • 值得多说一句的是:类似于这些软件/工具的下载,不要最新的,也不要太老的
  • 最新的版本出了问题不好解决,太老的版本可能又会有兼容性问题

 2. 安装

说明一下:

  • 可能第一个界面是需要注册账号的,直接使用邮箱+密码注册,然后会给你的邮箱中发个邮件,最后你在你的邮箱中确认那个邮件就行了(如果在安装之间断网,那么就不需要注册账号)
  • 下一步,下一步,然后选择那四个组件就行了,不要全选

3. 配置环境变量 

        为了让操作系统/QT Creator工具,能够找到Qt SDK中提供的exe,也就是运行Qt 程序的时候,能够找到对应.dll 动态库,所以我们这里可以先把环境变量配置好

4. 基本介绍

        刚才其实我们就已经把QT开发环境下好了,可能桌面没有显示,按一下win键,如下图

5. 新建项目

  • 其实一路next下去也行

6. 项目结构

 6.1 qt_tmp.pro

qt_tmp.pro是项目的配置文件

说明一下:

  • 这里该怎么理解QT += 模块呢,其实就是我们在做大型项目是需要引入的第三方库,
  • 后面我们会需要引入multimedia 模块,而这个模块是跟音视频有关的模块,简单理解为第三方库也行

 6.2 mainwindow.h

说明一下:

  • Q_OBJECT其实是个宏,其中有QT中的信号与槽的宏定义,更详细的后面再说

关于QT中对头文件的设定:

  • 在使用Qt中内置的类时,一般来说包含头文件的名字就是和类名一致的,但是也不是所有的Qt类都需要显示包含头文件
  • 毕竟在c++中,一些头文件都是间接被包含的

关于构造函数中需要穿父类的指针说明:

  • QT中为了统一析构父类和子类,引入了对象树的概念(后面详细介绍)
  • 而创建对象时就需要把这个对象往树·上挂,而往树上挂就必须要指明父节点
  • 则构造的时候就需要传一个父类的指针

6.3 mainwindow.cpp

6.4 main.cpp

 说明一下:

  • 是先有应用,再有窗口,窗口是放在应用里的,可能有多个窗口,所以他们2个对象实例化的顺序不能颠倒

这里简单说一下a.exec(),其实可以简单的理解为一个死循环,但还做了一些其他的东西,

  • 如果没有最后一行代码,而是写成了return 0; 那么这个窗口会闪一下,就退出了
  • 简单解释:能出现窗口是因为有w.show();// 显示窗口,而w对象又是栈上开辟的,出了作用域就没了

顺便再说一下:w.show()

  • .show()方法表示让控件显示处理
  • .hide()方法表示让控件隐藏
  • widget的父类时QWidget,则上面的那两个方法都是QWidget提供的

6.5 mainwindow.ui

        上面那种格式是xml格式,而这里的xml格式就是去描述这个界面是怎么样的,进一步的qmake会调用相关的工具,依据这个xml文件生成一些c++代码,从而把完整的界面构建出来

6.6 临时文件

 

说明一下:

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

7. 第一个程序hello world

7.1 纯图形化方式

说明一下: 

  •  以图形化方式实现界面,主要是在qt designer 拖动控件调调属性

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

7.2 纯代码方式

方式一:重建一个cpp文件

#include <iostream>
#include <QMainWindow>
#include <QLabel>
#include <QApplication>
using namespace std;int main(int argc, char *argv[])
{//    cout << "Hello World!" << endl;QApplication a(argc, argv);// 因为show这个方法本来就是QMainWindow类的,所以直接实例化这个类的对象就行QMainWindow w;w.setGeometry(0,0,800,600);w.setWindowTitle("第一个程序");QLabel* label = new QLabel(&w);// 对这个控件做相关属性设置label->setText("hello world");label->setGeometry(160,110,400,71);label->setStyleSheet("font: 75 20pt Consolas;");w.show();return a.exec();
}

 

方式二:重建一个qt设计类文件

说明一下: 

  • 一般通过代码来构造界面的时候,通常会把构造界面放到Widget/MainWindow的构造函数中
  • 这里的this指针,是给当前这个label对象,指定一个父对象(以后会说的对象树)
  • 在QT中的字符串和C++/C中的字符串是不一样的,当时C++/C的字符串不好,所以QT为了字节的开发能变的顺畅,就自己发明了一套轮子,搞了一系列基础类,来支持QT的开发,包括但不限于
    字符串->QString 动态数组->QVector 链表->QList 字典->QMap
  • 在QString中也提供了 C风格字符串作为参数的构造函数,不显示构造QString,上述代码中,C风格字符串也会隐式构造成QString对象 
  • 每个标签都有一个同名的头文件,但有时候也会被其他头文件间接包含,
    比如: QString 对应的头文件,已经被很多Qt内置的其他类给间接包含了,因此一般不需要显示包含QString头文件

这里还有一个值得思考的点: 一个对象new出来了,但是却没有delete,这难道不会造成内存泄漏吗

  • ​​​​在上述代码中,Qt不会产生内存泄漏,label对象会在合适的时候被析构函数释放~~
  • 之所以能够把对象释放掉,主要是因为把这个对象挂到了对象树上了,交给Qt的对象树统一管理
  • 如果这个对象是在栈上创建的话,就有可能会存在一些"提前释放"的问题,此时就会导致对应的控件就在界面上不存在了
  • 推荐: 在堆上创建对象,并挂在对象树上

7.3 验证对象树会统一析构

  • 注: 上面少写了个delete
  •  自己实现label类,并继承QLabel,然后再自主实现析构函数
  • 则可以在输出日志中观察到 自己实现的析构函数被调用了,则对象树的确会统一析构
  • 这里使用qDebug进行输出日志(不建议使用cout),还有一个好处->可以进行统一关闭
    输出日志一般是在开发阶段,调试程序的时候使用

8. 快捷键

  • 注释:ctrl + /
  • 运⾏:ctrl + R
  • 编译:ctrl + B
  • 字体缩放:ctrl + ⿏标滑轮
  • 查找:ctrl + F
  • 整⾏移动:ctrl + shift + ⬆/⬇
  • 帮助⽂档:F1
  • ⾃动对⻬:ctrl + i
  • 同名之间的 .h 和 .cpp 的切换:F4

  • ⽣成函数声明的对应定义: alt + enter

9. 使⽤帮助⽂档 

  • 方法一:光标放到要查询的类名/⽅法名上, 直接按 F1(推荐)

  • 方法二:Qt Creator 左侧边栏中直接⽤⿏标单击 "帮助" 按钮

  • 方法三:找到 Qt Creator 的安装路径,在 "bin" ⽂件夹下找到 assistant.exe,双击打开;

10. Qt 中的命名规范

  • 类名:⾸字⺟⼤写,单词和单词之间⾸字⺟⼤写;MyClass  MyAdd

  • 函数名及变量名:⾸字⺟⼩写,单词和单词之间⾸字⺟⼤写;studentCount

11. Qt 窗⼝坐标体系

计算机中的坐标系和数学中的坐标系是不一样的,y轴是向下增长的

 


 

说明一下: 

  •  对于嵌套窗口,其坐标是相对于父窗口来说的

第三节 信号与槽

1. 基本概念

信号信号就是一个通知机制,比如在某个状态做了一个什么事情,或者状态发生了改变,需要有别人来处理的时候,就要发出一个信号,就相当于发出一个通知,信号是没有实现的,信号的成员函数是不需要被定义的

槽函数:信号发出去以后,一定要做一件事情,而这个事情就是由槽函数来做的,简单来说就是对信号做出的一种响应


         在QT中信号和槽函数一定要先进行绑定才可以的,且一定是先关联信号再进行绑定(顺序不能颠倒),后续只要信号触发了,Qt就会自动执行槽函数

        信号与槽这种机制是QT框架很重要的机制,它的优点有:松散耦合,但缺点:效率较低

2. 图形化操作

        比如我现在要在窗口中放置一个按钮,当我点击这个按钮的时候,这个窗口就会关闭 

        注意在ui界面中改了控件名字,是需要重新构建的,否则控件名将不会生效 

2.1 编写槽函数

        在ui界面中选中控件,然后右键转到槽,然后选择对应的信号

         之后在头文件对应的类中就会自动生成一个信号,并把光标跳转到自动生成的槽函数中

void MainWindow::on_pushButton_clicked()
{// 关闭窗口this->close();
}

说明一下:

  • 通过图形化界面的方式定义槽函数,的确很方便,很多步骤它都帮我们做完了
  • 但在真实的开发环境中我们需要自定义信号,自定槽函数,自己去关联信号与槽函数,

3. 纯代码操作 

 3.1 connect函数

        在 Qt 中,QObject 类提供了⼀个静态成员函数 connect() ,该函数专⻔⽤来关联指定的信号函数和槽函数

connect(const QObject * sender,const char* signal,const QObject* receiver
const char* method, Qt::ConnectionType type = Qt::AutoConnection)

参数说明:

  • sender: 描述当前信号是那个控件发出的
  • signal: 描述信号类型
  • receiver描述了那个控件负责处理
  • method: 描述了这个控件怎么处理(要处理信号的对象提供成员函数)

    3.2 深度理解connect函数参数

    • 我们上面传信号和槽都是传递的函数指针,而在C++中是不允许使用2个不同指针类型,相互传参相互赋值的(函数传参,本质就是赋值)
    • 其实这个函数声明是以前旧版本的QT的connect函数声明,以前个信号参数传参,要搭配要给SIGNAL宏,给槽函数传参要搭配一个SLOT宏(这样做的目的是 将 传入参数 转成char*)
    • connect(button,SIGNAL(&QPuhsButton::clicked),this,SLOT(&Widget::close))

    但是后来因为书写起来太麻烦了,在Qt5时进行了改进

    •  使用模板实现泛型编程,重载构造函数  
    •   此时connect函数就有一定的参数检查功能:参数1和参数2不匹配 or 参数3和参数4不匹配,
      都会编译出错
    • 则要求参数2必须是参数1的成员函数,参数4必须是参数3的成员函数

    4. 查看信号与槽

     自己平时的积累 + 查看文档

    •  QPushButton自己本身没有clicked信号,但是它继承了QAbstractButton
    • 在QAbstractButton中就有一个clicked点击信号
    • 注: 这个QAbstractButton又使继承QWidget

    •  我们写的widget在定义的时候就是继承QWidget的,而QWidget就有close这个关闭槽
    • 注: QWidget这个类又是继承自QObject类的

    5. 自定义槽

    • 在Qt中,除了通过connect来连接信号槽之外,还可以通过函数名字的方式自动连接

    5. 自定义信号

    • 自定义信号,本质就是一个函数,只需要声明就行了,而这个函数的定义,是Qt在编译过程中,动生成的(我们无法干预)
    • 而作为信号参数,这个函数的返回值,必须是void的,有没有参数都是可以的,甚至可以支持重载
    • Qt内置的信号,不需要手动触发,而自定义信号,需要手动代码触发  emit mySignal();
    • 这里使用图形化方式创建一个按钮,在通过这个按钮发送 自定义信号
    • 发送信号的操作, 也可以在任意合适的代码中. 不一定非得在构造函数里

    6. 带参数的信号与槽

    •  带有参数的信号,要求信号的参数和槽的参数要一致

    • 类型,个数要满足要求(信号的参数个数要多于槽的参数个数)

    7. 额外说明

    7.1 前置条件

    •  Qt 中如果要让某个类能够使用信号槽(可以在类中定义信号和槽函数),则必须要在类最开始的地方,写下Q_OBJECT
    • 将这个宏展开,会得到一大段代码,然后而这一大段代码又可以展开

    7.2 设计目的

    • 解耦,: 把触发 用户操作的控件 和 处理对应用户的操作逻辑 解耦合
    • 实现"多对多"效果
      一个信号,可以connect到多个槽函数上
      一个槽函数也可以被多个信号connect
    • 但是多对多的需求实际中并不常见,所以以后图形化开发框架都没有支持多对多

    7.3 disconnect断开连接

    主动断开往往是把信号重新绑定到另一个槽函数上

    • 如果没有 disconnect, 就会构成 一个信号绑定了两个槽函数. 触发信号的时候, 两个槽函数都会执行

    7.4 lambda表达式 

    定义槽函数的时候 也是可以使用lambda表达式的,

    •  为了解决lambda访问作用域的问题,就需要引入变量捕捉的语法规则
    • lambda语法是c++11中引入的,对于Qt5及其更高版本,默认就是按照c++ 11来编译的
    • 如果使用Qt4或者更老的版本,就需要手动在.pro文件中加上C++的编译选项:CONFIG += c++11

    8. 登录案例(动手)

            我们要实现的是一个登录注册的页面,具体参考下图:

    要求说明:

    •  实现一个登录系统的主页面,2个功能,一个登录,一个注册
    • 登录成功,需要跳转到欢迎界面,并欢迎这个登录的用户
    • 注册成功,需要跳转到主页面,并把注册到的信息填入对于的输入框中
    • ..... 

    注意 信号与槽不能嵌套,且电脑分辨率不同,会对qt designer产生影响

    4.1 关键点:如何新建一个窗口 

     

    说明一下:

    • 然后选择对应的控件就行了 

    4.2 关键点:如何展示子类的页面

            推荐:在父类中添加一个成员变量,类型是子类的类型,然后在构造函数中实例化这个对象,如下:

            而想要展示一个页面直接调用show方法,关闭一个页面直接调用hide方法

    4.3 关键点:如何把父类的数据传递给子类

            可以使用信号与槽,但这里推荐直接传递参数就可以了,处理参数的函数在子类中实现就可以了, 毕竟父类中有个成员变量是指向子类的(具体情况如上)

    4.4 关键点:如何把子类的数据传递给父类

            推荐:使用自定义信号与槽,首先在子类中自定义一个信号,然后再父类的构造函数中connect自定义信号与槽,然后再子类中要传数据的地方emit自定义信号,对了槽函数需要在父类中实现

    第四节 Qt Core

    Qt Core也就是Qt的核心部分

     

    1. QString常见函数

    1.1 append - 字符串拼接

    void function1()
    {QString str = "123456";str.append("abc");// 字符串拼接qDebug() << str;
    }

    1.2 prepend - 头前拼接

    void function2()
    {QString str = "123456";str.prepend("abc");// 字符串拼接qDebug() << str;
    }

    1.3 arg - 格式化

    void function3()
    {QString str = "姓名:%1,年龄:%2,性别:%3";QString format = str.arg("张三").arg(30).arg("男");qDebug() << format;
    }

    1.4  contains - 判断是否包含

    void function4()
    {QString str = "123abc456";if(str.contains("abc")){qDebug() << "包含abc" << endl;}else{qDebug() << "不包含abc" << endl;}
    }

    1.5 isEmpty && isNull

    void function5()
    {QString str1 = "";QString str2;if(str1.isEmpty()){qDebug() << "str1是empty的" << endl;}if(str1.isNull()){qDebug() << "str1是null的" << endl;}if(str2.isEmpty()){qDebug() << "str2是empty的" << endl;}if(str2.isNull()){qDebug() << "str2是null的" << endl;}
    }

    说明一下:

    • isEmpty()是用来判断字符串是否是空字符串的
    • isNull()既能判断是否是NULL,而且还能判断是不是空字符串

    1.6 startWith -- 判断是否以xxxxx打头

    void function6()
    {QString str = "123456";if(str.startsWith("123")){qDebug() << "str是以12打头的" << endl;}
    }

     

    1.7 indexOf - 找xxx出现的位置

    void function7()
    {QString str1 = "www.baidu.com";//找第一次.出现的位置qDebug() << str1.indexOf(".") << endl;
    }

     

    说明一下:

    • 这个函数就类似于c++中的find函数
    • 同理下标索引也是从0开始的

    1.8 repalce - 替换

    QString &QString::replace(int position, int n, const QString &after)

    参数说明:

    • 第一个参数是被替换字符串的位置
    • 第二个参数是被替换字符串的长度
    • 第三个参数是新的字符串
    void function8()
    {QString str1 = "www.baidu.com";//找第一次.出现的位置QString target = ".";int index = str1.indexOf(target);str1.replace(index,target.length(),"//");qDebug() << str1 << endl;
    }

     1.9 split - 分割字符串

    void function9()
    {QString str = "111:222:333:444";// 将str按照:分割成4个子字符串QStringList list = str.split(":");qDebug() << list << endl;
    }

    说明一下:

    •  split的返回值是QStringList类型,而这个类型就相当于一个只能存放QString的vector

    1.10 string 与 QString相互转换 

    • QString::fromStdString(s);// 把std::string 转换成QString
    • s.toStdString();//把QString转换成std::string

    2. QByteArray

    多用于处理字节数组 

    void function1()
    {QString str = "123456";QByteArray data = str.toUtf8();QString str1 = QString(data);qDebug() << str1 << endl;
    }

    3. QStringList && QVector

     QStringLis等价于QVector<QString>

    void function2()
    {QStringList v1 = {"ab"};v1.push_back("cd");for(auto e : v1){qDebug() << e << " ";}QVector<QString> v2 = {"ab"};v2.push_back("cd");for(auto e : v2){qDebug() << e << " ";}
    }

     

    说明一下:

    •  除了QVector还有QListQStack等等,前面已经说了QT它又搞了一套新的轮子,
    • 其实就是又封装了一遍

    相关文章:

  • 六安品牌网站建设电话注册百度账号
  • 进一步加强政府网站建设百度用户服务中心官网电话
  • wordpress里验证谷歌站长高佣金app软件推广平台
  • 响应式网站模仿优化网络推广外包
  • 失物招领网站开发项目需求分析怎么找百度客服
  • wordpress app后端seo工具是什么意思
  • 电动汽车定速巡航模式控制设计方法
  • Flask(六) 数据库操作SQLAlchemy
  • 【LUT技术专题】1D和3DLUT的高效组合-SepLUT
  • Java 线程池技术深度解析与代码实战
  • Petrel导入well数据
  • Nginx性能优化配置指南
  • 【C/C++】C++ 编程规范:101条规则准则与最佳实践
  • [ruby on rails] ActiveJob中 discard_on,retry_on和 rescue_from的应用
  • Python Polars库详解:高性能数据处理的新标杆
  • 使用markRaw实例化echarts对象
  • Python中class对象/属性/方法/封装/继承/多态/魔法方法详解
  • Python案例练习:字典专题(分析文章的文字与次数、设计星座字典、凯撒密码、摩尔斯密码)
  • 利用folium实现全国高校分布地图显示
  • 验证 TCP 连接在异常情况下的断开机制之进程(客户端)被 kill 掉
  • 如何将适用于 Docker 的 ONLYOFFICE 文档更新到 v9.0
  • React性能优化精髓之一:频繁setState导致滚动卡顿的解决方案
  • Verilog基础:编译指令`default_nettype
  • 图像融合中损失函数【3】--梯度强度损失
  • 从零开始学习Spring Cloud Alibaba (一)
  • 市面上重要的AI开发工具和框架