QT学习笔记

哔哩哔哩上的视频学习地址最新QT从入门到实战完整版

视频对应下载安装QT方法QT下载安装

Day1

1 Qt的概述

1.1 什么是Qt

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

1.2 Qt的发展史

1991年 Qt最早由奇趣科技开发

1996年 进入商业领域,它也是目前流行的Linux桌面环境KDE的基础

2008年 奇趣科技被诺基亚公司收购,Qt称为诺基亚旗下的编程语言

2012年 Qt又被Digia公司收购

2014年4月 跨平台的集成开发环境Qt Creator3.1.0发布,同年5月20日配发了Qt5.3正式版,至此Qt实现了对iOS、Android、WP等各平台的全面支持。

当前Qt最新版本为 5.8.0

1.3 版本

Qt按照不同的版本发行,分为商业版和开源版

  • 商业版

    为商业软件提供开发,他们提供传统商业软件发行版,并且提供在商业有效期内的免费升级和技术支持服务。

  • 开源的LGPL版本:

    为了开发自有而设计的开放源码软件,它提供了和商业版本同样的功能,在GNU通用公共许可下,它是免费的。

1.4 Qt的下载与安装

Qt 5.9 之后的安装包与之前相比,不再区分 VS 版本和 MinGW 版本,而是全都整合到了一个安装包中。因此,与之前的安装包相比,体积也是大了不少。官网下载很慢,所以建议找一些国内镜像站和迅雷下载,具体的下载参考下面的博客。

Qt下载(多种下载通道+所有版本)

QT的安装过程下面这个博客写的很详细,贴一下大佬的博客。

QT5.11下载与安装教程

1.5 Qt的优点

  • 跨平台,几乎支持所有的平台

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

  • 可以进行嵌入式开发。

1.6 成功案例

  • Linux桌面环境KDE

  • WPS Office 办公软件

  • Skype 网络电话

  • Google Earth 谷歌地图

  • VLC多媒体播放器

  • VirtualBox虚拟机软件

2 创建Qt程序

略。。。。。。。。

项目文件结构

image-20210618154731509

main.cpp文件

#include "mywidget.h"
#include <QApplication>// 包含一个应用程序类的头文件

//main程序入口  argc命令行变量的数量  argv命令行变量的数组
int main(int argc, char *argv[])
{
    //a应用程序对象,在Qt中,应用程序对象 有且仅有一个
    QApplication a(argc, argv);
    //窗口对象  myWidget父类  -> QWidget
    myWidget w;
    //窗口对象 默认不会显示,必须要调用show方法显示窗口
    w.show();

    //让应用程序对象进入消息循环
    //当代码阻塞到这行
    return a.exec();
    //相当于下面
//    while(true)
//    {
//         if(点击叉子)
//        {
//            break;
//        }
//    }
}

mywidget.h文件

#ifndef MYWIDGET_H //如果没有宏定义就创建这个宏
#define MYWIDGET_H

#include <QWidget> //包含头文件 QWidget 窗口类

class myWidget : public QWidget
{
    Q_OBJECT // Q_OBJECT宏,允许类中使用信号和槽的机制
 
public:
    myWidget(QWidget *parent = 0); //构造函数
    ~myWidget(); //析构函数
};

#endif // MYWIDGET_H

mywidget.cpp

#include "mywidget.h"

myWidget::myWidget(QWidget *parent) 
    : QWidget(parent)	//初始化队列
{
}

myWidget::~myWidget()
{

}

.pro文件

QT       += core gui   Qt包含的模块

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets  //大于4版本以上 包含 widget模块

TARGET = 01_FirstProject  //目标   生成的.exe程序的名称
TEMPLATE = app       	  //模板   应用程序模板  Application  


SOURCES += main.cpp      //源文件
        mywidget.cpp

HEADERS  += mywidget.h    //头文件

一些快捷键:

注释:Ctrl + /

整行移动:Ctrl + shift + ↑或↓

帮助文档:F1

自动对齐:Ctrl + i

同名之间.h和.cpp的切换 F4

3 Qt程序

3.1 按钮的创建

//1.创建一个按钮
QPushButton * btn = new QPushButton;
//btn->show(); //show以顶层方式弹出窗口控件
//让btn对象 依赖在 myWidget窗口中
btn->setParent(this);

//显示文本
btn->setText("第一个按钮");

//2.创建第二个按钮 按照控件的大小创建窗口
QPushButton * btn2 = new QPushButton("第二个按钮",this);

//移动btn2按钮
btn2->move(100,100);

//按钮可不可以 重新制定大小 可以!
btn2->resize(50,50);

//重置窗口大小
resize(600,400);

//设置固定窗口大小
setFixedSize(600,400);

//设置窗口标题
setWindowTitle("第一个窗口");

3.2 对象模型树

Qt对象树

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

  • QObject是以对象树的形式组织起来的。

    • 当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是 parent,也就是父对象指针。

    • 这相当于,在创建QObject对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children()列表。

    • 当父对象析构的时候,这个列表中的所有对象也会被析构。(注意,这里的父对象并不是继承意义上的父类!)这种机制在 GUI 程序设计中相当有用。例如,一个按钮有一个QShortcut(快捷键)对象作为其子对象。当我们删除按钮的时候,这个快捷键理应被删除。这是合理的。

  • QWidget是能够在屏幕上显示的一切组件的父类。

    • QWidget继承自QObject,因此也继承了这种对象树关系。一个孩子自动地成为父组件的一个子组件。因此,它会显示在父组件的坐标系统中,被父组件的边界剪裁。例如,当用户关闭一个对话框的时候,应用程序将其删除,那么,我们希望属于这个对话框的按钮、图标等应该一起被删除。事实就是如此,因为这些都是对话框的子组件。
    • 当然,我们也可以自己删除子对象,它们会自动从其父对象列表中删除。比如,当我们删除了一个工具栏时,其所在的主窗口会自动将该工具栏从其子对象列表中删除,并且自动调整屏幕显示。

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

  • 当一个QObject对象在堆上创建的时候,Qt 会同时为其创建一个对象树。不过,对象树中对象的顺序是没有定义的。这意味着,销毁这些对象的顺序也是未定义的。

  • 任何对象树中的 QObject对象 delete 的时候,如果这个对象有 parent,则自动将其从 parent 的children()列表中删除;如果有孩子,则自动 delete 每一个孩子。Qt 保证没有QObject会被 delete 两次,这是由析构顺序决定的。

如果QObject在栈上创建,Qt 保持同样的行为。正常情况下,这也不会发生什么问题。来看下下面的代码片段:

{
    QWidget window;
    QPushButton quit("Quit", &window);
}

作为父组件的 window 和作为子组件的 quit 都是QObject的子类(事实上,它们都是QWidget的子类,而QWidget是QObject的子类)。这段代码是正确的,quit 的析构函数不会被调用两次,因为标准 C++要求,局部对象的析构顺序应该按照其创建顺序的相反过程。因此,这段代码在超出作用域时,会先调用 quit 的析构函数,将其从父对象 window 的子对象列表中删除,然后才会再调用 window 的析构函数。

但是,如果我们使用下面的代码:

{
    QPushButton quit("Quit");
    QWidget window;
    quit.setParent(&window);
}

情况又有所不同,析构顺序就有了问题。我们看到,在上面的代码中,作为父对象的 window 会首先被析构,因为它是最后一个创建的对象。在析构过程中,它会调用子对象列表中每一个对象的析构函数,也就是说, quit 此时就被析构了。然后,代码继续执行,在 window 析构之后,quit 也会被析构,因为 quit 也是一个局部变量,在超出作用域的时候当然也需要析构。但是,这时候已经是第二次调用 quit 的析构函数了,C++ 不允许调用两次析构函数,因此,程序崩溃了。

由此我们看到,Qt 的对象树机制虽然帮助我们在一定程度上解决了内存问题,但是也引入了一些值得注意的事情。这些细节在今后的开发过程中很可能时不时跳出来烦扰一下,所以,我们最好从开始就养成良好习惯,在 Qt 中,尽量在构造的时候就指定 parent 对象,并且大胆在堆上创建。

4 信号和槽

4.1系统自定义信号和槽

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

代码如下(点击按钮关闭窗口)

connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);
connect(sender, signal, receiver, slot);
  • sender:发出信号的对象

  • signal:发送对象发出的信号

  • receiver:接收信号的对象

  • slot:接收对象在接收到信号之后所需要调用的函数(槽函数)

4.2 自定义信号和槽

例子:下课了,(信号)老师饿了,学生(槽)请吃饭

Teacher类定义信号

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


signals:
    //自定义信号  写到signals下
    //返回值是void ,只需要声明,不需要实现
    //可以有参数,可以重载
    void hungry();

public slots:

};

学生类中定义槽

class Student : public QObject
{
    Q_OBJECT
public:
    explicit Student(QObject *parent = 0);

signals:

public slots:
    //早期Qt版本 必须要写到public slots,高级版本可以写到 public或者全局下
    //返回值 void ,需要声明,也需要实现
    //可以有参数,可以发生重载
    void  treat();

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

定义一个ClassIsOver的函数来触发相应的信号

//创建一个老师对象
    this->zt = new Teacher(this);

    //创建一个学生对象
    this->st = new Student(this);


//    //老师饿了 学生请客的连接
    connect(zt,&Teacher::hungry,st,&Student::treat);

//    //调用下课函数
    classIsOver();
void Widget::classIsOver()
{
    //下课函数,调用后 触发老师饿了的信号
    //emit zt->hungry();
    //使用emit来触发
    emit zt->hungry("宫保鸡丁");
}

自定义信号槽需要注意的事项:

  • 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);

  • 信号和槽函数返回值是 void

  • 信号只需要声明,不需要实现

  • 槽函数需要声明也需要实现

  • 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;

  • 使用 emit 在恰当的位置发送信号;

  • 使用connect()函数连接信号和槽。

  • 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数

  • 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。

  • 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。

4.3 自定义信号和槽出现重载

在写connect时需要利用函数指针明确指向函数的地址,如下:

(函数指针定义方式:函数返回类型(*指针变量名)(函数参数列表))

    //connect(ls,&Teacher::hungry,st,&Student::treat);
    void(Teacher:: *teacherSignal)(QString) = &Teacher::hungry;
    void(Student:: *studengSlot)(QString) = &Student::treat;
    connect(ls,teacherSignal,st,studengSlot);

4.4 QString转为Char *

先.ToUtf8()转为QByteArray

再.Data()转为char *

4.5信号可以连接信号

按钮的点击出发了老师的信号,老师的信号触发了学生的信号

注意这里是无参函数,如果把void改成QString会报错,因为第二个connect的信号和槽的参数不一致,(在帮助文档中Signal的信号参数是布尔值)信号参数要大于等于槽的参数个数。

    //无参信号和槽连接
    void(Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
    void(Student:: *studentSlot2)(void)  = &Student::treat;
    connect(zt,teacherSignal2,st,studentSlot2);
    //信号连接信号
    connect(btn,&QPushButton::clicked, zt, teacherSignal2);

4.6断开信号操作

disconnect(zt,teacherSignal2,st,studentSlot2);

4.7信号和槽的扩展

  • 一个信号可以和多个槽相连

    如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  • 多个信号可以连接到一个槽

    只要任意一个信号发出,这个槽就会被调用。

  • 一个信号可以连接到另外的一个信号

    当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。

  • 槽可以被取消链接

    这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。

  • 信号槽可以断开

    利用disconnect关键字是可以断开信号槽的

  • 使用Lambda 表达式

    在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
    在连接信号和槽的时候,槽函数可以使用Lambda表达式的方式进行处理。后面我们会详细介绍什么是Lambda表达式

4.8 Lambda表达式

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。首先看一下Lambda表达式的基本构成:

[capture](parameters) mutable ->return-type
{
statement
}

[函数对象参数](操作符重载函数参数)mutable ->返回值{函数体}

1.函数对象参数:

[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:

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

2.操作符重载函数参数

标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

3.可修改标示符;

mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。

  QPushButton * myBtn = new QPushButton (this);
    QPushButton * myBtn2 = new QPushButton (this);
    myBtn2->move(100,100);
    int m = 10;

    connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });

    connect(myBtn2,&QPushButton::clicked,this,[=] ()  { qDebug() << m; });

    qDebug() << m;

4.函数返回值;

->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

5.是函数体

{},标识函数的实现,这部分不能省略,但函数体可以为空。

Day1 作业

要求:一个按钮文本是“打开”,点击后打开新窗口,并且按钮文本变为“关闭”,再次点击后,新窗口关闭,并且按钮文本再次变为“打开”。

自己的代码如下。

#include "widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(300,200);
    QPushButton * btn = new QPushButton("打开",this);
    btn->move(100,100);
    //定义是个bool值表示新窗口是否打开,同时控制按钮文本的显示
    bool is_open = false;
    //定义新窗口
    QWidget * new_widget = new QWidget;
    //连接信号和槽,按钮点击后,在Lambda表达式中写一个对于是否打开的判断,窗口关闭就打开窗口,窗口打开就关闭
    connect(btn,&QPushButton::clicked,new_widget,[=]()mutable{
        if(!is_open){
            new_widget->show();
            btn->setText("关闭");
            is_open = true;
        }
        else{
            btn->setText("打开");
            new_widget->close();
            is_open = false;
        }
    });
}

Widget::~Widget()
{
}

Day2

1 QMainWindow

QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。

具体看图:

img

1.1 菜单栏

菜单栏和菜单项的创建

    //菜单栏  只能最多有一个
    //菜单栏创建
    QMenuBar * bar = menuBar();
    //将菜单栏放入到窗口中
    setMenuBar(bar);

    //创建菜单
    QMenu * fileMenu = bar->addMenu("文件");
    QMenu * editMenu = bar->addMenu("编辑");

    //创建菜单项
    QAction * newAction = fileMenu->addAction("新建");
    //添加分割线
    fileMenu->addSeparator();
    QAction * openAction = fileMenu->addAction("打开");

1.2 工具栏

   //工具栏  可以有多个
    QToolBar * toolBar = new QToolBar(this);
    addToolBar(Qt::LeftToolBarArea,toolBar);//默认工具栏停靠在左边

    //后期设置 只允许 左右停靠
    toolBar->setAllowedAreas( Qt::LeftToolBarArea | Qt::RightToolBarArea );

    //设置浮动
    toolBar->setFloatable(false);

    //设置移动 (总开关)
    toolBar->setMovable(false);

    //工具栏中可以设置内容
    toolBar->addAction(newAction);
    //添加分割线
    toolBar->addSeparator();
    toolBar->addAction(openAction);
    //工具栏中添加控件
    QPushButton * btn = new QPushButton("aa" , this);
    toolBar->addWidget(btn);

1.3 状态栏

    //状态栏 最多有一个
    QStatusBar * stBar = statusBar();
    //设置到窗口中
    setStatusBar(stBar);
    //放标签控件
    QLabel * label = new QLabel("提示信息",this);
    stBar->addWidget(label);//在左侧提示

    QLabel * label2 = new QLabel("右侧提示信息",this);
    stBar->addPermanentWidget(label2);//在右侧提示

1.4 铆接部件 (浮动窗口)

    QDockWidget * dockWidget = new QDockWidget("浮动",this);
    addDockWidget(Qt::BottomDockWidgetArea,dockWidget);//设置默认创建在下面
    //设置后期停靠区域,只允许上下
    dockWidget->setAllowedAreas( Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea );

2 资源文件

  • 将图片文件 拷贝到项目位置下

  • 右键项目->添加新文件 –> Qt - > Qt recourse File - >给资源文件起名

  • res 生成 res.qrc

  • open in editor 编辑资源

  • 添加前缀 添加文件

  • 使用 “ : + 前缀名 + 文件名 ”

3 对话框

3.1基本概念

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

  • 模态对话框,就是会阻塞同一应用程序中其它窗口的输入。模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的

  • 与此相反的是非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。

模块对话框

//模态对话框的创建    
connect(ui->actionNew,&QAction::triggered,[=](){
    QDialog dlg(this);
    dlg.exec();
});

非模块对话框

//创建非模块对话框的时候,如果像下面那个写的话,窗口会弹出一下就消失,因为 这个是在栈上创建的变量,当Lambda中的执行完后就会释放栈中的变量,窗口就消失了,应该在堆上创建
connect(ui->actionNew,&QAction::triggered,[=](){
    QDialog dlg2(this);
    dlg2.show();
});
//在堆上创建,当你点击x关闭对话框的时候,此时的内存并没有释放,
//所以我们要对这个对话框的属性进行设置,让其点击关闭后释放内存
QDialog * dlg2 = new QDialog(this);
dlg2->show();
dlg2->setAttribute(Qt::WA_DeleteOnClose);

3.2标准对话框

我们要在帮助文档中去看QMessageBox中的内容

image-20210626171411644

我们可以看到其参数类型和返回值,其中通过判断StandarButton的返回值来获取用户点击的类型。

//错误对话框
//QMessageBox::critical(this,"critical","错误");

//信息对话框
//QMessageBox::information(this,"info","信息");

//提问对话框
//参数1  父亲  参数2  标题  参数3  提示内容  参数4 按键类型  参数5 默认关联回车按键
//        if (QMessageBox::Save  ==  QMessageBox::question(this,"ques","提问",QMessageBox::Save|QMessageBox::Cancel,QMessageBox::Cancel))
//        {
//            qDebug() << "选择的是保存";

//        }
//        else
//        {
//            qDebug() << "选择的是取消";
//        }

//警告对话框
//QMessageBox::warning(this,"warning","警告");

其他标准对话框

//其他标准对话框
//颜色对话框
QColor color =  QColorDialog::getColor(QColor(255,0,0));
qDebug() << "r = " << color.red() << " g = " << color.green() << " b  = " << color.blue() ;

//文件对话框  参数 1  父亲  参数2  标题   参数3  默认打开路径  参数4  过滤文件格式
//返回值是 选取的路径
QString str = QFileDialog::getOpenFileName(this,"打开文件","C:\Users\zhangtao\Desktop","(*.txt)");
qDebug() << str;

//字体对话框
bool flag;
QFont font = QFontDialog::getFont(&flag,QFont("华文彩云",36));
qDebug() << "字体:" << font.family().toUtf8().data() << " 字号 "<< font.pointSize() << " 是否加粗"<< 		font.bold() << " 是否倾斜"<<font.italic();

3.3 界面布局

​ 实现登陆窗口

​ 利用布局方式 给窗口进行美化

​ 选取 widget 进行布局 ,水平布局、垂直布局、栅格布局

​ 给用户名、密码、登陆、退出按钮进行布局

​ 默认窗口和控件之间 有9间隙,可以调整 layoutLeftMargin

​ 利用弹簧进行布局

3.4 控件

按钮组

  • QPushButton 常用按钮

  • QToolButton 工具按钮 用于显示图片,如图想显示文字,修改风格:toolButtonStyle , 凸起风格autoRaise

  • radioButton 单选按钮,设置默认 ui->rBtnMan->setChecked(true);

  • checkbox多选按钮,监听状态,2 选中 1 半选 0 未选中

QListWidget 列表容器

//利用listWidget写诗
//    QListWidgetItem * item = new QListWidgetItem("锄禾日当午");
//    //将一行诗放入到listWidget控件中
//    ui->listWidget->addItem(item);
//    item->setTextAlignment(Qt::AlignHCenter);
//QStringList   QList<QString>
QStringList list ;
list << "锄禾日当午" << "旱地和下土" << "谁知盘中餐"<< "粒粒皆辛苦";
ui->listWidget->addItems(list);
  • QListWidgetItem * item 一行内容

  • ui->listWidget ->addItem ( item )

  • 设置居中方式item->setTextAlignment(Qt::AlignHCenter);

  • 可以利用addItems一次性添加整个诗内容

QTreeWidget 树控件

    //treeWidget树控件使用

    //设置水平头
    ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");

    QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");
    QTreeWidgetItem * minItem = new QTreeWidgetItem(QStringList()<< "敏捷");
    QTreeWidgetItem * zhiItem = new QTreeWidgetItem(QStringList()<< "智力");
    //加载顶层的节点
    ui->treeWidget->addTopLevelItem(liItem);
    ui->treeWidget->addTopLevelItem(minItem);
    ui->treeWidget->addTopLevelItem(zhiItem);

    //追加子节点
    QStringList heroL1;
    heroL1 << "刚被猪" << "前排坦克,能在吸收伤害的同时造成可观的范围输出";
    QTreeWidgetItem * l1 = new QTreeWidgetItem(heroL1);
    liItem->addChild(l1);
  • 设置头

  • ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");

  • 创建根节点

  • QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");

  • 添加根节点 到 树控件上

  • ui->treeWidget->addTopLevelItem(liItem);

  • 添加子节点

  • liItem->addChild(l1);

QTableWidget 表格控件

//TableWidget控件
//设置列数
ui->tableWidget->setColumnCount(3);

//设置水平表头
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<< "性别"<< "年龄");

//设置行数
ui->tableWidget->setRowCount(5);

//设置正文
//ui->tableWidget->setItem(0,0, new QTableWidgetItem("亚瑟"));
QStringList nameList;
nameList<< "亚瑟"<< "赵云"<< "张飞"<< "关羽" << "花木兰";

QList<QString> sexList;
sexList << "男"<< "男"<< "男"<< "男"<< "女";

for(int i = 0 ; i < 5 ;i ++)
{
    int col = 0;
    ui->tableWidget->setItem(i,col++, new QTableWidgetItem(nameList[i]));
    ui->tableWidget->setItem(i,col++, new QTableWidgetItem(sexList.at(i)));
    //int 转 QString
    ui->tableWidget->setItem(i,col++, new QTableWidgetItem( QString::number(i+18)));
}

image-20210627115756911

stackedWidget栈控件

//栈控件使用
//设置默认定位 scrollArea
ui->stackedWidget->setCurrentIndex(1);

//scrollArea按钮,通过按钮点击切换不同的页面
connect(ui->btn_scrollArea,&QPushButton::clicked,[=](){
    ui->stackedWidget->setCurrentIndex(1);
});

下拉框

    //下拉框
    ui->comboBox->addItem("奔驰");
    ui->comboBox->addItem("宝马");
    ui->comboBox->addItem("拖拉机");

    //点击按钮 选中拖拉机选项
    connect(ui->btn_select,&QPushButton::clicked,[=](){
        //ui->comboBox->setCurrentIndex(2);
        ui->comboBox->setCurrentText("拖拉机");
    });

QLabel

//利用QLabel显示图片
ui->lbl_Image->setPixmap(QPixmap(":/Image/butterfly.png"));

//利用QLabel显示 gif动态图片
QMovie * movie = new QMovie(":/Image/mario.gif");
ui->lbl_movie->setMovie(movie);
//播放动图
movie->start();

Day3

1 封装自定义控件

封装一个类似如图的空间,spinBox和horizontalSlider中一个变动,另外一个也会跟着变动,按钮可以对其数值进行设置和获取

image-20210627221722410

1.点击添加新文件 - Qt – 设计师界面类 (.h .cpp .ui),创建要封装的控件名称

2.ui中 设计 QSpinBox和QSlider 两个控件

3.Widget中使用自定义控件,拖拽一个Widget,点击提升为,点击添加封装控件名称,点击提升

4.在封装控件中写信号槽,实现两者value改变的关联

//QSpinBox中的valueChanged有函数的重载,我们需要定义一个匿名函数指针来标识使用函数参数类型
void(QSpinBox:: * spinbox)(int) = &QSpinBox::valueChanged;
connect(ui->spinBox,spinbox,ui->horizontalSlider,&QSlider::setValue);
connect(ui->horizontalSlider,&QSlider::valueChanged,ui->spinBox,&QSpinBox::setValue);

5.在封装类中添加getNum()和setNum()来改变其数值

2 QT中的事件

添加一个类mylabel封装一些鼠标的事件,注意把建好的类修改成的继承,这样在在主窗口添加的Label才可以提升为我们定义的控件,修改三处

image-20210628112716054image-20210628112745336

定义鼠标的相应事件

myLabel::myLabel(QWidget *parent) : QLabel(parent)
{
    //当设置了这个后,鼠标不用点击,就会输出轨迹的点坐标
    setMouseTracking(true);
}

void myLabel::enterEvent(QEvent * event){
    //qDebug() << "鼠标进入了";
}
void myLabel::leaveEvent(QEvent*){
    //qDebug() << "鼠标离开了";
}
void myLabel::mousePressEvent(QMouseEvent *ev){
    if(ev->button()==Qt::LeftButton)
    {
        //globalX来看你的鼠标在整个屏幕中的,x是看你在这个控件中的坐标
        QString str = QString("鼠标按下了x= %1  y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
        qDebug() << str;
    }
}
void myLabel::mouseReleaseEvent(QMouseEvent *ev){
    //通过Qt:LeftButton来判断按键的点击类型
    if(ev->button()==Qt::LeftButton)
    {
        QString str = QString("鼠标释放了x= %1  y=%2").arg(ev->x()).arg(ev->y());
        qDebug() << str;
    }
}
void myLabel::mouseMoveEvent(QMouseEvent *ev){
//    if(ev->button()==Qt::LeftButton)
//    {
        QString str = QString("鼠标移动了x= %1  y=%2").arg(ev->x()).arg(ev->y());
        qDebug() << str;
//    }
}

Qt中的格式化输出

QString str = QString("鼠标按下了x= %1  y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());

3 定时器

QTimer * timer = new QTimer(this);
//启动定时器
timer->start(500);

connect(timer,&QTimer::timeout,[=](){
    static int num = 1;

    //label4 每隔0.5秒+1,括号中将int转为Qstring类型
    ui->label_4->setText(QString::number(num++));
});

//点击暂停按钮 实现停止定时器
connect(ui->btn,&QPushButton::clicked,[=](){
    timer->stop();
});

4 event事件

用途:用于事件的分发,也可以做拦截(不建议)

所有的事件都会经过event进行分发

image-20210628224229689

bool myLabel::event(QEvent *e){
    if(e->type()==QEvent::MouseButtonPress){
        //将QEvent类型转为QMouseEvent
        QMouseEvent * ev = static_cast<QMouseEvent *>(e);
        QString str = QString("Event函数中鼠标按下了x= %1  y=%2 globalx=%3 globaly=%4").arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
        qDebug() << str;
        return true;
    }
    return QLabel::event(e);
}

5 事件过滤器

在程序将事件分发到事件分发器前,可以利用过滤器做拦截

//给label1 安装事件过滤器
// 步骤1  安装事件过滤器
ui->label->installEventFilter(this);

// 步骤2  重写 eventfilter事件
//Qobject参数是看你的触发的空间对象,QEvent是你事件的类型
bool Widget::eventFilter(QObject * obj , QEvent * e)
{
    if(obj == ui->label)
    {
        if(e->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent * ev  = static_cast<QMouseEvent *>(e);
            QString str = QString( "事件过滤器中::鼠标按下了 x = %1   y = %2  globalX = %3 globalY = %4 " ).arg(ev->x()).arg(ev->y()).arg(ev->globalX()).arg(ev->globalY());
            qDebug() << str;
            return true; //true代表用户自己处理这个事件,不向下分发
        }
    }
    //其他默认处理
    return QWidget::eventFilter(obj,e);
}

6 QPainter绘图

paintEvent是系统会自动调用的函数,可以进行在里写,界面就会显示了
void Widget::paintEvent(QPaintEvent *)
{
//    //实例化画家对象  this指定的是绘图设备
//    QPainter painter(this);

//    //设置画笔
//    QPen pen(QColor(255,0,0));
//    //设置画笔宽度
//    pen.setWidth(3);
//    //设置画笔风格
//    pen.setStyle(Qt::DotLine);
//    //让画家 使用这个笔
//    painter.setPen(pen);


//    //设置画刷
//    QBrush brush(Qt::cyan);
//    //设置画刷风格
//    brush.setStyle(Qt::Dense7Pattern);
//    //让画家使用画刷
//    painter.setBrush(brush);


//    //画线
//    painter.drawLine(QPoint(0,0) , QPoint(100,100));

//    //画圆 椭圆
//    painter.drawEllipse( QPoint(100,100) , 50,50);

//    //画矩形
//    painter.drawRect(QRect(20,20,50,50));

//    //画文字
//    painter.drawText(QRect(10,200,150,50) , "好好学习,天天向上");

//////////////////////////////高级设置 ///////////////////////////////

//    QPainter painter(this);
//    painter.drawEllipse(QPoint(100,50) , 50,50);
//    //设置 抗锯齿能力  效率较低
//    painter.setRenderHint(QPainter::Antialiasing);
//    painter.drawEllipse(QPoint(200,50) , 50,50);


//    画矩形
//    painter.drawRect(QRect(20,20,50,50));

//    //移动画家
//    painter.translate(100,0);

//    //保存画家状态
//    painter.save();

//    painter.drawRect(QRect(20,20,50,50));

//    painter.translate(100,0);

//    //还原画家保存状态
//    painter.restore();

//    painter.drawRect(QRect(20,20,50,50));
}

    /////////////////////////////////利用画家 画资源图片 ///////////////////
     QPainter painter(this);
     QPixmap pix = QPixmap(":/Image/Luffy.png");

    //如果超出屏幕 从0开始
     if(posX >= this->width())
     {
         posX = -pix.width();
     }

     painter.drawPixmap(posX,0,pix);
//点击移动按钮,移动图片
connect(ui->pushButton,&QPushButton::clicked,[=](){

    posX+=20;
    //如果要手动调用绘图事件 用update更新
    update();
});

//和定时器一起,实现图片的滚动
QTimer * timer = new QTimer(this);
timer->start(10);
connect(timer,&QTimer::timeout,[=](){
    posX++;
    update();
});


void Widget::paintEvent(QPaintEvent *)
{
     QPainter painter(this);
    //显示图片
     QPixmap pix = QPixmap(":/Image/Luffy.png");
    //如果超出屏幕 从0开始
     if(posX >= this->width())
     {
         posX = -pix.width();
     }
     painter.drawPixmap(posX,0,pix);
}

7 QPaintDevice绘图设备

QPixmap

//    //Pixmap绘图设备 专门为平台做了显示的优化
//    QPixmap pix(300,300);

//    //填充颜色
//    pix.fill(Qt::white);

//    //声明画家
//    QPainter painter(&pix);
//    painter.setPen(QPen(Qt::green));
//    painter.drawEllipse(QPoint(150,150) , 100,100);
//    //保存
//    pix.save("E:\pix.png");

QImage

//	  QImage 绘图设备  可以对像素进行访问
//    QImage img(300,300,QImage::Format_RGB32);
//    img.fill(Qt::white);

//    QPainter painter(&img);
//    painter.setPen(QPen(Qt::blue));
//    painter.drawEllipse(QPoint(150,150) , 100,100);

//    //保存
//    img.save("E:\img.png");

QPicture

QPicture pic;
QPainter painter;
painter.begin(&pic);  //开始往pic上画
painter.setPen(QPen(Qt::cyan));
painter.drawEllipse(QPoint(150,150) , 100,100);
painter.end(); //结束画画

//保存到磁盘
pic.save("E:\pic.zt");

8 QFile 对文件进行读写操作

点击按钮,读文件,并显示在textEdit中

connect(ui->pushButton,&QPushButton::clicked,[=](){

    QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\Users\zhangtao\Desktop");
    //将路径放入到lineEdit中
    ui->lineEdit->setText(path);

    //编码格式类
    //QTextCodec * codec = QTextCodec::codecForName("gbk");

    //读取内容 放入到 textEdit中
    // QFile默认支持的格式是 utf-8
    QFile file(path); //参数就是读取文件的路径
    //设置打开方式
    file.open(QIODevice::ReadOnly);

    //QByteArray array = file.readAll();

    QByteArray array;
    while( !file.atEnd())
    {
        array += file.readLine(); //按行读
    }

    //将读取到的数据 放入textEdit中
    ui->textEdit->setText(array);
    //ui->textEdit->setText( codec->toUnicode(array)  );

    //对文件对象进行关闭
    file.close();


    //进行写文件
    //        file.open(QIODevice::Append); //用追加方式进行写
    //        file.write("啊啊啊啊啊");
    //        file.close();



    //QFileInfo 文件信息类
    QFileInfo info(path);

    qDebug() << "大小:" << info.size() << " 后缀名:" << info.suffix() << " 文件名称:"<<info.fileName() << " 文件路径:"<< info.filePath();
    qDebug() << "创建日期:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
    qDebug() << "最后修改日期:"<<info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
});

对于读取的文件是GBK还是UTF-8进行先判断,然后再去进行转换

网上一个博客的解决代码如下

QString GetCorrectUnicode(const QByteArray &ba)
{
    QTextCodec::ConverterState state;
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QString text = codec->toUnicode( ba.constData(), ba.size(), &state);
    if (state.invalidChars > 0)
    {
        text = QTextCodec::codecForName( "GBK" )->toUnicode(ba);
    }
    else
    {
        text = ba;
    }

    return text;
}
原文地址:https://www.cnblogs.com/123-wind/p/15057491.html