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成功案例
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 的下载
- 注: 如果无法访问,则可以使用网盘链接: 链接:提取码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; 画刷(填充时的属性)