【Qt】界面优化

目录

一、QSS

[1.1 基本语法](#1.1 基本语法)

[1.2 QSS设置方法](#1.2 QSS设置方法)

[1.2.1 指定控件样式设置](#1.2.1 指定控件样式设置)

[1.2.2 全局样式设置](#1.2.2 全局样式设置)

[1.2.3 从文件加载样式表](#1.2.3 从文件加载样式表)

[1.2.4 使用Qt Designer编辑样式](#1.2.4 使用Qt Designer编辑样式)

[1.3 选择器](#1.3 选择器)

[1.3.1 介绍](#1.3.1 介绍)

[1.3.2 子控件选择器](#1.3.2 子控件选择器)

[1.3.3 伪类选择器](#1.3.3 伪类选择器)

[1.4 样式属性(盒模型)](#1.4 样式属性(盒模型))

[1.5 代码示例(登录界面)](#1.5 代码示例(登录界面))

二、绘图

[2.1 基础概念](#2.1 基础概念)

[2.2 绘制各种形状](#2.2 绘制各种形状)

[2.3 设置画笔](#2.3 设置画笔)

[2.4 设置画刷](#2.4 设置画刷)

[2.5 绘制图片](#2.5 绘制图片)

[2.6 画家设置](#2.6 画家设置)

[2.6.1 移动画家设置](#2.6.1 移动画家设置)

[2.6.2 保存/加载画家的状态](#2.6.2 保存/加载画家的状态)

[2.7 特殊的绘图设备](#2.7 特殊的绘图设备)

[2.7.1 QPixmap](#2.7.1 QPixmap)

[2.7.2 QImage](#2.7.2 QImage)

[2.7.3 QPicture](#2.7.3 QPicture)


一、QSS

1.1 基本语法

css 复制代码
选择器 {
    属性名: 属性值;
}
  • **选择器:**描述了"哪个widget要应用样式规则"
  • **属性:**是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值
css 复制代码
QPushButton {
    color: red;
}

**注意:**若通过 QSS 设置的样式和通过 C++ 代码设置的样式冲突,则QSS优先级更高

1.2 QSS设置方法

1.2.1 指定控件样式设置

QWidget 中包含了 setStyleSheet 方法,可以直接设置样式。给指定控件设置样式之后,该控件的子元素也会受到影响

**代码示例:**子控件受影响

在界面上创建一个按钮

修改widget.cpp,不给按钮设置样式,而是给 Widget 设置样式(Widget是QPushButton的父控件)

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setStyleSheet("QPushButton { color:red }");
}

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

运行程序,可以看到样式对于子控件按钮同样会生效

1.2.2 全局样式设置

可以通过 QApplication 的 setStyleSheet 方法设置整个程序的全局样式

全局样式优点:

  • 使同一个样式针对多个控件生效,代码更简洁
  • 所有控件样式内聚在一起,便于维护和问题排查

**代码示例:**使用全局样式

在界面上创建三个按钮

编辑main.cpp,设置全局样式

cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setStyleSheet("QPushButton { color:red; }");

    Widget w;
    w.show();
    return a.exec();
}

此时三个按钮的颜色都设置为红色了

**代码示例:**样式的层叠性

若通过全局样式给某个控件设置了属性1,通过指定控件样式给控件设置属性2,那么这两个属性都会产生作用

在上一个示例代码的基础上,编写widget.cpp,给第一个按钮设置字体大小

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setStyleSheet("QPushButton { font-size:30px; }");
}

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

运行程序可以看到,对于第一个按钮而言,同时具备了颜色和字体大小样式,而第二、三个按钮只有颜色样式,说明针对第一个按钮,两种设置方式设置的样式叠加了

**代码示例:**样式的优先级

若全局样式和指定控件样式冲突,则指定控件样式优先展示

在上一个示例代码的基础上,编写widget.cpp,给第二个按钮设置绿色

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->pushButton->setStyleSheet("QPushButton { font-size:30px; }");
    ui->pushButton_2->setStyleSheet("QPushButton { color:green }");
}

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

在CSS中也存在类似的优先级规则,通常而言都是"局部"优先级高于"全局"优先级,相当于全局样式先"奠定基调",再通过指定控件样式来"特事特办"

1.2.3 从文件加载样式表

上述代码都是把样式通过硬编码的方式设置的,这样使QSS代码和C++代码耦合在一起了,并不方便代码的维护。因此更好的做法是把样式放到单独的文件中,然后通过读取文件的方式来加载样式

**代码示例:**从文件加载全局样式

在界面上创建一个按钮

创建 resource.qrc 文集,并设定前缀为 /

创建 style.qss 文件,并添加到 resource.qrc 中

style.qss 是需要程序运行时加载的,为了规避绝对路径的问题,仍然使用 qrc 的方式来组织(即把资源文件内容打包到cpp代码中)。Qt Creator没有提供创建 qss 文件的选项,直接右键->新建文件->手动设置文件扩展名为qss即可

使用Qt Creator打开 style.qss 编写内容

css 复制代码
QPushButton {
    color: red;
}

修改main.cpp,新增一个函数加载样式,并在main函数中调用上述函数设置样式

cpp 复制代码
#include "widget.h"

#include <QApplication>
#include <QFile>

QString loadQSS()
{
    QFile file(":/style.qss");
    file.open(QFile::ReadOnly);
    QString style = file.readAll();
    file.close();
    return style;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString style = loadQSS();
    a.setStyleSheet(style);

    Widget w;
    w.show();
    return a.exec();
}

1.2.4 使用Qt Designer编辑样式

QSS也可以通过Qt Designer直接编辑,实时预览,同时也能免C++和QSS代码的耦合

**代码示例:**使用Qt Designer编辑样式

右键按钮,选择"改变样式表"

在弹出的样式表编辑器中,可以直接填写样式。填写完毕点击OK即可

此时Qt Designer的预览界面就会实时显示出样式的变化

这种方式设置样式,样式内容会被以xml格式记录到ui文件中

XML 复制代码
<property name="styleSheet">
<string notr="true">QPushButton { color: red; }</string>
</property>

同时在控件的 styleSheet 属性中也会体现

1.3 选择器

1.3.1 介绍

选择器 示例 说明
全局选择器 * 选择所有的widget
类型选择器(type selector) QPushButton 选择所有的QPushButton和其子类的控件
类选择器 .QPushButton 选择所有的QPushButton,不会选择子类
ID选择器 #pushButton_2 选择objectName为pushButton_2的控件
后代选择器 QDialog QPushButton 选择QDialog的所有后代中的QPushButton(子控件、孙子控件等)
子选择器 QDialog>QPushButton 选择QDialog的所有子控件中的QPushButton
并集选择器 QPushButton,QLineEdit 选择QPushButton、QLineEdit这两种控件
属性选择器 QPushButton[flat="false"] 选择所有QPushButton中,flat属性为false的控件

当某个控件,通过 类型选择器 和 ID选择器 设置了冲突的样式时,ID选择器样式优先级更高。同理,若是其他的多种选择器作用同一个控件时出现冲突的样式,也会涉及到优先级问题。Qt文档上有具体的优先级规则介绍(参见The Style Sheet Syntax的Conflict Resolution章节)

实践中可以简单的认为,选择器描述的范围越精准,则优先级越高。⼀般来说,ID选择器优先级是最高的

1.3.2 子控件选择器

有些控件内部包含了多个"子控件",如QComboBox的下拉后的面板,如QSpinBox的上下按钮等。可以通过子控件选择器::,针对上述子控件进行样式设置

**注意:**哪些控件拥有哪些子控件,参考文档Qt Style Sheets Reference中List of Sub-Controls章节

**代码示例:**设置下拉框的下拉按钮样式

在界面上创建一个下拉框并创建几个选项

创建 resource.qrc 并导入图片 down.png

使用子控件选择器 QComboBox::down-arrow 选中 QComboBox 的下拉按钮,再通过 image 属性设置图片

cpp 复制代码
#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString style = "QComboBox::down-arrow { image:url(:/down.png); }";
    a.setStyleSheet(style);

    Widget w;
    w.show();
    return a.exec();
}

1.3.3 伪类选择器

伪类选择器是根据控件所处的某个状态被选择的。如按钮被按下、输入框获取到焦点、鼠标移动到某个控件上等

  • 当状态具备时,控件被选中,样式生效
  • 当状态不具备时,控件不被选中,样式失效
  • 使用:的方式定义伪类选择器

常用的伪类选择器:

伪类选择器 说明
:hover 鼠标放在控件上
:pressed 鼠标左键按下时
:focus 获取输入焦点时
:enabled 元素处于可用状态时
:checked 被勾选时
:read-only 元素为只读状态时

这些状态可以使用 ! 来取反,如 :!hover 就是鼠标离开控件时,:!pressed 就是鼠标松开时,等等

更多伪类选择器的详细情况,参考Qt Style Sheets Reference 的 Pseudo-States 章节

1.4 样式属性(盒模型)

文档的 Qt Style Sheets Reference 章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等

在文档的 Customizing Qt Widgets Using Style Sheets 的 The Box Model 章节介绍了盒模型

  • Content矩形区域:存放控件内容,如包含的文本/图标等
  • Border矩形区域:控件的边框
  • Padding矩形区域:内边距,边框和内容之间的距离
  • Margin矩形区域:外边距,边框到控件 geometry 返回的矩形边界的距离

默认情况下,外边距、内边距、边框宽度都是0,可以通过一些QSS属性来设置上述的边距和边框的样式

QSS属性 说明
margin 设置四个方向的外边距,复合属性
padding 设置四个方向的内边距,复合属性
border-style 设置边框样式
border-width 边框粗细
border-color 边框的颜色
border 复合属性,相当于border-style + border-width + border-color

1.5 代码示例(登录界面)

在界面上创建元素

使用布局管理器,把上述元素包裹

两个输入框和按钮的minimumHeight均设置为50(元素在布局管理器中无法直接设置width和height,使用minimumWidth和minimumHeight代替,此时垂直方向的sizePolicy要设为fixed)

右键 QCheckBox,选择 Layout Alignment 可以设置checkbox的对齐方式(左对齐、居中对齐、右对齐)

上述控件添加一个父元素 QFrame 并设置 QFrame 和 窗口一样大

**注意:**顶层窗口的 QWidget 无法设置背景图片,因此需要再套上一层 QFrame。背景图片就设置到 QFrame 上即可

创建 resource.qrc并导入图片

编写QSS样式,使用 border-image 设置背景图片,而不是 background-image。主要是因为 border-image 是可以自动缩放的,这一点在窗口大小发生改变时是非常有意义的

背景色使用 transparent 表示完全透明(应用父元素的背景)

代码设置到QFrame的属性中即可

css 复制代码
QFrame {
	border-image: url(:/background.png);
}

QLineEdit {
	color: #8d98a1;
	background-color: #405361;
	padding: 0 5px;
	font-size: 20px;
	border-style: none;
	border-radius: 10px;
}

QCheckBox {
	color: white;
	background-color: transparent;
}

QPushButton {
	font-size: 20px;
	color: white;
	background-color: #555;
	border-style: outset;
	border-radius: 10px;
}
 
QPushButton:pressed {
	color: black;
	background-color: #ced1db;
	border-style: inset;
}

二、绘图

2.1 基础概念

Qt提供了画图相关的API,可以允许在窗口上绘制任意的图形形状,来完成更复杂的界面设计

**注意:**所谓的"控件",本质上也是通过画图的方式画上去的。画图 API 和 控件 之间的关系,可以类比成机器指令和高级语言之间的关系。控件是对画图 API 的进一步封装,画图 API 是控件的底层实现

绘图 API 的使用,一般不会在 QWidget 的构造函数中使用,而是要放到 paintEvent 事件中

paintEvent 事件会在以下情况下被触发:

  • 控件首次创建
  • 控件被遮挡,再解除遮挡
  • 窗口最小化,再恢复
  • 控件大小发生变化时
  • 主动调用 repaint() 或者 update() 方法(这两个方法都是 QWidget 的方法)
  • ......

2.2 绘制各种形状

绘制线段

cpp 复制代码
void drawLine(const QPoint &p1, const QPoint &p2);
//p1:绘制起点坐标
//p2:绘制终点坐标

void drawLine (int x1, int y1, int x2, int y2);
//x1,y1:绘制起点坐标
//x2,y2:绘制终点坐标

绘制矩形

cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height);
//x:窗⼝横坐标
//y:窗⼝纵坐标
//width:所绘制矩形的宽度
//height:所绘制矩形的⾼度

绘制圆形

cpp 复制代码
void QPainter::drawEllipse(const QPoint &center, int rx, int ry)
//center:中⼼点坐标
//rx:外接矩形横坐标
//ry:外接矩形纵坐标

绘制文本

2.3 设置画笔

QPainter在绘制时,是有一个默认的画笔的。在使用时也可以自定义画笔。在Qt中,QPen类中定义了 QPainter 应该如何绘制形状、线条和轮廓。同时通过 QPen类 可以设置画笔的线宽、颜色、样式等

  • 设置画笔颜色:QPen::QPen(const QColor &color) 画笔的颜色主要是通过 QColor 类设置
  • 设置画笔宽度:void QPen::setWidth(int width)
  • 设置画笔风格:void QPen::setStyle(Qt::PenStyle style)

2.4 设置画刷

在 Qt 中,画刷是使用 QBrush类 来描述,画刷大多用于填充。QBrush定义了QPainter的填充模式,具有样式、颜色、渐变以及纹理等属性

画刷的格式中定义了填充的样式,使用 Qt::BrushStyle 枚举,默认值是 Qt::NoBrush,也就是不进行任何填充。可以通过 Qt 助手 查找画刷的格式

2.5 绘制图片

Qt提供了四个类来处理图像数据:QImage、QPixmap、QBitmap和QPicture,都是常用的绘图设备。其中 QImage 主要用来进行 I/O 处理,对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;QPixmap主要用来在屏幕上显示图像,对在屏幕上显示图像进行了优化;QBitmap是QPixmap的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;QPicture用来记录并重演 QPainter 命令

绘制图片

平移图片

平移图片实际是通过改变坐标来实现。QPainter类提供了 translate()函数 来实现坐标原点的改变

缩放图片

在 Qt 中,图片的放大和缩小可以使用 QPainter类 中的drawPixmap()函数来实现

旋转图片

图片的旋转使用的是 QPainter类 中的 rotate()函数,默认是以原点为中心进行旋转的。若要改变旋转的中心,可以使用translate()函数完成

2.6 画家设置

2.6.1 移动画家设置

有时候在绘制多个图形时,使用同⼀坐标位置,那么绘制出来的图形肯定会重合。可以通过移动画家的位置来使图形不发生重合

2.6.2 保存/加载画家的状态

在绘制图形的过程中,可以通过 save() 函数来保存画家的状态,使用 restore() 函数还原画家状态

代码示例

上述示例中,在画第三个圆之前,由于还原了画家的状态,所以此时画家的位置坐标会移动到画家状态保存的地方,所以在绘制第三个圆的位置时实际是和第二个圆发生了重叠

2.7 特殊的绘图设备

前⾯的代码中是使用 QWidget 作为绘图设备,在 Qt 中还存在下列三个特殊的绘图设备

  • QPixmap 用于在显示器上显示图片
  • QImage 用于对图片进行像素级修改
  • QPicture 用于对 QPainter 的一系列操作进行存档

2.7.1 QPixmap

QPixmap核心特性

  • 使用 QPainter 直接在上面进行绘制图形
  • 通过文件路径加载并显示图片
  • 搭配 QPainter 的 drawPixmap()函数,可以把这个图片绘制到QLabel、QPushButton等控件上
  • 和系统/显示设备强相关,不同系统/显示设备下,QPixmap 的显示可能会有所差别

2.7.2 QImage

QImage核心特性

  • 使用 QPainter 直接在上面进行绘制图形
  • 通过文件路径加载并显示图片
  • 能够针对图片进行像素级别的操作(操作某个指定的像素)
  • 独立于硬件的绘制系统,能够在不同系统之上提供一致的显示

2.7.3 QPicture

QPicture核心特性

  • 使用 QPainter 直接在上面进行绘制图形
  • 通过文件路径加载并显示图片
  • 能够记录 QPainter 的操作步骤
  • 独立于硬件的绘制系统,能够在不同系统之上提供一致的显示

**注意:**QPicture加载的必须是自身的存档文件,而不能是任意的png、jpg等图片文件

认识

QPicture类似于很多游戏的Replay功能。如war3经典游戏,即使是一场60分钟的长时间局,生成的replay文件,也不过几百个KB

此处的Replay功能并非是把整个游戏画面都录制保存下来,而是记录了地图中发生的所有事件(地图元素,玩家单位操作,中立生物行为等...)

当回放Replay时,其实就是把上述记录的事件再一条一条的执行一遍,即可还原之前的游戏场景

代码示例

若要记录下 QPainter 的命令,先要使用 QPainter::begin() 函数,将 QPicture 实例作为参数传递进去,以便告诉系统开始记录,记录完毕后使用 QPainter::end() 命令终止

相关推荐
float_com16 分钟前
【STL】stack,deque,queue 基础,应用与操作
c++·stl·极速入门
学习使我变快乐24 分钟前
C++:用类实现链表,队列,栈
开发语言·c++·链表
lmy_t30 分钟前
C++之第十二课
开发语言·c++
tyler-泰勒1 小时前
初始c++:入门基础(完结)
java·开发语言·c++
sinat_276522573 小时前
C++中move的使用
开发语言·c++
Langneer3 小时前
Qt 状态机编程,双层状态机,实现暂停恢复
开发语言·qt
微尘83 小时前
C语言存储类型 auto,register,static,extern
服务器·c语言·开发语言·c++·后端
三玖诶3 小时前
如何在 Qt 的 QListWidget 中为某一行添加点击事件
开发语言·qt
金博客4 小时前
Qt 模型视图(二):模型类QAbstractItemModel
c++·qt6.7.2
InJre4 小时前
QT widgets 窗口缩放,自适应窗口大小进行布局
开发语言·qt·ui