Qt学习笔记

Qt学习笔记整理,内容主要包含:

  • QT的概述
  • 创建QT的项目(hello qt)
  • 第一个窗口及窗口属性
  • 第一个按钮
  • 信号与槽机制
  • 带菜单栏的窗口
  • 对话框
  • 布局
  • 常见的控件
  • QT消息机制以及事件
  • 绘图与绘图设备

笔记整理时间:2023年3月24日~2023年3月29日
代码仓库:https://gitee.com/wwyybtt/qt

文章目录

    • @[toc]
  • 1. Qt概述
    • 1.1 什么是Qt
    • 1.2 Qt的发展史
    • 1.3 支持的平台
    • 1.4 优点
    • 1.5 成功案例
    • 1.6 Qt的下载与安装
    • 1.7 QtCreator介绍
  • 2. 创建Qt项目
    • 2.1 快捷键
  • 3. 设置窗口属性
    • 3.1 确定代码书写位置
    • 3.2 帮助手册如何查看
    • 3.3 设置窗口属性及中文乱码解决
  • 4. 第一个按钮
    • 4.1 创建第一个按钮
    • 4.2 对象树(对象模型)
    • 4.3 对象树使用注意
    • 4.4 Qt窗口坐标体系
  • 5. 信号与槽机制
    • 5.1 信号与槽机制介绍
    • 5.2 connect函数与系统自带的信号和槽函数
      • 5.2.1 connect函数常用的格式
      • 5.2.2 系统自带的信号与槽函数
      • 5.2.3 实例
    • 5.3 自定义信号和槽
      • 5.3.1 无参的信号与槽
      • 5.3.2 重载自定义信号与槽(有参)
    • 5.4 信号与槽总结
    • 5.5 信号槽的扩展
  • 6. Lambda表达式
    • 6.1 Lambda表达式的介绍
    • 6.2 Lambda表达式的使用
  • 7. 带菜单栏的窗口
    • 7.1 QMainWindow概述
    • 7.2 QMainWindow菜单栏
    • 7.3 工具栏
    • 7.4 状态栏
    • 7.5 铆接部件
    • 7.6 核心部件
    • 7.7 资源文件
    • 7.8 背景图设置
    • 7.9 ui
  • 8. 对话框
    • 8.1 标准对话框
    • 8.2 自定义对话框
    • 8.3 文件对话框
    • 8.4 选择颜色对话框
    • 8.5 选择字体对话框
    • 8.6 消息对话框
    • 8.7 输入对话框
  • 9. 布局管理器
    • 9.1 概述
    • 9.2 布局的使用
    • 9.3 实现一个登录功能
  • 10. 常用的控件
    • 10.1 QLabel控件使用
    • 10.2 QLineEdit
    • 10.3 自定义控件
  • 11. Qt消息机制和事件
    • 11.1 事件
    • 11.2 常见事件函数
    • 11.3 窗口关闭/重设大小事件函数
    • 11.4 鼠标事件
    • 11.5 重写事件分发函数
    • 11.6 事件过滤器
  • 12. 绘图与绘图设备
    • 12.1 QPainter
    • 12.2 画笔使用
    • 12.3 绘图设备

1. Qt概述

1.1 什么是Qt

Qt是一个跨平台C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。

常见GUI:

  • Qt:支持多平台开发;支持CSS;面向对象特性体现突出;发展趋势良好。
  • MFC:仅在Windows开发;运行效率高;库安全性好。

1.2 Qt的发展史

  • 1991年Qt最早由奇趣科技开发
  • 1996年进入商业领域,它也是目前流行的linux桌面环境KDE的基础
  • 2008年奇趣科技被诺基亚公司收购,Qt称为诺基亚旗下的编程语言
  • 2012年Qt又被 Digia公司收购
  • 2014年4月跨平台的集成开发环境Qt Creator3.1.0发布,同年5月20日配发了Qt5.3正式版,至此Qt实现了对i0S、Android、WP等各平台的全面支持。当前Qt最新版本为5.13.2(2019.12之前)

1.3 支持的平台

  • Windows - XP、Vista、Win7、Win8、Win2008、Win10
  • Uinux/X11 - Linux、Sun Solaris、HP-UX、Compaq Tru64 UNIX、IBM AIX、SGI IRIX、FreeBSD、BSD/OS、和其他很多X11平台
  • Macintosh - Mac 0s x
  • Embedded -有帧缓冲支持的嵌入式Linux平台,Windows CE

1.4 优点

  • 跨平台,几乎支持所有的平台
  • 接口简单,容易上手,学习QT框架对学习其他框架有参考意义。
  • 一定程度上简化了内存回收机制
  • 开发效率高,能够快速的构建应用程序。
  • 有很好的社区氛围,市场份额在缓慢上升。可以进行嵌入式开发。

1.5 成功案例

  • Linux桌面环境KDE
  • Skype 网络电话
  • Google Earth谷歌地图
  • VLC多媒体播放器
  • virtualBox虚拟机软件
  • 咪咕音乐
  • WPS Office
  • 极品飞车

1.6 Qt的下载与安装

下载地址:https://download.qt.io/archive/qt/

安装流程参考:http://t.csdn.cn/sb407

这里以5.13版本为例:

  • image.png
  • 下载对应平台的安装包
    • 我选择的是:qt-opensource-windows-x86-5.13.2.exe
  • 执行安装程序
  • 注册登录账户
    • image.png
  • 选择安装路径
    • 我选择的是:D:\ProgramFiles\Qt\Qt5.13.2
  • 组件选择
    • image.png
    • 预计使用6G左右空间
  • 安装完成

1.7 QtCreator介绍

Qt和QtCreator的区别:

  • Qt:通俗来说,开发工具包
  • QtCreator:集成的编译器,Qt的桌面环境

QtCreator主页面:

  • 欢迎选项
    • 工程:创建工程、打开工程
    • 示例:demo程序,可下载运行研读代码
    • 教程:一般需要翻墙才能看
  • 编辑选项:
    • 编辑项目文件
  • 设计选项:
    • 设计UI
  • Debug选项:
    • 调试
  • 项目设置选项:
    • 一般不设置
  • 帮助选项:
    • 帮助手册
    • 可以查询
  • image.png

2. 创建Qt项目

创建项目的方式:

  • 方式1:欢迎->Projects->New Project
  • 方式2:菜单栏->文件->新建文件或项目

打开项目:

  • 打开之前创建的项目
  • 方式1:欢迎->Projects->Open Project
  • 方式2:菜单栏->文件->打开文件或项目

创建工程时需要注意:

  • 项目名称
    • 一般不要有特殊符号,不要有中文
    • 选择Application->Qt Widgets Application
    • 例:01_demo
  • 创建路径(项目保存路径)
    • 路径不要带中文
    • 更改为:D:\2021code\Qt,设置为默认的项目路径
  • 创建类的基类:
    • 三种基类(Base class)
      • QMainWindow:带菜单栏的窗口
      • QWidget:空白窗口
      • QDialog:对话窗口
      • image.png
    • 首次创建项目,我们选择:Details->Base calss->Qwidget;Source file->取消勾选Generate form(即不使用ui)
      • image.png
    • 创建类的时候,类名首字母大写
  • 编译和运行
    • image.png

Qt项目框架及文件介绍:

  • image.png
  • .pro文件:工程文件,是qmake自动生成的用于生产makfile的配置文件
QT       += core gui //包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于Qt4版本才包含此模块
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS //定义编译选项,表示有些功能被标记为过时了,编译器就会发出警告
SOURCES += \
    main.cpp \ //源文件
    widget.cpp

HEADERS += \
    widget.h //头文件
  • main.cpp
#include "widget.h" //Qt中一个类对应一个头文件,类名就是头文件名

#include <QApplication> //Qt系统提供的标准类名声明头文件

int main(int argc, char *argv[])
{
    QApplication a(argc, argv); //应用程序类(整个后台管理的命脉,处理应用程序的初始化和结束,事件处理调度。注意不管有多少窗口,一个QApplication类就可)
    Widget w; //实例化对象,调用构造函数
    w.show(); //显示图形界面
    return a.exec(); //主事件循环,在exec函数中,Qt接受并处理用户和系统的事件并且将他们传递给适当的窗口控件
}
  • widget.cpp
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
}

Widget::~Widget()
{
}
  • widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};
#endif // WIDGET_H

2.1 快捷键

代码自动对齐:Ctrl + i

快速添加/取消注释:Ctrl + /

3. 设置窗口属性

3.1 确定代码书写位置

.pro文件用于生成可执行文件,.main.cpp主函数,widget.cpp类的函数,widget.h类和头文件。

一般情况下,窗口的属性和添加控件、对控件的操作都会在类的构造函数中书写

  • 优点:可以让主程序中无多余代码

3.2 帮助手册如何查看

  • 方法1:帮助->定位到索引->输入要查询的内容
    • image.png
  • 方法2:Assistant,Qt助手
    • image.png

帮助手册如何查看,以QWidget类为例:

  • image.png
  • 索引->对应的类
    • 类的说明:类的头文件、组件、基类、派生类
    • 类的内容(查找函数的途径):公有函数(Public Funcations)、重载公有函数(Reimplemented Public Functions)、公有的槽函数(Public Slots)……
    • 注意:如果有些函数在类中未找到,还可以去基类中查找。

3.3 设置窗口属性及中文乱码解决

中文乱码解决:

  • 在创建项目之前,Qt主界面->工具->选项->文本编辑器->行为->默认编码,选择UTF-8,Apply->OK

设置窗口属性:在widget.cpp中

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //修改窗口的标题(第一个窗口)
    this->setWindowTitle("第一个窗口");
    //设置窗口的大小,设置完成可以拉伸
    //this->resize(800, 600);
    //设置窗口固定大小,设置完成不可以拉伸
    this->setFixedSize(500, 500);
}

4. 第一个按钮

新建工程:02_demo

4.1 创建第一个按钮

使用帮助手册查找QPushButton类

  • #include

创建按钮的步骤:

  1. 包含头文件(.cpo)及模块(.pro)
#include <QPushButton>	//.cpp
QT += widgets		//.pro
  1. 调用类的构造函数创建并显示按钮
//创建按钮方式1
QPushButton* button = new QPushButton;
//button->show();//会新开一个窗口显示按钮

//设置按钮的父对象为窗口,使按钮在窗口上显示
button->setParent(this);
  1. 设置按钮的属性
//----------设置按钮的属性
//设置按钮的文字、内容
button->setText("第一个按钮");
//设置按钮的显示位置
button->move(100, 100);
//设置按钮的大小
button->setFixedSize(400, 400);
  1. 创建按钮的第二种方法
//创建按钮方式2
QPushButton* button2 = new QPushButton("第二个按钮", this);
this->resize(600, 400);

创建按钮两种方式的区别:

  • 方式1:窗口默认大小,按钮显示在左上角
  • 方式2:窗口根据按钮的大小来创建,使用方法2,一般还需要调用resize函数重置窗口大小

4.2 对象树(对象模型)

在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。

概念:Qt对象间父子关系。

  • QObject是以对象树的形式组织起来的。
    • 当你创建一个QObject对象时,会看到QObject_的构造函数接收一个QObject指针作为参数,这个参数就是parent,也就是父对象指针。这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。
    • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类! )
  • QWidget是能够在屏幕上显示的一切组件的父类。
    • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
    • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

解决问题:Qt引对象树的概念,在一定程度上解决了内存问题。

  • 当一个QObject对象在堆上创建的时候,Qt会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。
  • 任何对象树中的QObject对象 delete 的时候,如果这个对象有parent,则自动将其从parent 的children()列表中删除;如果有孩子,则自动delete每一个孩子。Qt保证没有QObject会被delete 两次,这是由析构顺序决定的。
  • 如果QObject在栈上创建,Qt保持同样的行为。正常情况下,这也不会发生什么问题。

4.3 对象树使用注意

C++在栈上创建对象时,是后创建的先析构,对于Qt的对象树,有以下需要注意的地方:

// 代码1
QWidget window;
QPushButton button = QPushButton("退出", &window);

// 代码2
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
  • 代码1无问题:
    • 因为栈一般先构造的后析构,代码1中执行结束后会先析构button,同时将button从window的子对象列表删除,然后析构window。因为window中已经无button子对象,仅析构window。
  • 代码2有问题:
    • 代码执行结束后会先析构window,而析构window时,会先析构它的子对象quit,然后析构window。接下来会再次析构quit,会导致二次析构,程序崩溃!
  • 如何解决:在Qt中,尽量在构造的时候就指定parent对象,并且大胆地在堆上创建!

4.4 Qt窗口坐标体系

窗口坐标体系:

  • image.png
  • 注意:对于嵌套窗口,其坐标是相对于父窗口来说的。

5. 信号与槽机制

5.1 信号与槽机制介绍

信号槽是Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个**函数(称为槽(slot))**绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。

  • 这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
    • image.png
  • 优点:松散耦合,信号发出端和接受端可以毫无关联,如果要关联就用connect函数

5.2 connect函数与系统自带的信号和槽函数

5.2.1 connect函数常用的格式

connect()函数是QObject类中的公有函数,其声明如下:

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

connect()函数最常用的一般形式:

connect(sender, signal, receiver, slot);
  • sender:发出信号的对象
  • signal:发送对象发出的信号
  • receiver:接收信号的对象
  • slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

5.2.2 系统自带的信号与槽函数

大部分的类都存在槽函数和信号,打开帮助手册,以QWidget为例:

  • image.png
  • Public Slots:公有的槽函数
  • Signals:信号,有时类中没有Signals时,可以查找其基类

5.2.3 实例

要求:创建一个按钮,点击按钮能实现关闭窗口的功能。

新建项目:03_demo

//创建按钮
QPushButton* button = new QPushButton("点击关闭窗口", this);
//重置窗口大小
this->resize(600, 400);

//信号与槽函数
connect(button, &QPushButton::clicked, this, &Widget::close);

5.3 自定义信号和槽

5.3.1 无参的信号与槽

所在项目:03_demo

步骤1:确定场景

  • 老师饿了,学生请客(以这个为例)
  • 小哥敲门,家人开门(自己练习实现)

步骤2:添加老师类和学生类

步骤3:

  • 在老师类中声明信号(声明即可)
    • teacher.h
    • 信号一般在类声明中的signals下写
    • 信号返回值为void,参数可以添加或为空
    • 仅声明,不实现
    • 一般情况下可以重载
  • 并且在学生类中声明槽函数(声明并实现)
    • 在student.h声明
    • 槽函数声明一般在类声明中的public slots下写(对于高版本的Qt,也可以写到public或者全局)
    • 槽函数返回值为void,参数可以添加或者为空
    • 槽函数声明且实现
    • 槽函数定义实现在student.cpp中
//teacher.h
signals:
    void hungury();

//student.h
public slots:
    void treat();

//student.cpp
void Student::treat()
{   
    qDebug() << "请吃饭"; //包含头文件:#include <QDebug>
}

步骤4:创建老师对象和学生对象,并使用connect连接

  • 在widget.cpp中创建并连接
this->tea = new Teacher(this);
this->stu = new Student(this);
connect(tea, &Teacher::hungury, stu, &Student::treat);

步骤5:触发信号

  • widget.h中声明触发信号的成员函数
  • widget.cpp定义触发信号的成员函数
  • 调用该函数
//widget.h
public:
    void ClassOver();

//widget.cpp
void Widget::ClassOver()
{
    emit tea->hungury();
}

//调用
ClassOver();

补充:

  • 点击按钮,请老师吃饭
connect(button, &QPushButton::clicked, this, &Widget::ClassOver);

this->tea = new Teacher(this);
this->stu = new Student(this);
connect(tea, &Teacher::hungury, stu, &Student::treat);
  • 信号连接信号
this->tea = new Teacher(this);
this->stu = new Student(this);
connect(tea, &Teacher::hungury, stu, &Student::treat);

connect(button, &QPushButton::clicked, tea, &Teacher::hungury);

5.3.2 重载自定义信号与槽(有参)

步骤1:重新写信号(带参数)

void hungury(QString food);

步骤2:重新写槽函数声明及定义(带参数)

void treat(QString food);

void Student::treat(QString food)
{
    qDebug() << "请老师吃饭:" << food;
}

步骤3:由于函数重载了,所以需要利用函数指针指向函数地址,然后再作连接

//定义函数指针
void (Teacher::*teachersignal)(QString) = &Teacher::hungury;
void (Student::*studentslot)(QString) = &Student::treat;

connect(tea, teachersignal, stu, studentslot);
ClassOver();

5.4 信号与槽总结

自定义信号与槽注意事项:

  • 发送者与接受者需要是QObject的子类(槽函数全局,lambda除外)。
  • 信号与槽函数返回值都是void。
  • 信号需要声明,不需要定义实现。槽函数需要声明也需要定义实现。
  • 槽函数是普通的成员函数,作为成员函数,会受到public、private、protected的影响。
  • 使用emit在恰当的位置发送信号。
  • 使用connect()函数连接信号和槽。
  • 任何成员函数、static函数、全局函数和Lambda 表达式都可以作为槽函数。
  • 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
  • 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
    • image.png

5.5 信号槽的扩展

  • 一个信号可以和多个槽相连
    • 如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
  • 多个信号可以连接到一个槽
    • 只要任意一个信号发出,这个槽就会被调用。
  • 一个信号可以连接到另外的一个信号
    • 当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
  • 槽可以被断开连接,使用disconnect。
  • 槽也可以被取消连接(当一个对象delete了,就会取消这个对象上的槽)
  • 使用C++11中的lambda表达式

6. Lambda表达式

6.1 Lambda表达式的介绍

概念:C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。

语法:

[capture](parameters) mutable ->return-type{statement};
1[capture]捕获列表,捕获的是那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量
2(parameters)参数列表,与普通函数的参数列表一致的。
3mutable可修改标示符,按值传递捕获列表参数时(默认仅读权限),加上mutable修饰符后,可以修改按值传递进来的拷贝
4->return-type返回值类型
5{statement}函数体,内容跟普通函数一致
6、分号不能省略

注意:

  • [] 标识一个Lambda的开始,这部分必须存在,不能省略。
  • () 参数列表,如果不需要传递参数的话,()可以一同省略。
  • 如果使用mutable,参数列表 () 不能省略的即使参数为空;如果使用mutable,修改拷贝,而不是值本身。
  • 返回值类型,如果不需要,->return-type都可省略。
  • {函数体},可以使用参数列表,也可以使用捕获列表。

补充:对于捕获列表[]的参数形式,有如下情况

  • 空。没有使用任何函数对象参数。
  • =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda 所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
  • &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的 this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
  • this。函数体内可以使用Lambda所在类中的成员变量。
  • a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的的拷贝,因为默认情况下函数是const_的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
  • &a。将a 按引用进行传递。
  • a,&b。将a按值进行传递,b按引用进行传递。
  • =,&a,&b。除a和b 按引用进行传递外,其他参数都按值进行传递。
  • &,a, b。除 a和b按值进行传递外,其他参数都按引用进行传递。

6.2 Lambda表达式的使用

一个简单的lambda表达式:

auto fun = [](){
    qDebug() << "Lambda is running!";
};

fun();

//
[]{
	qDebug() << "Lambda is running!";
}();

一个带参数和返回值的lambda:

auto fun = [](int a, int b){
    qDebug() << "Lambda is running!";
    return a + b;
};

int sum = fun(100, 200);
qDebug() << sum;

对于mutable:如果缺少mutable,下列代码将报错;省略()也会报错

    int m = 10;

    auto fun = [m]()mutable{
        qDebug() << "Lambda is running!";
        m = 300;
    };

结合信号与槽:槽函数可以使用lambda,但后面的分号要省略

QPushButton* myBtn = new QPushButton("点击", this);
this->resize(600, 400);

connect(myBtn, &QPushButton::clicked, this, [=](){qDebug() << "按钮被按下";});   //这里的lambda省略了;

7. 带菜单栏的窗口

7.1 QMainWindow概述

QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个铆接部件(dock widgets)、一个状态栏(status bary及一个中心部件(central widget)。

以Qt界面为例,说明各个部件:

  • image.png
  • 铆接部件:浮动窗口
  • 中心部件:写代码的地方

7.2 QMainWindow菜单栏

创建新的项目:04_demo

菜单栏类:QMenuBar

菜单类:QMenu

QAction类:充当子菜单(菜单项)

菜单栏创建方法:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);

    //创建菜单栏
    QMenuBar* menubar = new QMenuBar(this);//使用new创建
    //QMenuBar* menubar = menuBar();//使用成员函数创建
    this->setMenuBar(menubar);

    //创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("构建");

    //添加菜单到菜单栏
    menubar->addMenu(menu1);
    menubar->addMenu(menu2);
    menubar->addMenu(menu3);

    //创建菜单项/子菜单
    QAction* act1 = new QAction("打开文件");
    QAction* act2 = new QAction("另存为");
    QAction* act3 = new QAction("关闭");

    //添加菜单项到菜单
    menu1->addAction(act1);
    menu1->addAction(act2);
    menu1->addAction(act3);

	//使用信号实现关闭功能(点击菜单项实现对应功能)
    connect(act3, &QAction::triggered, this, &QMainWindow::close);
}

7.3 工具栏

工具栏类:QToolBar

QAction类:充当子工具/工具项

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);

    //创建菜单栏
    //QMenuBar* menubar = new QMenuBar(this);
    QMenuBar* menubar = menuBar();
    this->setMenuBar(menubar);

    //创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("构建");

    //添加菜单到菜单栏
    menubar->addMenu(menu1);
    menubar->addMenu(menu2);
    menubar->addMenu(menu3);

    //创建菜单项/子菜单
    QAction* act1 = new QAction("打开文件");
    QAction* act2 = new QAction("另存为");
    QAction* act3 = new QAction("关闭");

    //添加菜单项到菜单
    menu1->addAction(act1);
    menu1->addAction(act2);
    menu1->addAction(act3);

    //使用信号实现关闭功能
    connect(act3, &QAction::triggered, this, &QMainWindow::close);


    //创建工具栏
    QToolBar* toolbar = new QToolBar(this);

    //添加工具栏到窗口
    this->addToolBar(toolbar);

    //创建工具项
    QAction* act4 = new QAction("开始执行不调试");
    QAction* act5 = new QAction("开始调试");
    QAction* act6 = new QAction("编译");

    //添加工具项到工具栏
    toolbar->addAction(act4);
    toolbar->addAction(act5);
    toolbar->addAction(act6);

    //设置工具栏的停靠区域(左或右)
    //this->addToolBar(Qt::LeftToolBarArea, toolbar);
    //toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

    //修改工具栏不可移动(默认可上下左右移动)
    //toolbar->setMovable(false);

    //设置工具栏的浮动状态(默认可悬浮窗口)
    //toolbar->setFloatable(false);
}

7.4 状态栏

状态栏有且只能有一个。

状态栏类:QStatusBar

标签类:QLabel

状态栏信息分类:

  • 临时信息:使用方法showMessage
  • 正式信息:使用方法addWidget
  • 永久信息:使用方法addPermanentWidget
  • 注意:临时信息和正式信息都显示在左侧,添加时避免同时添加,不然会有一个显示不出。
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);

    //创建菜单栏
    //QMenuBar* menubar = new QMenuBar(this);
    QMenuBar* menubar = menuBar();
    this->setMenuBar(menubar);

    //创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("构建");

    //添加菜单到菜单栏
    menubar->addMenu(menu1);
    menubar->addMenu(menu2);
    menubar->addMenu(menu3);

    //创建菜单项/子菜单
    QAction* act1 = new QAction("打开文件");
    QAction* act2 = new QAction("另存为");
    QAction* act3 = new QAction("关闭");

    //添加菜单项到菜单
    menu1->addAction(act1);
    menu1->addAction(act2);
    menu1->addAction(act3);

    //使用信号实现关闭功能
    connect(act3, &QAction::triggered, this, &QMainWindow::close);


    //创建工具栏
    QToolBar* toolbar = new QToolBar(this);

    //添加工具栏到窗口
    //this->addToolBar(toolbar);
    this->addToolBar(Qt::LeftToolBarArea, toolbar);

    //创建工具项
    QAction* act4 = new QAction("开始执行不调试");
    QAction* act5 = new QAction("开始调试");
    QAction* act6 = new QAction("编译");

    //添加工具项到工具栏
    toolbar->addAction(act4);
    toolbar->addAction(act5);
    toolbar->addAction(act6);

    //设置工具栏的停靠区域(左或右)
    //this->addToolBar(Qt::LeftToolBarArea, toolbar);
    //toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

    //修改工具栏不可移动(默认可上下左右移动)
    //toolbar->setMovable(false);

    //设置工具栏的浮动状态(默认可悬浮窗口)
    //toolbar->setFloatable(false);


    //创建状态栏
    //QStatusBar* statusbar = new QStatusBar(this);
    QStatusBar* statusbar = statusBar();

    //添加状态栏到窗口
    this->setStatusBar(statusbar);

    //给状态栏添加信息
    //临时信息
    //statusbar->showMessage("页面显示成功");
    //statusbar->showMessage("页面显示成功", 3000);//3000毫秒后消失

    //正式信息(一般位于状态栏的左侧)
    QLabel* label1 = new QLabel("左边信息", this);
    statusbar->addWidget(label1);

    //永久信息(一般位于状态栏的右侧)
    QLabel* label2 = new QLabel("wwyybtt", this);
    statusbar->addPermanentWidget(label2);
}

7.5 铆接部件

铆接部件类:QDockWidget

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(800, 600);

    //创建菜单栏
    //QMenuBar* menubar = new QMenuBar(this);
    QMenuBar* menubar = menuBar();
    this->setMenuBar(menubar);

    //创建菜单
    QMenu* menu1 = new QMenu("文件");
    QMenu* menu2 = new QMenu("编辑");
    QMenu* menu3 = new QMenu("构建");

    //添加菜单到菜单栏
    menubar->addMenu(menu1);
    menubar->addMenu(menu2);
    menubar->addMenu(menu3);

    //创建菜单项/子菜单
    QAction* act1 = new QAction("打开文件");
    QAction* act2 = new QAction("另存为");
    QAction* act3 = new QAction("关闭");

    //添加菜单项到菜单
    menu1->addAction(act1);
    menu1->addAction(act2);
    menu1->addAction(act3);

    //使用信号实现关闭功能
    connect(act3, &QAction::triggered, this, &QMainWindow::close);


    //创建工具栏
    QToolBar* toolbar = new QToolBar(this);

    //添加工具栏到窗口
    //this->addToolBar(toolbar);
    this->addToolBar(Qt::LeftToolBarArea, toolbar);

    //创建工具项
    QAction* act4 = new QAction("开始执行不调试");
    QAction* act5 = new QAction("开始调试");
    QAction* act6 = new QAction("编译");

    //添加工具项到工具栏
    toolbar->addAction(act4);
    toolbar->addAction(act5);
    toolbar->addAction(act6);

    //设置工具栏的停靠区域(左或右)
    //this->addToolBar(Qt::LeftToolBarArea, toolbar);
    //toolbar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

    //修改工具栏不可移动(默认可上下左右移动)
    //toolbar->setMovable(false);

    //设置工具栏的浮动状态(默认可悬浮窗口)
    //toolbar->setFloatable(false);


    //创建状态栏
    //QStatusBar* statusbar = new QStatusBar(this);
    QStatusBar* statusbar = statusBar();

    //添加状态栏到窗口
    this->setStatusBar(statusbar);

    //给状态栏添加信息
    //临时信息
    //statusbar->showMessage("页面显示成功");
    //statusbar->showMessage("页面显示成功", 3000);//3000毫秒后消失

    //正式信息(一般位于状态栏的左侧)
    QLabel* label1 = new QLabel("左边信息", this);
    statusbar->addWidget(label1);

    //永久信息(一般位于状态栏的右侧)
    QLabel* label2 = new QLabel("wwyybtt", this);
    statusbar->addPermanentWidget(label2);


    //创建铆接部件
    //QDockWidget* dockwidget = new QDockWidget(this);
    QDockWidget* dockwidget = new QDockWidget("first", this);

    //添加铆接部件到窗口
    //this->addDockWidget(dockwidget);
    this->addDockWidget(Qt::TopDockWidgetArea, dockwidget);//添加到上边,下边可使用Qt::BottomDockWidgetArea

}

7.6 核心部件

核心部件也叫中心部件。

//创建记事本作为核心部件
QTextEdit* edit = new QTextEdit("文本编辑器", this);

//添加核心部件到窗口
this->setCentralWidget(edit);

7.7 资源文件

Qt资源系统是一个跨平台的资源机制,用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。**如果你的程序需要加载特定的资源(图标、文本翻译等),那么,将其放置在资源文件中,就再也不需要担心这些文件的丢失。也就是说,如果你将资源以资源文件形式存储,它是会编译到可执行文件内部。

使用Qt Creator可以很方便地创建资源文件。我们可以在工程上点右键,选择“添加新文件…”,可以在Qt分类下找到“Qt资源文件”:

  • image.png

右侧的编辑区有个“添加”,我们首先需要添加前缀,比如我们将前缀取名为images。然后选中这个前缀,继续点击添加文件,可以找到我们所需添加的文件。这里,我们选择dog.jpg文件。当我们完成操作之后,Qt Creator应该是这样子的:

  • image.png

接下来,我们还可以添加另外的前缀或者另外的文件。这取决于你的需要。当我们添加完成之后,我们可以像前面那样,通过使用:开头的路径来找到这个文件。比如,若我们的前缀是/images,文件是 document-open.png,那么就可以使用: / images/document-open.png_找到这个文件。

这么做带来的一个问题是,如果以后我们要更改文件名,比如将docuemnt-open. png度成docopen. png,那么,所有使用了这个名字的路径都需要修改。所以,更好的办法是,我们**给这个文件去一个“别名”,以后就以这个别名来引用这个文件。**具体做法是,选中这个文件,添加别名信息:

  • image.png
  • image.png

添加资源的步骤:

  • 在工程中新增资源文件(右键工程)
  • 给资源文件增加前缀(方便在代码中寻找)
  • 给资源文件添加资源(图片等)
  • 起别名(选用)

7.8 背景图设置

//给窗口设置背景图
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    this->setFixedSize(800, 600);
    this->setAutoFillBackground(true);//允许背景图填满窗口/允许绘制

    //创建图片控件
//    QPixmap pix;
//    pix.load(":/open");
    QPixmap pix = QPixmap(":/open").scaled(this->size());//控制大小

    QPalette palette;
    palette.setBrush(QPalette::Background, QBrush(pix));

    this->setPalette(palette);
}

7.9 ui

ui的功能:绘制界面(通过拖拽控件)

新建项目:05_demo,并且勾选创建ui文件

  • image.png
  • image.png
  • 形成桌面文件,可以通过拖拽方式添加窗口控件
  • image.png
  • image.png
  • image.png

此外,ui也支持代码修改

8. 对话框

  • 对话框是GUI程序中不可或缺的组成部分。很多不能或者不适合放入主窗口的功能组件都必须放在对话框中设置。对话框通常会是一个顶层窗口,出现在程序最上层,用于实现短期任务或者简洁的用户交互。

  • Qt中使用QDialog类实现对话框。就像主窗口一样,我们通常会设计一个类继承QDialog。QDialog(及其子类,以及所有Qt::Dialog 类型的类)的对于其parent指针都有额外的解释:如果parent为 NULL,则该对话框会作为一个顶层窗口,否则则作为其父组件的子对话框(此时,其默认出现的位置是 parent的中心)。顶层窗口与非顶层窗口的区别在于,顶层窗口在任务栏会有自己的位置,而非顶层窗口则会共享其父组件的位置。

  • 对话框分为模态对话框和非模态对话框。

    • 模态对话框,就是会阻塞同一应用程序中其它窗口的输入。模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的。
    • 与此相反的是非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。

8.1 标准对话框

标准对话框:所谓标准对话框,是Qt内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。Qt的内置对话框大致分为以下几类:

  • QColorDialog:选择颜色;
  • QFileDialog:选择文件或者目录;
  • QFontDialog:选择字体;
  • QInputDialog:允许用户输入一个值,并将其值返回;
  • QMessageBox:模态对话框,用于显示信息、询问问题等;
  • QPageSetupDialog:为打印机提供纸张相关的选项;
  • QPrintDialog:打印机配置;
  • QPrintPreviewDialog:打印预览;
  • QProgressDialog:显示操作过程。

8.2 自定义对话框

自定义对话框可以有模态与非模态对话框

  • 使用QDialog::exec()实现应用程序级别的模态对话框
  • 使用QDialog::open()实现窗口级别的模态对话框
  • 使用QDialog::show()实现非模态对话框。
//自定义模态对话框
QDialog dialog;
dialog.setWindowTitle("Hello, dailog");
dialog.exec();

下面将exec()修改为show()

//自定义非模态对话框
QDialog dialog;
dialog.setWindowTitle("Hello, dailog");
dialog.show();//

上述代码执行现象:

  • 事与愿违﹖对话框一闪而过!这是因为,show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。注意,dialog是建立在栈上的,show()函数返回,MainWindow: : open()函数结束,dialog 超出作用域被析构,因此对话框消失了。知道了原因就好改了,我们将 dialog改成堆上建立,当然就没有这个问题了
//自定义非模态对话框
QDialog* dialog = new QDialog(this);
//设置自动销毁
dialog->setAttribute(Qt::WA_DeleteOnClose);

dialog->setWindowTitle("Hello, dailog");
dialog->show();

关于非模态对话框,还需注意:

  • 对话框要创建在堆上
  • 由于对话框的特性(没有parent,没有指定this作为parent),可以设置对话框关闭,自动销毁对话框。

8.3 文件对话框

新建项目:06_demo

文件对话框需要的类:QFileDialog

可以查看静态成员函数:

  • image.png
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDialog>
#include <QFileDialog>
#include <QInputDialog>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    setFixedSize(800, 600);
    ui->setupUi(this);
    ui->pushButton->setFixedSize(200, 30);
    ui->pushButton_2->setFixedSize(200, 30);

}

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


void MainWindow::on_pushButton_clicked()
{
    //打开一个文件
    QString fileName = QFileDialog::getOpenFileName(this, tr("打开文件"), "./", tr("Images (*.png *.xpm *.jpg);;Text (*.txt)"));
    if(!fileName.isEmpty())
    {
        ui->plainTextEdit->appendPlainText(fileName);
    }
}

void MainWindow::on_pushButton_2_clicked()
{
    //打开多个文件
    QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("打开文件"), "./", tr("Images (*.png *.xpm *.jpg);;Text (*.txt)"));

    for(int i = 0; i < fileNames.count(); i++)
    {
        qDebug()<<fileNames.at(i);
    }
}

getOpenFileName函数的参数列表:

  • 父类对象指针
  • 文件对话框的标题
  • 打开文件路径
  • 文件过滤器
    • 文件过滤器写法:
      • "Images (*.png *.xpm *.jpg)"
      • "Images (*.png *.xpm *.jpg);;Text (*.txt *.docx *.pdf)"

8.4 选择颜色对话框

需要的类:QColorDialog

静态成员函数:getColor,可以生成颜色选择对话框,返回值是一个颜色变量,如果在颜色对话框选择了颜色则有效,若取消则无效。

#include <QColorDialog>

void MainWindow::on_pushButton_3_clicked()
{
    QPalette pal = ui->plainTextEdit->palette();//获取现有palette
    QColor initColor = pal.color(QPalette::Text);//获取现有文字颜色
    QColor color = QColorDialog::getColor(initColor, this, "选择颜色");
    //判断选择的颜色是否有效
    if(color.isValid())
    {
        pal.setColor(QPalette::Text, color);
        ui->plainTextEdit->setPalette(pal);
    }
}

8.5 选择字体对话框

需要的类:QFontDialog

静态成员函数:getFont,生成选择字体对话框,返回值不能判断有效,一般是根据getFont的第一个参数逻辑变量是否为true进行判断。

#include <QFontDialog>

void MainWindow::on_pushButton_4_clicked()
{
    bool ok;
    QFont initFont = ui->plainTextEdit->font();//获取原有文本框字体
    //QFont font = QFontDialog::getFont(&ok, QFont("Times", 12), this);
    QFont font = QFontDialog::getFont(&ok, initFont, this);
    if(ok)//如果ok为true,选择字体有效
    {
        ui->plainTextEdit->setFont(font);
    }
}

8.6 消息对话框

需要的类:QMessageBox

静态成员函数:

  • image.png
#include <QMessageBox>

void MainWindow::on_pushButton_5_clicked()
{
    //错误弹窗
    QMessageBox::critical(this, "错误消息对话框", "程序出现错误");

    //警告弹窗
    QMessageBox::warning(this, "警告消息对话框", "程序出现了警告,可能存在越界");

    //消息弹窗
    //QMessageBox::information(this, "消息对话框", "程序执行结束");
    QMessageBox::information(this, "消息对话框", "程序执行结束", QMessageBox::Ok, QMessageBox::NoButton);

    //消息选择弹窗
    QMessageBox::StandardButton result;
    result = QMessageBox::question(this, "选择消息框", "文件已 修改,是否保存", QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::NoButton);
    if(result == QMessageBox::Yes)
    {
        qDebug()<<"正在保存";
    }
    else if(result == QMessageBox::No)
    {
        qDebug()<<"不保存";
    }
    else
    {
        qDebug()<<"取消操作";
    }
}

8.7 输入对话框

需要的类:QInputDialog,可以输入文本、整型数据、浮点型数据等

#include <QInputDialog>

void MainWindow::on_pushButton_6_clicked()
{
    //生成输入文字对话框
    bool ok;
    QString text = QInputDialog::getText(this, tr("输入文字对话框"), tr("请输入文字"), QLineEdit::Normal, "demo", &ok);

    if(ok && !text.isEmpty())
    {
        ui->plainTextEdit->appendPlainText(text);
    }
}

9. 布局管理器

9.1 概述

所谓GUI 界面,归根结底,就是一堆组件的叠加。我们创建一个窗口,把按钮放上面,把图标放上面,这样就成了一个界面。在放置时,组件的位置尤其重要。我们必须要指定组件放在哪里,以便窗口能够按照我们需要的方式进行渲染。这就涉及到组件定位的机制。

Qt提供了两种组件定位机制:绝对定位和布局定位。

  • 绝对定位:需要提供组件的长、高,坐标值
    • 绝对定位就是一种最原始的定位方法:给出这个组件的坐标和长宽值。这样,Qt 就知道该把组件放在哪里以及如何设置组件的大小。但是这样做带来的一个问题是,如果用户改变了窗口大小,比如点击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的组件是不会有任何响应的。这也很自然,因为你并没有告诉Qt,在窗口变化时,组件是否要更新自己以及如何更新。或者,还有更简单的方法:禁止用户改变窗口大小。但这总不是长远之计。
  • 布局定位:需要指定使用哪种布局(垂直、水平、网格、窗体等)
    • 布局定位:你只要把组件放入某一种布局,布局由专门的布局管理器进行管理。当需要调整大小或者位置的时候,Qt使用对应的布局管理器进行调整。
    • 布局定位完美的解决了使用绝对定位的缺陷。
    • Qt提供的布局中以下三种是我们最常用的:
      • QHBoxLayout:按照水平方向从左到右布局;
      • QVBoxLayout:按照竖直方向从上到下布局;
      • QGri dLayout:在一个网格中进行布局,类似于HTML,的 table;
    • 系统自带的四种布局我们一般不使用。更方便的使用方式是通过widget,直接使用四种布局。
      • image.png

9.2 布局的使用

通过ui直接拖拽使用。

创建新项目:07_demo,设计一个登录界面的布局

  • image.png
  • image.png

9.3 实现一个登录功能

在07_demo创建一个设计师类,新增一个页面

  • image.png
  • image.png

10. 常用的控件

10.1 QLabel控件使用

QLabel是我们最常用的控件之一,其功能很强大,我们可以用来显示文本,图片和动画等。

创建新项目:08_demo

创建QLabel:

  • 方式1:使用代码(头文件QLabel)
#include <QLabel>

QLabel* label = new QLabel(this);
label->setText("这是代码创建的标签");
  • 方式2:使用ui绘制(直接拖拽)

10.2 QLineEdit

单行文本编辑框的使用。

新建项目:09_demo

命令创建:

#include <QLineEdit>
QLineEdit* edit = new QLineEdit(this);
edit->setText("这是代码创建的编辑框");

ui创建:直接拖拽

ui->lineEdit->setEchoMode(QLineEdit::Password);//密码模式

设置输出模式:

  • 显示(默认)
  • 不显示
  • 密码

10.3 自定义控件

在搭建Qt窗口界面的时候,在一个项目中很多窗口,或者是窗口中的某个模块会被经常性的重复使用。一|般遇到这种情况我们都会将这个窗口或者模块拿出来做成一个独立的窗口类,以备以后重复使用。

在使用Qt的ui_文件搭建界面的时候,工具栏栏中只为我们提供了标准的窗口控件,如果我们想使用自定义控件怎么办?

新建项目:10_demo

  • 自定义控件一般是重复性使用的窗口或者窗口中的模块。
  • 解决问题:将许多重复性使用的窗口或者窗口中的模块封装成自定义控件,可以减少创建次数,仅创建一次,使用时提升。

11. Qt消息机制和事件

11.1 事件

事件(event)是由系统或者Qt本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候,都会发出一个相应的事件。一些事件在对用户操作做出响应时发出,如键盘事件等;另一些事件则是由系统自动发出,如计时器事件。

事件要经过以下四个阶段:

  • 事件派发
  • 事件过滤器
  • 事件分发
  • 事件处理阶段

事件循环的开始:exec()函数

Qt程序需要在main()函数创建一个QApplication对象,然后调用它的exec()函数。这个函数就是开始Qt 的事件循环。在执行exec()函数之后,程序将进入事件循环来监听应用程序的事件。当事件发生时,Qt将创建一个事件对象。Qt中所有事件类都继承于QEvent。在事件对象创建完毕后,Qt 将这个事件对象传递给QObject_的event()函数。event()函数并不直接处理事件,而是按照事件对象的类型分派给特定的事件处理函数(eventhandler),关于这一点,会在后边详细说明。

11.2 常见事件函数

在所有组件的父类QWidget中,定义了很多事件处理的回调函数,如

  • keyPressEvent()
  • keyReleaseEvent()
  • mouseDoubleClickEvent()
  • mouseMoveEvent()
  • mousePressEvent()
  • mouseReleaseEvent()等。

详细的信息可查看帮助手册。

  • image.png
  • image.png

这些函数都是protected virtual的(虚函数,可在子类重写),也就是说,我们可以在子类中重新实现这些函数。

11.3 窗口关闭/重设大小事件函数

新建项目:11_demo

步骤:

  • 新增一个类继承于QWidget(新建工程)
  • 子类.h中声明重写的事件函数
  • 子类.cpp中去实现事件函数
//Widget.h
protected:
    void closeEvent(QCloseEvent* event);//光标定位到当前行,按下Alt + Enter可以快速在.cpp添加定义
    void resizeEvent(QResizeEvent *event);

//widget.cpp
void Widget::closeEvent(QCloseEvent *event)
{
    int ret = QMessageBox::question(this, "提示", "您确定要关闭窗口?");
    if(ret == QMessageBox::Yes)
    {
        event->accept();
    }
    else
    {
        event->ignore();
    }
}

void Widget::resizeEvent(QResizeEvent *event)
{
    qDebug()<<"oldsize = "<<event->oldSize();
    qDebug()<<"newsize = "<<event->size();
}

11.4 鼠标事件

新建项目12_demo(widget.h和widge.cpp从11_demo拷贝),并新增一个类MyLabel,继承于QLabel

  • image.png
  • image.png
  • image.png

鼠标按下、释放、移动事件步骤:

  • 新建label类继承QLabel(新建工程,ui绘制label,新增c++类继承QWiget,代码修改继承QLabel,ui绘制label提升新创建label类)
  • 查看帮助文档QLabel
    • image.png
  • 在mylabel.h声明事件函数,在mylabel.cpp实现事件函数(定位到.h的声明:Alt + Enter)
//mylabel.h
#ifndef MYLABEL_H
#define MYLABEL_H

#include <QLabel>


class MyLabel : public QLabel
{
    Q_OBJECT
public:
    explicit MyLabel(QWidget *parent = nullptr);

signals:

public slots:

protected:
    void mousePressEvent(QMouseEvent *ev);
    void mouseReleaseEvent(QMouseEvent *ev);
    void mouseMoveEvent(QMouseEvent *ev);

};

#endif // MYLABEL_H



//mylabel.cpp
#include "mylabel.h"
#include <QDebug>
#include <QMouseEvent>

MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
    this->setMouseTracking(true);//默认是false,只有鼠标按下后移动才会追踪,修改为true,鼠标移动就会立即追踪
}

void MyLabel::mousePressEvent(QMouseEvent *ev)
{
    qDebug()<<"标签被鼠标按下了";
    if(ev->button() == Qt::LeftButton)
    {
        qDebug()<<"左键按下了";
    }
    else if(ev->button() == Qt::RightButton)
    {
        qDebug()<<"右键按下了";
    }
    else
    {
        qDebug()<<"其他按下了";
    }
}

void MyLabel::mouseReleaseEvent(QMouseEvent *ev)
{
    qDebug()<<"在标签上的鼠标释放了";
}

void MyLabel::mouseMoveEvent(QMouseEvent *ev)
{
    qDebug()<<"在标签上的鼠标移动了" << "  " << ev->x() << "  " << ev->y();
}

11.5 重写事件分发函数

在项目12_demo中,重写一个按下Tab的键盘事件分发函数。

//.h
bool event(QEvent* event);

//.cpp
bool Widget::event(QEvent *event)
{
    //判断事件类型(键盘事件)
    if(event->type() == QEvent::KeyPress)
    {
        // 将 QEvent类型的event转换成QKeyEvent
        QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
        if(keyEvent->key() == Qt::Key_Tab)
        {
            qDebug()<<"Tab被按下";
            return true;
        }
    }

    return QWidget::event(event);
  
    //如果传入的事件已被识别并且处理,则需要返回 true,否则返回false。如果返回值是 true,那么Qt会认为这个事件已经处理完毕,不会再将这个事件发送给其它对象,而是会继续处理事件队列中的下一事件。
    //在event()函数中,调用事件对象的accept)和ignore()函数是没有作用的,不会影响到事件的传播。
}

注意:

  • 事件分发函数的返回值bool。
  • 我们处理过自己感兴趣的事件之后,可以直接返回true,表示我们已经对此事件进行了处理;对于其它我们不关心的事件,则需要调用父类的event()函数继续转发,否则这个组件就只能处理我们定义的事件了。

11.6 事件过滤器

有时候,对象需要查看、甚至要拦截发送到另外对象的事件。例如,对话框可能想要拦截按键事件,不让别的组件接收到;或者要修改回车键的默认处理。

使用:重写(但实际上,我们很少重写)

步骤:

  • 声明
  • 实现(重写):实现(先判断是否是要过滤事件的组件,如果是是要过滤事件的组件,再去判断事件是否过滤,如果过滤返回true,如果不过滤返回false;如果不是要过滤事件的组件,返回父类的事件过滤函数)
  • 安装过滤器(使用前要进行安装):void QObject::installEventFilter ( Q0bject * filterobj )
  • 移除过滤器:void Qobject::removeEventFilter( Qobject * filterobj )
//.h
virtual bool Q0bject::eventFilter ( Q0bject * watched,QEvent * event );

//.cpp
bool Mainwindow::eventFilter(Qobject *obj,QEvent *event){
    if (obj == textEdit) {
        if ( event->type() == QEvent : :KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);qDebug() <<"Ate key press" << keyEvent->key();
            return true;
        } else {
            return false;
        }
    } else {
        ll pass the event on to the parent classreturn QMainwindow : : eventFilter(obj, event) ;
    }
  
}

补充:我们可以向一个对象上面安装多个事件处理器,只要调用多次instailEventFilter()函数。如果一个对象存在多个事件过滤器,那么,最后一个安装的会第一个执行,也就是后进先执行的顺序。

12. 绘图与绘图设备

12.1 QPainter

  • Qt的绘图系统允许使用相同的API在屏幕和其它打印设备上进行绘制。整个绘图系统基于QPainter,QPainterDevice和 QPaintEngine_三个类.
  • QPainter用来执行绘制的操作;QPaintDevice是一个二维空间的抽象,这个二维空间允许QPainter_在其上面进行绘制,也就是QPainter_工作的空间;QPaintEngine提供了画笔(QPainter)在不同的设备上进行绘制的统一的接口。QPaintEngine_类应用于QPainter和 QPaintDevice之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心QPaintEngine这个类的。
  • 我们可以把QPainter理解成画笔;把QPaintDevice理解成使用画笔的地方,比如纸张、屏幕等;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,我们设计了QPaintEngine类,这个类让不同的纸张、屏幕都能使用一种画笔。

三个类之间的层次关系:

  • image.png
  • 上面的示意图告诉我们,Qt的绘图系统实际上是,使用QPainter在QPainterDevice上进行绘制,它们之间使用QPaintEngine进行通讯(也就是翻译QPainter的指令)。

12.2 画笔使用

新建项目:13_demo

类:QPainter、QPaintEvent

画笔使用:帮助手册

  • image.png
void Widget::paintEvent(QPaintEvent *p)//页面加载,update
{
    //qDebug()<<"------------";
    QPainter painter(this);
    painter.drawLine(80, 100, 500, 500);//单条线
    painter.setPen(Qt::red);//更换画笔颜色
    painter.drawRect(0, 0, 100, 400);//矩形
    painter.setPen(QPen(Qt::blue, 5));//更换画笔颜色和线条大小
    painter.drawEllipse(0, 0, 400, 200);
}

12.3 绘图设备

绘图设备是指继承QPainterDevice的子类。Qt 一共提供了四个这样的类,分别是QPixmap、QBitmap、QImage和QPicture。其中:

  • QPixmap专门为图像在屏幕上的显示做了优化。
  • QBitmap是QPixmap的一个子类,它的色深限定为1,可以使用QPixmap的isQBitmap(函数来确定这个QPixmap是不是一个QBitmap。
  • Qlmage专门为图像的像素级访问做了优化。
  • QPicture则可以记录和重现QPainter的各条命令。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

录音文件如何转换成文字文件?

当今社会&#xff0c;录音文件转换成文字文件的需求越来越多&#xff0c;例如会议记录、采访笔录、讲座笔记等。但是&#xff0c;手动逐词逐句转录录音文件费时费力&#xff0c;尤其是文件较大的情况下&#xff0c;会给人带来很大的烦恼。因此&#xff0c;本文将介绍几种常见的…

算法设计-hw2

一、从分治到动态规划 1.1 动态规划的性质 ​ 动态规划具有以下三个明显特性&#xff1a; 无后效性&#xff1a;如果给定某一阶段的状态&#xff0c;则在这一阶段以后过程的发展不受这阶段以前各段状态的影响。如果说的直白一些&#xff0c;就是当我们求出 dpidp_idpi​ 的时…

剑指 Offer 40. 最小的k个数 剑指 Offer 42. 连续子数组的最大和

剑指 Offer 40. 最小的k个数 输入整数数组 arr &#xff0c;找出其中最小的 k 个数。例如&#xff0c;输入4、5、1、6、2、7、3、8这8个数字&#xff0c;则最小的4个数字是1、2、3、4。 示例 1&#xff1a; 输入&#xff…

HTML5 <form> 标签

HTML5 <form> 标签 实例 带有两个输入字段和一个提交按钮的 HTML 表单&#xff1a; <form action"demo_form.php" method"get">First name: <input type"text" name"fname"><br>Last name: <input type&qu…

永久删除文件不得恢复吗 不小心永久删除文件怎么办

一般情况下&#xff0c;我们清理电脑文件时都不是彻底删除文件。这些被删除的文件&#xff0c;基本上都可以通过电脑回收站直接恢复。那么&#xff0c;永久删除文件不得恢复吗&#xff0c;不小心删除永久文件怎么办&#xff1f;今天作者就和大家一起探讨这两个问题。 一、永久…

网络映射工具

网络映射&#xff1a;定义 网络映射是用于发现新设备、接口以及可视化物理和虚拟网络连接的过程。网络拓扑映射提供对 IT 基础架构的全面可见性。网络映射工具通过精确定位网络故障来帮助简化网络监控。 如何进行网络映射 使用专门的网络映射软件是完成网络映射的最有效方法…

【ssl认证、证书】SSL 证书基本概念、证书格式、openssl和keytool的区别

文章目录1. keytool VS openssl2. X.509 VS PKCS2.1 PKCS2.2 X.5092.2.1 证书编码格式2.2.1.1 DER 证书编码格式二进制2.2.1.2 文本格式 pem2.2.2 文件后缀名3. 常见Web服务软件及证书格式参考相关文章&#xff1a;//-----------Java SSL begin----------------------【ssl认证…

安卓5.0以上7.0以下使用Termux

参考&#xff1a;https://zhuanlan.zhihu.com/p/400507701 说明&#xff1a; Termux支持5.0以上的安卓系统。 Termux7.3版本之后&#xff0c;仅支持7.0以上的安卓系统。 1 安装Termux 设备信息 手机&#xff1a;vivo x7 系统版本&#xff1a;Android 5.1.1 使用安装包&…

C 头文件

C 头文件 头文件是扩展名为 .h 的文件&#xff0c;包含了 C 函数声明和宏定义&#xff0c;被多个源文件中引用共享。有两种类型的头文件&#xff1a;程序员编写的头文件和编译器自带的头文件。 在程序中要使用头文件&#xff0c;需要使用 C 预处理指令 #include 来引用它。前…

第一节:机器学习和 scikit-learn 介绍

目录 0、介绍 1、监督学习介绍 监督学习的类型 2、非监督学习介绍 3、scikit-learn 介绍 0、介绍 机器学习&#xff08;英语&#xff1a;Machine learning&#xff09;如今越来越热门&#xff0c;而入门机器学习的门槛也变得越来越低。得益于优秀的机器学习框架和工具&#…

SqlServer实用系统视图,你了解多少?

SqlServer实用系统视图&#xff0c;你了解多少&#xff1f;前言master..spt_valuessysdatabasessysprocesses一套组合拳sysobjectssys.all_objectssyscolumnssystypessyscommentssysindexes结束语前言 在使用任何数据库软件的时候&#xff0c;该软件都会提供一些可能不是那么公…

算法设计-hw1

一、时间复杂度的估计 1.1 三种方法 ​ 估算分治问题的时间复杂度一共有三种普遍的方法&#xff1a; 1.2 代入法 ​ 代入法需要结合第二数学归纳法使用&#xff0c;使用起来还是很简单的&#xff08;难点在于猜测&#xff09;&#xff0c;比如估算 T(n)T(n4)T(3n4)n(n>4)…

40分钟快速入门Dart基础(上)

教大家快速学习一门新语言&#xff1a; 第一是零基础&#xff1a;那我们只能靠自己脚踏实地的多写多想慢慢熟悉你所选择的语言 &#xff0c;没有别的办法。&#xff08;但是dart确实目前为止最好学的没有之一的语言&#xff09;第二是有基础&#xff1a;小伙伴们如何快速学习…

C++环境设置

本地环境设置 如果您想要设置 C 语言环境&#xff0c;您需要确保电脑上有以下两款可用的软件&#xff0c;文本编辑器和 C 编译器。 文本编辑器 这将用于输入您的程序。文本编辑器包括 Windows Notepad、OS Edit command、Brief、Epsilon、EMACS 和 vim/vi。 文本编辑器的名…

文本编辑格式的 又一次进化 从 txt道md

别再傻傻用txt做笔记了,用md不香吗,可以设置颜色,字体大小 从 txt道md .md 和 .txt 都是文本文件格式&#xff0c;但它们之间有一些区别和进步&#xff1a; .md 文件可以使用 Markdown 语言编写&#xff0c;支持更丰富的文本格式&#xff0c;如标题、列表、链接、图片、代码块…

简化你的代码,提高生产力:这10个Lambda表达式必须掌握

前言 Lambda表达式是一种在现代编程语言中越来越常见的特性&#xff0c;可以简化代码、提高生产力。这篇文章将介绍10个必须掌握的Lambda表达式&#xff0c;这些表达式涵盖了在实际编程中经常用到的常见场景&#xff0c;例如列表操作、函数组合、条件筛选等。通过学习这些Lambd…

【C语言】数组指针-c语言的任督二脉

视频链接:bilibili 关于指针需要注意的地方 只有以下两种情况数组名表示的是整个数组 1.sizeof(数组名) 2.&数组名 除此之外数组名表示的都是首元素地址 一、字符指针 是一个指向字符的指针 int main() {char ch w;char* p &ch;//char* ch2 "abcdef"…

Netty组件Future、Promise、Handler、Pipline、ByteBuf

Future&Promise Netty中的Future与jdk中的Future同名&#xff0c;但是是两个接口&#xff0c;netty的Future继承自jdk的Future,而Promise又对netty Future进行了扩展 jdk Future只能同步等待任务结束(或成功、或失败)才能得到结果netty Future可以同步等待任务结束得到结…

阿里云弹性计算高级产品专家马小婷:ECS 使用成熟度评估与洞察

2023 年 3 月 22 日&#xff0c;【全新升级 阿里云 ECS CloudOps 2.0 来啦】发布会正式播出&#xff0c;本次发布会上阿里云宣布 CloudOps&#xff08;云上自动化运维&#xff09;套件全新升级&#xff0c;并发布了 CloudOps 云上自动化运维白皮书 2.0 版本。阿里云弹性计算高级…

【数据结构】栈和队列(笔记总结)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&…