#QT 笔记一

  • 重点:面试考试大概率涉及,需要不借助任何资料掌握。
  • 掌握:面试考试可能涉及,需要不借助任何资料掌握。
  • 熟悉:面试考试可能涉及,可以稍微参考资料掌握。
  • 了解:面试考试小概率涉及,面试拔高加分。

一、QT简介

1. Qt是什么?

这门课的定位:

  • C++的实践课
  • 系统性认识图像界面编程GUI
  • 新的就业方向

Qt虽然被经常当做是一个基于C++语言的图形用户界面(GUI)的开发框架,用来开发图形用户界面的应用程序,但这不是Qt的全部。

除此之外,Qt还支持许多非图形用户界面的功能。

例如:文件IO、多线程、网络通信、数据库、图像处理、音视频处理等。

学习Qt五阶段

  • 第一阶段 Qt简介、UI入门、信号槽
  • 第二阶段 Qt中常用组件与常用类
  • 第三阶段 多窗口编程
  • 第四阶段 文件IO、数据库、网络编程
  • 第五阶段 项目打包、项目实践

Qt在软件开发领域,使用广泛,主要分为以下三类:

1.纯软件开发

此方向与嵌入式的关系不大,这种方式开发的Qt程序本身作为一款独立的产品:

2.嵌入式上位机

用来控制和获取下位机的数据的应用程序,例如:

3. 直接成为嵌入式产品的控制程序

对于某些自带屏幕的嵌入式产品,内部可以使用Qt来开发交互程序。例:

Qt最重要的特点就是其跨平台特性

Qt的跨平台是       一次编程,到处编译

Java的跨平台是   一次编译,   到处运行

另外Qt还有一些通用特性:

  • 面向对象开发
  • 丰富的应用程序接口API
  • 易用的开发环境
  • 开源
  • 大量的开发文档

2. 新建项目

本次Qt开发仍然使用C++课程中的Qt Creator,但是需要把软件的编码设置成初始值:

更改完成后,才可以建立一个正常支持中文的项目。操作步骤如下:

  1. 在Qt Creator中点击
  2. 在弹出的窗口中,按照下图操作
  3. 在弹出的窗口中设置路径和名称,注意不要包含中文字符!!
  4. 直接点击“下一步”
  5. 在弹出窗口中,选择基类为“QDialog”,取消“创建界面”选项,然后点击”下一步“。
  6. 在项目管理界面,直接点击完成。可以看到项目中包含的文件
  7. 点击可以编译并运行项目,项目运行后可以看到一个可视化窗口

3. 构建目录和工作目录

项目运行后,存在两个重要目录构建目录和工作目录

3.1 构建目录

存放编译过程中产生的文件,位置在

3.2 工作目录

工作目录就是创建项目第三步时设置的目录,用于存放项目的源文件、头文件以及开发文件。

工作目录下包含以下文件

4. 项目结构

4.1 项目配置文件.pro

此文件在C++项目中也存在,用于配置项目的参数。

在Windows的文件管理器中,直接双击.pro文件可以导入项目。

.pro结构如下:

#-------------------------------------------------
#
# Project created by QtCreator 2024-05-29T10:04:09
#
#-------------------------------------------------
# 添加core核心模块 gui 传统图像界面模块
QT       += core gui

# 当Qt的主版本号大于4时 添加widgets新的图像界面模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

# 设置的项目文件,生成的可执行文件名
TARGET = Day1_24031_helloQt
# 项目构建方式
TEMPLATE = app

# 项目中包含的源文件
SOURCES += main.cpp\
        dialog.cpp
# 项目中包含的头文件
HEADERS  += dialog.h

4.2 用户文件.user

用户文件在Qt Creator中不可见,但是确实存在于工作目录中(项目构建后)。

此文件时Qt根据当前计算机的开发环境自动生成的文件,每个计算机几乎不通用此文件。因此在提交作业时,先删除此文件。

4.3 主文件 main.cpp

程序的入口,包含主函数。通常不要主动修改代码。

#include "dialog.h"
#include <QApplication>
/**
 * @brief main 主函数,程序的入口
 * @return 
 */
int main(int argc, char *argv[])
{
    // 创建了一个管家类栈内存对象,管理着整个程序
    QApplication a(argc, argv);
    // 创建了一个自定义对话框窗口的栈内存
    Dialog w;
    // 展示
    w.show();

    return a.exec(); // 进入主事件循环
}

4.4 头文件dialog.h

在Qt中一个自定义类的声明都写在.h中,这个类所用到的其他类头文件,也在此文件中引入。

#ifndef DIALOG_H
#define DIALOG_H
// Qt 自带类型通常以 Q 开头
#include <QDialog>
/**
 * @brief The Dialog class 自定义对话框窗口
 * 继承QDialog类(对话框窗口类)
 */
class Dialog : public QDialog
{
    Q_OBJECT // 先不管

public:
    Dialog(QWidget *parent = 0);// 构造函数的声明
    ~Dialog();                  // 析构函数的声明
};

#endif // DIALOG_H

4.5 源文件dialog.cpp

包含与头文件配套的类外定义。

#include "dialog.h"
/**
 * @brief Dialog::Dialog 构造函数定义
 * @param parent
 */
Dialog::Dialog(QWidget *parent)
    : QDialog(parent) // 透传构造
{
    
}

// 析构函数
Dialog::~Dialog()
{

}

5. 帮助文档

学习Qt一定要学习如何查阅帮助我呢当,通常对于不是很常用的内容在开发时都是随用随查,是官方的一手资料。

有三种查询文档的方式:

1.直接启动Assistant程序,是一个独立的文档程序。

2.在Qt Creator中点击,可以打开一个内置的文档Assistant程序。


3.光标点击到要查询的内容上,双击F1,可以直接跳转到对应文档内容。

需要关注文档重点的位置:

6. 调试信息

在C语言及C++中,无论是printf() 还是 cout输出的内容不区分前后台,但是在Qt中输出的内容是区分前后台的,通常前台指的是用户图形界面,用户可以直接看到,后台指的是在Qt Creator中(控制台)中,这里显示的后台信息是用户不可见,只对开发者开放。

使用QDebug类的qDebug()函数输出调试信息,支持中文。整个使用的方式与cout类似,但是有以下区别:

  • 连续输出时,自动添加空格
  • 每次输出语句结束时,自动添加换行

dialog.h

#ifndef DIALOG_H  
#define DIALOG_H  // 防止头文件被多次包含

#include <QDialog> // 包含 QDialog 类的定义,Qt库中用于创建对话框窗口的类。
#include <QDebug>  // 包含 QDebug 类的定义,用于调试输出信息

class Dialog : public QDialog  //定义了一个名为 Dialog 的类,并且该类继承自 QDialog 类。
{
    Q_OBJECT   //一个Qt宏,支持信号和槽机制

public:  // 声明了 Dialog 类的公有成员函数
    Dialog(QWidget *parent = 0);  // 接受一个 QWidget 指针作为父窗口参数
    ~Dialog(); //析构函数声明。用于清理 Dialog 对象销毁时的资源
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)  // 构造函数的实现,接受一个 QWidget 指针作为父窗口参数。
{
    qDebug() << "构造函数" << "Hello World!";
}

Dialog::~Dialog() / 析构函数的实现,用于在对象销毁时执行清理操作
{
    qDebug() << "析构函数";
}

tips:

当程序正在运行,再次运行可能会出现以下情况,只需要关闭正在运行的重复项目就可以了。

二、UI入门

1. QWidget类

QWidget是所有界面组件和窗口的基类,内部包含了一些最基础的界面特性。

常用属性:

  • x : const int

横坐标,每个图像的左上角为定位点,横轴的零点在最左边,正方向向右

  • y : const int

纵坐标,每个图像的左上角为定位点,纵轴的零点在最上边,正方向向下

虽然无法直接修改,可以通过以下函数来间接修改:

// 移动对象到(x,y)
// 参数1 新的横坐标
// 参数2 新的纵坐标
void	move(int x, int y)

需要注意的是,位置包含边框

  • width : const int

宽度,不包含边框

  • height : const int

高度,不包含边框

虽然无法直接修改,可以通过以下函数来间接修改:

// 参数1 新的宽度
// 参数2 新的高度
void	resize(int w, int h)

下面的函数可以同时设置以上四个属性:

void	setGeometry(int x, int y, int w, int h)

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    qDebug() << "构造函数" << "Hello World!";
    // 将对话框窗口移动到屏幕坐标 (200, 200) 位置。
    this->move(200,200);
    // 将对话框窗口的大小设置为宽200,高600像素。
    resize(200,600);
    // 同时更改对话框的位置和大小。
    // 将对话框移动到屏幕坐标 (300, 300) 位置,并将其大小设置为宽600,高200像素。
    setGeometry(300,300,600,200);
}

Dialog::~Dialog()
{
    qDebug() << "析构函数";
}

2. 添加子组件

上面的窗口什么都没有,实际上可以向窗口添加若干子组件,实现不同的显示与交互效果,本节以QPushButton(按压式按钮)组件为例。

QPushButton要持续存在,直到窗口关闭,因此使用堆内存。按照C++的内存回收逻辑,

子组件在父窗口的构造函数中创建,在析构函数销毁。

// 参数1 按钮上的文字
// 参数2 现阶段可以认为是给当前组件一个父窗口
QPushButton(const QString & text, QWidget * parent = 0)

以下是一个预设的QPushButton样式表,可以根据实际情况自行改动

#define QPushButton_STYTLE (QString("\
/*按钮普通态*/\
QPushButton\
{\
    font-family:Microsoft Yahei;\
    /*字体大小为20点*/\
    font-size:20pt;\
    /*字体颜色为白色*/\
    color:white;\
    /*背景颜色*/\
    background-color:rgb(14 , 150 , 254);\
    /*边框圆角半径为8像素*/\
    border-radius:8px;\
}\
/*按钮悬停态*/\
QPushButton:hover\
{\
    /*背景颜色*/\
    background-color:rgb(100 , 137 , 255);\
}\
/*按钮按下态*/\
QPushButton:pressed\
{\
    /*背景颜色*/\
    background-color:rgb(14 , 135 , 10);\
    /*左内边距为3像素,让按下时字向右移动3像素*/\
    padding-left:3px;\
    /*上内边距为3像素,让按下时字向下移动3像素*/\
    padding-top:3px;\
}"))

推荐两个配色网站:

在线颜色选择器 | RGB颜色查询对照表

Color Palette Generator - Create Beautiful Color Schemes

// 设置样式表
void	setStyleSheet(const QString & styleSheet)

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

#define QPushButton_STYTLE (QString("\
/*按钮普通态*/\
QPushButton\
{\
    font-family:Microsoft Yahei;\
    /*字体大小为20点*/\
    font-size:20pt;\
    /*字体颜色为白色*/\
    color:white;\
    /*背景颜色*/\
    background-color:rgb(255, 0, 102);\
    /*边框圆角半径为8像素*/\
    border-radius:8px;\
}\
/*按钮悬停态*/\
QPushButton:hover\
{\
    /*背景颜色*/\
    background-color:rgb(100 , 137 , 255);\
}\
/*按钮按下态*/\
QPushButton:pressed\
{\
    /*背景颜色*/\
    background-color:rgb(14 , 135 , 10);\
    /*左内边距为3像素,让按下时字向右移动3像素*/\
    padding-left:3px;\
    /*上内边距为3像素,让按下时字向下移动3像素*/\
    padding-top:3px;\
}"))


class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton *btn;// 成员变量 
   // 指向 QPushButton 对象的指针,表示一个按钮
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent) 
    : QDialog(parent)   //在构造 Dialog 对象时,调用 QDialog 基类的构造函数,并传递 parent 参数,初始化基类部分。
{
    move(300,300);
    resize(400,400);
    // 创建按钮按钮对象
    // 参数2使用了this指针+多态
    btn = new QPushButton("你好",this); 
    // 按钮的父对象为当前的 Dialog 对象,使用了 this 指针。
    // 这样可以确保按钮在对话框内,并且在对话框销毁时一起销毁
    
    btn->setStyleSheet(QPushButton_STYTLE); // 设置按钮的样式表为宏定义的PushButton_STYTLE
    btn->setGeometry(200,200,150,50);
}

Dialog::~Dialog()
{
    // C++内存回收
    delete btn;
}

//
//继承与多态:Dialog 类继承自 QDialog,并在构造函数中初始化基类部分。
//this 指针:在成员函数中使用 this 指针,指向当前对象。这在创建按钮时用于将按钮的父对象设置为当前 //Dialog 对象。
//内存管理:手动删除 btn,确保在 Dialog 对象销毁时释放按钮占用的内存,防止内存泄漏。

Tips:

帮助文档翻页技巧

  1. ​直接点击小箭头
  2. Alt + 方向键
  3. 鼠标侧键

三、信号槽

1. 概念

信号和槽是两种函数,这是Qt在C++ 的基础上新增的特性,类似于其他技术里的回调或委托。

可以理解为信号槽机制就是:“如果A对象……则B对象……”

信号槽通过程序员提前设定的约定,可以实现对象之间的通信,有两个先决条件:

  • 通信对象必须从QObject类中派生出来   QObject是所有Qt类型的基类
  • 类中要有Q_OBJECT宏

2. 函数原型

信号槽需要在使用之前进行约定,这个约定被称为连接,使用下面的函数实现:

【例子】如果小曾考100,则彤姐请吃饭

// 参数1 发射者,表示因发起的对象,通常是一个名词(小曾)
// 参数2 信号函数,表示因发起的动作,通常是一个动词(考100),需要用SIGNAL()包裹
// 参数3 接收者,表示果发起的对象,通常是一个名词(彤姐)
// 参数4 槽函数,表示果发起的动作,通常是一个动词(请吃饭),需要用SLOT()包裹
QObject::​connect(const QObject * sender, 
                 const char * signal, 
                 const QObject * receiver,
                 const char * method)[static]

3. 实现

为了学习,把信号槽分为以下三种实现方式。

  • 自带信号→自带槽
  • 自带信号→自定义槽
  • 自定义信号

3.1 自带信号自带槽

这种连接方式是最简单的,因为信号函数和槽函数是Qt内置的,只要查询出函数后,使用connect函数连接即可。

【例子】点击按钮,关闭窗口

#include "dialog.h" // 包含自定义的头文件"dialog.h",其中声明了Dialog类

// Dialog类构造函数,接受一个QWidget指针作为父窗口参数
Dialog::Dialog(QWidget *parent)
    : QDialog(parent) // 调用基类QDialog的构造函数进行初始化
{
    move(300,300); // 将对话框移动到屏幕坐标 (300, 300) 位置
    resize(400,400); // 将对话框大小设置为 400x400 像素

    // 创建按钮对象,显示文本“你好”,将其父对象设置为对话框本身
    // 参数1:按钮显示的文本
    // 参数2:父对象,指定为当前对话框(this)
    btn = new QPushButton("你好",this);

    // 设置按钮的样式表,假设QPushButton_STYTLE是已定义的样式字符串
    btn->setStyleSheet(QPushButton_STYTLE);

    // 设置按钮的位置和大小
    // 参数1:按钮左上角的X坐标
    // 参数2:按钮左上角的Y坐标
    // 参数3:按钮的宽度
    // 参数4:按钮的高度
    btn->setGeometry(200,200,150,50);

    // 连接按钮的clicked信号到对话框的close槽函数
    // 使用新式信号和槽连接语法
    // 参数1:信号发送者对象(btn)
    // 参数2:信号(&QPushButton::clicked)
    // 参数3:信号接收者对象(this,即对话框)
    // 参数4:槽函数(&QDialog::close)
    connect(btn, &QPushButton::clicked, this, &QDialog::close);
}

// Dialog类析构函数
Dialog::~Dialog()
{
    // 释放按钮对象所占用的内存,以防止内存泄漏
    delete btn;
}

3.2 自带信号自定义

Qt不可能给我们内置所有动作特别一些复杂动作需要开发者手动编写槽函数这种方法也是所有连接最多

槽函数实际上一种特殊成员函数在声明权限作用主要是修饰作为普通成员函数效果不影响信号槽连接效果

【例子】点击按钮向右移动10像素同时输出当前窗口坐标

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton *btn;
   // 自定义槽函数
private slots:// 最小权限法则
    void mySlot(); // 小驼峰命名法:第一个单词首字母小写,其他单词首字母大写
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    move(300,300);
    resize(400,400);
    btn = new QPushButton("移动",this);
    btn->move(200,200);

    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot())); 
    // 将按钮的 clicked() 信号连接到当前对象的 mySlot() 槽函数。
    // 当按钮被点击时,将调用 mySlot()。
}

Dialog::~Dialog()
{

}

// 源文件定义
void Dialog::mySlot()
{
    // 先获得之前的坐标
// this 指针指向当前对象,而当前对象是一个 QDialog 对象。
// QDialog 继承自 QWidget,QWidget 类提供了用于获取窗口位置和大小的相关成员函数,
// 如 x()、y()、width() 和 height() 等
// 由于 Dialog 类继承自 QDialog,而 QDialog 又继承自 QWidget,
// 所以 this->x() 实际上是调用了 QWidget 的 x() 方法。
// QWidget 类的方法可以直接在 Dialog 对象中使用,因为 Dialog 是 QWidget 的子类。
    int x = this->x();
    int y = this->y();
    // 移动到目标位置
    move(x + 10,y + 10);
    // 输出当前位置
    qDebug() << this->x() << this->y();

}

3.3 自定义信号

为了讲解强行使用自定义信号并非问题最优解主要写法

信号函数一种非常特殊函数因为只有声明没有定义没有函数因此无法调用只能使用emit关键字发射

例子】点击按钮关闭窗口

3.1信号连接方式

腾讯文档-流程图插件icon-default.png?t=N7T8https://docs.qq.com/flowchart-addon

本节强行中间添加一层自定义信号转发过程

腾讯文档-流程图插件

表示信号槽连接 表示内部实现

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton *btn;

private slots:
    void mySlot(); // 自定义槽函数

    // 声明自定义信号
signals:
    void mySignal();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(400,400);
    move(400,400);
    btn = new QPushButton("关闭窗口",this);
    btn->move(200,200);
    // 第一个信号槽连接
    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot()));

    // 第二个信号槽连接
    connect(this,SIGNAL(mySignal()),
            this,SLOT(close()));
}

Dialog::~Dialog()
{
    delete btn;
}

void Dialog::mySlot()
{
    qDebug() << "发射信号!";
    // 发射自定义信号
    emit mySignal();
}

3. 信号传参

例子】点击按钮按钮上显示点击次数

正常解法

QPushButton文字属性text : QString可以使用setter函数更改按钮文字

void	setText(const QString & text)

腾讯文档-流程图插件

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPushButton>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton *btn;
    int count;
private slots:
    void mySlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    move(200,200);
    btn = new QPushButton("0",this);
    btn->move(200,200);

    connect(btn,SIGNAL(clicked()),
            this,SLOT(mySlot()));
    count = 0;
}

Dialog::~Dialog()
{

}
void Dialog::mySlot()
{
    // 静态局部变量
    // static int count = 0;
    count++;
    // 类型转换int → QString
    QString text = QString::number(count);
    // 更改按钮文字
    btn->setText(text);
}
需要注意的
  • 理论上可以传递任意多个多个参数但是实际上1-2居多
  • 信号参数个数必须大于等于槽函参数个数
  • 信号的参数类型必须槽函数参数类型匹配

5. 对应关系

5.1 一对多

一对多一个信号可以连接多个槽函数对于一对多关系可以合并成一对一因为槽函也是一个成员函数可以整合一个槽函数调用

dialog.h

#ifndef DIALOG_H
#define DIALOG_H  // 防止头文件被重复包

#include <QDialog>  //  Qt 中的对话框基类
#include <QPushButton> // 包含了 QPushButton 类的定义
#include <QDebug> // 包含了 QDebug 类的定义,用于调试输出
class Dialog : public QDialog  /声明了一个名为 Dialog 的类,并且它是 QDialog 的子类
{
    Q_OBJECT  //宏  信号和槽机制会用到

public:
    Dialog(QWidget *parent = 0); // 构造函数 接受一个父级窗口指针参数 默认没有父窗口
    ~Dialog(); // 析构函数  销毁对象时清理

private: //声明了两个私有成员变量
    QPushButton *btn1; // 指向 QPushButton 的指针,表示按钮
    QPushButton *btn2;

private slots: // 声明了三个私有槽函数
    void slot1();
    void slot2();
    void slot3();

};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent) // Dialog 类的构造函数实现,初始化列表将 parent 参数
                      //传递给基类 QDialog 的构造函数。
{
    resize(500,500); //  设置对话框的尺寸为500x500像素
    move(200,200);
    btn1 = new QPushButton("一对多",this); // 创建一个新的按钮 btn1 
                                           // 并将其设置为 Dialog 对象的子对象
    btn1->move(200,200);
    btn2 = new QPushButton("一对一",this);
    btn2->move(200,300);

    // 一对多
    connect(btn1,SIGNAL(clicked()),
            this,SLOT(slot1()));
    //将 btn1 的 clicked 信号连接到 Dialog 类的 slot1() 槽函数。



    connect(btn1,SIGNAL(clicked()),
            this,SLOT(slot2()));
    //将 btn1 的 clicked 信号连接到 Dialog 类的 slot2() 槽函数。

 

    // 一对多的是可以灵活处理每一个对应关系
    // 例如可以随时断开某个信号槽的连接
    // 断开的方式与连接的方式完全一致,只要在函数名前加dis
    disconnect(btn1,SIGNAL(clicked()),
               this,SLOT(slot2()));

    // 一对一
    connect(btn2,SIGNAL(clicked()),
            this,SLOT(slot3()));
    // 将 btn2 的 clicked 信号连接到 Dialog 类的 slot3() 槽函数。
}


Dialog::~Dialog() // 销毁对象时执行清理工作
{
    delete btn1; // 释放 btn1 按钮的内存
    delete btn2; // 释放 btn2 按钮的内存
}



void Dialog::slot1()
{
    qDebug() << "槽函数1";
}

void Dialog::slot2()
{
    qDebug() << "槽函数2";
}


void Dialog::slot3()
{
    // 直接调用槽函数
    slot1();
    slot2();
}

5.2多对一

多对一多个信号连接同一个槽函数多对一问题在于槽函数无法直接判断哪个信号触发槽函数但是可以通过sender函数槽函数获得发射对象通过对象比对判断来源

dialog.h

#ifndef DIALOG_H
#define DIALOG_H  //防止头文件重复定义

#include <QDialog> //对话框基类

#include <QPushButton> // 包含了 QPushButton 类的定义,这是一个按钮控件
#include <QDebug>  // 包含了 QDebug 类的定义,用于调试输出

class Dialog : public QDialog
{
    Q_OBJECT //声明一个名为 Dialog 的类,继承自 QDialog,并启用 Qt 的信号和槽机制


public:
    Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    QPushButton *btn1;
    QPushButton *btn2;

private slots:
    // 多对一槽函数
    void moreToOneSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"


Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    resize(500,500);
    btn1 = new QPushButton("按钮1",this);
    btn2 = new QPushButton("按钮2",this);
    btn1->move(200,200);
    btn2->move(200,300);

    connect(btn1,SIGNAL(clicked()),this,SLOT(moreToOneSlot()));
    connect(btn2,SIGNAL(clicked()),this,SLOT(moreToOneSlot()));
}


Dialog::~Dialog()
{
    delete btn1;
    delete btn2;
}


void Dialog::moreToOneSlot()
{
    // 通过sender函数来获得发射者对象
    if(btn1 == sender())
    {
        qDebug() << "按钮1";
    }else if (btn2 == sender())
    {
        qDebug() << "按钮2";
    }
}

四、基本组件

1. Designer设计师

Qt包含一个Designer 程序,用于通过可视化界面设计开发界面

保存文件格式为.ui(界面文件)界面文件内部使用xml语法标签式语言

Qt Creator创建项目选中界面文件选项可以让自带的窗口使用创建使用界面文件。

可以看到项目里多了一个界面文件双击此文件可以直接使用内置Designer程序打开并设计

Designer区域划分如下

所有Designer操作都可以通过C++代码实现

2.布局Layout

可以布局看成一个透明盒子内部可以放置子组件这些子组件按照布局预设规则自动排序

垂直布局:内部组件竖着排成一排。

水平布局:内部组件横着排成一排。

格栅布局内部组件排布成n*m

表单布局:用户随意搭建

垂直布局和水平布局使用方式类似只是不同常用属性如下

选中布局,点击可以打破布局

布局可以贴合窗口只要选中窗口对象再次点击按钮之一即可

使用伸展器可以填充空白

布局可以嵌套对于外层布局而言内层布局相当一个外层布局子组件

3. QWidget

QWidget属性Designer显示为淡黄色下面一些常用属性

4. 界面文件C++代码关系

5. QLabel标签

QLabel用于显示文字图片需要注意QLabel不能用户交互只能展示使用因此没有合适信号函数

QLabel常用属性如下

Qt可以直接本地读取图片支持相对路径绝对路径但是建议这样原因换一台计算机运行程序图片路径可能会存在变化

建议先把图片导入项目成为项目资源直接使用Qt虚拟资源路径导入图片可以任何环境使用这些资源图片

Qt支持以下几种常用图片格式

jpg(不包含透明度)、png(包含透明度)、gif(动图)等

注意导入图片不能特别分辨率过高或文件体积过大)因为图片操作非常消耗资源

下面图片成为项目资源操作步骤

  1. 命名图片文件(不包含中文)放到项目工作目录
  2. Qt Creator选中项目名称鼠标右键点击添加新文件
  3. 弹出窗口按照下图所示进行操作
  4. 弹出窗口资源文件命名:res
  5. 项目管理界面直接点击完成可以看到项目多了一个.qrc格式文件。
  6. 选中qrc文件,点击,可以资源文件新建一个虚拟路径
  7. 选中qrc文件,点击,可以导入图片项目作为资源
  8. 导入成功可以.qrc文件中看到导入成功的图片
  9. 点击重新构建项目然后可以Designer找到图片资源使用。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QPixmap>  // 图片类
#include <QSize>    // 大小类
#include <QMovie>   // 电影类

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QMovie* movie;// 电影对象,用于播放gif
};

#endif // DIALOG_H
dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    // 创建一个图片对象
    // 图片资源的路径(qrc文件中选择图片右键复制)
    QPixmap pic(":/new/prefix1/st.png");
    // 创建一个包含目标缩放大小的QSize
    // 参数1 宽度
    // 参数2 高度
    QSize size(ui->label_2->width(),ui->label_2->height());

    // 缩放
    // 参数1 QSize对象,表示目标尺寸
    // 参数2 缩放模式Qt::AspectRatioMode的枚举值
    // 参数3 以速度或质量优先 Qt::TransformationMode
    // 返回值 转换后的QPixmap
    pic = pic.scaled(size,Qt::IgnoreAspectRatio,Qt::FastTransformation);

    // 使用组件显示图片
    ui->label_2->setPixmap(pic);


    // 创建电影对象
    // 参数为资源路径
    movie = new QMovie(":/new/prefix1/gege.gif");
    // 给QLabel设置电影
    ui->label->setMovie(movie);
    // 播放电影
    movie->start();
}

Dialog::~Dialog()
{
    delete ui;
    delete movie;
}
需要注意的是,尽量在项目开发之前使用PS等软件预先处理好图片,减少代码运行的开销,提升代码的运行效率,减少资源占用。

6.QAbstractButton按钮类(掌握)

QAbstractButton类是按钮的抽象基类,内部包含按钮很多基础属性和函数。

QAbstractButton 
                          QCheckBox  多选按钮
                          QRadioButton 单选按钮
                          QPushButton   按压式按钮
                          QToolButton    工具按钮

常用属性:

文本

图标

图标尺寸

是否可选

是否已选

按钮类常用信号如下:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDebug>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private slots:
    // 与void	toggled(bool checked)连接的槽函数
    void toggledSlot(bool);
    // 主动获取按钮的checked值
    void btnClickedSlot();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    // 连接信号槽
    connect(ui->radioButton,SIGNAL(toggled(bool)),
            this,SLOT(toggledSlot(bool)));

    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot()));
}

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

void Dialog::toggledSlot(bool checked)
{
    qDebug() << "今晚吃不吃KFC" << checked;
}

void Dialog::btnClickedSlot()
{
    // 主动获取按钮状态,以肯德基为例
    bool result = ui->radioButton->isChecked();
    qDebug() << "今晚吃不吃KFC" << result;
}

可以使用QButtonGroup组件对多个按钮进行分组,这是一种按钮的逻辑分组,没有任何UI效果,其主要的目的是用一个信号槽同时监控多个按钮对象的状态。

信号函数如下:

参数中表示当前触发按钮的本身,int id表示当前触发按钮的序号

// 向按钮组添加按钮
void QButtonGroup::​addButton(QAbstractButton * button, int id = -1)

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;

private slots:
    // 与void	valueChanged(int i)链接的槽函数
    void valueChangedSlot(int);
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->dial,SIGNAL(valueChanged(int)),
            this,SLOT(valueChangedSlot(int)));
}

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

void Dialog::valueChangedSlot(int value)
{
    ui->horizontalScrollBar->setValue(value);
    ui->verticalScrollBar->setValue(value);
    ui->horizontalSlider->setValue(value);
    ui->verticalSlider->setValue(value);
    ui->spinBox->setValue(value);
    ui->doubleSpinBox->setValue(value);
    ui->progressBar->setValue(value);
}

五、常用类

1. QString字符串类

QString是Qt的字符串类,使用Unicode编码。

C++的std::string使用ASCII编码

QString的每个字符都是一个16位的QChar,而不是8位的char。

如果后续的学习工作中出现中文乱码问题,可以参考:

​​​​​​从此乱码是路人

QString几乎支持所有的std::string的API 

除此之外也会增加一些新函数

// int → QString
// 参数1 要转换的数字
// 参数2 进制
// 返回值:转换后的新QString对象
QString QString::​number(int n, int base = 10)[static]


// int → QString
// 参数1 要转换的数字
// 参数2 进制
// 返回值:转换之后当前对象,支持链式调用
QString & QString::​setNum(int n, int base = 10)


// QString → int
// 参数1 转换成功或失败
// 参数2 进制
// 返回值 转换后的int数值,转换失败返回0
int QString::​toInt(bool * ok = 0, int base = 10) const

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    QString str = "けこか你好吗";
    ui->label->setText(str);
    // int → QString
    int a = 255;
    qDebug() << QString::number(a,16);// "ff"
    qDebug() << QString::number(a,8); // "377"
    qDebug() << str.setNum(a,2).append("!");

    // QString → int
    bool result = false;
    str = "0";
    qDebug() << str.toInt(&result);
    qDebug() << result;
    str = "lol";
    qDebug() << str.toInt(&result);
    qDebug() << result;
}

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

2. 容器类

Qt  容器类    C++的STL中的容器类

1.重写

2.速度和存储优化

3.减少了可执行文件的生成体积

4.兼容C++的STL容器的接口

5.更安全,多个线程中并发访问

2.1 顺序容器——QList类

  1. 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件”
  2. C++  -->    C++ Class    --> choose
  3. 在弹出的窗口中输入类名(使用帕斯卡命名法\大驼峰)
  4. 在项目管理界面点击完成,可以看到新的文件在项目中存在了

student.h

#ifndef STUDENT_H
#define STUDENT_H
// 引入Qt的头文件
#include <QString>

class Student
{
public:
    Student(QString ,int ,QString);
    ~Student();

    QString getName() const;
    void setName(const QString &value);

    int getAge() const;
    void setAge(int value);

    QString getMajor() const;
    void setMajor(const QString &value);

private:
    QString name;
    int age;
    QString major;
};

#endif // STUDENT_H

student.cpp

#include "student.h"

Student::Student(QString name ,int age , QString major)
{
    this->name = name;
    this->age = age;
    this->major = major;
}

Student::~Student()
{

}
QString Student::getName() const
{
    return name;
}

void Student::setName(const QString &value)
{
    name = value;
}
int Student::getAge() const
{
    return age;
}

void Student::setAge(int value)
{
    age = value;
}
QString Student::getMajor() const
{
    return major;
}

void Student::setMajor(const QString &value)
{
    major = value;
}

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDebug>
#include <QList>
#include "student.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    // 创建一个QList对象
    QList<Student> class24031;
    // 准备一些学生对象
    Student s1("窦逸凡",18,"电子信息工程");
    Student s2("杨雨学",18,"LOL");
    Student s3("蔡徐坤",26,"唱跳rap篮球");
    Student s4("沈彤",25,"班主任");
    Student s5("韦朕",27,"PUBG");

    // 添加元素
    class24031.append(s1);// 向后追加
    class24031.push_back(s2);// 向后追加
    class24031.push_front(s3);// 向前追加
    class24031.prepend(s4);// 向前追加
    class24031 << s5;

    // 取出单个元素
    qDebug() << class24031.at(2).getName();

    // 插入元素
    // 参数1 插入的位置
    // 参数2 插入的元素
    class24031.insert(1,s5);

    qDebug() << "_____________________" << endl;
    // STL迭代器
    for(QList<Student>::const_iterator iter = class24031.begin();
        iter != class24031.end();iter++)
    {
        Student s = *iter;
        qDebug() << s.getName() << s.getAge() << s.getMajor();
    }

    qDebug() << "_____________________" << endl;

    // Java 风格迭代器
    // 只读:QListIterator
    // 读写:QMutableListIterator
    QListIterator<Student> iter(class24031);
    while(iter.hasNext())
    {
        Student s = iter.next();// 向后移动迭代器
        qDebug() << s.getName() << s.getAge() << s.getMajor();
    }

    QList<int> list;
    list << 34 << 12 << 56 << 34 << 77;
    list.removeAll(34); // 移除所有34
    list.removeFirst(); // 移除第一个元素
    list.removeLast(); // 移除最后一个元素

    list << 111 << 111 << 111;
    
    list.removeOne(111);// 移除第一个111
    list.removeAt(0);   // 移除第一个元素
    
    // 修改元素
    // 参数1 修改元素的位置
    // 参数2 修改元素的数值
    list.replace(0,666);
    qDebug() << list;
    
    
}

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

2.2 关联容器——QMap类

重新实现了STL里的std::map类

QMap也兼容了map类的所有API接口,也增加了新的Qt的API。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QMap>// 头文件
#include <QDebug>

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{

    QMap<QString,QString> map;// 创建一个堆内存的关联容器
    // 插入数据
    // 参数1 KEY
    // 参数2 VALUE
    map.insert("姓名","蔡徐坤");
    map.insert("年龄","25");
    map.insert("地址","北京东城区");
    map.insert("专业","练习生");
    map.insert("爱好","唱跳rap篮球");


    // 如果容器中的元素类型支持qDebug输出,则容器本身也支持
    qDebug() << map;

    // 删除键值对
    qDebug() << map.remove("地址"); // 1
    qDebug() << map.remove("地址"); // 0

    // 判断某个键在不在
    qDebug() << map.contains("地址");
    if(map.contains("爱好"))
        map["爱好"] = "唱跳";

    // 取出元素
    // 参数1 KEY
    // 参数2 取出VALUE失败时,输出的默认值
    qDebug() << map.value("年龄","empty");
    qDebug() << map.value("年龄2","empty");

    qDebug() << map;

    qDebug() << endl;

    // STL迭代器
    for(QMap<QString,QString>::iterator iter = map.begin();
        iter != map.end(); iter++)
    {
        // 输出键与值
        qDebug() << iter.key() << iter.value();
    }

    // Java风格迭代器
    // 读写:QMutableMapIterator<Key, T>
    // 只读:QMapIterator<Key, T>
    QMapIterator<QString,QString> iter(map);
    while(iter.hasNext())
    {
        iter.next();
        // 输出键与值
        qDebug() << iter.key() << iter.value();
    }

}

Dialog::~Dialog()
{

}

3. Qt数据类型熟悉)

3.1 跨平台数据类型

Qt一个跨平台开发框架所以必须保障每个平台数据类型长度保持一致因此Qt常见数据类型定义类型符号

甚至可以QVariant完成数据类型转换

3.2 QVariant 统一变量类型

QVariant类型可以常见Qt类型完成相互转换因此此类型具有类似于多态性质通常作为中间函数返回值

   // int → QString
    qint64 a = 12345;
    QVariant qv(a);
    QString text = qv.toString();
    qDebug() << text; // "12345"

3.3 QStringList 字符串列表

几乎相当于QList<QString>

4. 时间与日期处理

Qt使用QDate处理日期使用QTime处理时间使用QDateTime处理时间日期以下QDateTime讲解

需要注意是,QDateTime数据来自于系统时间日期所以修改系统数据影响到QDateTime数据

// 返回1970年1月1日00:00:00到现在毫秒数
qint64 QDateTime::​currentMSecsSinceEpoch()[static]
// 返回一个包含当前时间和日期的QDateTime对象
QDateTime QDateTime::​currentDateTime()[static]
// 格式化时间与日期
// 参数为时间和日期的格式
QString QDateTime::​toString(const QString & format) const

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDateTime>
#include <QDebug>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    qint64 start = QDateTime::currentMSecsSinceEpoch();
    ui->setupUi(this);

    qint64 time = QDateTime::currentMSecsSinceEpoch();
    qDebug() << time - start;

    // 输出随机数
    // 生成随机数种子
    qsrand(time);
    // 1-10随机数
    int rand = qrand() % 10 + 1;
    qDebug() << rand;

    // 获取一个基于当前时间和日期的对象
    QDateTime dt = QDateTime::currentDateTime();
    // 时间和日期的格式化
    QString text = dt.toString("yyyy-MM-dd hh:mm:ss t");
    qDebug() << text;
}

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

还有一些时间日期相关组件

.ui

dialog.h

#ifndef DIALOG_H //防止同一个头文件被重复包含
#define DIALOG_H

#include <QDialog> //Qt对话窗口基类
#include <QDateTime> //包含 QDataTime类的定义,处理日期时间
#include <QDebug> //将调试信息输出到控制台
#include <QDate> //包含QData类的定义,处理日期

namespace Ui { //定义一个命名空间 Ui
class Dialog;  //Dialog 的类的前向声明
}

class Dialog : public QDialog //声明Dialog,继承QDialog
{
    Q_OBJECT //宏,实现信号和槽机制

public:
    explicit Dialog(QWidget *parent = 0);//声明Dialog类的构造函数
                              //显式构造,接受QWight指针作为它的父对象
                                    //默认为0,表示没有父对象
    ~Dialog();//声明析构函数,清理对象分配的资源,释放内存

private:
    Ui::Dialog *ui; //声明一个名为ui的Ui::Dialog类指针成员变量
                   //访问Qt Designer中创建的用户界面元素

private slots:
    // 与void	currentPageChanged(int year, int month)链接的槽函数
    void pageSlot(int,int);
    // 与void	selectionChanged()连接的槽函数
    void seletChangeSlot();
};

#endif // DIALOG_H  //关闭保护  防止同一个头文件被重复包含

dialog.cpp

#include "dialog.h"  //dialog.h头文件
#include "ui_dialog.h"  //Qt Designer生成的与UI文件交互的头文件

Dialog::Dialog(QWidget *parent) : //Dialog类的构造函数,接受QWight类型的指针作为父对象,
    QDialog(parent), //在初始化列表中调用了QDialog的构造函数
    ui(new Ui::Dialog)  //创建ui::Dialog类实例,赋值给成员变量ui
{
    //获取毫秒级别的时间戳,存储到start变量中
    qint64 start = QDateTime::currentMSecsSinceEpoch();

    //调用ui对象的setupUI函数,将组件加载到窗口
    ui->setupUi(this);

    //获取毫秒级别的时间戳,存储到time变量中
    qint64 time = QDateTime::currentMSecsSinceEpoch();
    //计算差值  
    qDebug() << time - start;
  

    // 输出随机数
    // 生成随机数种子
    qsrand(time);
    // 1-10随机数
    int rand = qrand() % 10 + 1; 
    qDebug() << rand;

    // 获取一个基于当前时间和日期的对象
    QDateTime dt = QDateTime::currentDateTime();
    // 时间和日期的格式化
    QString text = dt.toString("yyyy-MM-dd hh:mm:ss t");
    qDebug() << text;

    
    connect(ui->calendarWidget,SIGNAL(currentPageChanged(int,int)),
            this,SLOT(pageSlot(int,int)));
 // 将 ui->calendarWidget 的 currentPageChanged(int,int) 信号
//连接到 Dialog 类的//(pageSlot(int,int) 槽函数。
    connect(ui->calendarWidget,SIGNAL(selectionChanged()),
            this,SLOT(seletChangeSlot()));
    
}

Dialog::~Dialog() //析构函数的实现
{
    delete ui; //清除对象资源分配,释放内存
}

void Dialog::pageSlot(int year, int month)
{
    qDebug() << "翻页了" << year << "年" << month << "月";
}

void Dialog::seletChangeSlot()
{
    // 获取当前日期
    QDate date = ui->calendarWidget->selectedDate();
    // 格式化
    qDebug() << "选择了" << date.toString("dddd-dd");
}

5. QTimer定时器类

QTimer可以实现一个延时任务周期任务

QTimer常用属性

  • interval : int  间隔时间单位毫秒
  • singleShot : bool   是否一次性
  • active : const bool 当前定时器运行状态

QTimer常用函数

// 构造函数
QTimer::QTimer(QObject * parent = 0)
// 启动定时器,如果定时器已经在运行,则会重启
void QTimer::​start()[slot]
// 定时器触发时发射的信号
void QTimer::​timeout()[signal]
// 停止定时器运行
void QTimer::​stop()[slot]

使用LCD Number 七段数码管来显示当地时间

dialog.h

#ifndef DIALOG_H //防止同一个头文件被重复包含
#define DIALOG_H

#include <QDialog> //包含QDialog类的头文件,Qt对话窗口基类
#include <QTimer> //包含QTimer类的头文件,定时
#include <QDebug> //包含QDebug类的头文件,将调试信息输出到控制台
#include <QDateTime> // 包含QDateTime类的头文件,日期和时间

namespace Ui { //定义一个命名空间 Ui
class Dialog; //Dialog 的类的前向声明
}

class Dialog : public QDialog   //声明Dialog,继承QDialog
{
    Q_OBJECT //宏,实现信号和槽机制

public:
    explicit Dialog(QWidget *parent = 0); //声明Dialog类的构造函数,
                                       //显式构造,接受QWight指针作为它的父对象
                                  //默认为0,表示没有父对象
    ~Dialog(); //声明析构函数,清理对象分配资源,释放内存

private:
    Ui::Dialog *ui; //声明一个名为ui的Ui::Dialog类指针成员变量
                     //访问Qt Designer中创建的用户界面元素
    QTimer *timer; //声明了一个名为 timer 的指针,其类型为 QTimer
private slots:
    // 与void	timeout()连接的槽函数
    void timeoutSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h" //对话框类的头文件
#include "ui_dialog.h" //Qt Designer 自动生成的用户界面文件的头文件。

Dialog::Dialog(QWidget *parent) : //Dialog类的构造函数,接受QWight类型的指针作为父对象,
    QDialog(parent), //在初始化列表中调用了基类QDialog的构造函数,
    ui(new Ui::Dialog) //创建新的ui::Dialog类实例,赋值给成员变量ui
{
    //调用ui对象的setupUi函数,将组件加载到窗口
    ui->setupUi(this);

    // 创建定时器对象
    timer = new QTimer(this);
    // 设置触发时间
    timer->setInterval(1000);//1.增加刷新频率
    // 设置是否为单次触发
    timer->setSingleShot(false);

    //通过 connect() 函数将定时器的 timeout() 信号与 timeoutSlot() 槽函数连接起来,
    //这样每当定时器超时时,都会调用 timeoutSlot() 函数。
    connect(timer,SIGNAL(timeout()),
            this,SLOT(timeoutSlot()));
    timer->start();
    timeoutSlot(); //2.开启之后再采样
}

Dialog::~Dialog() //析构函数实现
{
    delete ui; //清理对象分配资源,释放内存空间

}


void Dialog::timeoutSlot()
{
    qDebug() << "到点儿了";

    // 获得当前时间的对象
    QDateTime dt = QDateTime::currentDateTime();
    // 格式化
    QString text = dt.toString("hh:mm:ss");
    ui->lcdNumber->display(text);
}

六、多窗口编程

1. QMessageBox消息对话框

QMessageBox继承自QDialog,用于显示一个模态对话框,用于用户前台信息通知或询问用户问题并接受问题答案。

QMessageBox可以支持使用四种预设的风格样式:

QDialog的Qt源码中的派生类往往都是一些在特定场合下使用的预设好的对话框窗口,这些窗口的使用无需创建对象,直接使用静态成员函数弹窗,使用返回值来作为这个窗口的结果。

// 参数1 parent参数
// 参数2 窗台标题,相当于窗口类的windowsTitle属性
// 参数3 信息内容
// 返回值 用户点击按钮的类型
StandardButton QMessageBox::​critical|information|question|warning
(
    QWidget * parent, 
    const QString & title, 
    const QString & text
    )
[static]

自定义窗口:

// 设置窗口标题
void QMessageBox::setWindowTitle(const QString & title)
// 设置图标
void	setIconPixmap(const QPixmap & pixmap)

// 添加按钮到QMessageBox
// 参数1 添加的按钮对象
// 参数2 添加按钮的规则
void QMessageBox::addButton(QAbstractButton * button, ButtonRole role)

dialog.h

#ifndef DIALOG_H //防止同一个头文件重复包含
#define DIALOG_H

#include <QDialog> //Qt对话窗口基类
#include <QButtonGroup> //按钮组
#include <QMessageBox> //模态对话框

namespace Ui { //定义一个命名空间 Ui
class Dialog; //Dialog 的类的前向声明
}

class Dialog : public QDialog // 声明Dialog,继承QDialog;
{
    Q_OBJECT //宏,信号和槽机制

public:
    explicit Dialog(QWidget *parent = 0);//声明Dialog类的构造函数
                                  //显式构造,接受QWidget指针作为它的父对象
                                   //默认为0,表示没有父对象
    ~Dialog();//声明析构函数,清理对象分配的资源,释放内存

private:
    Ui::Dialog *ui;  //声明一个名为ui的Ui::Dialog类指针成员变量
                     //访问Qt Designer中创建的用户界面元素
    QButtonGroup *group; //声明一个名为group的QButtonGroup类指针成员变量

    QMessageBox *box;
        // 显示自定义窗口
    void customMessageBox();
        // 按钮
        QPushButton *btn1;
        QPushButton *btn2;
        QPushButton *btn3;

private slots:
    // 与void	buttonClicked(int id)连接的槽函数
    void btnsClickedSlot(int);
};

#endif // DIALOG_H //关闭防止同一个头文件重复包含保护

dialog.cpp

#include "dialog.h"  //包含对话框类的头文件
#include "ui_dialog.h" //Qt Designer自动生成的用户界面文件的头文件

Dialog::Dialog(QWidget *parent) ://Dialog类的构造函数,接受QWight类型的指针作为父对象
    QDialog(parent),//在初始化列表中调用了QDialog的构造函数并传递参数,
                    //Dialog 对象就会作为一个 QDialog 对象进行初始化
    ui(new Ui::Dialog) //创建新的ui::Dialog类实例,赋值给成员变量ui
{
    //调用ui对象的setupUi函数,将组件加载到窗口
    ui->setupUi(this);

    group = new QButtonGroup(this);
    group->addButton(ui->pushButton,1);
    group->addButton(ui->pushButton_2,2);
    group->addButton(ui->pushButton_3,3);
    group->addButton(ui->pushButton_4,4);
    group->addButton(ui->pushButton_5,5);

    connect(group,SIGNAL(buttonClicked(int)),
            this,SLOT(btnsClickedSlot(int)));

}

Dialog::~Dialog()//构造函数实现
{
    delete ui; //清理对象分配资源,释放内存
}


void Dialog::customMessageBox()
{
    // 创建一个QMessageBox对象
    box = new QMessageBox(this);
    // 设置标题
    box->setWindowTitle("自定义QMessageBox");
    // 图片对象
    QPixmap map(":/new/prefix1/image1.png");
    // 设置图片
    box->setIconPixmap(map);
    // 设置信息
    box->setText("这是一个我自己设计的QMessageBox");

    // 初始化按钮
    btn1 = new QPushButton("是",box);
    btn2 = new QPushButton("否",box);
    btn3 = new QPushButton("啥也不是",box);

    // 把按钮放置到QMessageBox 的规定位置
    box->addButton(btn1,QMessageBox::YesRole);
    box->addButton(btn2,QMessageBox::NoRole);
    box->addButton(btn3,QMessageBox::HelpRole);

    // 显示对话框
    box->show();
}

void Dialog::btnsClickedSlot(int id)
{
    if(id == 1)
    {
        QMessageBox::StandardButton result = QMessageBox::question(this,"问题","你今早吃了吗?");
        if(result == QMessageBox::Yes)
        {
            close();
        }
        else if(result == QMessageBox::No)
        {

        }
    }else if(id == 2)
    {
        QMessageBox::information(this,"信息","今天早吃的油旋");
    }else if(id == 3)
    {
        QMessageBox::warning(this,"警告","今天早上吃的不太舒服");
    }else if(id == 4)
    {
        QMessageBox::critical(this,"错误","食物中毒了");
    }else if(id == 5)
    {
     customMessageBox();
    }
    else
    {

    }
}

2. QWidget类

QWidget类是所有窗口和组件的基类,之前认识他更多的是站在组件的角度,实际上QWidget作为所有窗口的类似,也具有很多窗口的特性,窗口类的继承结构如下:

新建一个项目,使自带窗口类继承QWidget:

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 当QWidget类的构造函数parent参数使用默认值0时,表示创建的是独立窗口
    // 当QWidget类的构造函数parent参数传递参数时(parent),新创建的QWidget对象
    // 会成为子窗口(内嵌窗口)
    Widget w;
    w.show();

    return a.exec();
}

QWidget类作为窗口的基类,内部也规定了很多处窗口的特性:

  • windowTitle : QString

窗口标题

  • windowFlags : Qt::WindowFlags

使用setter函数设置多个标记时,使用 | 分割(多个窗口的标记之间有可能会出现冲突),如下所示

setWindowFlags(Qt::WindowStaysOnTopHint|Qt::FramelessWindowHint)

// 设置窗口flags
void	setWindowFlags(Qt::WindowFlags type)

// 设置窗口状态
void QWidget::​setWindowState(Qt::WindowStates windowState)

窗口状态有时可能与窗口标记(windowFlags)冲突

示例代码:

setWindowState(Qt::WindowFullScreen);
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    // 设置窗口标题
    setWindowTitle("我的窗口");
    // 设置窗口最上层显示且无边框
    setWindowFlags(Qt::WindowStaysOnTopHint|Qt::FramelessWindowHint);

    // 设置窗口状态全屏
    setWindowState(Qt::WindowFullScreen);
}

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

3. parent参数

目前对parent参数的理解有以下几点:

  • parent 参数表示子组件位于哪个窗口
  • parent参数决定了QWidget对象是独立窗口还是内嵌窗口

实际上parent参数还表示Qt的内存回收机制,如果对象a作为对象b构造时的parent参数,表示对象a是对象b 的父对象(非继承),这是一种内存回收依赖关系对象b跟随对象a销毁一并销毁此时无需手动控制对象b销毁过程(手动调用delete)。

绝大多数情况下建议堆内存对象传递parent参数

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDebug>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;

private slots:
    void btnYesClickedSlot();
    void btnNoClickedSlot();
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnYesClickedSlot()));
    connect(ui->pushButton_2,SIGNAL(clicked()),
            this,SLOT(btnNoClickedSlot()));
}

Dialog::~Dialog()
{
    qDebug() << "析构函数";
    delete ui;
}

void Dialog::btnYesClickedSlot()
{
    // 传递parent参数,在创建一个Dialog对象
    Dialog *d = new Dialog(this);
    d->show();
}

void Dialog::btnNoClickedSlot()
{
    // 不传递parent参数,在创建一个Dialog对象
    Dialog *d = new Dialog;
    d->show();
}

4.堆栈窗口QStackedWidget

通常作为独立窗口的内嵌窗口(组件),并于QListWidget进行联动。

// 每次选择行改变时发射,参数为当前行号

void QListWidget::currentRowChanged(int currentRow)[signal]

// 设置当前页

void QStackedWidget::setCurrentIndex(int index)[slot]

// 添加一个Item

void QListWidget::addItem(const QString & label)

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    // 向QListWidget里添加Item
    ui->listWidget->addItem("红");
    ui->listWidget->addItem("绿");
    ui->listWidget->addItem("蓝");
    // 连接信号槽
    connect(ui->listWidget,SIGNAL(currentRowChanged(int)),
            ui->stackedWidget,SLOT(setCurrentIndex(int)));
}

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

5. QMainWindow 主窗口类

QMainWindow是最适合作为主窗口的类型,因为其包含多个组成成分

5.1 QMenuBar 菜单栏

菜单栏的构建可以通过Designer,也可以通过C++代码实现,但是不能混用。

相关C++函数如下:

// 向菜单栏中添加一级菜单

// 参数为菜单的文字

// 返回值是添加的菜单对象

QMenu * QMenuBar::​addMenu(const QString & title)
 

// 向菜单中添加下一级菜单

// 参数为菜单的文字

// 返回值是添加的菜单对象

QMenu * QMenu::​addMenu(const QString & title)

// 向菜单中添加动作

// 参数为动作的文字

// 返回值是添加的动作对象

QAction * QMenu::​addAction(const QString & text)

dialog.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 添加一级菜单
    QMenu* menuFile = ui->menuBar->addMenu("文件");
    QMenu* menuEdit = ui->menuBar->addMenu("编辑");
    // 向一级菜单中添加二级菜单
    QMenu* meunRecent = menuFile->addMenu("最近访问的文件...");
    // 向一级菜单中添加动作
    menuFile->addAction("新建文件或项目...");
    menuFile->addAction("打开文件或项目...");
    // 向二级菜单中添加动作
    meunRecent->addAction("Qt.cpp");
    meunRecent->addAction("Qt.h");
}

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

为了使QAction点击后有触发效果,需要使用对应的信号连接槽函数,QAction的信号函数如下:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void actionNewTriggledSlot();// 点击新建项目的槽函数
    void actionCppSlot();// 点击Qt.cpp的槽函数
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    // 添加一级菜单
    QMenu* menuFile = ui->menuBar->addMenu("文件");
    QMenu* menuEdit = ui->menuBar->addMenu("编辑");
    // 向一级菜单中添加二级菜单
    QMenu* meunRecent = menuFile->addMenu("最近访问的文件...");
    // 向一级菜单中添加动作
    QAction *actionNew =  menuFile->addAction("新建文件或项目...");
    menuFile->addAction("打开文件或项目...");
    // 向二级菜单中添加动作
    QAction *actionCPP = meunRecent->addAction("Qt.cpp");
    meunRecent->addAction("Qt.h");

    // 连接信号槽
    connect(actionNew,SIGNAL(triggered(bool)),
            this,SLOT(actionNewTriggledSlot()));

    connect(actionCPP,SIGNAL(triggered(bool)),
            this,SLOT(actionCppSlot()));

}

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

void MainWindow::actionNewTriggledSlot()
{
    ui->plainTextEdit->appendPlainText("新建了一个文档");
}

void MainWindow::actionCppSlot()
{
    ui->plainTextEdit->appendPlainText("打开了Qt.cpp");
}

5.2 QToolBar 工具栏

工具栏的按钮往往使用菜单栏中的QAction对象,但是需要给QAction设置图标:

// 添加一个已有的QAction对象(提前设置图标)到工具栏
void QToolBar::addAction(QAction * action)

mainWindow.cpp

ui->mainToolBar->addAction(ui->actionQt_cpp);

5.3 QStatusBar 状态栏

可以通过以下两个槽函数进行信息的展示和消除:

// 在状态栏展示信息
// 参数1 展示信息的内容
// 参数2 信息显示的时间(单位毫秒),默认值0表示一直显示
void QStatusBar::showMessage(const QString & message, int timeout = 0)[slot]

// 清空状态栏显示
void QStatusBar::​clearMessage()[slot]

ui->statusBar->showMessage("新建了!",3000);

6. 新建自定义窗口类

定义Qt窗口类的步骤如下:

  1. 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件”
  2. 在弹出的窗口中,如下所示操作
  3. 在弹出的窗口中,选择界面模版,点击“下一步”
  4. 在弹出的窗口中,输入类名后,点击“下一步”
  5. 在项目管理界面,直接点击“完成”。可以看到新的窗口类文件已经添加到项目中。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include "mydialog.h"

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;

private slots:
    void btnClickedSlot();// 按钮点击的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnClickedSlot()));
}

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

void Dialog::btnClickedSlot()
{
    // 创建MyDialog对象并展示
    MyDialog *md = new MyDialog(this);

    md->show();

    // 限制按钮只能点一次
    ui->pushButton->setEnabled(false);
}

7. 对象传值

7.1 父对象子对象

【例】转动黄球蓝球跟着

此处父对象子对象指的Qtparent参数依赖关系并非继承关系后文

这种情况最佳解决方案使用C++成员函数传参

7.2 子对象父对象

【例子】转动蓝球,黄球一起跟着转

这种情况最佳解决方案信号槽传参子对象发射参数信号函数父对象使用带参数槽函数接收

8. 事件机制(熟悉)

事件机制是Qt的一种底层机制,通过层层传递,程序员可以在传递的层级中检测或处理这些事件。

本次学习主要在窗口类中实现事件函数,从而检测到事件的传递。可以利用事件的触发时机从而实现一些特定的效果。事件函数的类型众多,包括但不限于:

// 绘制事件
void QWidget::paintEvent(QPaintEvent * event) [virtual protected] 
// 大小改变事件
void QWidget::resizeEvent(QResizeEvent * event) [virtual protected] 

// 鼠标按压事件
void QWidget::mousePressEvent(QMouseEvent * event) [virtual protected]
// 鼠标释放事件
void QWidget::mouseReleaseEvent(QMouseEvent * event) [virtual protected]
// 鼠标双击事件
void QWidget::mouseDoubleClickEvent(QMouseEvent * event) [virtual protected]
// 鼠标移动事件
void QWidget::mouseMoveEvent(QMouseEvent * event) [virtual protected]

// 移动事件
void QWidget::moveEvent(QMoveEvent * event) [virtual protected]

// 按键按压事件
void QWidget::keyPressEvent(QKeyEvent * event) [virtual protected]
// 按键释放事件
void QWidget::keyReleaseEvent(QKeyEvent * event) [virtual protected]

// 获取焦点事件
void QWidget::focusInEvent(QFocusEvent * event) [virtual protected]
// 失去焦点事件
void QWidget::focusOutEvent(QFocusEvent * event) [virtual protected]

// 关闭事件
void QWidget::closeEvent(QCloseEvent * event) [virtual protected]

// 鼠标进入事件
void QWidget::enterEvent(QEvent * event) [virtual protected]
// 鼠标离开事件
void QWidget::leaveEvent(QEvent * event) [virtual protected]

事件函数的基础使用只需要在对应的类中覆盖基类的事件函数即可。事件函数的参数包含了当前事件数据的对象。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QDebug>
#include <QPainter> // 画家类
#include <QKeyEvent> // 按键事件类

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();
protected:
    // 绘制事件
    void paintEvent(QPaintEvent * event);

    // 按键按压事件
    void keyPressEvent(QKeyEvent * event);

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
}

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

void Dialog::paintEvent(QPaintEvent *event)
{
    // 创建一个画家对象
    QPainter painter(this);
    // 创建图片对象
    QPixmap map(":/new/prefix1/five.jpg");

    // 绘制图片
    // 参数1 横轴坐标
    // 参数2 纵轴坐标
    // 参数3 绘制宽度
    // 参数4 绘制高度
    // 参数5 绘制内容(图片对象)
    painter.drawPixmap(0,0,this->width(),this->height(),map);
    qDebug() << this->width() << this->height();
    qDebug() << this->x() << this->y();
}

void Dialog::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_A) // 如果按键为A
    {
        int value = ui->progressBar->value();
        ui->progressBar->setValue(--value);
    }else if (event->key() == Qt::Key_D)
    {
        int value = ui->progressBar->value();
        ui->progressBar->setValue(++value);
    }else if (event->key() == Qt::Key_W)
    {
        ui->progressBar->setValue(100);
    }else if (event->key() == Qt::Key_S)
    {
        ui->progressBar->setValue(0);
    }
}


 

【问题】事件函数和信号槽的区别?

  • 事件由具体对象进行处理
  • 信号由具体对象主动产生
  • 改写事件处理函数可能导致程序行为发生改变
  • 信号是否存在对应的槽函数不会改变程序的行为

七、文件IO

1. QFileDialog 文件对话框

与QMessageBox一样,QFileDialog也继承了QDialog类,直接使用静态成员函数弹窗,弹窗的结果通过返回值获取

// 获得一个打开或保存的文件路径
// 参数1 父对象
// 参数2 即windowTitle属性(界面标题)
// 参数3 在哪个目录下打开,默认值为文件的工作目录
// 参数4 文件格式过滤器
// 返回值:选择的文件路径,如果选择失败,返回空字符
QString QFileDialog::​getOpenFileName|getSaveFileName
    (QWidget * parent = 0, 
    const QString & caption = QString(), 
    const QString & dir = QString(), 
    const QString & filter = QString())[stat]

2. QFileInfo文件信息类(熟悉)

只需要创建对象后通过各种成员函数直接获取文件信息

// 构造函数
// 参数为文件路径,如果文件合法,仍然可以创建出QFileInfo对象
QFileInfo::​QFileInfo(const QString & file)
// 判断文件是否存在
// 如果存在返回true 否则返回false
bool QFileInfo::​exists() const
// 返回文件的大小,单位字节
qint64 QFileInfo::​size() const
// 获取文件的基础名称
QString QFileInfo::​baseName() const
// 返回最后一次修改的日期和时间
QDateTime QFileInfo::​lastModified() const
// 返回可读性
bool QFileInfo::​isReadable() const

3. QFile文件读写类

Qt所有IO都继承QIODevice QIODevice规定最基础IO相关

这些接口虽然不同派生类实现有所区别调用方式一致

// 构造函数
// 参数为文件的路径,如果是非法路径,也能创建出对象,但是不能正常IO
QFile::QFile(const QString & name)

// 判断QFile对应的文件是否存在
bool QFile::exists() const

// 打开文件流
// 参数为打开的模式
// 返回值为打开的结果
bool QIODevice::open(OpenMode mode)[virtual]

// 读取最大长度为maxSize的数据到返回值中
QByteArray QIODevice::read(qint64 maxSize)[virtual]

// 写出数据
// 参数为写出的内容
// 返回值为实际写出的字节数,出错返回-1
qint64 QIODevice::write(const QByteArray & byteArray)[virtual]

// 是否读到文件尾
bool QIODevice::atEnd() const[virtual]

// 关闭文件流
void QIODevice::close()[virtual]

// 清空缓存区
bool QFileDevice::flush()

// 返回输入流的大小,单位字节
qint64 QIODevice::size() const[virtual]

dialog.h

dialog.cpp

思考】上面代码真的没问题

当拷贝大文件时会出现程序卡顿如果尝试关闭则会触发

4.UI与耗时操作

在默认情况Qt项目单线程这个自带线程用于处理程序主要任务UI交互也被称为主线程UI线程

如果主线程执行耗时操作IO复杂算法导致主线程原本执行操作被阻塞甚至无法关闭形成假死”现象

操作系统发现某个进程无法正常关闭会弹出程序未响应窗口引导用户选择是否强制关闭当前进程

解决以上问题方法使用多线程

5. QThread 线程类

5.1复现程序未响应熟悉)

QThreadQt线程可以使用下面函数实现函数模拟耗时操作

// 强制线程睡眠msecs个毫秒
void QThread::msleep(unsigned long msecs)[static]

dialog.h

dialog.cpp

5.2 创建并启动一个子线程(掌握)

主线程以外线程都是 子线程子线程不能执行主线程UI操作只能执行耗时操作

下面创建启动一个自定义子线程步骤

  1. Qt Creator选择项目名称鼠标右键添加新文件。
  2. 在弹出窗口先设置类名然后填写基类名称QObject最后点击下一步
  3. 在项目管理界面直接点击完成可以看到线程文件已经创建。
  4. 选择新建头文件继承QObject更改为QThread
  5. 选择新建.cpp文件透传构造QObject改成QThread
  6. 自定义线程覆盖基类QThreadrun函数

// 此函数是子线程执行的起始点、也是子线程结束点
void QThread::run()[virtual protected]

  1. run函数函数体编写子线程要执行耗时操作
  2. 创建自定义子线程对象并调用start函数启动子线程

// 启动子线程,调用此函数后,会在子线程中自动执行run函数
// 
void QThread::start(Priority priority = InheritPriority)[slot]

5.3 异步刷新(掌握)

实际开发中主线程子线程可能毫无关系前提下各干各的最常见情况主线程分配子线程一个耗时任务子线程需要耗时任务执行情况反馈主线程主线程刷新子线程耗时操作并展示UI效果

例如子线程拷贝文件主线程显示拷贝文件进度

通常子线程主线程对象子对象因此异步刷新就是对象通信问题使用信号槽解决

写一个简单伪拷贝例子使用for循环模拟文件拷贝功能

进度条100弹出提示框问题频繁抖动窗口会出现焦点抢夺问题导致卡死解决方法使用hide()函数隐藏主窗口

5.4 线程停止(掌握)

子线程往往执行耗时操作伴随着循环因此建议使用粗暴方式直接停止线程因为强行停止会导致耗时操作资源无法回收问题

可以循环基础上设置标志位方式使线程停止

dialog.h

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值