Qt桌面应用开发

Qt桌面应用开发

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等各平台的全面支持。



1.3 支持的平台

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


1.4 Qt版本

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

  • 商业版

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

  • 开源的LGPL版本:

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



1.5 Qt的下载与安装

1.5.1下载地址:

http😕/www.qt.io/download-open-source/
setup包安装方式
如果需要源码包安装往下
在这里插入图片描述

1.5.2 Linux Host



1.5.2 OS X Host

1.5.3 Windows Host



1.5.4安装

默认安装(建议组件全部选中)

  • Qt对不同的平台提供了不同版本的安装包,可根据实际情况自行下载安装,本文档使用qt-opensource-windows-x86-mingw482_opengl-5.3.1 版本进行讲解


1.6 Qt的优点

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


1.7 成功案例

  • Linux桌面环境KDE
  • WPS Office 办公软件
  • Skype 网络电话
  • Google Earth 谷歌地图
  • VLC多媒体播放器
  • VirtualBox虚拟机软件


1.8 命令规范

  • 类名首字母大小 单词和单词直接 首字母大写
  • 变量、函数名、首字母小写 和单词之间首字母大写


1.9 快捷键

快捷键 操作符
运行 ctrl + R
编译 ctrl + B
查询 ctrl + F
注释 ctrl +/ 或 ctrl +?
帮助 F1
字体缩放 ctrl +鼠标滚轮
整行代码移动 ctrl +shift + ↑ ↓
自动对齐 ctrl +i
同名之间的.h .cpp 之间切换 F4


2 创建Qt项目

2.1 使用向导创建

打开Qt Creator 界面选择 New Project或者选择菜单栏 【文件】-【新建文件或项目】菜单项

在这里插入图片描述

弹出New Project对话框,选择Qt Widgets Application,

在这里插入图片描述

选择【Choose】按钮,弹出如下对话框

在这里插入图片描述

设置项目名称和路径,按照向导进行下一步,

在这里插入图片描述

选择编译套件

在这里插入图片描述

向导会默认添加一个继承自CMainWindow的类,可以在此修改类的名字和基类。默认的基类有QMainWindow、QWidget以及QDialog三个,我们可以选择QWidget(类似于空窗口),这里我们可以先创建一个不带UI的界面,继续下一步

在这里插入图片描述

系统会默认给我们添加main.cpp、mywidget.cpp、 mywidget.h和一个.pro项目文件,点击完成,即可创建出一个Qt桌面程序。



2.2 手动创建

添加一个空项目

在这里插入图片描述

选择【choose】进行下一步。设置项目名称和路径 —> 选择编译套件 --> 修改类信息 --> 完成(步骤同上),生成一个空项目。在空项目中添加文件:在项目名称上单击鼠标右键弹出右键菜单,选择【添加新文件】

在这里插入图片描述

弹出新建文件对话框

在这里插入图片描述

在此对话框中选择要添加的类或者文件,根据向导完成文件的添加。



2.3 .pro文件

在这里插入图片描述

在使用Qt向导生成的应用程序.pro文件格式如下:

QT       += core gui  //包含的模块

greaterThan(QT\_MAJOR\_VERSION, 4): QT += widgets //大于Qt4版本 才包含widget模块

CONFIG += c++17

TARGET = QtFirst  //应用程序名  生成的.exe程序名称

TEMPLATE = app    //模板类型    应用程序模板

SOURCES += main.cpp\   //源文件
        mywidget.cpp
        
HEADERS  += mywidget.h   //头文件

.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件。.pro文件的写法如下:

    1. 注释
      从“#”开始,到这一行结束。
    1. 模板变量告诉qmake为这个应用程序生成哪种makefile。下面是可供使用的选择:TEMPLATE = app
    • app -建立一个应用程序的makefile。这是默认值,所以如果模板没有被指定,这个将被使用。
    • lib - 建立一个库的makefile。
    • vcapp - 建立一个应用程序的VisualStudio项目文件。
    • vclib - 建立一个库的VisualStudio项目文件。
    • subdirs -这是一个特殊的模板,它可以创建一个能够进入特定目录并且为一个项目文件生成makefile并且为它调用make的makefile。
    1. #指定生成的应用程序名:
      TARGET = QtDemo
    1. #工程中包含的头文件
      HEADERS += include/painter.h
    1. #工程中包含的.ui设计文件
      FORMS += forms/painter.ui
    1. #工程中包含的源文件
      SOURCES += sources/main.cpp sources
    1. #工程中包含的资源文件
      RESOURCES += qrc/painter.qrc
    1. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
      这条语句的含义是,如果QT_MAJOR_VERSION大于4(也就是当前使用的Qt5及更高版本)需要增加widgets模块。如果项目仅需支持Qt5,也可以直接添加“QT += widgets”一句。不过为了保持代码兼容,最好还是按照QtCreator生成的语句编写。
    1. #配置信息
      CONFIG用来告诉qmake关于应用程序的配置信息。
      CONFIG += c++17 //使用c++17的特性
      在这里使用“+=”,是因为我们添加我们的配置选项到任何一个已经存在中。这样做比使用“=”那样替换已经指定的所有选项更安全。


2.4 一个最简单的Qt应用程序

main入口函数中

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

int main(int argc, char *argv[])  //程序入口 argc 命令行变量数量, argv 命令行变量数组
{
      
    QApplication a(argc, argv); //a 应用程序对象  在QT中,应用程序对象 有且仅有一个
    Widget w;   //通过 窗口类 实例化对象w 
    w.show();  //窗口是不会默认弹出的。需要调用show方法进行显示 
    return a.exec(); //a.exec() 进入消息循环机制,阻塞功能
}

解释:

  • argc 命令行变量数量, argv 命令行变量数组
  • Qt系统提供的标准类名声明头文件没有.h后缀
  • Qt一个类对应一个头文件,类名就是头文件名
  • QApplication应用程序类
    • 管理图形用户界面应用程序的控制流和主要设置。
    • 是Qt的整个后台管理的命脉它包含主事件循环,在其中来自窗口系统和其它资源的所有事件处理和调度。它也处理应用程序的初始化和结束,并且提供对话管理
    • 对于任何一个使用Qt的图形用户界面应用程序,都正好存在一个QApplication 对象,而不论这个应用程序在同一时间内是不是有0、1、2或更多个窗口。
  • a.exec()

程序进入消息循环,等待对用户输入进行响应。这里main()把控制权转交给Qt,Qt完成事件处理工作,当应用程序退出的时候exec()的值就会返回。在exec()中,Qt接受并处理用户和系统的事件并且把它们传递给适当的窗口部件。



3 第一个Qt小程序

3.1 按钮的创建

在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮
//头文件 #include

    //按钮
    QPushButton * btn = new QPushButton;   
    //设置父窗口,如果想显示到当前窗口,需要做依赖this
    btn->setParent(this);
    
    //设置文字,显示文字
    btn->setText("德玛西亚");

    //移动位置
    btn->move(100,100);

    //第二种创建
    QPushButton * btn2 = new QPushButton("第二个",this);

    //重新指定窗口大小
    this->resize(600,400);

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

    //限制窗口大小
    this->setFixedSize(600,400);

在这里插入图片描述

上面代码中,一个按钮其实就是一个QPushButton类下的对象,如果只是创建出对象,是无法显示到窗口中的,所以我们需要依赖一个父窗口,也就是指定一个父亲利用setParent函数即可,如果想设置按钮上显示的文字利用setText,移动按钮位置用move,对于窗口而言,我们可以修改左上角窗口的标题setWindowTitle,重新指定窗口大小:resize,或者设置固定的窗口大小setFixedSize;

结论:

  • 指定一个父亲利用:setParent
  • 设置按钮上显示的文字利用:setText
  • 移动按钮位置用:move
  • 修改左上角窗口的标题:setWindowTitle
  • 重新指定窗口大小: resize
  • 设置固定的窗口大小: setFixedSize


3.2 自定义继承一个按钮

  1. 首先添加一个C++ 类
    选择离PushButton最近的一个基类, 其他选项默认
    在这里插入图片描述

  2. 修改头文件继承的父类
    把 QWidget 更改为QPushButton
    在这里插入图片描述

  3. 修改源文件父类的初始化
    在这里插入图片描述

  4. 调用
    在这里插入图片描述

  5. 运行测试
    关闭窗口时调用了析构函数
    在这里插入图片描述
    在这里插入图片描述



3.3 对象模型(对象树)

请添加图片描述

在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 对象,并且大胆在堆上创建。



3.4 Qt窗口坐标体系

坐标体系:
以左上角为原点(0,0),X向右增加,Y向下增加。

在这里插入图片描述

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



4 信号和槽机制

请添加图片描述

发送端 信号返回void 只需声明,不需要实现可以有参数,可以发生重载
接收端 返回值是void 需要声明,也需要有实现,可以有参(和发送端一直),可以重载

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



4.1 系统自带的信号和槽

下面我们完成一个小功能,上面我们已经学习了按钮的创建,但是还没有体现出按钮的功能,按钮最大的功能也就是点击后触发一些事情,比如我们点击按钮,就把当前的窗口给关闭掉,那么在Qt中,这样的功能如何实现呢?

其实无法两行代码就可以搞定了,我们看下面的代码

QPushButton * quitBtn = new QPushButton("关闭窗口",this);
connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);

第一行是创建一个关闭按钮,这个之前已经学过,第二行就是核心了,也就是信号槽的使用方式
connect()函数最常用的一般形式:

connect(sender, signal, receiver, slot);

参数解释:

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

那么系统自带的信号和槽通常如何查找呢,这个就需要利用帮助文档了,在帮助文档中比如我们上面的按钮的点击信号,在帮助文档中输入QPushButton,首先我们可以在Contents中寻找关键字 signals,信号的意思,但是我们发现并没有找到,这时候我们应该想到也许这个信号的被父类继承下来的,因此我们去他的父类QAbstractButton中就可以找到该关键字,点击signals索引到系统自带的信号有如下几个

在这里插入图片描述

这里的clicked就是我们要找到,槽函数的寻找方式和信号一样,只不过他的关键字是slot。



4.2 自定义信号和槽

使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。

下面我们看看使用 Qt 的信号槽:
首先定义一个学生类和老师类:
老师类中声明信号 饿了 hungry
在这里插入图片描述

  1. 在老师teacher.h 头文件中
    在这里插入图片描述

    signals:
        void hungury();  
    
  2. 在student.h 头文件中
    在这里插入图片描述

    //学生类中声明槽   请客 treat
    public slots:
        void treat();
    

    在student.cpp中实现
    在这里插入图片描述

  3. 到widget.h 添加老师和学生
    在这里插入图片描述

  4. 在widget.cpp 中实现下课函数
    在这里插入图片描述

//	在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客
void MyWidget::ClassIsOver()
{
   
    //发送信号
    emit teacher->hungury();
}

//学生响应了槽函数,并且打印信息
//自定义槽函数 实现
void Student::eat(){
   
       qDebug() << "该吃饭了!";
}

//在窗口中连接信号槽
teacher = new Teacher(this);
student = new Student(this);
connect(teacher,&Teacher::hungury,student,&Student::treat);

并且调用下课函数,测试打印出 “该吃饭了”
自定义的信号 hungry带参数,需要提供重载的自定义信号和 自定义槽

void hungury(QString name);  自定义信号
void treat(QString name );    自定义槽

但是由于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后在做连接

void (Teacher:: * teacherSingal)(QString) = &Teacher::hungury;
void (Student::  * studentSlot)(QString) = &Student::treat;
connect(teacher,teacherSingal,student,studentSlot);
  1. 自定义信号槽需要注意的事项:
  • 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
  • 信号和槽函数返回值是 void
  • 信号只需要声明,不需要实现
  • 槽函数需要声明也需要实现
  • 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
  • 使用 emit 在恰当的位置发送信号;
  • 使用connect()函数连接信号和槽。
  • 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
  • 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
  • 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。


4.3信号槽的拓展

  • 一个信号可以和多个槽相连
    如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。

  • 多个信号可以连接到一个槽
    只要任意一个信号发出,这个槽就会被调用

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

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

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

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



4.4 Qt4版本的信号槽写法

connect(zt,**SIGNAL**(hungry(QString)),st,**SLOT**(treat(QString)));

例:下图
在这里插入图片描述

在这里插入图片描述

这里使用了SIGNAL和SLOT这两个宏,将两个函数名转换成了字符串。注意到connect()函数的 signal 和 slot 都是接受字符串,一旦出现连接不成功的情况,Qt4是没有编译错误的(因为一切都是字符串,编译期是不检查字符串是否匹配),而是在运行时给出错误。这无疑会增加程序的不稳定性。

Qt5在语法上完全兼容Qt4,而反之是不可以的。



4.5 Lambda表达式

C++11中的Lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。
如果在信号和槽中使用Lambda表达式,this都可以省略
首先看一下Lambda表达式的基本构成:

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

例:如图是一个点击退出按钮的匿名函数
在这里插入图片描述

调用1:

   QPushButton *btn = new QPushButton("aaa",this);
   [=](){
   
      btn->setText("bbb");
   }();
   //后面加()才可以调用

调用2:

   QPushButton *btn = new QPushButton("aaa",this);
   QPushButton *btn2 = new QPushButton("aaa",this);
   [btn2](){
   
      //btn->setText("bbb");  //报错,只传入btn2
      btn2->setText("bbb");
   }();
   //后面加()才可以调用

① 函数对象参数;
[],标识一个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按值进行传递外,其他参数都按引用进行传递。

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

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

QPushButton * myBtn = new QPushButton (this);
QPushButton * myBtn2 = new QPushButton (this);
myBtn2->move(100,100);
int m = 10;
//加上mutable 修饰符后,可以修改按值传递进来的拷贝
connect(myBtn,&QPushButton::clicked,this,[m] ()mutable {
    m = 20; qDebug() << m; });
connect(myBtn2,&QPushButton::clicked,this,[=] ()  {
    qDebug() << m; });
qDebug() << m;

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

//-> 返回值类型
int num = [=]()->int{
   
   return 1000;
}();
qDebug()<<"num = "<<num;

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



5 QMainWindow

QMainWindow是一个为用户提供主窗口程序的类,
包含一个菜单栏(menu bar)、
多个工具栏(tool bars)、
多个锚接部件(dock widgets)、
一个状态栏(status bar)及
一个中心部件(central widget),
是许多应用程序的基础,如文本编辑器,图片编辑器等。
默认会生成了UI文件,很多不需要自己代码再编写:以下是文案在没有UI文件中操作使用

在这里插入图片描述

默认生成UI文件后ui->setupUi(this) 已经使用了UI文件 ,只需要在Ui下面构建自己的代码即可
在这里插入图片描述



5.1 菜单栏 QMenuBar

一个主窗口最多只有一个菜单栏。位于主窗口顶部、主窗口标题栏下面。
需要添加头文件

  • 创建菜单栏,通过QMainWindow类的menubar()函数获取主窗口菜单栏指针
    QMenuBar * = menuBar() const
//1. 菜单栏只能有一个
QMenuBar *bar = menuBar();
setMenuBar(bar);
  • 创建菜单,调用QMenu的成员函数addMenu来添加菜单
    QAction* addMenu(QMenu * menu)
    QMenu* addMenu(const QString & title)
    QMenu* addMenu(const QIcon & icon, const QString & title)
//创建菜单
QMenu *fileMenu = bar->addMenu("文件");
QMenu *editMenu = bar->addMenu("编辑");
  • 创建菜单项,调用QMenu的成员函数addAction来添加菜单项
    QAction* activeAction() const
    QAction* addAction(const QString & text);
    QAction* addAction(const QIcon & icon, const QString & text);
    QAction* addAction(const QString & text, const QObject * receiver, const char * member, const QKeySequence & shortcut = 0);
    QAction* addAction(const QIcon & icon, const QString & text, const QObject * receiver, const char * member, const QKeySequence & shortcut = 0);
//创建菜单项
QAction*newAction = fileMenu->activeAction("新建");
//添加分割线
fileMenu->addSeparator();
QAction*openAction = fileMenu->activeAction("打开");

查看效果
在这里插入图片描述

  • 如果带有资源文件可以给菜单项添加图标
    在这里插入图片描述

    在这里插入图片描述

Qt 并没有专门的菜单项类,只是使用一个QAction类,抽象出公共的动作。当我们把QAction对象添加到菜单,就显示成一个菜单项,添加到工具栏,就显示成一个工具按钮。用户可以通过点击菜单项、点击工具栏按钮、点击快捷键来激活这个动作。



5.2 工具栏 QToolBar

主窗口的工具栏上可以有多个工具条,通常采用一个菜单对应一个工具条的的方式,也可根据需要进行工具条的划分。

  • 直接调用QMainWindow类的addToolBar()函数获取主窗口的工具条对象,每增加一个工具条都需要调用一次该函数。
//添加工具栏可以有多个
QToolBar * toolBar = new QToolBar(this);
addToolBar(toolBar);

在这里插入图片描述

  • 插入属于工具条的动作,即在工具条上添加操作。
    通过QToolBar类的addAction函数添加。

  • 工具条是一个可移动的窗口,它的停靠区域由QToolBar的allowAreas决定,包括:

    • Qt::LeftToolBarArea 停靠在左侧
    • Qt::RightToolBarArea 停靠在右侧
    • Qt::TopToolBarArea 停靠在顶部
    • Qt::BottomToolBarArea 停靠在底部
    • Qt::AllToolBarAreas 以上四个位置都可停靠
//工具栏停靠在左边
QToolBar * toolBar = new QToolBar(this);
addToolBar(Qt::LeftToolBarArea,toolBar);   //停靠在左侧

在这里插入图片描述

使用setAllowedAreas()函数指定停靠区域:

//设置只运行左右停靠
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);

使用setMoveable()函数设定工具栏的可移动性:

//设置移动(总开关)
toolBar->setMoveable(false)//工具条不可移动, 只能停靠在初始化的位置上

在这里插入图片描述

使用setFloatable () 函数设定工具栏浮动

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

工具栏中放入小部件:

toolBar->addAction(newAction);  //也可以添加分割线
toolBar->addAction(openAction);

在这里插入图片描述

UI文件中编辑
在这里插入图片描述



5.3 状态栏 QStatusBar

  • 派生自QWidget类,使用方法与QWidget类似,QStatusBar类常用成员函数:
//状态栏 只能有一个
QStatusBar * stBar = statusBar();
setStatusBar(stBar);
QLabel* label = new QLabel("左侧提示信息",this);
stBar->addWidget(label);

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

在这里插入图片描述
默认UI文件中需要到代码中添加状态栏
在这里插入图片描述

  • 状态栏也只能最多有一个
//添加小部件
void addWidget(QWidget * widget, int stretch = 0)

//插入小部件
int	insertWidget(int index, QWidget * widget, int stretch = 0)

//删除小部件
void removeWidget(QWidget * widget)


5.4 铆接部件 QDockWidget

铆接部件 QDockWidget,也称浮动窗口,可以有多个。

//铆接部件(浮动窗口)
QDockWidget * dock = new QDockWidget("aaa",this);
addDockWidget(Qt::LeftDockWidgetArea,dock);
//只运行左右和顶部停靠
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea | Qt::TopDockWidgetArea);  //设置区域范围

在这里插入图片描述

UI文件中编辑
在这里插入图片描述



5.5 核心部件(中心部件)

setCentralWidget
除了以上几个部件,中心显示的部件都可以作为核心部件,例如一个记事本文件,可以利用QTextEdit做核心部件

//设置核心部件为 文本编辑 只能有一个
QTextEdit * edit = new QTextEdit(this);
setCentralWidget(edit);

在这里插入图片描述



5.6 资源文件

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

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

在这里插入图片描述

点击“选择…”按钮,打开“新建 Qt 资源文件”对话框。在这里我们输入资源文件的名字和路径:

在这里插入图片描述

点击下一步,选择所需要的版本控制系统,然后直接选择完成。我们可以在 Qt Creator 的左侧文件列表中看到“资源文件”一项,也就是我们新创建的资源文件:

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述

这样,我们可以直接使用:/images/doc-open引用到这个资源,无需关心图片的真实文件名。

如果我们使用文本编辑器打开 res.qrc 文件,就会看到一下内容:

<RCC>
    	<qresource prefix="/images">
        	<file alias="doc-open">document-open.png</file>
    	</qresource>
    	<qresource prefix="/images/fr" lang="fr">
        	<file alias="doc-open">document-open-fr.png</file>
    	</qresource>
</RCC>

我们可以对比一下,看看 Qt Creator 帮我们生成的是怎样的 qrc 文件。当我们编译工程之后,我们可以在构建目录中找到 qrc_res.cpp 文件,这就是 Qt 将我们的资源编译成了 C++ 代码。



6 对话框QDialog

6.1 基本概念

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

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

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

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

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



6.2 标准对话框

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

Qt 的内置对话框大致分为以下几类:

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

取消点击关闭的按钮,和帮助按钮

this->setWindowFlags(windowFlags()&~Qt::WindowCloseButtonHint&~Qt::WindowContextHelpButtonHint &~Qt::WindowMinMaxButtonsHint);
  1. QMessageBox对话框
    错误提示对话框
    在这里插入图片描述
    信息提示对话框
    在这里插入图片描述

    在这里插入图片描述

    询问提示对话框
    QMessageBox::question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton)
    参数1 父窗口
    参数2 标题
    参数3 提示信息
    参数4 按键类型
    参数5 默认关联回车按键
    在这里插入图片描述

    在这里插入图片描述

    可以把用户点击做判断

    //询问提示对话框
    // 参数1   父窗口  参数2  标题  参数3  提示信息  参数4  按键类型  参数5  默认关联回车按键
    if( QMessageBox::Save ==  QMessageBox::question(this,"询问","question" , QMessageBox::Save | QMessageBox::Cancel  ,QMessageBox::Cancel)){
         
        qDebug()<<"点击的是保存";
    }else{
         
        qDebug()<<"点击的是取消";
    }
    

    自定义信息框图标

    QMessageBox succeed_box(QMessageBox::NoIcon,"Connection Succeed","Connection successful");
    succeed_box.setWindowIcon(QPixmap(":/images/logo.ico"));
    succeed_box.setIconPixmap(QPixmap(":/images/succeed_box.png"));
    succeed_box.exec();
    

    在这里插入图片描述

  2. 颜色对话框
    需要包含头文件
    例:

    //颜色对话框
    QColor color = QColorDialog::getColor(Qt::red);
    qDebug() << color.red() << color.green() << color.blue() ;
    

    在这里插入图片描述

  3. 文件对话框
    需要包含头文件

    //文件对话框   默认打开文件目录为C:\\Users\\bbb\\Desktop  ,过滤文件格式为.doc
    QString fileName = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\bbb\\Desktop","(*.doc)");
    qDebug () <<fileName;
    

    在这里插入图片描述

  4. 字体对话框
    需要包含头文件
    把Qstring转为UTF8 toUtf8().data()

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

    在这里插入图片描述



6.3 自定义消息框

Qt 支持模态对话框和非模态对话框。
模态与非模态的实现:

  • 使用QDialog::exec()实现应用程序级别的模态对话框
  • 使用QDialog::open()实现窗口级别的模态对话框
  • 使用QDialog::show()实现非模态对话框。


6.3.1模态对话框

  • Qt 有两种级别的模态对话框:

    • 应用程序级别的模态
      当该种模态的对话框出现时,用户必须首先对对话框进行交互,直到关闭对话框,然后才能访问程序中其他的窗口。
  • 窗口级别的模态
    该模态仅仅阻塞与对话框关联的窗口,但是依然允许用户与程序中其它窗口交互。窗口级别的模态尤其适用于多窗口模式。

一般默认是应用程序级别的模态。

在下面的示例中,我们调用了exec()将对话框显示出来,因此这就是一个模态对话框。当对话框出现时,我们不能与主窗口进行任何交互,直到我们关闭了该对话框。

QDialog dialog;
dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.exec();

6.3.2非模态对话框

下面我们试着将exec()修改为show(),看看非模态对话框:

QDialog dialog(this);
dialog.setWindowTitle(tr("Hello, dialog!"));
dialog.show();

是不是事与愿违?对话框竟然一闪而过!这是因为,**show()函数不会阻塞当前线程,对话框会显示出来,然后函数立即返回,代码继续执行。**注意,dialog 是建立在栈上的,show()函数返回,MainWindow::open()函数结束,dialog 超出作用域被析构,因此对话框消失了。知道了原因就好改了,我们将 dialog 改成堆上建立,当然就没有这个问题了:

QDialog *dialog = new QDialog;
dialog->setWindowTitle(tr("Hello, dialog!"));
dialog->show();

如果你足够细心,应该发现上面的代码是有问题的:dialog 存在内存泄露!dialog 使用 new 在堆上分配空间,却一直没有 delete。解决方案也很简单:将 MainWindow 的指针赋给 dialog 即可。还记得我们前面说过的 Qt 的对象系统吗?

不过,这样做有一个问题:如果我们的对话框不是在一个界面类中出现呢?由于QWidget的 parent 必须是QWidget指针,那就限制了我们不能将一个普通的 C++ 类指针传给 Qt 对话框。另外,如果对内存占用有严格限制的话,当我们将主窗口作为 parent 时,主窗口不关闭,对话框就不会被销毁,所以会一直占用内存。在这种情景下,我们可以设置 dialog 的WindowAttribute:

QDialog *dialog = new QDialog;
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setWindowTitle(tr("Hello, dialog!"));
dialog->show();

setAttribute()函数设置对话框关闭时,自动销毁对话框。



6.4 消息对话框

QMessageBox用于显示消息提示。我们一般会使用其提供的几个 static 函数:

  • 显示关于对话框。
void about(QWidget * parent, const QString & title, const QString & text)

这是一个最简单的对话框,其标题是 title,内容是 text,父窗口是 parent。对话框只有一个 OK 按钮。

  • 显示关于 Qt 对话框。该对话框用于显示有关 Qt 的信息。
void aboutQt(QWidget * parent, const QString & title = QString())
  • 显示严重错误对话框。
StandardButton critical(QWidget * parent, const QString & title, const QString & text, 
StandardButtons buttons = Ok, 
StandardButton defaultButton = NoButton)

这个对话框将显示一个红色的错误符号。我们可以通过 buttons 参数指明其显示的按钮。默认情况下只有一个 Ok 按钮,我们可以使用StandardButtons类型指定多种按钮。

  • 与QMessageBox::critical()类似,不同之处在于这个对话框提供一个普通信息图标。
StandardButton information(QWidget * parent, const QString & title, const QString & text, 
StandardButtons buttons = Ok, 
StandardButton defaultButton = NoButton)
  • 与QMessageBox::critical ()类似,不同之处在于这个对话框提供一个问号图标,并且其显示的按钮是“是”和“否”。
StandardButton question(QWidget * parent,const QString & title, const QString & text, 
StandardButtons buttons = StandardButtons( Yes | No ), 
StandardButton defaultButton = NoButton) 
  • 与QMessageBox::critical()类似,不同之处在于这个对话框提供一个黄色叹号图标。
StandardButton warning(QWidget * parent, const QString & title, const QString & text, 
StandardButtons buttons = Ok, 
StandardButton defaultButton = NoButton)

我们可以通过下面的代码来演示下如何使用QMessageBox。

if (QMessageBox::Yes == QMessageBox::question(this,tr("Question"), 
        tr("Are you OK?"),
        QMessageBox::Yes | QMessageBox::No,
        QMessageBox::Yes)) 
{
   
   QMessageBox::information(this, tr("Hmmm..."), tr("I'm glad to hear that!"));
} else {
   
        QMessageBox::information(this, tr("Hmmm..."), tr("I'm sorry!"));
}

我们使用QMessageBox::question()来询问一个问题。

  • 这个对话框的父窗口是 this。

QMessageBox是QDialog的子类,这意味着它的初始显示位置将会是在 parent 窗口的中央。

  • 第二个参数是对话框的标题。
  • 第三个参数是我们想要显示的内容。
  • 第四个参数是关联的按键类型,我们可以使用或运算符(|)指定对话框应该出现的按钮。比如我们希望是一个 Yes 和一个 No。
  • 最后一个参数指定默认选择的按钮。

这个函数有一个返回值,用于确定用户点击的是哪一个按钮。按照我们的写法,应该很容易的看出,这是一个模态对话框,因此我们可以直接获取其返回值。

QMessageBox类的 static 函数优点是方便使用,缺点也很明显:非常不灵活。我们只能使用简单的几种形式。为了能够定制QMessageBox细节,我们必须使用QMessageBox的属性设置 API。如果我们希望制作一个询问是否保存的对话框,我们可以使用如下的代码:

QMessageBox msgBox;
msgBox.setText(tr("The document has been modified."));
msgBox.setInformativeText(tr("Do you want to save your changes?"));
msgBox.setDetailedText(tr("Differences here..."));
msgBox.setStandardButtons(QMessageBox::Save| QMessageBox::Discard| QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Save);

int ret = msgBox.exec();
switch (ret) {
   
case QMessageBox::Save:
    qDebug() << "Save document!";
    break;
    
case QMessageBox::Discard:
    qDebug() << "Discard changes!";
    break;

case QMessageBox::Cancel:
    qDebug() << "Close document!";
    break;
}

msgBox 是一个建立在栈上的QMessageBox实例。我们设置其主要文本信息为“The document has been modified.”,informativeText 则是会在对话框中显示的简单说明文字。下面我们使用了一个detailedText,也就是详细信息,当我们点击了详细信息按钮时,对话框可以自动显示更多信息。我们自己定义的对话框的按钮有三个:保存、丢弃和取消。然后我们使用了exec()是其成为一个模态对话框,根据其返回值进行相应的操作。



6.5 标准文件对话框

QFileDialog,也就是文件对话框。在本节中,我们将尝试编写一个简单的文本文件编辑器,我们将使用QFileDialog来打开一个文本文件,并将修改过的文件保存到硬盘。

首先,我们需要创建一个带有文本编辑功能的窗口。借用我们前面的程序代码,应该可以很方便地完成:

openAction = new QAction(QIcon(":/images/file-open"),tr("&Open..."), this);
openAction->setStatusTip(tr("Open an existing file"));
saveAction = new QAction(QIcon(":/images/file-save"), tr("&Save..."), this);
saveAction->setStatusTip(tr("Save a new file"));

QMenu *file = menuBar()->addMenu(tr("&File"));
file->addAction(openAction);
file->addAction(saveAction);

QToolBar *toolBar = addToolBar(tr("&File"));
toolBar->addAction(openAction);
toolBar->addAction(saveAction);

textEdit = new QTextEdit(this);
setCentralWidget(textEdit);

我们在菜单和工具栏添加了两个动作:打开和保存。接下来是一个QTextEdit类,这个类用于显示富文本文件。也就是说,它不仅仅用于显示文本,还可以显示图片、表格等等。不过,我们现在只用它显示纯文本文件。QMainWindow有一个setCentralWidget()函数,可以将一个组件作为窗口的中心组件,放在窗口中央显示区。显然,在一个文本编辑器中,文本编辑区就是这个中心组件,因此我们将QTextEdit作为这种组件。

我们使用connect()函数,为这两个QAction对象添加响应的动作:

connect(openAction, &QAction::triggered, 
this, &MainWindow::openFile);
connect(saveAction, &QAction::triggered, 
this, &MainWindow::saveFile);

下面是最主要的openFile()和saveFile()这两个函数的代码:

//打开文件

void MainWindow::openFile(){
   
    QString path = QFileDialog::getOpenFileName(this,tr("Open File"), ".", tr("Text Files(*.txt)"));
    if(!path.isEmpty()) {
   
        QFile file(path);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
   
                QMessageBox::warning(this, tr("Read File"),tr("Cannot open file:\n%1").arg(path));
                return;
        }
           QTextStream in(&file);
           textEdit->setText(in.readAll());
           file.close();
    }else {
   
        QMessageBox::warning(this, tr("Path"),tr("You did not select any file."));
    }
 }
//保存文件
void MainWindow::saveFile(){
   

    QString path = QFileDialog::getSaveFileName(this,tr("Open File"), ".", tr("Text Files(*.txt)"));
    if(!path.isEmpty()) {
   
        QFile file(path);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
   
            QMessageBox::warning(this, tr("Write File"),tr("Cannot open file:\n%1").arg(path));
            return;
        }
         QTextStream out(&file);
        out << textEdit->toPlainText();
        file.close();
    } else {
   
        QMessageBox::warning(this, tr("Path"),tr("You did not select any file."));
     }
}

在openFile()函数中,我们使用QFileDialog::getOpenFileName()来获取需要打开的文件的路径。这个函数原型如下:

QString getOpenFileName(QWidget \* parent = 0,
const QString & caption = QString(),
const QString & dir = QString(),
const QString & filter = QString(),
QString \* selectedFilter = 0,
Options options = 0)

不过注意,它的所有参数都是可选的,因此在一定程度上说,这个函数也是简单的。这六个参数分别是:

  • parent:父窗口。

Qt 的标准对话框提供静态函数,用于返回一个模态对话框;

  • caption:对话框标题;
  • dir:对话框打开时的默认目录
    • “.” 代表程序运行目录
    • “/” 代表当前盘符的根目录(特指 Windows 平台;Linux 平台当然就是根目录),这个参数也可以是平台相关的,比如“C:\”等;
  • filter:过滤器。

我们使用文件对话框可以浏览很多类型的文件,但是,很多时候我们仅希望打开特定类型的文件。比如,文本编辑器希望打开文本文件,图片浏览器希望打开图片文件。过滤器就是用于过滤特定的后缀名。如果我们使用“Image Files(*.jpg *.png)”,则只能显示后缀名是 jpg 或者 png 的文件。如果需要多个过滤器,使用“;;”分割,比如“JPEG Files(*.jpg);;PNG Files(*.png)”;

  • selectedFilter:默认选择的过滤器;
  • options:对话框的一些参数设定

比如只显示文件夹等等,它的取值是enum QFileDialog::Option,每个选项可以使用 | 运算组合起来。

**QFileDialog::getOpenFileName()返回值是选择的文件路径。**我们将其赋值给 path。通过判断 path 是否为空,可以确定用户是否选择了某一文件。只有当用户选择了一个文件时,我们才执行下面的操作。

在saveFile()中使用的QFileDialog::getSaveFileName()也是类似的。使用这种静态函数,在 Windows、Mac OS 上面都是直接调用本地对话框,但是 Linux 上则是QFileDialog自己的模拟。这暗示了,如果你不使用这些静态函数,而是直接使用QFileDialog进行设置,那么得到的对话框很可能与系统对话框的外观不一致。这一点是需要注意的。



6.6 点击二维码,显示新窗口

在这里插入图片描述
在这里插入图片描述

实现代码:
图片为一个PushButton ,以下是接到槽函数

//点击显示二维码
void U_login::on_btn_qrcode_clicked()
{
   
    //隐藏登录页面
    this->hide();
    //创建新的二维码窗口
    QDialog qr_code = QDialog();
//    qr_code.setWindowIcon(QIcon(":/images/logo.ico"));
//    qr_code.setWindowIconText("QRcode");
//    qr_code.setWindowFlags(Qt::FramelessWindowHint);  //设置无边框
    UI_helper::setFramelessForm(&qr_code);  //自定义设置无边框

    //设置窗口大小
    qr_code.setFixedSize(172,213);
    //label 图片
    QLabel qrcode_label = QLabel(&qr_code);
    qrcode_label.setPixmap(QPixmap(":/images/ui_qrcode.jpg"));
    qrcode_label.move(9,9);

    //pushbutton按钮
    QPushButton btn_qrcode = QPushButton("返 回",&qr_code);
    btn_qrcode.move(9,174);
    btn_qrcode.setMinimumSize(55,30);
    btn_qrcode.setMaximumSize(55,30);

    //点击返回按钮信号槽
    connect(&btn_qrcode,&QPushButton::clicked,&qr_code,[&, this](){
   
        qr_code.close();
        this->show();
        qr_code.deleteLater();
    });

    //窗口循环
    qr_code.exec();
}

7 布局管理器

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

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

  • 绝对定位就是一种最原始的定位方法:给出这个组件的坐标和长宽值。

这样,Qt 就知道该把组件放在哪里以及如何设置组件的大小。但是这样做带来的一个问题是,如果用户改变了窗口大小,比如点击最大化按钮或者使用鼠标拖动窗口边缘,采用绝对定位的组件是不会有任何响应的。这也很自然,因为你并没有告诉 Qt,在窗口变化时,组件是否要更新自己以及如何更新。或者,还有更简单的方法:禁止用户改变窗口大小。但这总不是长远之计。

  • 布局定位:你只要把组件放入某一种布局,布局由专门的布局管理器进行管理。当需要调整大小或者位置的时候,Qt 使用对应的布局管理器进行调整。
    布局定位完美的解决了使用绝对定位的缺陷。
    Qt 提供的布局中以下三种是我们最常用的:

  • QHBoxLayout:按照水平方向从左到右布局;

  • QVBoxLayout:按照竖直方向从上到下布局;

  • QGridLayout:在一个网格中进行布局,类似于 HTML 的 table;



7.1 系统提供的布局控件

在这里插入图片描述

这4个为系统给我们提供的布局的控件,但是使用起来不是非常的灵活,这里就不详细介绍了。



7.2 利用widget做布局

第二种布局方式是利用控件里的widget来做布局,在Containers中

在这里插入图片描述

在widget中的控件可以进行水平、垂直、栅格布局等操作,比较灵活。

再布局的同时我们需要灵活运用弹簧的特性让我们的布局更加的美观,下面是一个登陆窗口,利用widget可以搭建出如下登陆界面:

  1. 添加一个widget,把用户名 、用户名输入框,密码、密码输入框拉进到widget里面
    在这里插入图片描述
    在这里插入图片描述

  2. 做一个栅格布局,再做一个垂直布局
    在这里插入图片描述

  3. 左右两边顶弹簧
    在这里插入图片描述

  4. 设置sizePolicy 垂直策略为固定
    在这里插入图片描述

  5. 上下间隙顶一个弹簧,固定弹簧高度
    在这里插入图片描述
    在这里插入图片描述

  6. 把所有的widget 的属性 layout 间隙调整为0
    在这里插入图片描述

  7. 把密码输入框为password模式
    在这里插入图片描述

  8. 设置窗口固定大小,可以灵活调整
    在这里插入图片描述

  9. 测试

    在这里插入图片描述
    在这里插入图片描述

8 常用控件

Qt为我们应用程序界面开发提供的一系列的控件,下面我们介绍两种最常用一些控件,所有控件的使用方法我们都可以通过帮助文档获取。



8.1 QLabel

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



8.1.1显示文字 (普通文本、html)

通过QLabel类的setText函数设置显示的内容:
void setText(const QString &)

  • 可以显示普通文本字符串
QLable *label = new QLable;
label->setText(“Hello, World!);
  • 可以显示HTML格式的字符串
    比如显示一个链接:
QLabel * label = new QLabel(this);
label ->setText("Hello, World");
label ->setText("<h1><a href=\"https://www.baidu.com\">百度一下</a></h1>");
label ->setOpenExternalLinks(true);

其中setOpenExternalLinks()函数是用来设置用户点击链接之后是否自动打开链接,如果参数指定为true则会自动打开。



8.1.2显示图片

可以使用QLabel的成员函数setPixmap设置图片

void	setPixmap(const QPixmap &)

首先定义QPixmap对象

QPixmap pixmap;

然后加载图片

pixmap.load(":/Image/boat.jpg");

或使用UI界面QT creator
在这里插入图片描述

最后将图片设置到QLabel中

QLabel *label = new QLabel;
label.setPixmap(pixmap);

在这里插入图片描述



8.1.3显示动画

可以使用QLabel 的成员函数setMovie加载动画,可以播放gif格式的文件

void	setMovie(QMovie * movie)

首先定义QMovied对象,并初始化:

QMovie *movie = new QMovie(":/Mario.gif");

播放加载的动画:

movie->start();

将动画设置到QLabel中:

QLabel *label = new QLabel;
label->setMovie(movie);

方式二:
给ui上面Qlabal设置动画

QMovie *move = new QMovie(this);  //用来播放gif
movie->setFileName(":/images/xxx.gif");
movie->start(); //播放
ui->gif_label->setMovie(movie);



8.2 QLineEdit

Qt提供的单行文本编辑框。



8.2.1设置/获取内容

  • 获取编辑框内容使用text(),函数声明如下:
QString	text() const
  • 设置编辑框内容
void setText(const QString &)


8.2.2设置显示模式

使用QLineEdit类的setEchoMode () 函数设置文本的显示模式,函数声明:

void setEchoMode(EchoMode mode)

EchoMode是一个枚举类型,一共定义了四种显示模式:

  • QLineEdit::Normal 模式显示方式,按照输入的内容显示。
  • QLineEdit::NoEcho 不显示任何内容,此模式下无法看到用户的输入。
  • QLineEdit::Password 密码模式,输入的字符会根据平台转换为特殊字符。
  • QLineEdit::PasswordEchoOnEdit 编辑时显示字符否则显示字符作为密码。

另外,我们再使用QLineEdit显示文本的时候,希望在左侧留出一段空白的区域,那么,就可以使用QLineEdit给我们提供的setTextMargins函数:

void setTextMargins(int left, int top, int right, int bottom)

用此函数可以指定显示的文本与输入框上下左右边界的间隔的像素数。



8.2.2设置在右边显示图标

在这里插入图片描述

ui->u_password->addAction(QIcon(":/images/diagram.ico"),QLineEdit::TrailingPosition);


8.3 PushButton

在Qt程序中,最常用的控件之一就是按钮了,首先我们来看下如何创建一个按钮

8.3.1 创建方式

方式一:在代码中创建

 //头文件 #include <QPushButton>
 QPushButton * btn = new QPushButton;  //按钮
 btn->setParent(this);   //设置父窗口
 btn->setText("德玛西亚"); //设置文字,显示文字
 btn->move(100,100);  //移动位置
 //第二种创建
 QPushButton * btn2 = new QPushButton("第二个",this); 
 this->resize(600,400);//重新指定窗口大小
 this->setWindowTitle("第一个窗口"); //设置窗口标题
 this->setFixedSize(600,400);   //限制窗口大小
 //添加图标
 btn2->setIcon(QIcon(":/images/app_minimize.ico"));

方式二:在Qt Creator Ui界面操作
在这里插入图片描述



8.3.2 设置PushButton 图标

在这里插入图片描述
在这里插入图片描述



8.3.3 设置PushButton图标大小

在这里插入图片描述



8.4 ToolButton

一般使用ToolButton来图片按钮
在这里插入图片描述

8.4.1添加文字

在这里插入图片描述

8.4.2 添加图片并设置大小

当添加万图片后,发现文字没有了
在这里插入图片描述

8.4.3 设置文字在图标下面

在这里插入图片描述



8.4.4 应用

    //设置标题
  • 11
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值