Bootstrap

QT开发(简洁版)

0. QT背景介绍

0.1 什么是QT

Qt 是⼀个 跨平台的 C++ 图形⽤⼾界⾯应⽤程序框架
开发者可以通过简单的 拖拽 组合 来实现复杂的应⽤程序,

0.2 Qt 的发展史 && 支持平台

  • 1991 年 Qt 最早由奇趣科技开发
  • 1996 年 进⼊商业领域,它也是⽬前流⾏的 Linux 桌⾯环境 KDE 的基础;
  • 2008 年 奇趣科技被诺基亚公司收购,Qt 成为诺基亚旗下的编程⼯具;
  • 2012 年 Qt ⼜被 Digia 公司收购;
  • 2014 年 4 ⽉ 跨平台的集成开发环境 Qt Creator3.1.0 发布,同年 5 ⽉ 20 ⽇发布了 Qt 5.3 正式版,⾄此 Qt 实现了对 IOS、Android、Embedded 等各平台的全⾯⽀持。

  • Windows ‒ XP、Vista、Win7、Win8、Win2008、Win10
  • Unix/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  
  • Android

 0.3 Qt 版本&& 优点

虽然目前最新的QT版本是QT6,但是我这里使用的是QT5

  • 跨平台 ,⼏乎⽀持所有的平台;
  • 接⼝简单,容易上⼿ ,学习 QT 框架对学习其他框架有参考意义。
  • ⼀定程度上简化了内存回收机制
  • 开发效率⾼,能够快速的构建应⽤程序。
  • 有很好的社区氛围,市场份额在缓慢上升。
  • 可以进⾏嵌⼊式开发。

0.4 Qt 的应⽤场景

  • 主要: 桌⾯应⽤程序, 嵌⼊式系统

  • 次要: 移动应⽤程序

注: 嵌入式系统指的是日常使用的: 冰箱,洗衣机,路由器,投影仪...之类的
      这些设备里面就使用了嵌入式系统

0.5 QT成功案例

Linux 桌⾯环境 KDE


 0.6 Qt 的发展前景

  • Qt 是⼀个 强⼤且⼴泛应⽤于跨平台软件开发的框架 。它提供了丰富的⼯具和库,可⽤于开发⾼质量、 ⾼效率的图形⽤⼾界⾯(GUI)应⽤程序

1. 搭建 Qt 开发环境 

第一部分: c++编译器(gcc,cl.exe....不是visual studio)

第二部分: Qt SDK(软件安装包)

第三部分: Qt 集成开发环境(IDE)

  • Qt 官方提供的Qt creator(不需要额外的配置,适合初学者)
  • Visual Studio 功能更强,但是需要额外的配置更多
  • Eclipse 不推荐

注: 这里只安装一个QT SDK就行了

1.1 Qt SDK 的下载

官网: Index of /archive/qt

  • 注: 如果无法访问,则可以使用网盘链接: 链接:提取码bite

1.2 Qt SDK 的安装

这里建议在双击之前断网,否则需要我们注册 Qt 账号登录后才能进⼊下 ⼀步安装 

  • 其他的,就不断下一步,下一步就行了 

1.3 配置环境变量

  至于为什么要配置这个环境变量: 

  • 为了让操作系统/QT Creator工具,能够找到Qt SDK中提供的exe,也就是运行Qt 程序的时候,能够找到对应.dll 动态库

 2. 认识QT Creator

2.1 使⽤ Qt Creator 新建项⽬

  •  其实,一路next,next也行

2.2 认识新建项目

 2.2.1 main.cpp

2.2.2 widget.h

 2.2.3 widget.cpp

2.2.4 widget.ui

  • Qt中使用xml文件就是去描述程序界面是什么样的
  • 进一步的qmake会调用相关的工具,依据这个xml文件生成一些c++代码,从而把完整的界面构建出来

 2.2.5 临时文件

  • 在运行一次程序之后,就会在 项目并列的地方,多出一个"build-xxxx"目录
  • 这个目录里面就是该项目运行过程中,生成的一些临时文件

2.3. 第一个hello world

2.3.1 图形化

  • 刚才往界面上拖拽了一个QLabel控件,此时,ui文件的xml中就会多出来这一段代码
  • 进一步的qmake就会在编译项目的时候,基于这个内容,生成一段C++代码,通过这个C++代码构建出界面内容

2.3.2 纯代码

  • 一般通过代码来构造界面的时候,通常会把构造界面放到Widget/MainWindow的构造函数中
  • 这里的this指针,是给当前这个label对象,指定一个父对象(以后会说的对象树)
  • 在QT中的字符串和C++/C中的字符串是不一样的,当时C++/C的字符串不好用,所以QT为了字节的开发能变的顺畅,就自己发明了一套轮子,搞了一系列基础类,来支持QT的开发,包括但不限于
    字符串->QString 动态数组->QVector 链表->QList 字典->QMap
  • 在QString中也提供了 C风格字符串作为参数的构造函数,不显示构造QString,上述代码中,C风格字符串也会隐式构造成QString对象 
  • 每个标签都有一个同名的头文件,但有时候也会被其他头文件间接包含,
    比如: QString 对应的头文件,已经被很多Qt内置的其他类给间接包含了,因此一般不需要显示包含QString头文件

这里还有一个值得思考的点: 一个对象new出来了,但是却没有delete,这难道不会造成内存泄漏吗

  • ​​​​在上述代码中,Qt不会产生内存泄漏,label对象会在合适的时候被析构函数释放~~
  • 之所以能够把对象释放掉,主要是因为把这个对象挂到了对象树上了,交给Qt的对象树统一管理
  • 如果这个对象是在栈上创建的话,就有可能会存在一些"提前释放"的问题,此时就会导致对应的控件就在界面上不存在了
  • 推荐: 在堆上创建对象,并挂在对象树上

2.3.3 验证对象树会统一析构

  • 注: 上面少写了个delete
  •  自己实现label类,并继承QLabel,然后再自主实现析构函数
  • 则可以在输出日志中观察到 自己实现的析构函数被调用了,则对象树的确会统一析构
  • 这里使用qDebug进行输出日志(不建议使用cout),还有一个好处->可以进行统一关闭
    输出日志一般是在开发阶段,调试程序的时候使用

 2.4 快捷键

  • 注释:ctrl + /
  • 运⾏:ctrl + R
  • 编译:ctrl + B
  • 字体缩放:ctrl + ⿏标滑轮
  • 查找:ctrl + F
  • 整⾏移动:ctrl + shift + ⬆/⬇
  • 帮助⽂档:F1
  • ⾃动对⻬:ctrl + i
  • 同名之间的 .h 和 .cpp 的切换:F4
  • ⽣成函数声明的对应定义: alt + enter

2.5 使⽤帮助⽂档 

  • 光标放到要查询的类名/⽅法名上, 直接按 F1(推荐)
  • Qt Creator 左侧边栏中直接⽤⿏标单击 "帮助" 按钮
  • 找到 Qt Creator 的安装路径,在 "bin" ⽂件夹下找到 assistant.exe,双击打开;

2.6 Qt 中的命名规范

  • 类名:⾸字⺟⼤写,单词和单词之间⾸字⺟⼤写;MyClass  MyAdd

  • 函数名及变量名:⾸字⺟⼩写,单词和单词之间⾸字⺟⼤写;studentCount

2.7 Qt 窗⼝坐标体系

计算机中的坐标系和数学中的坐标系是不一样的,y轴是向下增长的

 


 

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

3. 信号与槽

优点: 松散耦合   缺点: 效率较低

信号源: 由那个控件发出的信号

信号类型: 用户进行不同的操作,就可能会触发不同的信号

信号处理的方式: 槽(slot) -> 函数,这个函数的本质就是一种回调函数(callback)

Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来,后续只要信号触发了,Qt就会自动执行槽函数

 而在Qt中,一定是先关联信号 和 槽 ,然后再触发这个信号,顺序不能颠倒否则信号就不知道如何处理

 3.1 connect函数

在 Qt 中, QObject 类提供了⼀个静态成员函数 connect() ,该函数专⻔⽤来关联指定的信号函数和槽 函数

  • connect(const QObject * sender,const char* signal,const QObject* receiver
    const char* method, Qt::ConnectionType type = Qt::AutoConnection)
  • sender: 描述当前信号是那个控件发出的
  • signal: 描述信号类型
  • receiver: 描述了那个控件负责处理
  • method: 描述了这个控件怎么处理(要处理信号的对象提供成员函数)

3.2 深度理解connect函数参数

  • 我们上面传信号和槽都是传递的函数指针,而在C++中是不允许使用2个不同指针类型,相互传参相互赋值的(函数传参,本质就是赋值)
  • 其实这个函数声明是以前旧版本的QT的connect函数声明,以前个信号参数传参,要搭配要给SIGNAL宏,给槽函数传参要搭配一个SLOT宏(这样做的目的是 将 传入参数 转成char*)
  • connect(button,SIGNAL(&QPuhsButton::clicked),this,SLOT(&Widget::close))

但是后来因为书写起来太麻烦了,在Qt5时进行了改进

  •  使用模板实现泛型编程,重载构造函数  
  •   此时connect函数就有一定的参数检查功能:参数1和参数2不匹配 or 参数3和参数4不匹配,
    都会编译出错
  • 则要求参数2必须是参数1的成员函数,参数4必须是参数3的成员函数

3.2 查看信号与槽

 自己平时的积累 + 查看文档

  •  QPushButton自己本身没有clicked信号,但是它继承了QAbstractButton
  • 在QAbstractButton中就有一个clicked点击信号
  • 注: 这个QAbstractButton又使继承QWidget

  •  我们写的widget在定义的时候就是继承QWidget的,而QWidget就有close这个关闭槽
  • 注: QWidget这个类又是继承自QObject类的

3.3 自定义槽

 3.3.1 纯代码

3.3.2 图形化

  • 使用图形化的方式创建,QTCreator 会直接给我们生成好一个函数on_pushButton_clicked
    
  • 在Qt中,除了通过connect来连接信号槽之外,还可以通过函数名字的方式自动连接

  •  Qt中调用这个函数的时候,就会触发上述自动连接信号槽的规则
  • 这个正是在自动生成的ui_widget.h中调用的

3.4 自定义信号(了解)

  • 自定义信号,本质就是一个函数,只需要声明就行了,而这个函数的定义,是Qt在编译过程中,自动生成的(我们无法干预)
  • 而作为信号参数,这个函数的返回值,必须是void的,有没有参数都是可以的,甚至可以支持重载
  • Qt内置的信号,不需要手动触发,而自定义信号,需要手动代码触发  emit mySignal();
  • 这里使用图形化方式创建一个按钮,在通过这个按钮发送 自定义信号
  • 发送信号的操作, 也可以在任意合适的代码中. 不一定非得在构造函数里

3.6 带参数的信号与槽

  •  带有参数的信号,要求信号的参数和槽的参数要一致

  • 类型,个数要满足要求(信号的参数个数要多于槽的参数个数)

3.7 额外说明

3.7.1 前置条件

  •  Qt 中如果要让某个类能够使用信号槽(可以在类中定义信号和槽函数),则必须要在类最开始的地方,写下Q_OBJECT
  • 将这个宏展开,会得到一大段代码,然后而这一大段代码又可以展开

 3.7.2 设计目的

  • 解耦,: 把触发 用户操作的控件 和 处理对应用户的操作逻辑 解耦合
  • 实现"多对多"效果
    一个信号,可以connect到多个槽函数上
    一个槽函数也可以被多个信号connect
  • 但是多对多的需求实际中并不常见,所以以后图形化开发框架都没有支持多对多

 3.7.3 disconnect断开连接

主动断开往往是把信号重新绑定到另一个槽函数上

  • 如果没有 disconnect, 就会构成 一个信号绑定了两个槽函数. 触发信号的时候, 两个槽函数都会执行

3.7.4 lambda表达式 

定义槽函数的时候,也是可以使用lambda表达式的,

  •  为了解决lambda访问作用域的问题,就需要引入变量捕捉的语法规则
  • lambda语法是c++11中引入的,对于Qt5及其更高版本,默认就是按照c++ 11来编译的
  • 如果使用Qt4或者更老的版本,就需要手动在.pro文件中加上C++的编译选项:CONFIG += c++11

4. 常用控件

 4.1 QWidget 核心属性

4.1.0 objectName 

4.1.1 enabled

API说明
isEnabled()
获取到控件的可⽤状态
setEnabled
设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤

 4.1.2 geometry && window frame

geometry: x y width height

API
说明
geometry()
获取到控件的位置和尺⼨. 返回结果是⼀个 QRect, 包含了 x, y, width, height. 其
中 x, y 是左上⻆的坐标
setGeometry (QRect)
setGeometry (int x, int y,
int width, int height)
设置控件的位置和尺⼨. 可以直接设置⼀个 QRect, 也可以分四个属性单独设置.

 

  •  注意: setGeometry(QRect),使用这个函数可能会改变原来的控件的width 和 heiv

API
说明(以下API:  计算时包含 window frame)
x()
获取横坐标
y()
获取纵坐标
pos()
返回 QPoint 对象, ⾥⾯包含 x(), y(), setX(), setY() 等⽅法.
frameSize()
返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等⽅法.
frameGeometry()
返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y, width, size.
API
说明(以下API:  计算时不包含 window frame)
width()
获取宽度
height()
获取⾼度
size()
返回 QSize 对象, ⾥⾯包含 width(), height(), setWidth(), setHeight() 等⽅法.
rect()
返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取并设置 x,
y, width, size.
geometry()
返回 QRect 对象. QRect 相当于 QPoint 和 QSize 的结合体. 可以获取 x, y,
width, size
setGeometry()
直接设置窗⼝的位置和尺⼨. 可以设置 x, y, width, height, 或者 QRect 对象

4.1.3 windowTitie 

API说明
windowTitle()
获取到控件的窗⼝标题.
setWindowTitle(const QString& title)
设置控件的窗⼝标题

 4.1.4 windowlcon

API
说明
windowIcon()
获取到控件的窗⼝图标. 返回 QIcon 对象
setWindowIcon(const QIcon& icon )
设置控件的窗⼝图标.
通过 qrc 管理图⽚作为图标

 给Qt项目引入一个额外的xml文件(后缀名使用.qrc表示),在这个xml中把要使用的图片资源给导入进来,并且在xml中记录

Qt在编译项目的时候,就会根据qrc中描述的图片信息,找到图片内容,并且提取出图片的二进制数据,把这些二进制数据转换成C++代码,最终编译到exe里


 新建qrc文件

  • 注意 : 添加的⽂件必须是在 qrc ⽂件的同级⽬录, 或者同级⽬录的⼦⽬录中. 

 

  •  在引入了qrc机制之后,就可以直接用相对路径访问的这个图片
  • qrc机制主要解决2个问题:
    确保图片所在的路径在目标用户机器上存在
    确保图片不会被用户搞没了

4.1.5 windowOpacity 

API说明
windowOpacity()
获取到控件的不透明数值. 返回 float , 取值为 0.0 -> 1.0
其中 0.0 表⽰全透明,1.0 表⽰完全不透明
setWindowOpacity(float n)
设置控件的不透明数值.

  •  注意: 窗口的不透明度,变化并非是精确的,这主要和浮点数在内存中的存储有关

4.1.6 cursor

API
说明
cursor()
获取到当前 widget 的 cursor 属性, 返回 QCursor 对象.
当⿏标悬停在该 widget 上时, 就会显⽰出对应的形状
setCursor(const QCursor& cursor)
设置该 widget 光标的形状. 仅在⿏标停留在该 widget 上时⽣效.
QGuiApplication::setOverrideCursor(co
nst QCursor& cursor )
设置全局光标的形状. 对整个程序中的所有 widget 都会⽣效. 覆盖
上⾯的 setCursor 设置的内容.

 

  • QPixmap是一个位图对象 

4.1.7 font

API
说明
font()
获取当前 widget 的字体信息. 返回 QFont 对象
setFont(const QFont& font)
设置当前 widget 的字体信息

 关于 QFont

属性
说明
family
字体家族. ⽐如 "楷体", "宋体", "微软雅⿊" 等.
pointSize
字体⼤⼩
weight
字体粗细. 以数值⽅式表⽰粗细程度取值范围为 [0, 99], 数值越⼤, 越
bold
是否加粗. 设置为 true, 相当于 weight 为 75. 设置为 false 相当于 weight 为 50
italic
是否倾斜
underline
是否带有下划线
strikeOut
是否带有删除线

  •  也可以使用ui界面直接修改

4.1.8 tooltip(解释说明的意思)

API
说明
setToolTip
设置 toolTip. ⿏标悬停在该 widget 上时会有提⽰说明.
setToolTipDuring
设置 toolTip 提⽰的时间. 单位 ms. 时间到后 toolTip ⾃动消失

4.1.9 focusPolicy(设置控件获取到焦点)

设置控件获取到焦点的策略. ⽐如某个控件能否⽤⿏标选中或者能否通过 tab 键选中.
API
说明
focusPolicy()
获取该 widget 的 focusPolicy, 返回 Qt::FocusPolicy
setFocusPolicy(Qt::FocusPolicy policy )
设置 widget 的 focusPolicy.

 

  • Qt::NoFocus :控件不会接收键盘焦点
  • Qt::TabFocus :控件可以通过Tab键接收焦点
  • Qt::ClickFocus :控件在⿏标点击时接收焦点
  • Qt::StrongFocus :控件可以通过Tab键和⿏标点击接收焦点 (默认值)
  • Qt::WheelFocus : 类似于 Qt::StrongFocus , 同时控件也通过⿏标滚轮获取到焦点 (新增
    的选项, ⼀般很少使⽤)

  • ui界面上也可以直接更改 

4.1.10 styleSheet

通过 CSS 设置 widget 的样式.

  •  也可以在ui界面上右键,然后再添加编辑样式表,

  • 注意:这里的css样式别写错了,如果写错了Qt是不会报错的

4.2 按钮类 

4.2.1 Pushbutton(普通按钮)

API说明
text
按钮中的⽂本
icon
按钮中的图标
iconSize
按钮中图标的尺⼨
shortCut
按钮对应的快捷键
autoRepeat
按钮是否会重复触发. 当⿏标左键按住不放时,
如果设为 true, 则会持续产⽣⿏标点击事件;
如果设为 false, 则必须释放⿏标, 再次按下⿏标时才能产⽣点击事件.
(相当于游戏⼿柄上的 "连发" 效果)
autoRepeatDelay
重复触发的延时时间. 按住按钮多久之后, 开始重复触发.
 autoRepeatInterval
重复触发的周期.
  •  QAbstractButton 作为 QWidget 的⼦类, 当然也继承了 QWidget 的属性. 上⾯
    介绍的 QWidget ⾥的各种属性⽤法, 对于 QAbstractButton 同样适⽤


4.2.2 Radio Buttion(单选)  

API说明
checkable
是否能选中
checked
是否已经被选中. checkable 是 checked 的前提条件
autoExclusive
是否排他.
选中⼀个按钮之后是否会取消其他按钮的选中.
对于 QRadioButton 来说默认就是排他的


4.2.3 QButtonGroup(按钮组)

  • QButtonGroup表示: 每⼀组内部来控制排他, 但是组和组之间不能排他 

4.2.4 Check Box(多选)

 4.2.5 Tool Button(工具按钮)

QToolButton 的⼤部分功能, 和 QPushButton 是⼀致的. 但是 QToolButton 主要应⽤在⼯
具栏, 菜单等场景. 

4.3 显示

4.3.1 Lable(普通文本框)

属性
说明
text
QLabel 中的⽂本
textFormat
Qt::PlainText 纯⽂本
Qt::RichText 富⽂本(⽀持 html 标签)
Qt::MarkdownText markdown 格式
Qt::AutoText 根据⽂本内容⾃动决定⽂本格式
pixmap
QLabel 内部包含的图⽚
scaledContents
设为 true 表⽰内容⾃动拉伸填充 QLabel
设为 false 则不会⾃动拉伸
alignment
对⻬⽅式.
可以设置⽔平和垂直⽅向如何对⻬
wordWrap
设为 true 内部的⽂本会⾃动换⾏.
设为 false 则内部⽂本不会⾃动换⾏.
indent
设置⽂本缩进. ⽔平和垂直⽅向都⽣效
margin
内部⽂本和边框之间的边距.
不同于于 indent, 但是是上下左右四个⽅向都同时有效.
⽽ indent 最多只是两个⽅向有效(具体哪两个⽅向有效取决于 alignment )
openExternalLinks
是否允许打开⼀个外部的链接.
(当 QLabel ⽂本内容包含 url 的时候涉及到)
buddy
给 QLabel 关联⼀个 "伙伴" , 这样点击 QLabel 时就能激活对应的伙伴.
例如伙伴如果是⼀个 QCheckBox, 那么该 QCheckBox 就会被选中


  • 虽然 QPushButton 也可以通过设置图标的⽅式设置图⽚, 但是并⾮是⼀个好的选择. 更多的时候还是希望通过 QLabel 来作为⼀个 更单纯的显⽰图⽚ 的⽅式.

 上述的案例存在一个问题,用户在拉伸窗口时,图片的size并没有随之改变,我们需要引入事件来解决

Qt中,表示用户的操作,有两类概念:一个是信号,另一个是事件~~

 

  

  • 当用户拖拽窗口大小的时候,就会触发resize事件(resizeEvent)
  • 像resize这样的事件,是连续变化的,把窗口尺寸从A拖到B这个过程中,就会触发一系列的resizeEvent,然后就可以借助resizeEvent来完成上述的功能

  • 此处设置的缩进,即使文本换行了后续的行也会产生缩进,不仅仅是首行缩进

 

  • 在QT中,QLabel中写的文本,是可以指定"快捷键",但这里的快捷键的规则功能上,要比QPushButton要弱一点
  • 它表示快捷键的操作是 & 跟上一个字符来表示快捷键
    比如: &A => 通过键盘上的alt+a来触发
  • 绑定了伙伴关系之后,通过快捷键就可以选中对应的单选按钮/复选按钮

4.3.2 LCD Number(LCD电子数字)

QLCDNumer 是⼀个专⻔⽤来显⽰数字的控件. 类似于 "⽼式计算器" 的效果.  

属性
说明
intValue
QLCDNumber 显⽰的数字值(int).
value
QLCDNumber 显⽰的数字值(double). 和intValue 是联动的.
例如给 value 设为 1.5, intValue 的值就是 2.
另外,设置 value 和 intValue 的⽅法名字为 display , ⽽不是 setValue 或 者 setIntValue
digitCount
显⽰⼏位数字.
mode
数字显⽰形式.
1. QLCDNumber::Dec :⼗进制模式,显⽰常规的⼗进制数字。
2. QLCDNumber::Hex :⼗六进制模式,以⼗六进制格式显⽰数字。
3. QLCDNumber::Bin :⼆进制模式,以⼆进制格式显⽰数字。
4. QLCDNumber::Oct :⼋进制模式,以⼋进制格式显⽰数字
只有⼗进制的时候才能显⽰⼩数点后的内容
segmentStyle
设置显⽰⻛格.
1. QLCDNumber::Flat :平⾯的显⽰⻛格,数字呈现在⼀个平坦的表⾯上。
2. QLCDNumber::Outline :轮廓显⽰⻛格,数字具有清晰的轮廓和阴影效果。
3. QLCDNumber::Filled :填充显⽰⻛格,数字被填充颜⾊并与背景区分开
smallDecimalPoin
设置⽐较⼩的 ⼩数点.

关于定时器 

  •  在C++标准库中,没有提供定时器的实现,但Boost里面提供了对应的功能
  • 而在Qt中也封装了对应的定时器~~(结合了信号槽机制)
  • 通过QTimer这个类创建处理的对象,就会产生一个timeout这样的信号
  • 再结合connect,把这个timeout信号绑定到需要的槽函数中,就可以执行周期性的修改LCDNumber中的数字

  •  在Qt里面,界面中有一个专门的线程去负责维护更新(主线程->main函数所在的线程)
  • 而对于GUI来说,内部包含了很多的隐藏状态,Qt为了保证修改界面的过程中,线程安全是不会受影响的,所以Qt就禁止了其他线程直接修改界面
    ui->lcdNumer->display(value),形如这种操作,就是在修改界面
  • 对于Qt的槽函数来说,默认情况下,槽函数都是由主线程调用的,在槽函数中修改界面是没有任何问题的
    但要是自己创建了一个新线程,再修改界面就会出错,程序就会异常退出

4.3.3  ProgressBar(进度条)

属性说明
minimum
进度条最⼩值
maximum
进度条最⼤值
value
进度条当前值
alignment
⽂本在进度条中的对⻬⽅式
Qt::AlignLeft : 左对⻬
Qt::AlignRight : 右对⻬
Qt::AlignCenter : 居中对⻬
Qt::AlignJustify : 两端对⻬
textVisible
进度条的数字是否可⻅.
orientation
进度条的⽅向是⽔平还是垂直
invertAppearance
是否是朝反⽅向增⻓进度
textDirection
⽂本的朝向
format
展⽰的数字格式.
%p :表⽰进度的百分⽐(0-100)
%v :表⽰进度的数值(0-100)
%m :表⽰剩余时间(以毫秒为单位)
%t :表⽰总时间(以毫秒为单位)

  • 进度条的更新也是需要搭配定时器来完成的

 4.3.4 Calendar Widget(日历)

属性说明
selectDate
当前选中的⽇期
minimumDate
最⼩⽇期
maximumDate
最⼤⽇期
firstDayOfWeek
每周的第⼀天(也就是⽇历的第⼀列) 是周⼏
gridVisible
是否显⽰表格的边框
selectionMode
是否允许选择⽇期
navigationBarVisible
⽇历上⽅标题是否显⽰
horizontalHeaderFormat
⽇历上⽅标题显⽰的⽇期格式
verticalHeaderFormat
⽇历第⼀列显⽰的内容格式
dateEditEnabled
是否允许⽇期被编辑

重要信号
信号
说明
selectionChanged(const QDate&)
当选中的⽇期发⽣改变时发出
activated(const QDate&)
当双击⼀个有效的⽇期或者按下回⻋键时发出,
形参是⼀个QDate类型,保存了选中的⽇期
currentPageChanged(int, int)
当年份⽉份改变时发出,形参表⽰改变后的新年份和⽉份

4.4 输入类控件 

4.4.1 Line Edit(单行输入框)

属性说明
text
输⼊框中的⽂本
inputMask
输⼊内容格式约束
maxLength
最⼤⻓度
frame
是否添加边框
echoMode
显⽰⽅式.
QLineEdit::Normal :这是默认值,⽂本框会显⽰输⼊的⽂本。
QLineEdit::Password :在这种模式下,输⼊的字符会被隐藏,
通常⽤星号(*)或等号(=)代替。
QLineEdit::NoEcho :在这种模式下,⽂本框不会显⽰任何输⼊
的字符
cursorPosition
光标所在位置
alignment
⽂字对⻬⽅式, 设置⽔平和垂直⽅向的对⻬
dragEnabled
是否允许拖拽
readOnly
是否是只读的(不允许修改)
placeHolderText
当输⼊框内容为空的时候, 显⽰什么样的提⽰信息
clearButtonEnabled
是否会⾃动显⽰出 "清除按钮"

核⼼信号

属性
说明
void cursorPositionChanged(int old, int new)
当⿏标移动时发出此信号,old为先前的位置,new为新位置
void editingFinished()
当按返回或者回⻋键时,或者⾏编辑失去焦点时,发出此信号
void returnPressed()
当返回或回⻋键按下时发出此信号.
如果设置了验证器, 必须要验证通过, 才能触发
void selectionChanged()
当选中的⽂本改变时,发出此信号
void textChanged(const
QString &text)
当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。
代码对⽂本的修改 能够 触发这个信号
void textEdited(const QString
&text))
当QLineEdit中的⽂本改变时,发出此信号,text是新的⽂本。
代码对⽂本的修改 不能 触发这个信号.

  • inputMask 只能进⾏简单的输⼊格式校验.
    实际开发中, 基于 正则表达式 的⽅式是更核⼼的⽅法

使⽤正则表达式验证输⼊框的数据
要求在输⼊框中输⼊⼀个合法的电话号码(1 开头, 11 位, 全都是数字). 如果验证不通过, 则确定按钮⽆法点击

 

  •  这里说明一下state validator(QString&,int &)函数的2个参数,
  • 第一个参数是要验证的字符串,参数类型是QString & 不是const QString&,所以这里我创建了一个临时对象
  • 第二个参数表示,如果这个字符串不符合规则,是从那个位置开始的

4.4.2 Text Edit(多行输入框)

QTextEdit 表⽰多⾏输⼊框. 也是⼀个富⽂本 & markdown 编辑器. 并且能在内容超出编辑框范围时⾃动提供滚动条

属性说明
markdown
输⼊框内持有的内容. ⽀持 markdown 格式. 能够⾃动的对markdown ⽂本进⾏
渲染成 html
html
输⼊框内持有的内容. 可以⽀持⼤部分 html 标签. 包括 img 和 table 等
placeHolderText
输⼊框为空时提⽰的内容.
readOnly
是否是只读
undoRedoEnable
是否开启 undo / redo 功能.
按下 ctrl + z 触发 undo
按下 ctrl + y 触发 redo
autoFormating
开启⾃动格式化.
tabstopWidth
按下缩进占多少空间
overwriteMode
是否开启覆盖写模式
acceptRichText
是否接收富⽂本内容
verticalScrollBarPolicy
垂直⽅向滚动条的出现策略
Qt::ScrollBarAsNeeded : 根据内容⾃动决定是否需要滚动条。这是默认值。
Qt::ScrollBarAlwaysOff : 总是关闭滚动条。
Qt::ScrollBarAlwaysOn : 总是显⽰滚动条。
horizontalScrollBarPolicy
⽔平⽅向滚动条的出现策略
Qt::ScrollBarAsNeeded : 根据内容⾃动决定是否需要滚动条。这是默认值。
Qt::ScrollBarAlwaysOff : 总是关闭滚动条。
Qt::ScrollBarAlwaysOn : 总是显⽰滚动条

核心信号

核心属性说明
textChanged()
⽂本内容改变时触发
selectionChanged()
选中范围改变时触发
cursorPositionChanged()
光标移动时触发
undoAvailable(bool)
可以进⾏ undo 操作时触发
redoAvailable(bool)
可以进⾏ redo 操作时触发
copyAvaiable(bool)
⽂本被选中/取消选中时触发

 4.4.3 Combo Box(下拉选择框)

属性说明
currentText
当前选中的⽂本
currentIndex
当前选中的条⽬下标
从 0 开始计算. 如果当前没有条⽬被选中, 值为 -1
editable
是否允许修改
设为 true 时, QComboBox 的⾏为就⾮常接近 QLineEdit , 也可以设置 validator
iconSize
下拉框图标 (⼩三⻆) 的⼤⼩
maxCount
最多允许有多少个条⽬

核⼼⽅法

⽅法
说明
addItem(const QString&)
添加⼀个条⽬
currentIndex()
获取当前条⽬的下标
从 0 开始计算. 如果当前没有条⽬被选中, 值为 -1
currentText()
获取当前条⽬的⽂本内容

核⼼信号
方法说明
activated(int)
activated(const QString & text)
当⽤⼾选择了⼀个选项时发出.
这个时候相当于⽤⼾点开下拉框, 并且⿏标划过某个选项.
此时还没有确认做出选择
currentIndexChanged(int)
currentIndexChanged(const QString
& text)
当前选项改变时发出.
此时⽤⼾已经明确的选择了⼀个选项.
⽤⼾操作或者通过程序操作都会触发这个信号
editTextChanged(const QString &
text)
当编辑框中的⽂本改变时发出
(editable 为 true 时有效)

  •  下拉框里的条目,一般都是从网络/文件中读取的
  • QString::fromStdString(s);// 把std::string 转换成QString
  • s.toStdString();//把QString转换成std::string

4.4.4 Spin Box(微调框) 

属性说明
value
存储的数值
singleStep
每次调整的 "步⻓". 按下⼀次按钮数据变化多少
displayInteger
数字的进制. 例如 displayInteger 设为 10, 则是按照 10 进制表⽰. 设为 2 则为 2 进制表⽰
minimum
最⼩值
maximum
最⼤值
suffix
后缀
prefix
前缀
wrapping
是否允许换⾏
frame
是否带边框
alignment
⽂字对⻬⽅式.
readOnly
是否允许修改
buttonSymbol
按钮上的图标.
UpDownArrows 上下箭头形式
PlusMinus 加减号形式
NoButtons 没有按钮
accelerated (加速的)
按下按钮时是否为快速调整模式
correctionMode
输⼊有误时如何修正.
QAbstractSpinBox::CorrectToPreviousValue : 如果⽤⼾输⼊了⼀个⽆效的值(例如,在只能显⽰正整数的SpinBox中输⼊了负数),那么SpinBox会恢复为上⼀个有效值。例如,如果SpinBox的初始值是1,⽤⼾
输⼊了-1(⽆效),然后SpinBox会恢复为1。
QAbstractSpinBox::CorrectToNearestValue : 如果⽤⼾输⼊了⼀个 ⽆效的值,SpinBox会恢复为最接近的有效值。例如,如果SpinBox的初始值是1,⽤⼾输⼊了-1(⽆效),那么SpinBox会恢复为0
keyboardTrack
是否开启键盘跟踪.
设为 true, 每次在输⼊框输⼊⼀个数字, 都会触发⼀次 valueChanged() 和
textChanged() 信号.
设为 false, 只有在最终按下 enter 或者输⼊框失去焦点, 才会触发
valueChanged() 和 textChanged() 信号.
核⼼信号
信号
说明
textChanged(QString)
微调框的⽂本发⽣改变时会触发. 参数 QString 带有 前缀 和 后缀.
valueChanged(int)
微调框的⽂本发⽣改变时会触发.
参数 int, 表⽰当前的数值

4.4.5 Date Edit & Time Edit  (日期&时间微调框)

这⼏个控件⽤法⾮常相似, 这里以 QDateTimeEdit 为例进⾏介绍
属性说明
dateTime
时间⽇期的值. 形如 2000/1/1 0:00:00
date
单纯⽇期的值. 形如 2001/1/1
time
单纯时间的值. 形如 0:00:00
displayFormat
时间⽇期格式. 形如 yyyy/M/d H:mm
y 表⽰年份
M 表⽰⽉份
d 表⽰⽇期
H 表⽰⼩时
m 表⽰分钟
s 表⽰秒
注意: 这⾥的格式化符号的含义, 不要记忆. 不同语⾔/库的设定规则
是存在差异的. ⼀定是⽤的时候再去查
minimumDateTime
最⼩时间⽇期
maximumDateTime
最⼤时间⽇期
timeSpec
Qt::LocalTime :显⽰本地时间。
Qt::UTC :显⽰协调世界时(UTC)。
Qt::OffsetFromUTC :显⽰相对于UTC的偏移量(时差)
  •  UTC 时间是⼀个基于原⼦钟的标准时间
  • 如我们使⽤的北京时间, 位于 "东⼋区", 就需要在 UTC 时间基础上 +8 个⼩时的时差

 核⼼信号

信号
说明
dateChanged(QDate)
⽇期改变时触发.
timeChanged(QTime)
时间改变时触发.
dateTimeChanged(QDateTime)
时间⽇期任意⼀个改变时触发.

 

4.4.6 Dial(旋转按钮)

属性说明
value
持有的数值.
minimum
最⼩值
maximum
最⼤值
singleStep
按下⽅向键的时候改变的步⻓
pageStep
按下 pageUp / pageDown 的时候改变的步⻓
sliderPosition
界⾯上旋钮显⽰的 初始位置
tracking
外观是否会跟踪数值变化.
默认值为 true. ⼀般不需要修改.
wrapping
是否允许循环调整.
即数值如果超过最⼤值, 是否允许回到最⼩值.
(调整过程能否 "套圈")
notchesVisible
是否显⽰ 刻度线
notchTarget
刻度线之间的相对位置.
数字越⼤, 刻度线越稀疏

核心信号

属性说明
valueChanged(int)
数值改变时触发
rangeChanged(int, int)
范围变化时触发

4.4.7 Slider(滑动条) 

属性
说明
value
持有的数值.
minimum
最⼩值
maximum
最⼤值
singleStep
按下⽅向键的时候改变的步⻓
pageStep
按下 pageUp / pageDown 的时候改变的步⻓
sliderPosition
滑动条显⽰的 初始位置
tracking
外观是否会跟踪数值变化.
默认值为 true. ⼀般不需要修改
orientation
滑动条的⽅向是⽔平还是垂直
invertedAppearance
是否要翻转滑动条的⽅向
tickPosition
刻度的位置
tickInterval
刻度的密集程度

核⼼信号

属性
说明
valueChanged(int)
数值改变时触发
rangeChanged(int, int)
范围变化时触发

  • 通过这些设置,就可以让滑动条控制拉伸窗口的大小 

  •  也可以通过QShortcut这里个类实现快捷键,并connect槽函数

4.5 多元素控件

  • QListWidget QListView 列表
  • QTableWidget QTableView 表格
  • QTreeWidget QTreeView 树形

4.5.0 xxWidget vs xxView是什么区别

  • xxView是MVC结构的一种典型实现,MVC是软件开发中,非常经典的软件结构组织形式
    M model数据,V view 视图(界面),C controller控制器 数据和视图之间的业务流程

  • xxView只是负责实现了视图,不负责数据如何存储表示,更不负责数据和视图之间的交互

  • 而xxWidget是基于xxView封装而来的,同时吧model和controller都帮我们实现好了
    可以直接使用,它提供了功能很方便的api,让我们直接使用

 4.5.1 List Widget(纵向列表)

属性说明
currentRow
当前被选中的是第⼏⾏
count
⼀共有多少⾏
sortingEnabled
是否允许排序
isWrapping
是否允许换⾏
itemAlignment
元素的对⻬⽅式
selectRectVisible
被选中的元素矩形是否可⻅
spacing
元素之间的间隔

 核⼼⽅法

⽅法
说明
addItem(const QString& label)
addItem(QListWidgetItem * item)
列表中添加元素
currentItem()
 返回 QListWidgetItem* 表⽰当前选中的元素
setCurrentItem(QListWidgetItem* item)
设置选中哪个元素
setCurrentRow(int row)
设置选中第⼏⾏的元素
insertItem(const QString& label, int row)
insertItem(QListWidgetItem * item, int row)
在指定的位置插⼊元素
item(int row)
返回 QListWidgetItem* 表⽰第 row ⾏的元素
takeItem(int row)
删除指定⾏的元素, 返回 QListWidgetItem* 表⽰是哪个元素被删
除了

核心信号

方法说明
currentItemChanged(QListWidgetItem*
current, QListWidgetItem* old)
选中不同元素时会触发. 参数是当前选中的元素和之前选中的元素
currentRowChanged(int)
选中不同元素时会触发. 参数是当前选中元素的⾏数.
itemClicked(QListWidgetItem* item)
点击某个元素时触发
itemDoubleClicked(QListWidgetItem* item)
双击某个元素时触发
itemEntered(QListWidgetItem* item)
⿏标进⼊元素时触发

  •  对于添加条目,可以直接通过图形化界面的方式

 

  •  使用QListWidget时,记得包含头文件<QListWidget>

 4.5.2 Table Widget(表单)

TableWidget控件相当于二维ListWidget

属性说明
item(int row, int column)
根据⾏数列数获取指定的 QTableWidgetItem*
setItem(int row, int column,
QTableWidget*)
根据⾏数列数设置表格中的元素
currentItem()
返回被选中的元素 QTableWidgetItem*
currentRow()
返回被选中元素是第⼏⾏
currentColumn()
返回被选中元素是第⼏列
row(QTableWidgetItem* )
获取指定 item 是第⼏⾏
column(QTableWidgetItem* )
获取指定 item 是第⼏列
rowCount()
获取⾏数
columnCount()
获取列数
insertRow(int row)
在第 row ⾏处插⼊新⾏
insertColumn(int column)
在第 column 列插⼊新列
removeRow(int row)
删除第 row ⾏

 removeColumn(int column)

删除第 column 列
setHorizontalHeaderItem(int column, QTableWidget*)
设置指定列的表头
setVerticalHeaderItem(int row, QTableWidget*)
设置指定⾏的表头

 核⼼信号

信号
说明
cellClicked(int row, int column)
点击单元格时触发
cellDoubleClicked(int row, int
column)
双击单元格时触发
cellEntered(int row, int column)
⿏标进⼊单元格时触发
currentCellChanged(int row, int column, int previousRow, int previousColumn)
选中不同单元格时触发
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{ 
    ui->setupUi(this);
    ui->lineEdit->setPlaceholderText("请输入一列名");
    ui->lineEdit_2->setPlaceholderText("请输入一行名");
    // 初始化表单
    // 创建 3 行
    ui->tableWidget->insertRow(0);
    ui->tableWidget->insertRow(1);
    ui->tableWidget->insertRow(2);

    // 给 3 行设定行名
    ui->tableWidget->setVerticalHeaderItem(0, new QTableWidgetItem("NO.1"));
    ui->tableWidget->setVerticalHeaderItem(1, new QTableWidgetItem("NO.2"));
    ui->tableWidget->setVerticalHeaderItem(2, new QTableWidgetItem("NO.3"));

    // 创建 3 列
    ui->tableWidget->insertColumn(0);
    ui->tableWidget->insertColumn(1);
    ui->tableWidget->insertColumn(2);

    // 给 3 列设定列名
    ui->tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("学号"));
    ui->tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("姓名"));
    ui->tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("年龄"));

    // 设置初始数据
    ui->tableWidget->setItem(0, 0, new QTableWidgetItem("1001"));
    ui->tableWidget->setItem(0, 1, new QTableWidgetItem("张三"));
    ui->tableWidget->setItem(0, 2, new QTableWidgetItem("20"));

    ui->tableWidget->setItem(1, 0, new QTableWidgetItem("1002"));
    ui->tableWidget->setItem(1, 1, new QTableWidgetItem("李四"));
    ui->tableWidget->setItem(1, 2, new QTableWidgetItem("21"));

    ui->tableWidget->setItem(2, 0, new QTableWidgetItem("1003"));
    ui->tableWidget->setItem(2, 1, new QTableWidgetItem("王五"));
    ui->tableWidget->setItem(2, 2, new QTableWidgetItem("19"));

}

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

void Widget::on_pushButton_addrow_clicked()
{
   int row = ui->tableWidget->rowCount();
   ui->tableWidget->insertRow(row);
   const QString& name = ui->lineEdit_2->text();
   ui->tableWidget->setVerticalHeaderItem(row,new QTableWidgetItem(name));
}

void Widget::on_pushButton_addcolum_clicked()
{
    int col = ui->tableWidget->columnCount();
    ui->tableWidget->insertColumn(col);
    const QString& name = ui->lineEdit->text();
    ui->tableWidget->setHorizontalHeaderItem(col,new QTableWidgetItem(name));
}

void Widget::on_pushButton_delrow_clicked()
{
    int row = ui->tableWidget->currentRow();
    ui->tableWidget->removeRow(row);
}

void Widget::on_pushButton_delcolum_clicked()
{
    int cur = ui->tableWidget->currentColumn();
    ui->tableWidget->removeColumn(cur);
}

4.5.3 Tree Widget (树状形节点表单)

属性说明
clear
清空所有⼦节点
addTopLevelItem(QTreeWidgetItem* item)
新增顶层节点
topLevelItem(int index)
获取指定下标的顶层节点.
topLevelItemCount()
获取顶层节点个数
indexOfTopLevelItem(QTreeWidgetItem*
item)
查询指定节点是顶层节点中的下标
takeTopLevelItem(int index)
删除指定的顶层节点. 返回 QTreeWidgetItem* 表⽰被删除
的元素
currentItem()
获取到当前选中的节点, 返回 QTreeWidgetItem*
setCurrentItem(QTreeWidgetItem* item)
选中指定节点
setExpanded(bool)
展开/关闭节点
setHeaderLabel(const QString& text)
设置 TreeWidget 的 header 名称.

核心方法

方法说明
addChild(QTreeWidgetItem* child)
新增⼦节点
childCount()
⼦节点的个数
child(int index)
获取指定下标的⼦节点. 返回 QTreeWidgetItem*
takeChild(int index)
删除对应下标的⼦节点
removeChild(QTreeWidgetItem*
child)
删除对应的⼦节点
parent()
获取该元素的⽗节点

核⼼信号

信号说明
currentItemChanged(QTreeWidgetItem*
current, QTreeWidgetItem* old)
切换选中元素时触发
itemClicked(QTreeWidgetItem* item, int col)
点击元素时触发
itemDoubleClicked(QTreeWidgetItem* item, int col)
双击元素时触发
itemEntered(QTreeWidgetItem* item, int col)
⿏标进⼊时触发
itemExpanded(QTreeWidgetItem* item)
元素被展开时触发
itemCollapsend(QTreeWidgetItem* item)
元素被折叠时触发
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{ 
    ui->setupUi(this);
    ui->treeWidget->setHeaderLabel("动物");

    QTreeWidgetItem* item1 = new QTreeWidgetItem();
    item1->setText(0, "猫");
    ui->treeWidget->addTopLevelItem(item1);//添加顶层节点

    QTreeWidgetItem* item2 = new QTreeWidgetItem();
    item2->setText(0, "狗");
    ui->treeWidget->addTopLevelItem(item2);//添加顶层节点

    QTreeWidgetItem* item3 = new QTreeWidgetItem();
    item3->setText(0, "鸟");
    ui->treeWidget->addTopLevelItem(item3);//添加顶层节点
}

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

void Widget::on_pushButton_clicked()
{
    const QString& text = ui->lineEdit->text();
    if(text.isEmpty()){
        return;
    }
    // 添加到顶层节点中
    QTreeWidgetItem* item = new QTreeWidgetItem();
    item->setText(0,text);
    ui->treeWidget->addTopLevelItem(item);
}

void Widget::on_pushButton_2_clicked()
{
     const QString& text = ui->lineEdit->text();
     if (text.isEmpty()) {
        return;
     }
     // 获取到当前选中的节点
     QTreeWidgetItem* currentItem = ui->treeWidget->currentItem();
     if (currentItem == NULL) {
        return;
     }
     // 构造新的 item
     QTreeWidgetItem* newItem = new QTreeWidgetItem();
     newItem->setText(0, text);
     // 添加 item 到选中节点
     currentItem->addChild(newItem);
     // 展开父节点
     currentItem->setExpanded(true);
}

void Widget::on_pushButton_3_clicked()
{
    QTreeWidgetItem* cur = ui->treeWidget->currentItem();
    if(cur == NULL){
        return;
    }
    // 获取当前节点的父节点
    QTreeWidgetItem* parent = cur->parent();
    if(parent == NULL){
        // 顶层节点
        // 查询指定节点是顶层节点中的下标
        int index = ui->treeWidget->indexOfTopLevelItem(cur);
        ui->treeWidget->takeTopLevelItem(index);
    }
    else{
        // 非顶层节点
        parent->removeChild(cur);
    }
}

  •  QTreeWidget控件虽然是树形结构,但是这个树形结构,没有体现出根节点,
    是从根节点的下一次子节点开始计算的
  • 针对顶层节点来说,这里也是一个类似于"List"这样的结构

 4.6 容器类控件

4.6.1 Group Box(控件组)

属性
说明
title
分组框的标题
alignment
分组框内部内容的对⻬⽅式
flat
是否是 "扁平" 模式
checkable
是否可选择.
设为 true, 则在 title 前⽅会多出⼀个可勾选的部分
checked
描述分组框的选择状态 (前提是 checkable 为 true)

4.6.2 Tab Widget(多页面) 

属性说明
tabPosition
标签⻚所在的位置.
North 上⽅
South 下⽅
West 左侧
East 右侧
currentIndex
当前选中了第⼏个标签⻚ (从 0 开始计算)
currentTabText
当前选中的标签⻚的⽂本
currentTabName
当前选中的标签⻚的名字
currentTabIcon
当前选中的标签⻚的图标
currentTabToolTip
当前选中的标签⻚的提⽰信息
tabsCloseable
标签⻚是否可以关闭
movable
标签⻚是否可以移动

核⼼信号
属性
说明
currentChanged(int)
在标签⻚发⽣切换时触发, 参数为被点击的选项卡编号
tabBarClicked(int)
在点击选项卡的标签条的时候触发. 参数为被点击的选项卡编号
tabBarDoubleClicked(int)
在双击选项卡的标签条的时候触发. 参数为被点击的选项卡编号.
tabCloseRequest(int)
在标签⻚关闭时触发. 参数为被关闭的选项卡编号. ​​​​​​
  • Qt 中使⽤ ⽗⼦关系 决定控件的相对位置

 4.7 布局控件

4.7.1 QVBoxLayout(垂直布局)

Layout 只是⽤于界⾯布局, 并没有提供信号 

属性
说明
layoutLeftMargin
左侧边距
layoutRightMargin
右侧边距
layoutTopMargin
上⽅边距
layoutBottomMargin
下⽅边距
layoutSpacing
相邻元素之间的间距

  • 界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变  

 

  • 界面上的按钮不会随窗口尺寸变化而发生改变 
  • 通过 Qt Designer 创建的布局管理器, 其实是先创建了⼀个 widget, 设置过 geometry 属性
    的. 再把这个 layout 设置到这个 widget 中.
  • 实际上, ⼀个 widget 只能包含⼀个 layout.

4.7.2 QHBoxLayout(水平布局) 

Layout 只是⽤于界⾯布局, 并没有提供信号 

属性
说明
layoutLeftMargin
左侧边距
layoutRightMargin
右侧边距
layoutTopMargin
上⽅边距
layoutBottomMargin
下⽅边距
layoutSpacing
相邻元素之间的间距

  • 界⾯上的按钮就存在于布局管理器中. 随着窗⼝尺⼨变化⽽发⽣改变 

  • 布局控件是可以嵌套的所以 结合 QHBoxLayout QVBoxLayout , 就可以做出各种复杂的界⾯了 

4.7.3 QGridLayout(网格布局) 

属性说明
layoutLeftMargin
左侧边距
layoutRightMargin
右侧边距
layoutTopMargin
上⽅边距
layoutBottomMargin
下⽅边距
layoutHorizontalSpacing
相邻元素之间⽔平⽅向的间距
layoutVerticalSpacing
相邻元素之间垂直⽅向的间距
layoutRowStretch
⾏⽅向的拉伸系数
layoutColumnStretch
列⽅向的拉伸系数

  •  使⽤ addWidget 添加控件到布局管理器中. 但是添加的同时会指定两个坐标. 表⽰放在第⼏⾏, 第⼏列
  • 任意调整⾏列, 即可看到不同的效果

  • 注意: 如果要设置垂直方法的拉伸 直接 setRowStretch 效果不明显, 因为每个按钮的⾼度是固定的. 需要
  • 把按钮的垂直⽅向的 sizePolicy 属性设置为 QSizePolicy::Expanding 尽可能填
    充满布局管理器, 才能看到效果
    btn1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

 4.7.4 QFormLayout(表单布局)

属于是 QGridLayout 的特殊情况, 专⻔⽤于实现 两列表单的布局

这种表单布局多⽤于让⽤⼾填写信息的场景. 左侧列为提⽰, 右侧列为输⼊框 

4.7.5 Spacer(空白布局) 

使⽤布局管理器的时候, 可能需要 在控件之间, 添加⼀段空⽩ . 就可以使⽤ QSpacerItem 来表⽰.
属性说明
width
宽度
height
⾼度
hData
⽔平⽅向的 sizePolicy
QSizePolicy::Ignored : 忽略控件的尺⼨,不对布局产⽣影响。
QSizePolicy::Minimum : 控件的最⼩尺⼨为固定值,布局时不会超过该值。
QSizePolicy::Maximum : 控件的最⼤尺⼨为固定值,布局时不会⼩于该值。
QSizePolicy::Preferred : 控件的理想尺⼨为固定值,布局时会尽量接近该
值。
QSizePolicy::Expanding : 控件的尺⼨可以根据空间调整,尽可能占据更多空
间。
QSizePolicy::Shrinking : 控件的尺⼨可以根据空间调整,尽可能缩⼩以适应
空间。
vData
垂直⽅向的 sizePolicy
选项同上.

  • 调整 QSpacerItem 不同的尺⼨, 即可看到不同的间距 

5. QT窗口

Qt 窗⼝ 是通过 QMainWindow 类 来实现的。

  • 菜单栏(menu bar)
  • ⼯具栏(tool bars)
  • 浮动窗⼝(铆 接部件)(dock widgets)
  • 状态栏(status bar)
  • 中⼼部件(central widget)

5.1 菜单栏

⼀个主窗⼝最多只有⼀个菜单栏

5.1.1 创建菜单栏&&菜单&&菜单项 

  • 这里的菜单项和后面的工具栏的工具是同一个类,都是QAction 

 5.1.2 添加快捷键 && 链接信号槽 

  •  菜单项点击的时候会触发triggered信号,和前面那个信号类似

5.1.3 创建子菜单

  • 就是在菜单中嵌套另一个菜单,就叫做子菜单

5.1.4 设置菜单项图标

  •  如果给菜单也加上图标的话,就会把菜单的text覆盖

5.1.5 关于正确创建QMenuBar类 

  •  如果我们之前创建的项目,没有勾选ui文件,此时这2种代码都是正确的
  • 但是如果勾选了自动生成ui文件,那么第2种代码就会引起内存泄漏,因为Qt已经自动生成了一个QMenuBar(在ui文件中,各个类的结构中可以看到),同时后面的QStatusBar也是一样的问题

5.2 ⼯具栏

⼯具栏是应⽤程序中集成各种功能实现快捷键使⽤的⼀个区域 

5.2.1 创建工具栏 

  •  工具栏是可以有多个,并且可以像浮动窗口一样浮动和移动的

5.2.2 设置工具栏停靠位置 

  •  Qt::LeftToolBarArea 停靠在左侧
  • Qt::RightToolBarArea 停靠在右侧
  • Qt::TopToolBarArea 停靠在顶部
  • Qt::BottomToolBarArea 停靠在底部
  • Qt::AllToolBarAreas 以上四个位置都可停靠
  • Qt中很多枚举类型值,都不建议死记,建议不清楚的时候查查文档

5.3 状态栏

状态栏是应⽤程序中输出简要信息的区域

5.3.1 创建工具栏 

5.4 滑动窗口 

5.4.1 创建滑动窗口

  • 浮动窗口内部, 添加一些其他的控件,不能直接给这个浮动窗口添加子控件, 而是需要创建出一个单独的 QWidget, 把要添加的控件加入到 QWidget 中

  • 然后再把这个 QWidget 设置到 dockWidget 中.

5.5 对话框

5.5.1 创建对话框

  • QDialog不同于其他控件,此处QDialog每次按下按钮,都会创建一个新的QDialog对象
  • 而一个程序运行过程中,可能会无数次触发点击这个按钮,就会产生无数个这样的对象
    不好好处理,就会出现内存泄漏的问题,

  • Qt为了方便我们使用,就封装了一层,在实际使用的时候,只要给dialog设置上下面这个属性
    就会在关闭的时候自动进行delete(这个属于Qt内置的功能)
  • dialog->setAttribute(Qt::WA_DeleteOnClose);

5.5.2 自定义对话框 

  •  想要自定义对话框,就需要继承自QDialog创建类,这里是通过代码自定义的

  • 这个操作会创建出一个ui文件以及对应的类 
  • 通过图形化的方式创建会比代码创建的更快一些,因为会自动生成ui文件

5.5.3 模态与非模态

  • dialog->show() 非模态: 弹出对话框的时候,用户可以操作父窗口 
  • dialog->exec() 模态: 弹出对话框的时候,用户无法操作父窗口

  • Qt 提供了多种可复⽤的对话框类型,即 Qt 标准对话框。Qt 标准对话框全部继承于 QDialog类 

 5.5.4 QMessageBox(消息对话框)

Question
⽤于正常操作过程中的提问
Information
⽤于报告正常运⾏信息
Warning
⽤于报告⾮关键错误
Critical
⽤于报告严重错误

 

  •  QMessageBox 使用场景更多的是模态的.

 

  •  也可以通过QMessageBox中的静态成员函数,快速生成消息对话框

 5.5.5 QColorDialog(调色板对话框)

  •  直接调用QColorDialog静态成员函数生成对象是最快的

5.5.6  QFileDialog(文件对话框)

  • 此处的打开/保存这里的功能都是需要额外去实现的,并不是直接一保存就保存好了 

5.5.7 QInputDialog(输入对话框) 

5.5.8 QFontDialog(字体对话框) 

6.系统相关

6.1 事件

事件是应⽤程序内部或者外部产⽣的事情或者动作的统称

信号槽 : 用户进行的各项操作,就可能会产生出信号,可以给某个信号指定槽函数,当信号触发时,就能够自动的执行到对应的槽函数

事件: 用户进行的各种操作,也会产生事件,程序员同样可以给事件关联上处理函数(处理的逻辑),当事件触发的时候,就能够执行到对应的代码

总结:信号槽就是对于事件的进一步封装,事件是信号槽的底层机制

6.1.0 为什么会出现事件

  •  在实际Qt开发程序的过程中,绝大部分和用户之间进行的交互都是通过"信号槽"来完成的

  • 但是在有些特殊情况下,信号槽不一定能搞定(某个用户的动作行为,Qt没有提供对应的信号)

  • 此时就需要通过重写事件处理函数的形式,来手动处理事件的响应逻辑

  • 让当前的类,重写某个事件处理函数,这里用到的是"多态"机制,创建子类,继承自Qt中已有的类
    再在子类中重写父类的事件处理函数
    ,后续事件触发的过程中,就会通过多态这样的机制,执行到我们自己写的子类函数中

6.1.1 QMouseEvent(鼠标事件)

#ifndef LABEL_H
#define LABEL_H

#include <QWidget>
#include <QLabel>
#include <QMouseEvent>
class Label : public QLabel
{
    Q_OBJECT
public:
    Label(QWidget* parent);
    void mousePressEvent(QMouseEvent *event);// 按下
    void mouseReleaseEvent(QMouseEvent *event);// 释放
    void mouseDoubleClickEvent(QMouseEvent *event);// 双击
};

#endif // LABEL_H
#include "label.h"
#include <QDebug>
Label::Label(QWidget* parent) : QLabel(parent)
{
    ;
}

void Label::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton){
        //qDebug() << "按下左键";
        this->setText("按下左键");
    }
    else if(event->button() == Qt::RightButton){
        qDebug() << "按下右键";
    }
}

void Label::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton){
        qDebug() << "释放左键";
    }
    else if(event->button() == Qt::RightButton){
        qDebug() << "释放右键";
    }
}

void Label::mouseDoubleClickEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton){
        qDebug() << "双击左键";
    }
    else if(event->button() == Qt::RightButton){
        qDebug() << "双击右键";
    }
}

  • 先在ui界面中创建label控件,再新创建一个文件,继承QLabel
  • 注意:这里需要再ui界面中把这个控件提升为我们自己写的类
  • 这里是针对QLabel重写的事件,自然也只能在label控件中看到效果

  • 这里是在widget中重写的事件,则这个大窗口都会有效果  
  • Qt为了保证程序的流畅性,默认情况下不会对鼠标移动进行追踪,鼠标移动的时候不会调用
    mouseMoveEvent,除非显示告诉Qt要追踪鼠标位置

6.1.2 QWheelEvent(鼠标滚轮事件)  

6.1.3 QKeyEvent(键盘事件) 

 6.1.4 QTimerEvent(时间事件)

  • 此处的timerId类似于linux中的文件描述符
  • QTimer的背后是QTimerEvent定时器事件进行支持的,所以要实现定时器,通常使用QTimer

6.1.5 QMoveEvent(窗口移动事件) 

6.1.6 QResizeEvent(窗口尺寸事件) 

6.2 文件

6.2.1 QFile类的各种操作

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPlainTextEdit>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void test_read();
    void test_write();
private:
    Ui::MainWindow *ui;
    QPlainTextEdit* edit;// 纯文件控件
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 获取到菜单栏
    QMenuBar* menuBar = this->menuBar();

    // 添加菜单
    QMenu* menu = new QMenu("文件");
    menuBar->addMenu(menu);

    // 添加菜单项
    QAction* action1 = new QAction("打开");
    QAction* action2 = new QAction("保存");
    menu->addAction(action1);
    menu->addAction(action2);

    // 指定一个输入框
    edit = new QPlainTextEdit();
    QFont font;
    font.setPixelSize(20);
    edit->setFont(font);

    // 添加中心控件
    this->setCentralWidget(edit);

    // 链接信号槽
    connect(action1,&QAction::triggered,this,&MainWindow::test_read);
    connect(action2,&QAction::triggered,this,&MainWindow::test_write);
}

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

void MainWindow::test_read()
{
    // step1: 先弹出"打开文件" 对话框,让用户选择打开哪个文件
    QString path = QFileDialog::getOpenFileName(this);

    // step2: 把文件名显示到状态栏中
    QStatusBar* statusBar = this->statusBar();
    statusBar->showMessage(path);

    // step3: 根据用户选择的路径,构建一个QFile对象,并打开文件
    QFile file(path);
    bool ret = file.open(QFile::ReadOnly);// 只读
    if(!ret){
        statusBar->showMessage(path+"打开文件失败");
    }

    // step4: 读取文件
    QString text = file.readAll();

    // step5: 关闭文件
    file.close();

    // step6: 将读取到的内容设置到输入框中
    edit->setPlainText(text);

}

void MainWindow::test_write()
{
    // step1: 先弹出"保存文件" 对话框,让用户选择打开哪个文件
    QString path = QFileDialog::getSaveFileName(this);

    // step2: 把文件名显示到状态栏中
    QStatusBar* statusBar = this->statusBar();
    statusBar->showMessage(path);

    // step3: 根据用户选择的路径,构建一个QFile对象,并打开文件
    QFile file(path);
    bool ret = file.open(QFile::WriteOnly);// 只写
    if(!ret){
        statusBar->showMessage(path+"打开文件失败");
    }

    // step4: 写文件
    const QString& text = edit->toPlainText();
    file.write(text.toUtf8());

    // step5: 关闭文件
    file.close();

}


  •  在Qt 中提供的这一套文件操作,使用起来还是非常简单的

6.2.2 QFileInfo(文件相关属性) 

 6.3 线程

6.3.1 QThread

6.3.2 QMutex

 

  •  这里创建了2个线程,一个线程对num循环5k次,由于没加锁导致结果不是1w

  •  这里把锁加上就不会出现各个线程相互竞争的问题了

6.3.3 QMutexLocker 

 

  • 因为上面的锁很容易忘记释放,忘记unlock,在逻辑复杂的情况下
  • Qt中也有一个对互斥锁进行封装的类->QMutexLocker,类似与std::mutex智能指针 
  • C++11 也引入了std::lock_guard

6.3.4 其他补充说明

条件变量:QWaitCondition

信号量:QSemaphore

读写锁:QReadLocker、QWriteLocker、QReadWriteLock

  • 这里就暂不介绍了,这里类和API用法和Linux中的类似,就是对系统函数/接口的封装,

  • 要用到的时候,再查查文档

6.4 网路

  •  为了能够引入头文件,这里需要添加Network

6.4.1 UDP协议的回显交互

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // step1: 创建套接字对象
    sock = new QUdpSocket(this);
    this->setWindowTitle("服务器");

    // step2: 连接信号槽
    connect(sock,&QUdpSocket::readyRead,this,&Widget::processRequest);

    // step3: 绑定端口
    bool ret = sock->bind(QHostAddress::Any,9090);// 任意ip
    if(!ret){
        QMessageBox::critical(this,"服务器启动出错",sock->errorString());
        return;
    }
}

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

void Widget::processRequest()
{
    // step1: 读取请求并解析
    const QNetworkDatagram& requestDatagram = sock->receiveDatagram();
    QString request = requestDatagram.data();

    // step2: 根据请求计算响应(由于是回显服务器,响应不需要计算,就是请求本身)
    QString response = process(request);

    // step3: 把响应写回客户端
    QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());
    sock->writeDatagram(responseDatagram);

    // 把这次交互的信息, 显示到界面上.
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())
            + "] req: " + request + ", resp: " + response;
    ui->listWidget->addItem(log);
}

QString Widget::process(const QString &request)
{
    return request;
}

  • 这是Udp_server.cpp文件

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>
// 定义2个常量,描述服务器的IP 和 端口
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    // step1: 创建套接字对象
    socket = new QUdpSocket(this);
    this->setWindowTitle("客户端");

    // step2: 连接信号槽
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processResponse);
}

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

void Widget::on_pushButton_clicked()
{
    // 1.获取到输入框的内容
    const QString& text = ui->lineEdit->text();
    // 2.构造UDP的请求数据
    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);
    // 3.发送请求数据
    socket->writeDatagram(requestDatagram);
    // 4.把发送的请求也添加到列表框中
    ui->listWidget->addItem("客户端说: " + text);
    // 5. 把输入框的内容也清空一下
    ui->lineEdit->setText("");
}

void Widget::processResponse()
{


    // 通过这个函数来处理收到的响应
    // 1.读取到响应数据
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response = responseDatagram.data();
    // 2. 把响应数据显示到界面上
    ui->listWidget->addItem("服务器回应: " +response);
}
  •  这是udp_client.cpp文件

6.4.2 TCP协议的回显交互

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTcpSocket>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //1. 创建QTcpServer实例
    tcpServer = new QTcpServer(this);
    this->setWindowTitle("服务端");

    // 2.连接信号槽
    connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);

    // 3. 绑定并监听端口号
    bool ret = tcpServer->listen(QHostAddress::Any,9090);
    if(!ret){
        QMessageBox::critical(this,"服务器启动失败!",tcpServer->errorString());
        return;
    }
}

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

void Widget::processConnection()
{
    // 1. 通过 tcpServer 拿到一个 socket 对象, 通过这个对象来和客户端进行通信.
    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
    QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端上线!";
    ui->listWidget->addItem(log);

    // 2. 通过信号槽, 来处理客户端发来请求的情况.
    connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {
        // a) 读取出请求数据. 此处 readAll 返回的是 QByteArray, 通过赋值转成 QString
        QString request = clientSocket->readAll();
        // b) 根据请求处理响应
        const QString& response = process(request);
        // c) 把响应写回到客户端
        clientSocket->write(response.toUtf8());
        // d) 把上述信息记录到日志中.
        QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] "
                + " req: " + request + ", resp: " + response;
        ui->listWidget->addItem(log);
    });

    // 3. 通过信号槽, 来处理客户端断开连接的情况.
    connect(clientSocket, &QTcpSocket::disconnected, this, [=]() {
        // a) 把断开连接的信息通过日志显示出来.
        QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "] 客户端下线!";
        ui->listWidget->addItem(log);
        // b) 手动释放 clientSocket. 直接使用 delete 是下策, 使用 deleteLater 更加合适的.
        // delete clientSocket;
        clientSocket->deleteLater();
    });
}

// 此处写的是回显服务器.
QString Widget::process(const QString request)
{
    return request;
}
  •  这是tcp_server.cpp文件

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>

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

    // 1. 设置窗口标题
    this->setWindowTitle("客户端");
    // 2. 创建 socket 对象的实例
    socket = new QTcpSocket(this);
    // 3. 和服务器建立连接.
    socket->connectToHost("127.0.0.1", 9090);
    // 4. 连接信号槽, 处理响应
    connect(socket, &QTcpSocket::readyRead, this, [=]() {
        // a) 读取出响应内容
        QString response = socket->readAll();
        // b) 把响应内容显示到界面上.
        ui->listWidget->addItem("服务器说: " + response);
    });
    // 5. 等待连接建立的结果. 确认是否连接成功.
    bool ret = socket->waitForConnected();
    if (!ret) {
        QMessageBox::critical(this, "连接服务器出错", socket->errorString());
        exit(1);
    }
}

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


void Widget::on_pushButton_clicked()
{
    // 1. 获取到输入框中的内容
    const QString& text = ui->lineEdit->text();
    // 2. 发送数据给服务器.
    socket->write(text.toUtf8());
    // 3. 把发的消息显示到界面上.
    ui->listWidget->addItem("客户端说: " + text);
    // 4. 清空输入框的内容.
    ui->lineEdit->setText("");
}
  •  这是tcp_client.cpp文件

6.4.3 HTPP协议

7. QSS

Qt中的QSS和前端中的CSS类似,也是通过设置样式美化界面

7.1 纯代码引入 

7.2 qrc机制引入 

7.3 ui界面中引入(推荐) 

7.4 子控件

 查阅文档: Qt Style Sheets Reference

  •  记不住就多查查

8. 绘画 

画图相关的操作,一般不会放到QWidget的构造函数中调用执行,而是Qt提供了要给paintEvet事件处理函数,在这里进行调用,和它对应的是QPaintEvent事件

  • QPainter: 提供一系列的绘画方法,实现绘画动作的
  • QPaintDevice: 画板,其实QWidget也是QPaintDevice的子类
  • QPen: 画笔
  • QBrush; 画刷(填充时的属性)
;