一、CSS
1、背景介绍
在网页前端开发领域中,CSS 是一个至关重要的部分,描述了一个网页的 “样式”,从而起到对网页美化的作用。
所谓样式,包括不限于大小、位置、颜色、背景、间距、字体等等。
现在的网页很难找到没有 CSS 的,可以说让 “界面好看” 是一个刚需。
对于针对特定专业领域用户的软件产品,界面设计是否重要?
可参考:
(11 封私信 / 80 条消息) 对于针对特定专业领域用户的软件产品,界面设计是否重要? - 知乎 (zhihu.com)
网页开发作为 GUI 的典型代表,也对于其他客户端 GUI 开发产生了影响,Qt 也是其中之一。
Qt 仿照 CSS 的模式,引入了 QSS,来对 Qt 中的控件做出样式上的设定,从而允许我们写出界面更好看的代码。同样受到 HTML 的影响,Qt 还引入了 QML 来描述界面,甚至还可以直接把一个原生的 html 页面加载到界面上。
当然,由于 Qt 本身的设计理念和网页前端还是存在一定差异的,因此 QSS 中只能支持部分 CSS 属性。整体来说 QSS 要比 CSS 更简单一些。
注意:如果通过 QSS 设置的样式和通过 C++ 代码设置的样式冲突,则 QSS 优先级更高。
2、基本语法
对于 CSS 来说,基本的语法结构非常简单。
选择器 { 属性名: 属性值; }
QSS 沿用了这样的设定:
选择器 { 属性名: 属性值; }
其中:
选择器描述了 “哪个 widget 要应用样式规则”。属性则是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值。例如:
QPushButton { color: red; }
或者:
QPushButton { color: red;}
上述代码的含义表示,针对界面上所有的 QPushButton,都把文本颜色设置为红色。
编写 QSS 时使用单行的格式和多行的格式均可。
【QSS 基本使用】
(1)在界面上创建一个按钮
(2)编写代码,设置样式
(3)运行程序
观察效果,可以看到文本已经是绿色了:
注意:上述代码中,只针对这一个按钮通过 setStyleSheet 方法设置的样式,此时这个样式仅针对该按钮生效。如果创建其他按钮,其他按钮不会受到影响。
其它格式:
3、QSS 设置方式
(1)指定控件样式设置
QWidget 中包含了 setStyleSheet 方法,可以直接设置样式。另一方面,给指定控件设置样式之后,该控件的子元素也会受到影响。
【子元素受到影响】
A. 在界面上创建两个按钮和一个单行编辑框
B. 修改 widget.cpp
这次不再给按钮设置样式,而是给 Widget 设置样式(Widget 是 QPushButton 的父控件):
C. 运行程序
可以看到样式对于 this 的子控件按钮同样会生效,但是必须是和选择器相关的。
(2)全局样式设置
可以通过 QApplication 的 setStyleSheet 方法设置整个程序的全局样式。
全局样式优点:
使同一个样式针对多个控件生效,代码更简洁。把界面上所有控件样式内聚在⼀起,便于维护和问题排查。【使用全局样式】
A. 在界面上创建三个按钮
B. 编辑 main.cpp,设置全局样式
C. 运行程序
可以看到此时三个按钮的颜色都设置为红色了:
【样式的层叠特性】
如果通过全局样式给某个控件设置了属性 1,通过指定控件样式给控件设置属性 2,那么这两个属性都会产生作用
A. 在界面上创建三个按钮
B. 编写 main.cpp,设置全局样式,把按钮文本设置为红色
C. 编写 widget.cpp,给第一个按钮设置字体大小
D. 运行程序
可以看到,对于第一个按钮来说,同时具备了颜色和字体大小样式,而第二个按钮只有颜色样式。说明针对第一个按钮,两种设置方式设置的样式叠加起来了。
形如上述这种属性叠加的效果被称为 “层叠性”。
CSS 全称为 Cascading Style Sheets,其中 Cascading 就是 “层叠性” 的意思,QSS 也继承了这样的设定。实际上把 QSS 叫做 QCSS 也许更合适一些。
【样式的优先级】
如果全局样式和指定控件样式冲突,则指定控件样式优先展示。
A. 在界面上创建三个按钮
B. 编辑 main.cpp,把全局样式设置为红色
C. 编辑 widget.cpp,把第二个按钮样式设为绿色
D. 运行程序
观察效果,可以看到第二个按钮已经成为绿色了,但是第一个按钮和第三个按钮仍然是红色。
在 CSS 中也存在类似的优先级规则。通常来说都是 “局部” 优先级高于 “全局” 优先级,相当于全局样式先 “奠定基调”,再通过指定控件样式来 “特事特办”。
实际开发中,可以在全局样式中设置比较通用的样式来统一整个程序的界面风格。如果需要针对某个控件进行微调,可以使用局部样式来做出调整。
(3)从文件加载样式表
上述代码都是把样式通过硬编码的方式设置的,这样使 QSS 代码和 C++ 代码耦合在一起了,并不方便代码的维护。因此更好的做法是把样式放到单独的文件中,然后通过读取文件的方式来加载样式。
【从文件加载全局样式】
A. 在界面上创建一个按钮
B. 创建 resource.qrc 文件,并设定前缀为 /
C. 创建 style.qss 文件,并添加到 resource.qrc 中
style.qss 是需要程序运行时加载的。为了规避绝对路径的问题,仍然使用 qrc 的方式来组织(即把资源文件内容打包到 cpp 代码中)。Qt Creator 没有提供创建 qss 文件的选项,直接 “右键” -> “新建” -> “文本文档”,手动设置文件扩展名为 qss 即可。D. 使用 Qt Creator 打开 style.qss,编写内容
E. 修改 main.cpp,新增一个函数用来加载样式
F. 修改 main.cpp,在 main 函数中调用上述函数,并设置样式
G. 运行程序
可以看到样式已经生效了:
理论上来说 Qt 应该要提供直接从文件加载样式表的接口。类似于 setStyleSheetFromFile(const QString& path) 这种,在内部把读文件操作封好。
(4)使用 Qt Designer 编辑样式
QSS 也可以通过 Qt Designer 直接编辑,从而起到实时预览的效果,同时也能避免 C++ 和 QSS 代码的耦合。
【使用 Qt Designer 编辑样式】
A. 在界面上创建一个按钮
B. 选择最外层的窗口,右键按钮,选择 “改变样式表”
C. 在弹出的样式表编辑器中,可以直接填写样式,填写完毕点击 OK 即可
这里进行的修改都会记录到 ui 文件中,并且在程序运行时自动生效,还能进行实时预览。
D. 此时 Qt Designer 的预览界面就会实时显示出样式的变化
E. 运行程序
可以看到样式确实发生了改变:
这种方式设置样式,样式内容会被以 xml 格式记录到 ui 文件中。
同时在控件的 styleSheet 属性中也会体现:
由于设置样式太灵活,有很多地方都能设置,所以当我们发现一个控件的样式不符合预期的时候,要记得排查这四个地方:
全局样式(QAppplication 设置的)指定控件样式(这个控件是否设置了样式)qss 文件中的样式 ui 文件中的样式指定控件的父控件的样式(可能是从父控件继承过来的)在实际开发中,如果需要设置样式,建议最好统一使用某一种方式来设置。
4、选择器
(1)选择器概况
QSS 的选择器支持以下几种:
总体来说,QSS 选择器的规则和 CSS 选择器基本一致。
【使用类型选择器选中子类控件】
A. 在界面上创建一个按钮
B. 修改 main.cpp,设置全局样式
注意 :此处选择器使用的是 QWidget。QPushButton 也是 QWidget 的子类,所以会受到 QWidget 选择器的影响。C. 运行程序
可以看到按钮的文本颜色已经是红色了:
D. 如果把上述样式代码修改为下列代码
此时按钮的颜色不会发送改变。此时只是选择 QWidget 类,而不会选择 QWidget 的子类 QPushButton 了。
【使用 id 选择器】
A. 在界面上创建 3 个按钮,objectName 为 pushButton、pushButton_2、pushButton_3
B. 编写 main.cpp,设置全局样式
先通过 QPushButton 设置所有的按钮为红色。再通过 #pushButton 和 #pushButton_2 分别设置这两个按钮为绿色和黄色。C. 执行效果
当某个控件身上,通过类型选择器和 ID 选择器设置了冲突的样式时,ID 选择器样式优先级更高。
同理,如果是其他的多种选择器作用同一个控件时出现冲突的样式,也会涉及到优先级问题。Qt 文档上有具体的优先级规则介绍(参见 The Style Sheet Syntax 的 Conflict Resolution 章节)
这里的规则计算起来非常复杂(CSS 中也存在类似的设定)。可以简单的认为,选择器描述的范围越精准,则优先级越高。一般来说,ID 选择器优先级是最高的。
如果属性不冲突,还是会同时生效:
【使用并集选择器】
A. 创建三个按钮、一个 label、一个单行输入框
B. 编写 main.cpp,设置全局样式
C. 运行程序
可以看到这三种控件的文字颜色都设置为了红色:
并集选择器是一种很好的代码复用的方式,很多时候我们希望界面上的多个元素风格是统一的,就可以使用并集选择器,把样式属性同时指定给多种控件。
也可以指定 id 选择器:
展示效果:
(2)子控件选择器(Sub-Controls)
有些控件内部包含了多个 “子控件”,比如 QComboBox 的下拉后的面板,比如 QSpinBox 的上下按钮等。可以通过子控件选择器 ::,针对上述子控件进行样式设置。
哪些控件拥有哪些子控件,参考文档 Qt Style Sheets Reference 中 List of Sub-Controls 章节。
【设置下拉框的下拉按钮样式】
A. 在界面上创建一个下拉框,并创建几个选项
B. 创建 resource.qrc,并导入图片 down.png
C. 修改 main.cpp,编写全局样式
使用子控件选择器 QComboBox::down-arrow 选中了 QComboBox 的下拉按钮。再通过 image 属性设置图片。D. 执行程序
【修改进度条的颜色】
A. 在界面上创建一个进度条
B. 在 Qt Designer 右侧的属性编辑器中,找到 QWidget 的 styleSheet 属性
编辑如下内容:
其中的 chunk 是选中进度条中的每个 “块”,使用 QProgressBar::text 则可以选中文本。同时把 QProcessBar 的 alignment 属性设置为垂直水平居中。此处如果不设置 alignment,进度条中的数字会跑到左上角(这个怀疑是 Qt 本身的 bug,暂时只能先使⽤ alignment 来手动调整一下)。
C. 执行程序
可以看到如下效果,就得到了一个红色的进度条:
通过上述方式,也可以修改文字的颜色,字体大小等样式。
(3)伪类选择器(Pseudo-States)
伪类选择器,是根据控件所处的某个状态被选择的。
例如按钮被按下,输入框获取到焦点,鼠标移动到某个控件上等。
当状态具备时,控件被选中,样式生效。
当状态不具备时,控件不被选中,样式失效。
使用 : 的方式定义伪类选择器。
常用的伪类选择器:
这些状态可以使用 ! 来取反,比如 :!hover 就是鼠标离开控件时,:!pressed 就是鼠标松开时,等等。更多伪类选择器的详细情况可以参考 Qt Style Sheets Reference 的 Pseudo-States 章节。
【设置按钮的伪类样式】
A. 在界面上创建一个按钮
B. 编写 main.cpp,创建全局样式
C. 运行程序
可以看到默认情况下按钮文字是红色,鼠标移动上去是绿色,鼠标按下按钮是蓝色:
上述代码也可以使用事件的方式来实现。
【使用事件方式实现同样效果】
A. 创建 MyPushButton 类,继承自 QPushButton
B. 把生成代码中的构造函数改成带参数 QWidget* 版本的构造函数(否则无法和 Qt Designer 生成的代码适配)
// mypushbutton.h#include <QPushButton>class MyPushButton : public QPushButton{public: MyPushButton(QWidget* parent);};// mypushbutton.cpp#include "mypushbutton.h"MyPushButton::MyPushButton(QWidget* parent) : QPushButton(parent){}
C. 在界面上创建按钮,并提升为 MyPushButton 类型
右键按钮,选择 “提升为...”:
填写提升的类名和头文件:
提升完毕后,在右侧对象树这里,就可以看到类型的变化。
D. 重写 MyPushButton 的四个事件处理函数
a. 修改 mypushbutton.h
class MyPushButton : public QPushButton{public: MyPushButton(QWidget* parent); void mousePressEvent(QMouseEvent* e); void mouseReleaseEvent(QMouseEvent* e); void enterEvent(QEvent* e); void leaveEvent(QEvent* e);};
b. 修改 mypushbutton.cpp
初始化设为红色鼠标进⼊时设为绿色,离开是还原红色鼠标按下时设为蓝色,松开时还原绿色(松开时鼠标还是在按钮里)MyPushButton::MyPushButton(QWidget* parent) : QPushButton(parent){ this->setStyleSheet("QPushButton { color: red; }");}void MyPushButton::mousePressEvent(QMouseEvent *e){ this->setStyleSheet("QPushButton { color: blue; }");}void MyPushButton::mouseReleaseEvent(QMouseEvent *e){ this->setStyleSheet("QPushButton { color: green; }");}void MyPushButton::enterEvent(QEvent *e){ this->setStyleSheet("QPushButton { color: green; }");}void MyPushButton::leaveEvent(QEvent *e){ this->setStyleSheet("QPushButton { color: red; }");}
E. 运行程序
可以看到效果和上述案例一致:
很明显,实现同样的功能,伪类选择器要比事件的方式简单很多。但是不能就说事件机制就不好,事件可以完成的功能很多,不仅仅是样式的改变,还可以包含其他业务逻辑,这一点是伪类选择器无法替代的。
5、样式属性
QSS 中的样式属性非常多,大部分的属性和 CSS 是非常相似的。
文档的 Qt Style Sheets Reference 章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等。
相关的代码示例,在后面具体介绍。在翻阅文档的时候涉及到一个关键术语 “盒模型”(Box Model)。
(1)盒模型(Box Model)
在文档的 Customizing Qt Widgets Using Style Sheets 的 The Box Model 章节介绍了盒模型:
⼀个遵守盒模型的控件,由上述几个部分构成。
Content 矩形区域:存放控件内容,比如包含的文本 / 图标等。Border 矩形区域:控件的边框。Padding 矩形区域:内边距,边框和内容之间的距离。Margin 矩形区域:外边距,边框到控件 geometry 返回的矩形边界的距离。默认情况下,外边距、内边距、边框宽度都是 0。
可以通过一些 QSS 属性来设置上述的边距和边框的样式:
【设置边框和内边距】
A. 在界面上创建一个 label
B. 修改 main.cpp, 设置全局样式
border: 20px solid green 相当于 border-style: solid; border-width: 2px; border-color: green; 三个属性的简写形式。padding-left: 50px; 是给左侧设置内边距。C. 运行程序
【设置外边距】
为了方便确定控件位置,演示外边距效果,使用代码创建⼀个按钮。
A. 修改 widget.cpp,创建按钮,并设置样式
B. 运行程序
可以看到,当前按钮的边框被外边距挤的缩小了,但是获取到的按钮的 Geometry 是不变的。
6、控件样式示例
(1)按钮
【自定义按钮】
A. 界面上创建一个按钮
B. 右键 -> 改变样式表,使用 Qt Designer 设置样式
C. 执行程序
点击 “按钮”:
D. 属性小结
形如 #dadbde 是计算机中通过十六进制表示颜色的方式。
(2)复选框
【自定义复选框】
A. 创建一个 resource.qrc 文件,并导入以下图片
使用黑色作为默认形态使用蓝色作为 hover 形态使用红色作为 pressed 形态 注意这里的文件命名。B. 创建一个复选框
C. 编辑复选框的样式
D. 运行程序
鼠标点击选中,再取消的过程,可以看到此时的复选框就变得丰富起来了:
E. 小结
(3)单选框
【自定义单选框】
A. 创建 resource.qrc 文件,并导入以下图片
使用黑色作为默认形态使用蓝色作为 hover 形态使用红色作为 pressed 形态注意这里的文件命名。
B. 在界面上创建两个单选按钮
C. 在 Qt Designer 中编写样式
此处为了让所有 QRadioButton 都能生效,把样式设置在 Widget 上了,并且使用后代选择器选中了 QWidget 里面的 QRadioButton。
注意 :QSS 中有些属性,子元素能继承父元素(例如 font-size、color 等),但是也有很多属性是不能继承的。 具体哪些能继承哪些不能继承,规则比较复杂,我们不去具体研究,实践中我们编写更精准的选择器是上策。
QWidget QRadioButton { font-size: 20px;}QWidget QRadioButton::indicator { width: 20px; height: 20px;}QWidget QRadioButton::indicator:unchecked { image: url(:/radio-unchecked.png);}QWidget QRadioButton::indicator:unchecked:hover { image: url(:/radio-unchecked_hover.png);}QWidget QRadioButton::indicator:unchecked:pressed { image: url(:/radio-unchecked_pressed.png);}QWidget QRadioButton::indicator:checked { image: url(:/radio-checked.png);}QWidget QRadioButton::indicator:checked:hover { image: url(:/radio-checked_hover.png);}QWidget QRadioButton::indicator:checked:pressed { image: url(:/radio-checked_pressed.png);}
D. 运行程序
(4)输入框
【自定义单行编辑框】
A. 在界面上创建一个单行编辑框
B. 在 Qt Designer 中编写样式
C. 执行程序
输入 "hello world",选中 "rld":
(5)列表
【自定义列表框】
A. 在界面上创建一个 ListView
B. 编写代码
C. 执行程序
鼠标停放在上面:
选中:
qlineargradient 有 6 个参数。 x1, y1:标注了⼀个起点x2, y2:标注了⼀个终点 这两个点描述了⼀个 “方向”。 例如 x1: 0, y1: 0, x2: 0, y2: 1 就是垂直方向从上向下进行颜色渐变。x1: 0, y1: 0, x2: 1, y2: 0 就是水平方向从左向右进行颜色渐变。x1: 0, y1: 0, x2: 1, y2: 1 就是从左上往右下方向进行颜色渐变. stop0 和 stop1 描述了两个颜色,渐变过程就是从 stop0 往 stop1 进行渐变的。
【理解渐变色】
A. 界面不创建任何控件
B. 编写样式
QWidget { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop: 0 #fff, stop: 1 #000);}
当前按照垂直从上往下从白色过渡到黑色。
执行效果:
C. 修改代码
QWidget { background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop: 0 #fff, stop: 1 #000);}
当前按照水平从左往右从白色过渡到黑色。
执行效果:
(6)菜单栏
【自定义菜单栏】
A. 创建菜单栏
创建若干菜单项和一个分隔符:
B. 编写样式
C. 执行效果
(7)登录界面
【基于上述学习过的 QSS 样式,制作一个美化版本的登录界面】
A. 在界面上创建元素,并使用布局管理器把相关元素包裹一下
使用 QVBoxLayout 来管理上述控件。两个输入框和按钮的 minimumHeight 均设置为 50(元素在布局管理器中无法直接设置 width 和 height,使用 minimumWidth 和 minimumHeight 代替,此时垂直方向的 sizePolicy 要设为 fixed)。右键 QCheckBox,选择 Layout Alignment 可以设置 checkbox 的对齐方式(左对齐,居中对齐,右对齐)。B. 设置背景图片
把上述控件添加一个父元素 QFrame,并设置 QFrame 和窗口一样大。
顶层窗口的 QWidget 无法设置背景图片,因此我们可以再套上一层 QFrame,背景图片就设置到 QFrame 上即可。
创建 resource.qrc,并导入图片:
编写 QSS 样式:
使用 border-image 设置背景图片,而不是 background-image。主要是因为 border-image 是可以自动缩放的,这一点在窗口大小发生改变时是非常有意义的。
效果:
C. 设置输入框样式
编写 CSS 代码:
运行程序效果:
D. 设置 checkbox 样式
背景色使用 transparent 表示完全透明(应用父元素的背景)。
执行效果:
E. 设置按钮样式
执行程序:
最终完整样式代码,这些代码设置到 QFrame 的属性中即可。通常我们建议把样式代码集中放置,方便调整和排查。
7、小结
QSS 本身给 Qt 提供了更丰富的样式设置的能力,但是整体来说 QSS 的功能是不如 CSS 的。在 CSS 中,整个网页的样式都是 CSS 一手负责,CSS 功能更强大,并且也更可控。相比之下,Qt 中是以原生 API 为主,来控制控件之间的尺寸、位置等,QSS 只是起到辅助的作用。
而且 Qt 中提供的一些 “组合控件”(像 QComboBox、QSpinBox 等)内部的结构是不透明的,此时进行一些样式设置也会存在一定的局限性。另外,做出好看的界面,光靠 QSS 是不够的,更重要的是需要专业美工做出设计稿。
二、绘图
1、基本概念
虽然 Qt 已经内置了很多的控件,但是不能保证现有控件就可以应对所有场景。很多时候我们需要更强的 “自定制” 能力。
Qt 提供了画图相关的 API,允许我们在窗口上绘制任意的图形形状来完成更复杂的界面设计。
所谓的 “控件” 本质上也是通过画图的方式画上去的。
画图 API 和控件之间的关系,可以类比成机器指令和高级语言之间的关系。
控件是对画图 API 的进一步封装,画图 API 是控件的底层实现。
(1)绘图 API 核心类
绘图 API 的使用一般不会在 QWidget 的构造函数中使用,而是要放到 paintEvent 事件中。
paintEvent 会在以下情况下被触发:
控件首次创建。控件被遮挡,再解除遮挡。窗口最小化,再恢复。控件大小发生变化时。主动调用 repaint() 或者 update() 方法(这两个方法都是 QWidget 的方法)。... ...2、绘制各种形状
(1)绘制线段
void drawLine(const QPoint &p1, const QPoint &p2);参数: p1:绘制起点坐标 p2:绘制终点坐标
void drawLine ( int x1, int y1, int x2, int y2 );参数: x1,y1:绘制起点坐标 x2,y2:绘制终点坐标
示例:
A. 在 "widget.h" 头文件中声明绘图事件
B. 在 "widget.cpp" 文件中重写 paintEvent() 方法
实现效果如下:
(2)绘制矩形
void QPainter::drawRect(int x, int y, int width, int height); 参数: x:窗⼝横坐标; y:窗⼝纵坐标; width:所绘制矩形的宽度; height:所绘制矩形的⾼度;
示例:
实现效果如下:
(3)绘制圆形
void QPainter::drawEllipse(const QPoint ¢er, int rx, int ry)参数: center:中⼼点坐标 rx:横坐标 ry:纵坐标
示例:
实现效果:
(4)绘制文本
QPainter 类中不仅提供了绘制图形的功能,还可以使用 QPainter::drawText() 函数来绘制文字,也可以使用 QPainter::setFont() 设置字体等信息。
示例:
实现效果:
(5)设置画笔
QPainter 在绘制时,是有一个默认的画笔的。在使用时也可以自定义画笔。在 Qt 中,QPen 类中定义了 QPainter 应该如何绘制形状、线条和轮廓。同时通过 QPen 类可以设置画笔的线宽、颜色、样式、画刷等。
画笔的颜色可以在实例化画笔对象时进行设置,画笔的宽度是通过 setWidth() 方法进行设置,画笔的风格是通过 setStyle() 方法进行设置,设置画刷主要是通过 setBrush() 方法。
设置画笔颜色:QPen::QPen(const QColor &color) 画笔的颜色主要是通过 QColor 类设置; 设置画笔宽度:void QPen::setWidth(int width) 设置画笔风格:void QPen::setStyle(Qt::PenStyle style)画笔的风格有:
【画笔的使用】
实现效果如下:
(6)设置画刷
在 Qt 中,画刷是使用 QBrush 类来描述,画刷大多用于填充。QBrush 定义了 QPainter 的填充模式,具有样式、颜色、渐变以及纹理等属性。
画刷的格式中定义了填充的样式,使用 Qt::BrushStyle 枚举,默认值是 Qt::NoBrush,也就是不进行任何填充。可以通过 Qt 助手查找画刷的格式。如下图示:
设置画刷主要通过 void QPen::setBrush(const QBrush &brush) 方法,其参数为画刷的格式。
示例:
实现效果:
3、绘制图片
Qt 提供了四个类来处理图像数据:QImage、QPixmap、QBitmap 和 QPicture,它们都是常用的绘图设备。其中QImage主要用来进行 I/O 处理,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;QBitmap 是 QPixmap 的子类,用来处理颜色深度为 1 的图像,即只能显示黑白两种颜色;QPicture 用来记录并重演 QPainter 命令。
(1) 绘制简单图片
A. 新建 Qt 项目,基类选择 QWidget,项目名称为 QPainter。
在 "widget.h" 头文件中声明绘画事件,如下图示:
B. 添加资源文件
首先准备一些图片资源文件,并将这些图片资源文件放在同一个文件夹中,将该文件夹复制到本项目中:
C. 选中项目文件,鼠标右键 ——> add new...
D. 点击 "add new..." 之后,出现如下界面
E. 选择 "Choose..." 之后,给资源文件命名
F. 点击 “下一步”,出现如下界面,点击 “完成”
H. 给资源文件添加前缀,并将资源文件添加至项目中
I. 将所有的资源文件添加到项目中,方便后续使用
J. 点击 “构建并运行” 按钮,将资源文件添加到项目中
K. 在 "widget.cpp" 文件中实现画图片功能
实现效果如下:
(2)平移图片
平移图片实际是通过改变坐标来实现。QPainter 类中提供了 translate() 函数来实现坐标原点的改变。
示例:
实现效果如下:
(3)缩放图片
示例:
效果展示:
(4)旋转图片
图片的旋转使用的是 QPainter 类中的 rotate() 函数,它默认是以原点为中心进行旋转的。如果要改变旋转的中心,可以使用 translate() 函数完成。
示例:
实现效果:
4、其他设置
(1)移动画家位置
有时候在绘制多个图形时,想使用同一坐标位置,那么绘制出来的图形肯定会重合,此时,可以通过移动画家的位置来使图形不发生重合。
【未移动画家位置】
实现效果如下:
【移动画家位置】
使用 translate 移动画家所在位置:
实现效果如下:
(2)保存 / 加载画家的状态
在绘制图形的过程中,可以通过 save() 函数来保存画家的状态,使用 restore() 函数还原画家状态。
save() 函数原型如下:
void QPainter::save();
restore() 函数原型如下:
void QPainter:: restore ();示例:
实现效果如下:
说明:在画第三个圆之前,由于还原了画家的状态,所以此时画家的位置坐标会移动到画家状态保存的地方,所以在绘制第三个圆的位置时实际是和第二个圆发生了重叠。
5、特殊的绘图设备
前面的代码中我们是使用 QWidget 作为绘图设备,在 Qt 中还存在下列三个比较特殊的绘图设备。
QPixmap 用于在显示器上显示图片QImage 用于对图片进行像素级修改QPicture 用于对 QPainter 的一系列操作进行存档(1)QPixmap
QPixmap 核心特性: 使用 QPainter 直接在上面进行绘制图形。通过文件路径加载并显示图片。搭配 QPainter 的 drawPixmap() 函数,可以把这个图片绘制到⼀个 QLabel、QPushButton 等控件上。和系统 / 显示设备强相关,不同系统 / 显示设备下,QPixmap 的显示可能会有所差别。示例:
实现效果:
(2)QImage
QImage 的核心特性:
使用 QPainter 直接在上面进行绘制图形。通过文件路径加载并显示图片。能够针对图片进行像素级别的操作(操作某个指定的像素)。独立于硬件的绘制系统,能够在不同系统之上提供一致的显示。【QImage 作为绘图设备的使用】
【QImage 绘图时对像素的修改】
A. 新建 Qt 项目,添加图片资源文件到项目中
B. 在 widget.h 头文件中声明绘图事件
C. 在 widget.cpp 文件中重写绘图事件,使用 QImage 对图片像素进行修改
通过 setPixel 设置某个像素的颜色值使用 qRgb 表示一个具体的颜色D. 执行效果
a. 没有修改像素之前
b. 修改像素之后
(3)QPicture
QPicture 核心特性:
使用 QPainter 直接在上面进行绘制图形。通过文件路径加载并显示图片。能够记录 QPainter 的操作步骤。独立于硬件的绘制系统,能够在不同系统之上提供⼀致的显示。 注意 :QPicture 加载的必须是自身的存档文件,而不能是任意的 png, jpg 等图片文件。如果要记录下 QPainter 的命令,首先要使用 QPainter::begin() 函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end() 命令终止。
示例:
展示效果如下:
通过 QPicture 重现绘图指令后,实现的效果如下:
三、其他话题
Qt 中对于界面的优化美化,还涉及到很多其他的话题,如果在工作中如果涉及到了,再针对性学习即可。
Qt 动画 Qt 3D 图形 QQuick 使用第三方控件库 Qt Design Studio ... ...