<Qt> 界面优化

news2024/12/23 18:46:38

目录

前言:

背景介绍

一、QSS基本语法 

二、QSS设置方式

(一)指定控件样式设计

(二)全局样式设置

(三)从文件加载样式表

(四)Qt Designer 编辑样式

三、选择器

(一)子控件选择器

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

四、样式属性

(一)盒模型(Box Model)

(二)控件样式示例

1. 按钮

2. 复选框

3. 单选框

4. 输入框

5. 列表

6. 菜单栏

7. 登录界面

(三)小结

五、绘图

(一)基本概念

(二)绘制各种形状

1. 绘制线段

2. 绘制矩形

3. 绘制圆形

4. 绘制文本

5. 设置画笔

6. 设置画刷

(三)绘制图片

1. 绘制简单图片

2. 平移图片

3. 缩放图片

4. 旋转图片


前言:

背景介绍

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

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

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

网页开发作为GUI的典型代表,也对于其他客户端GUI开发产生了影响,Qt也是其中之一。
Qt仿照CSS的模式,引入了QSS,来对Qt中的控件做出样式上的设定,从而允许程序员写出界面更好看的代码。

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

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

一、QSS基本语法 

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

选择器
{
	属性名:属性值
}

其中:

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

例如:

QPushButton { color: red; }

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

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

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

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 的父控件):

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优先级更高
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文件中的 QApplication 的 setStyleSheet 方法设置整个程序的全局样式。

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

#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来说,它即继承了全局样式的文本颜色属性,也复用了自己样式的字体大小属性,这样两个样式属性叠加起来现象就叫样式的层叠属性。

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

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 的基础上加上冲突的指定样式:

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 即可:

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

#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选择器的影响。

#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();
}

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


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

a.setStyleSheet(".QWidget { color: red; }");

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

代码示例:使用id选择器

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

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

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属性设置图片。
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,创建全局样式

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 Sheets 的The 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; 是给左侧设置内边距。
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();
}

运行效果如下: 

代码示例:设置外边距

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

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设置样式:

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形态.

创建一个复选框:

编辑复选框的样式:

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. 输入框

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

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

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并设置样式表:

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 进行渐变。

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

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,创建几个菜单项:

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,后者可以跟随控件的大小发生变化,而前者就是固定的,所以我们使用后者就可以了。后面就是自己设计一个界面:

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. 绘制线段

使用的是下面这个方法:

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

代码示例:绘制一个箭头

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

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. 绘制矩形

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

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

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

运行效果如下:  

3. 绘制圆形

下面是绘制圆形的方法:

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

运行效果如下: 

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

4. 绘制文本

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

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)

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) 方法,其参数为画刷的格式:

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、QBitmap QPicture,它们都是常用的绘图设备。

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

1. 绘制简单图片

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

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

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

运行效果如下: 

2. 平移图片

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

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

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

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

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

运行效果如下: 

3. 缩放图片

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

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

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

运行效果如下: 

4. 旋转图片

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

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学完了,不过还是不够深入,未来再加把劲吧! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

​​JVM三:JVM垃圾回收机制(GC)

1.什么是垃圾? 指的是不再使用的内存。 2.垃圾回收 将不用的内存&#xff0c;自动释放&#xff0c;解决内存泄露问题。 3.GC主要针对堆进行释放 GC是以"对象"为基本单位&#xff0c;进行回收&#xff0c;而不是字节。 垃圾回收&#xff08;GC&#xff09;主要处理三…

刑事拘留和逮捕在程序上有何区别?

1. 实施条件&#xff1a;刑事拘留是在有重大犯罪嫌疑且存在逃避侦查、串供或者其他妨碍刑事诉讼行为可能的情况下&#xff0c;由公安机关决定采取的临时剥夺人身自由的强制措施。而逮捕则是更为严厉的强制措施&#xff0c;通常在犯罪嫌疑人涉嫌的重大犯罪事实已经查清&#xff…

【贝壳找房】测试开发一面凉经--北京现场面(附面试答案)

1.单链表反转写 单链表是一种线性结构&#xff0c;它是由一个个节点&#xff08;Node&#xff09;组成的。并且每个节点&#xff08;Node&#xff09;是由一块数据域&#xff08;data&#xff09;和一块指针域&#xff08;next&#xff09;组成。 节点的数据域&#xff1a;da…

vue element-plus el-drawer 自定义抽屉标题 template slot动态标题定义方法

默认抽屉定义&#xff0c; 这里的title就是标题的定义 <el-drawer v-model"drawer" title"I am the title" :direction"direction" :before-close"handleClose" > 如果我们需要自定义这个标题&#xff0c;就需要使用slot <e…

ebpf教程(4.1):XDP程序的加载

文章目录 前言环境准备加载XDP程序源码构建过程运行 前言 前置阅读要求&#xff1a; ebpf教程(3):使用cmake构建ebpf项目-CSDN博客[译] [论文] XDP (eXpress Data Path)&#xff1a;在操作系统内核中实现快速、可编程包处理&#xff08;ACM&#xff0c;2018&#xff09;xdp-t…

Zabbix 中网络设备(交换机和防火墙)的报警信息配置指南

简述 本文介绍配置 Zabbix 以监控网络设备&#xff08;如交换机和防火墙&#xff09;并发送钉钉报警信息&#xff0c;我将提供一个详细的步骤指南。 请确保你已经完成了以下前提条件&#xff1a; 1. Zabbix 已经搭建完成。2. 网络设备&#xff08;交换机和防火墙&#xff09…

空状态设计教程:连接用户体验的桥梁

空状态设计是产品设计中常被忽视却又极其重要的一环。它不仅是用户旅程的起点&#xff0c;更是塑造第一印象的关键。本文将引导你如何使用强大的设计工具设计出既美观又实用的空状态&#xff0c;以提升用户体验。 空状态设计的意义 空状态作为用户与产品初次邂逅的界面&#…

PostgreSQL-04-入门篇-连接多张表

文章目录 1. 连接设置样例表PostgreSQL 左连接PostgreSQL 右连接PostgreSQL 全外连接 2. 表别名简介表别名的实际应用1) 对长表名使用表别名&#xff0c;使查询更具可读性2) 在连接子句中使用表别名3) 在自连接中使用表别名 3. INNER JOIN 内连接简介PostgreSQL INNER JOIN 示例…

【机器学习】正则化,欠拟合与过拟合(详细代码与图片演示!助你迅速拿下!!!)

目录 &#x1f354;欠拟合与过拟合 1.1 欠拟合与过拟合定义 1.2 通过代码认识过拟合和欠拟合 1.3 原因以及解决办法 &#x1f354;正则化 2.1 什么是正则化 2.2 正则化类别 &#x1f354;小结 学习目标 &#x1f340; 掌握过拟合、欠拟合的概念 &#x1f340; 掌握过…

黄山黄小徽光影乐园:思特科技打造沉浸式光影乐园解决方案,快乐指数拉满了!

01      「黄小徽儿童光影乐园」是由思特科技全力打造&#xff0c;依托行业领先的数字光影技术与交互科技&#xff0c;专为3-8岁儿童设计的全场景、全交互、全沉浸的沉浸式光影乐园解决方案。    沉浸式光影乐园解决方案-黄小徽儿童光影乐园      02      思特…

无人机之固定翼无人机的组成

固定翼无人机是根据空气动力学原理设计机翼的形状&#xff0c;靠动力装置产生推力或者拉力&#xff0c;使无人机获得一定速度后&#xff0c;会导致空气在飞机上下表面的压力不同&#xff0c;进而产生升力&#xff0c;其升力主要来源于固定的机翼。大多数都是由机翼、机身、尾翼…

ultralytics实例分割mask读取

在前面学习YOLOv8的实例分割过程中&#xff0c;需要使用实例分割的数据集&#xff0c;其标签的标注格式如下&#xff1a; 实例分割勾勒轮廓 其中&#xff0c;第一个数字代表的是类别编号&#xff0c;后面的数据代表的是标注的坐标&#xff08;转换到0-1之间&#xff09;每两个…

编程修炼之Hibernate--- springboot启动初始化ddl过程与如何自定义修改 table 字段长度

文章目录 springboot启动初始化ddl过程如何自定义修改 table springboot启动初始化ddl过程 跟踪Springboot整合hibernate的启动代码&#xff1a; SessionFactoryImpl 的初始化里做了非常多的事情&#xff0c;初始化各种资源&#xff0c;并调用 SchemaManagementToolCoordinat…

c语言基础--------字符串指针

在 C 语言中&#xff0c;字符串指针是一个指向字符类型的指针&#xff0c;通常用于指向字符串的第一个字符。字符串在 C 语言中通常表示为字符数组&#xff0c;而字符串指针则是用来存储这种字符数组首地址的变量。 定义字符串指针 字符串指针的定义方式如下&#xff1a; ch…

Android更改包名和签名

一、更改包名 1、包名——鼠标右键——Refactor——Rename 修改自己想更改的包名和选择更改范围后点击Refactor就可以了 2.手动修改app的build.gradle文件中的applicationId&#xff08;改成和我们之前修改的包名相同&#xff09; 3.修改AndroidManifest.xml文件中的packag…

“AI+Security”系列第2期(三):面向LLM(大语言模型)的漏洞挖掘与对齐防御研究

近日&#xff0c;由安全极客、Wisemodel 社区和 InForSec 网络安全研究国际学术论坛联合主办的“AISecurity”系列第 2 期——对抗&#xff01;大模型自身安全的攻防博弈线上活动如期举行。 在此次活动中&#xff0c;前阿里云高级安全专家郑瀚带来了以《通往LLM安全对齐的道路…

网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)

大家好&#xff0c;我是程序员鱼皮&#xff0c;8 月 19 日下午&#xff0c;网易云音乐突发严重故障&#xff0c;并登顶微博热搜&#xff0c;跟黑神话悟空抢了热度。 根据用户的反馈&#xff0c;故障的具体表现为&#xff1a;用户无法登录、歌单加载失败、播放信息获取失败、无法…

SQL— DML语句学习【后端 10】

数据库操作-DML 详解 在数据库管理系统中&#xff0c;DML&#xff08;Data Manipulation Language&#xff0c;数据操作语言&#xff09;扮演着至关重要的角色&#xff0c;它负责对数据库中的数据进行增、删、改操作。掌握DML操作对于数据库的日常维护和管理至关重要。本文将详…

CSP 2023 普及组第一轮 - CSP/S 2023初试题 完善程序第二题解析

一、题目阅读 &#xff08;编辑距离&#xff09;给定两个字符串&#xff0c;每次操作可以选择删除&#xff08;Delete&#xff09;、插入&#xff08;Insert&#xff09;、替换&#xff08;Replace&#xff09;&#xff0c;一个字符&#xff0c;求将第一个字符串转换为第二个字…

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 文章目录 前言时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 一、BO-CNN-BiGRU-Attention模型1. 贝叶斯优化&#…