<Qt> 界面优化

目录

前言:

背景介绍

一、QSS基本语法

二、QSS设置方式

(一)指定控件样式设计

(二)全局样式设置

(三)从文件加载样式表

[(四)Qt Designer 编辑样式](#(四)Qt Designer 编辑样式)

三、选择器

(一)子控件选择器

(二)伪类选择器(Pseudo-States)

四、样式属性

[(一)盒模型(Box Model)](#(一)盒模型(Box Model))

(二)控件样式示例

[1. 按钮](#1. 按钮)

[2. 复选框](#2. 复选框)

[3. 单选框](#3. 单选框)

[4. 输入框](#4. 输入框)

[5. 列表](#5. 列表)

[6. 菜单栏](#6. 菜单栏)

[7. 登录界面](#7. 登录界面)

(三)小结

五、绘图

(一)基本概念

(二)绘制各种形状

[1. 绘制线段](#1. 绘制线段)

[2. 绘制矩形](#2. 绘制矩形)

[3. 绘制圆形](#3. 绘制圆形)

[4. 绘制文本](#4. 绘制文本)

[5. 设置画笔](#5. 设置画笔)

[6. 设置画刷](#6. 设置画刷)

(三)绘制图片

[1. 绘制简单图片](#1. 绘制简单图片)

[2. 平移图片](#2. 平移图片)

[3. 缩放图片](#3. 缩放图片)

[4. 旋转图片](#4. 旋转图片)


前言:

背景介绍

在网页前端开发领域中,CSS是一个至关重要的部分,描述了一个网页的"样式"从而起到对网页美化的作用。

所谓样式,包括不限于大小,位置,颜色,背景,间距,字体等等。

现在的网页很难找到没有CSS的.可以说让"界面好看"是一个刚需。

网页开发作为GUI的典型代表,也对于其他客户端GUI开发产生了影响,Qt也是其中之一。

Qt仿照CSS的模式,引入了QSS,来对Qt中的控件做出样式上的设定,从而允许程序员写出界面更好看的代码。

同样受到HTML的影响,Qt还引入了QML来描述界面,甚至还可以直接把一个原生的html页面加载到界面上.这部分内容咱们不讨论。

当然,由于Qt本身的设计理念和网页前端还是存在一定差异的,因此QSS中只能支持部分CSS属性。整体来说QSS要比CSS更简单一些。另外,大家也不必担心,没有CSS基础的同学并不影响学习QSS。

一、QSS基本语法

对于 CSS来说,基本的语法结构非常简单,QSS也沿用了这样的设定:

css 复制代码
选择器
{
	属性名:属性值
}

其中:

  • 选择器描述了"哪个widget要应用样式规则"。
  • 属性则是一个键值对,属性名表示要设置哪种样式,属性值表示了设置的样式的值。

例如:

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

上述代码的含义表示,针对界面上所有的QPushButton,都把文本颜色设置为红色。

注意:编写QSS时使用单行的格式和多行的格式均可。

代码示例:将按钮字体设置成红色

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    ui->pushButton->setStyleSheet("QPushButton {color: red;}");
}

运行效果如下:

二、QSS设置方式

(一)指定控件样式设计

QWidget中包含了setStyleSheet方法,可以直接设置样式,这样设置出来的样式只针对当前控件本身生效,在界面上其它控件不受影响 ;另一方面,给指定控件设置样式之后,该控件的子元素也会受到影响。

代码示例:给一控件设置样式,其挂载在当前控件上面的子控件也会收到影响。

给 widget 设置样式(widget 是QPushButton 的父控件):

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->setStyleSheet("QPushButton {color: red;}");
}

可以看到虽然只是给Widget设置样式,但子控件(两个按钮)也被设置了:

  1. 当然,如果父控件通过QSS设置样式过后,其挂载的子控件也使用了QSS来设置自己的样式,那么这时候子控件的样式设置以自己的为准:
  2. 如果通过QSS设置的样式和通过C++代码设置的样式冲突,则QSS优先级更高
cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 设置全局为绿色
    this->setStyleSheet("QPushButton {color : green;}");

    // 通过QSS设置按钮2的颜色为橙色
    ui->pushButton_2->setStyleSheet("QPushButton {color:  orange;}");

    // 通过代码设置按钮2的颜色为红色
    QPalette ret = ui->pushButton_2->palette();
    ret.setColor(QPalette::ButtonText, Qt::red);
    ui->pushButton_2->setPalette(ret);
}

运行效果如下:

(二)全局样式设置

向上面那样一个一个的指定每个控件的样式,得累死,并且一旦控件多起来,那么我们就可能在代码的任意地方都设置QSS,不方便统一管理,秉持着"高内聚,低耦合"的观念,我们建议将QSS放在一处设置,这样方便管理。

全局的样式设置通过main.cpp文件中的 QApplicationsetStyleSheet方法设置整个程序的全局样式。

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

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

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QPushButton{color:#2ec58c}");
    Widget w;
    w.show();
    return a.exec();
}

运行效果如下:

全局样式优点:

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

样式的层叠特性:

就比如说我们通过全局样式给按钮控件们设置了颜色为绿色,随后我们希望单独在调整一下按钮2的字体大小,于是我们又单独通过按钮2的setStyleSheet函数来设置QSS样式改变按钮2的字体大小为50像素,最终按钮2呈现出来的样式就是,字体为绿色,字体大小为50像素的情况,像这样,对于按钮2来说,它即继承了全局样式的文本颜色属性,也复用了自己样式的字体大小属性,这样两个样式属性叠加起来现象就叫样式的层叠属性。

在上述例子的基础上加上这行代码:

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 设置指定控件样式
    ui->pushButton_2->setStyleSheet("QPushButton{font-size:50px;}");
}

运行效果如下:

形如上述这种属性叠加的效果,我们称为"层叠性". CSS全称为Cascading Style

Sheets,其中Cascading就是"层叠性"的意思。QSS也继承了这 样的设定,实际上把QSS叫做QCSS也许更合适一些。


样式的优先级: 如果全局样式和指定样式冲突,则优先使用指定样式。

在示例 1 的基础上加上冲突的指定样式:

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 设置成红色
    ui->pushButton_2->setStyleSheet("QPushButton {color:red;}");
}

运行程序,可以看到,第二个按钮的字体颜色变成了红色:

(三)从文件加载样式表

上面两种样式设置方式,都是将 QSS代码 和 C++代码 融合在一起 了,在后期代码量打起来过后就会变得越来越不好维护 ,因此更好的做法是将样式单独放在一个文件中,然后从文件中读取样式表。

如何操作呢,还是使用qrc文件。 Qt Creator 没有提供创建 qss 文件的选项,需手动创建文件并设置文件扩展名为 qss即可:

创建好后就在文件中编辑一些控件的样式:

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);
    // 设置全局样式
    a.setStyleSheet(loadQSS());
    Widget w;
    w.show();
    return a.exec();
}

理论上来说 Qt 应该要提供直接从文件加载样式表的接口。类似于 setStyleSheetFromFile(const QString& path) 这种,在内部把读文件操作封好。

通过这样的方式也可以设置控件的样式,让 QSS代码 和 C++代码 进行了解耦,但是这种方式也仅供参考,下面还有一种方式。

(四)Qt Designer 编辑样式

QSS也可以通过Qt Designer直接编辑,从而起到实时预览的效果.同时也能避免C++和QSS代码的耦合。

前面我们也简单提到过这种方式,在 Qt Designer 中,右击控件就可以编辑样式,然后就会把代码添加到ui文件中:

当我们编辑完qss代码后,左下角也可以帮我们简单检查这个代码有没有问题:

并且可以实时看到界面的变化,在ui文件中就会多了一段代码:

三、选择器

在这里给出 QSS 几种常用的选择器:

选择器 实例 说明
全局选择器 * 选择所有的控件
类型选择器 QPushButton 、QLabel等 选择QPushButton、QLabel及其子类实例(这里的子类是继承关系上的)
类选择器 .QPushButton 选择所有的QPushButton控件,不考虑其子类的实例
ID选择器 #objectName 指定单独一个控件进行设置
后代选择器 QDialog QPushButton 选择 QDialog 的所有后代(⼦类控件,孙⼦类控件等等)中的 QPushButton
⼦选择器 QDialog > QPushButton 选择 QDialog 的所有⼦类控件中的 QPushButton
并集选择器 QPushButton, QLineEdit,QComboBox 选择 QPushButton、QLineEdit、QComboBox 这三种控件(即接下来的样式会针对这三种控件都⽣效)
属性选择器 QPushButton[flat="false"] 选择所有 QPushButton 中,flat 属性为 false 的控件

上述选择器我们也不需要全都掌握,就只熟悉最常用的几个即可(上述加粗的)。我们只需要记住几个常用的即可,不会的到时候查文档即可。

代码示例:用类型选择器选中子类控件

在界面上创建一个按钮:

修改main.cpp,设置全局样式

注意:此处选择器使用的是QWidge。QPushButton也是QWidget的子类,所以会受到QWidget选择器的影响。

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

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    a.setStyleSheet("QWidget { color: red; }");
    Widget w;
    w.show();
    return a.exec();
}

运行程序,可以看到按钮的文本颜色已经是红色了:


如果把上述样式代码修改为下列代码

cpp 复制代码
a.setStyleSheet(".QWidget { color: red; }");

此时按钮的颜色不会发生改变,此时只是选择QWidget类,而不会选择QWidget 的子类QPushButton了

代码示例:使用id选择器

在界面.上创建3个按钮,obj ectName为pushButton,pushButton_ 2,pushButton _3:

编写main.cpp,设置全局样式。先通过QPushButton设置所有的按钮为黄色,再通过**#pushButton** 和 #pushButton_ 2分别设置这两个按钮为红色和绿色:

cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 设置全局样式
    QString style = "";
    style += "QPushButton { color: yellow; }";
    style += "#pushButton { color: red; }";
    style += "#pushButton_2 { color: green; }";
    a.setStyleSheet(style);

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

或者 也可以直接在ui界面修改:

运行效果如下:

注意:当某个控件⾝上,通过类型选择器和 ID 选择器设置了冲突的样式时,ID 选择器样式优先级更⾼。同理,如果是其他的多种选择器作⽤同⼀个控件时出现冲突的样式,也会涉及到优先级问题。

Qt ⽂档上有具体的优先级规则介绍 (参⻅ The Style Sheet Syntax 的 Conflict Resolution 章

节)。这⾥的规则计算起来⾮常复杂(CSS 中也存在类似的设定),咱们此处对于优先级不做进⼀步讨论,实践中我们可以简单的认为,选择器描述的范围越精准,则优先级越⾼。⼀般来说,ID 选择器优先级是最⾼的。
代码示例:使用并集选择器

创建按钮,label,单行输入框:

运行程序,可以看到这三种控件的文字颜色都设置为了红色:

并集选择器是一-种很好的代码复用的方式。很多时候我们希望界面上的多个元素风格是统一的,就可以使用并集选择器,把样式属性同时指定给多种控件。

(一)子控件选择器

有些控件内部包含了多个"子控件 ".比如QComboBox的下拉后的面板,还有QSpinBox的上下按钮等;我们可以通过子控件选择器"::" 针对上述子控件进行样式设置;哪些控件拥有哪些⼦控件,参考⽂档 Qt Style Sheets Reference 中 List of Sub-Controls 章节。

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

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

创建resource.qrc,并导入图片:

修改main.cpp,编写全局样式.

  • 使用子控件选择器QComboBox::down-arrow选中了QComboBox的下拉按钮。
  • 再通过image属性设置图片。
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

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

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

运行效果如下:

(二)伪类选择器(Pseudo-States)

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

  • 当状态具备时,控件被选中,样式生效。
  • 当状态不具备时, 控件不被选中,样式失效。
  • 使用**:**的方式定义伪类选择器。
伪类选择器 说明
:hover 鼠标放在控件上
:pressed 鼠标左键按下时
:focus 获取输入焦点时
:enabled 元素处于可用状态时
:checked 被勾选时
:read-only 元素为只读状态时

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

等等。

代码示例:设置按钮的伪类样式

在界面上创建一个按钮:

编写main.cpp,创建全局样式

cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QString style = "";
    style += "QPushButton{color:red;}";
    style += "QPushButton:hover{color:green;}";
    style += "QPushButton:pressed{color:blue;}";
    a.setStyleSheet(style);
    Widget w;
    w.show();
    return a.exec();
}

运行程序,可以看到默认情况下按钮文字是红色,鼠标悬停上去是绿色,鼠标按下按钮是蓝色:

四、样式属性

QSS中的样式属性非常多,不需要都记住.核心原则还是用到了就去查。大部分的属性和CSS是非常相似的。文档的**Qt Style Sheets Reference** 章节详细介绍了哪些控件可以设置属性,每个控件都能设置哪些属性等。

在翻阅文档的时候涉及到一个关键术语"**盒模型" (Box Model),**这里我们需要介绍一下。

(一)盒模型(Box Model)

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

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

默认情况下,外边距,内边距,边框宽度都是0。

可以通过一些QSS属性来设置上述的边距和边距样式:

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

代码示例:设置边框和内边距

  • border:5px solid red 相当于border-style: solid;border-width: 5px;border-color: red; 三个属性的简写形式。
  • padding-left:10px; 是给左侧设置内边距。
cpp 复制代码
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    a.setStyleSheet("QLabel{border:5px solid red; padding-left:10px;}");

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

运行效果如下:

代码示例:设置外边距

为了方便确定控件位置,演示外边距效果,我们使用代码创建一个按钮:

cpp 复制代码
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPushButton *button = new QPushButton(this);
    button->setGeometry(0, 0, 100, 100);
    button->setText("hello");
    button->setStyleSheet("QPushButton{border:5px solid red; margin:20px;}");
    const QRect &rect = button->geometry();
    qDebug() << rect;
}

运行程序,可以看到,当前按钮的边框被外边距挤的缩小了,但是获取到的按钮的Geometry是不变的:

(二)控件样式示例

1. 按钮

代码示例:自定义按钮

界面上创建一个按钮:

右键->改变样式表,使用Qt Designer设置样式:

css 复制代码
QPushButton{
	font-size:20px;
	border:2px solid #8f8f91;
	border-radius:20px;
	background-color:#dadbde
}
QPushButton:pressed{
	background-color:#f6f7fa
}

执行程序,可以看到效果。点击按钮前:

点击按钮后:

属性解释:

  • font-size字体大小。
  • border-radius:圆角矩形,数值越大,角就越圆。
  • backgroung-color:背景色。

2. 复选框

示例:自定义复选框

  • 使用黑色作为默认形态.
  • 使用蓝色作为hover形态.
  • 使用红色作为pressed形态.

创建一个复选框:

编辑复选框的样式:

cpp 复制代码
QCheckBox{font:20px;}

QCheckBox::indicator{
		width:20px;
		height:20px;
}
QCheckBox::indicator:unchecked{
		image:url(:/checkbox-unchecked.png)
}
QCheckBox::indicator:unchecked:hover{
		image:url(:/checkbox-unchecked_hover.png)
}
QCheckBox::indicator:unchecked:pressed{
		image:url(:/checkbox-unchecked_pressed.png)
}
QCheckBox::indicator:checked{
		image:url(:/checkbox-checked.png)
}
QCheckBox::indicator:checked:hover{
		image:url(:/checkbox-checked_hover.png)
}
QCheckBox::indicator:checked:pressed{
		image:url(:/checkbox-checked_pressed.png)
}

运行程序,可以看到此时的复选框就变的丰富起来了:

3. 单选框

代码示例:自定义单选框

  • 使用黑色作为默认形态.
  • 使用蓝色作为hover形态.
  • 使用红色作为pressed形态.

在界面上创建两个单选按钮:

在Qt Designer中编写样式:

此处为了让所有QRadioButton都能生效,把样式设置在Widget.上了。并且使用后代选择器选中了QWidget里面的QRadioButton。

运行程序,观察效果:

4. 输入框

代码示例:自定义单行编辑框

在界面上创建一个单行编辑框,并设置样式:

cpp 复制代码
QLineEdit {
 	border-width: 1px; 
 	border-radius: 10px;
 	border-color: rgb(58, 58, 58);
 	border-style: inset;	
 	padding: 0 8px;
	color: rgb(255, 255, 255);
 	background:rgb(100, 100, 100);
 	selection-background-color: rgb(187, 187, 187);
	selection-color: rgb(60, 63, 65);
}

运行效果如下:

5. 列表

属性 说明
::item 选中 QListWidget 中具体的条目。
:hover 选中 鼠标悬停的条目
:selected 选中 被选中的条目
background 设置背景颜色
border 设置边框
qlineargradient 设置渐变色

代码示例:自定义列表框

在界面上创建一个ListWidget并设置样式表:

css 复制代码
QListWidget::item:hover{ /* 当鼠标进入选项时的样式 */
	background: rgb(125, 182, 255);
	color: rgb(233, 255, 151);
}
 
QListWidget::item:selected{ /* 当鼠标点击选项后的样式 */
	background: rgb(116, 202, 255);
	color: rgb(233, 255, 151);
}
 
QListWidget{
	font: 16pt "黑体";
	border: 2px solid rgb(125, 182, 255);
	background-color: rgb(233, 255, 151);
	border-radius: 20px;
	padding: 10px 10px 10px 10px;
    color: rgb(125, 182, 255);
}

运行效果如下:

下面再来介绍一下如何设置渐变色,使用的属性是qlineargradient,这个属性要填写六个参数:

  • 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 就是对角线方向从左上角到右下角进行颜色渐变。
  • stop0stop1 描述两种颜色,渐变过程就是从 stop0 往 stop1 进行渐变。

下面我们就可以给列表的背景和选项设置渐变色,但是专业的事还是要交给专业的人,配色这种事情还是应该交个专业的人,这里就随便选几个颜色设置一下:

cpp 复制代码
QListWidget::item:hover{ /* 当鼠标进入选项时的样式 */
	background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(233, 255, 151), stop:1 rgb(125, 182, 255));
	color: rgb(233, 255, 151);
}
 
QListWidget::item:selected{ /* 当鼠标点击选项后的样式 */
	background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(233, 255, 151), stop:1 rgb(125, 182, 255));
	color: rgb(233, 255, 151);
}
 
QListWidget{
	font: 16pt "黑体";
	border: 2px solid rgb(125, 182, 255);
	
	border-radius: 20px;
	padding: 10px 10px 10px 10px;
    color: rgb(125, 182, 255);
	background-color: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 rgb(233, 255, 151), stop:1  rgb(131, 255, 183))
}

运行效果如下:

6. 菜单栏

Qt 也支持对菜单栏进行样式设置:

代码示例:自定义菜单栏

创建菜单栏,创建项目的时候选择 QMainWindow,创建几个菜单项:

cpp 复制代码
QMenuBar {
	background-color:  rgb(220, 220, 220);
}
 
QMenuBar::item:selected {
	background-color: rgb(94, 95, 96);
	color: white;
}
 
QMenuBar::item:pressed {
	background-color: rgb(94, 95, 96);
	color: white;
}
 
QMenu::item:selected {
	border: 2px solid black; /* 为选中的菜单项添加一个框 */
}
 
QMenu::item{
	border: 2px solid transparent; /* 边框颜色设置为跟随父元素,文字就不会在选中的时候动了 */
}

运行效果如下:

7. 登录界面

现在我们就可以设置一个界面优美的登录界面,先放上几个控件,使用垂直 布局管理器,但是控件在垂直方向上的sizePolicyFixed(固定的),而且geometry也失效了,所以可以设置maximumSizeminimumSize设置一下高度,这样就是固定的了:

设置好后,我们可以为登录界面添加一个背景,但是在顶层窗口设置背景图是会失效的,所以就要给控件外面套上一个和窗口一样大小的 QFrame 控件,这个控件也是在左边的控件栏中可以找到的,之后把这个QFrame放大一下,把登录的控件拖进去:

下面就可以设置背景了,QFrame中有两个设置背景的方式,一个是background-image,另一个是border-image,后者可以跟随控件的大小发生变化,而前者就是固定的,所以我们使用后者就可以了。后面就是自己设计一个界面:

css 复制代码
QFrame {
	border-image: url(:/liyuu.jpg);
}
 
QLineEdit {
	border: none;
	padding: 0px 10;
	font-size: 20px;
	border-radius: 10px;
}
 
QCheckBox {
	font-size: 18px;
}
 
QPushButton {
	background-color: qlineargradient(x1:0 y1:0 x2:1 y2:0, stop:0 rgb(172, 187, 246), stop:1 rgb(239, 133, 143));
	border-radius: 10px;
	font-size: 20px;
	color: white;
}
 
QPushButton:pressed {
	background-color: qlineargradient(x1:0 y1:0 x2:1 y2:0, stop:0 rgb(152, 167, 226), stop:1 rgb(219, 113, 123));
}

运行效果如下:

(三)小结

QSS 本身给 Qt 提供了更丰富的样式设置的能力,但是整体来说 QSS 的功能是不如 CSS 的。在 CSS 中,整个网页的样式都是 CSS 一手负责,CSS 功能更强大,并且也更可控。相比之下,Qt 中是以原生 API 为主,来控制控件之间的尺寸、位置等,QSS 只是起到辅助的作用。

而且 Qt 中提供的一些 "组合控件"(像 QComboBox、QSpinBox 等)内部的结构是不透明的,此时进行一些样式设置也会存在一定的局限性。另外,做出好看的界面,光靠 QSS 是不够的,更重要的是需要专业美工做出设计稿。

五、绘图

(一)基本概念

虽然 Qt 已经内置了很多的控件,但是不能保证现有控件就可以应对所有场景。很多时候我们需要更强的 "自定制" 能力。Qt 提供了画图相关的 API,允许我们在窗口上绘制任意的图形形状来完成更复杂的界面设计。

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

说明
QPainter "绘画者",用来绘图的对象,提供了一系列draw方法,可以绘制各种图形。
QPaintDevice "画板",描述 QPainter 把图形画到哪个对象上,QWidget 也是一种 QPaintDevice。
QPen "画笔",描述 QPainter 画出来的线是什么样的。
QBrush "画刷",描述 QPainter 填充一个区域是什么样的。

绘图 API 的使用一般不会再构造函数中,而是在 Qt 提供的 paintEvent事件处理函数中调用。

关于 paintEvent事件,它会在以下情况下被触发:

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

(二)绘制各种形状

1. 绘制线段

使用的是下面这个方法:

cpp 复制代码
void drawLine(const QPoint &p1, const QPoint &p2);
 
参数:
    p1:绘制起点坐标
    p2:绘制终点坐标
cpp 复制代码
void drawLine ( int x1, int y1, int x2, int y2 );
 
参数:
    x1,y1:绘制起点坐标
    x2,y2:绘制终点坐标

代码示例:绘制一个箭头

事件的处理操作我们也知道了,就是重写父类的 paintEvent 方法:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);
 
    // 画横线
    painter.drawLine(QPoint(20, 20), QPoint(100, 20));
    // 画竖线
    painter.drawLine(20, 20, 20, 100);
    // 画斜线
    painter.drawLine(20, 20, 100, 100);
}

运行效果如下:

2. 绘制矩形

下面是使用的方法,参数也很好理解:

cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height);
    
参数:
    x:窗⼝横坐标;
    y:窗⼝纵坐标;
    width:所绘制矩形的宽度;
    height:所绘制矩形的⾼度;
cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    // 画矩形
    painter.drawRect(20, 100, 200, 100);
}

运行效果如下:

3. 绘制圆形

下面是绘制圆形的方法:

cpp 复制代码
void QPainter::drawRect(int x, int y, int width, int height);
    
参数:
    x:窗⼝横坐标;
    y:窗⼝纵坐标;
    width:所绘制矩形的宽度;
    height:所绘制矩形的⾼度;
cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);
 
    // 画圆形
    painter.drawEllipse(QPoint(120, 310), 100, 100);
}

运行效果如下:

上面是后面两个参数是一样的,横纵的半径是一样的,如果不一样会怎么样呢?也都能猜出来,绘画出一个椭圆:

4. 绘制文本

QPainter类 中还提供了可以绘制文字的方法,第一个参数为横坐标,表示文字最左侧的位置,第二个参数为 纵坐标,表示的是文字的"基线位置"(baseline),所白了就是英语本四线三格中,从下往上数第二条线:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    // 设置字体格式和颜色
    QFont font("黑体", 24);
    painter.setFont(font);
    painter.setPen(Qt::red);

    // 绘制文本
    painter.drawText(20, 100, "good morning");
}

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)
cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    // 设置画笔
    QPen pen(QColor(255, 0, 0)); // 设置成红色
    // 设置线条粗细
    pen.setWidth(5);
    // 设置线条风格
    pen.setStyle(Qt::DashLine);

    // 将自定义的pen设置进painter
    painter.setPen(pen);

    painter.drawEllipse(QPoint(400, 300), 100, 100);
}

运行效果如下:

6. 设置画刷

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

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

设置画刷主要通过 void QPen::setBrush(const QBrush &brush) 方法,其参数为画刷的格式:

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    // 设置画笔
    QPen pen(QColor(255, 0, 0)); // 设置成红色
    // 设置线条粗细
    pen.setWidth(5);
    // 设置线条风格
    pen.setStyle(Qt::DashLine);

    // 将自定义的pen设置进painter
    painter.setPen(pen);

    // 创建QBrush对象
    QBrush brush;
    brush.setColor(QColor(0, 0, 255));

    // 设置brush样式
    brush.setStyle(Qt::SolidPattern); // 实心样式

    // 将自定义的brush设置进painter
    painter.setBrush(brush);

    painter.drawEllipse(QPoint(400, 300), 100, 100);
}

运行效果如下:

(三)绘制图片

Qt 提供了四个类来处理图像数据:QImage、QPixmap、QBitmapQPicture,它们都是常用的绘图设备。

  • QImage主要用来进行 I/O 处理,它对 I/O 处理操作进行了优化,而且可以用来直接访问和操作像素;
  • QPixmap 主要用来在屏幕上显示图像,它对在屏幕上显示图像进行了优化;
  • QBitmap 是 QPixmap 的子类,用来处理颜色深度为1的图像,即只能显示黑白两种颜色;
  • QPicture 用来记录并重演 QPainter 命令。

1. 绘制简单图片

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    QPixmap pixmap(":/liyuu.jpg");

    // 前两个参数为起点;中间两个参数为图片大小,可以设置这两个参数实现图片的缩放
    painter.drawPixmap(0, 0, 800, 600, pixmap);
}

运行效果如下:

2. 平移图片

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

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    // 平移
    painter.translate(100, 100);

    QPixmap pixmap(":/liyuu.jpg");

    // 前两个参数为起点;中间两个参数为图片大小,可以设置这两个参数实现图片的缩放
    painter.drawPixmap(0, 0, 300, 300, pixmap);
}

运行效果如下:

3. 缩放图片

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    QPixmap pixmap(":/liyuu.jpg");

    // 前两个参数为起点;中间两个参数为图片大小,可以设置这两个参数实现图片的缩放
    painter.drawPixmap(100, 100, 400, 300, pixmap);
}

运行效果如下:

4. 旋转图片

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

cpp 复制代码
void Widget::paintEvent(QPaintEvent *event)
{
    // 绘制
    QPainter painter(this);

    QPixmap pixmap(":/liyuu.jpg");

    // 图片选择,本质上是吧QPointer对象进行了旋转,绘制出来的内容也就产生了旋转
    painter.rotate(180);
    painter.translate(-800, -600);// 这个方法就是改变坐标的原点
    painter.drawPixmap(100, 100, 400, 300, pixmap);
}

运行效果如下:

终于,qt学完了,不过还是不够深入,未来再加把劲吧!

相关推荐
Swift社区1 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht1 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht1 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20241 小时前
Swift 数组
开发语言
stm 学习ing2 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc3 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe4 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin4 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python