zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    湘湖的雪
    Lua 如何调用C打包的动态库
    自己制作一个USB自动挖矿器
    二十五块DIY 带屏幕可远程的温湿度传感器
    来看下,C# WebService WSDL自动生成代码,数组参数的BUG。。。ArrayOfString
    记一次非常规方法对接硬件设备(Grason Stadler GSI 61)
    京享值超8万的京东钻石用户告诉你套路是这样的
    C# 显示纯文本对齐封装(控制显示字体长度)
    [开源] C# 封装 银海医保的接口
    [开源]使用C# 对CPU卡基本操作封装
  • 原文地址:https://www.cnblogs.com/123-wind/p/15057491.html
Copyright © 2011-2022 走看看