当前位置: 首页 > news >正文

Qt 界面优化 --- 绘图

绘图

Qt 虽已内置诸多控件,但无法保证适配所有场景。很多时候,我们需要更灵活的 “自定义” 能力,在窗口上绘制任意图形形状,以实现复杂界面设计。Qt 提供了相关绘图 API,支持在窗口绘制任意图形。

基本概念

所谓 “控件” 本质上也是通过绘图方式生成的。画图是对控件的一种封装,可以类比成机器语言和高级语言的关系;控件是对画图 API 的进一步封装,能让画图 API 更简单实现。

绘图 API 核心类
说明
QPoint“绘图者” 或 “画笔” 位置,用来绘图时,提供了点(x 和 y 坐标),可在坐标系(多种相关图形类)里描述点。
QPointF同 QPoint,精度更高(用到浮点数),QPainter 绘制时,QPoint 会被隐式转换为 QPointF。
QPainter描述了 “绘图者” 要画到哪个对象上,绘制用的 QWidget 也属于 QPaintDevice(QWidget 是 QPaintDevice 的子类)。
QPen描述了 QPainter 画出来的线是什么样的。(线的属性)
QBrush描述了 QPainter 填充一个区域是什么样的。(区域,边界的属性)

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

关于 paintEvent

paintEvent 会在以下情况被触发:

  • 控件首次创建;(比如往 QWidget 上画画,QWidget 创建之前,画的东西当然不生效,首次创建 QWidget 就能显示出来画的东西)
  • 控件被遮挡后,再恢复显示/接触遮挡;(这个时机,我们进行绘制也是很重要的,否则就会在被遮挡之后就没了)
  • 窗口大小变化;
  • 窗口最小化、再恢复;
  • 主动调用 repaint() 或者 update() 方法(这两个方法都是 QWidget 的方法);
  • ……

因此,若把绘图 API 放到构造函数中调用,一旦出现上述情况,界面绘制效果无法确保符合预期。

绘制各种形状

绘制线段

示例 1

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

在 “widget.h” 头文件中声明事件:

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();// 声明绘画事件void paintEvent(QPaintEvent *event);
};#endif // WIDGET_H

在 “widget.cpp” 文件中编写 paintEvent() 方法:

void Widget::paintEvent(QPaintEvent *event)
{(void)event;//绘图工作就会放在这里被执行QPainter painter(this);//此处指定的this,不是父对象,而是指定绘制的设备(往啥东西上画)//画一个线段painter.drawLine(20, 20, 200, 20);//水平线段painter.drawLine(QPoint(20, 100), QPoint(200, 100));painter.drawLine(20, 20, 20, 300);//垂直线段painter.drawLine(20, 20, 100, 300);//斜画
}

实现效果

绘制矩形
void QPainter::drawRect(int x, int y, int width, int height);
// 参数:
// x,y:窗口横坐标;
// width:所绘制矩形的宽度;
// height:所绘制矩形的高度。

示例

void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象 this:表示的是在当前窗口中绘图,即绘图设备QPainter painter(this);// 绘制矩形painter.drawRect(20, 20, 100, 50);
}
绘制圆形
void QPainter::drawEllipse(const QPoint &center, int rx, int ry);
// 参数:
// center:中心坐标;
// rx:横坐标;
// ry:纵坐标。

示例

void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象 this:表示的是在当前窗口中绘图,即绘图设备QPainter painter(this);// 绘制圆painter.drawEllipse(QPoint(100, 100), 50, 50);
}

实现效果:在窗口中绘制出一个以 (100, 100) 为中心,横纵半径均为 50 的圆形。

绘制文字

QPainter 类中不仅提供了绘制图形的功能,还可以用 QPainter::drawText() 函数来绘制文字,也可以用 QPainter::setFont() 设置字体等信息。示例

#include "widget.h"
#include <QPainter>
#include <QFont>Widget::Widget(QWidget *parent): QWidget(parent)
{resize(400, 300);
}void Widget::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 实例化画家// 设置字体QFont font;font.setFamily("华文行楷");font.setPointSize(20);painter.setFont(font);// 设置文字颜色painter.setPen(Qt::red);// 绘制文字painter.drawText(QRect(100, 200, 400, 150), "天行健,君子以自强不息");
}

实现效果:在窗口中用红色、华文行楷字体(字号 20)绘制出 “天行健,君子以自强不息” 文字。

注意:

有时候在使用 painter.drawText(0, 100, "hello") 绘制文字时,要留意坐标的含义:

  • 横坐标 0,代表文字最左侧的位置,文字会从这个水平位置开始绘制。
  • 纵坐标 100,表示的是文字的 ** 基线(baseline)** 位置,基线是文字排版中用于对齐的一条假想线,像字母 “h”“l” 等有升部的字符,会在基线之上延伸,而 “g”“p” 等有降部的字符,会在基线之下延伸。
设置画笔

QPainter 在绘制时,要有一个形状、线条粗细的 “画笔”。在绘图时,QPen 可定义 “画笔”。在 Qt 中,QPen 类定义了 QPainter 绘制的 “笔” 的形状、线条粗细等。在使用时也可以自定义 “画笔”。QPen 的 “画笔” 的风格、颜色以及宽度等属性进行设置,“设置画笔” 需要通过 setPen() 方法进行设置,“画笔” 的颜色是 QColor 类实现方法进行设置,“设置画笔” 的宽度通过 setWidth() 方法进行设置。

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

画笔的风格有:

风格说明
Qt::NoPen没有线。例如,QPainter::drawRect() 填充但不绘制任何线条。
Qt::SolidLine一条简单的线。
Qt::DashLine由一些像素分隔的短线。
Qt::DotLine由一些像素分隔的点。
Qt::DashDotLine交替的点和短线。
Qt::DashDotDotLine一个点,一个短线,一个点。
Qt::CustomDashLine使用 QPainterPath 定义的自定义模式。
Qt::BDiagPattern从右上到左下的对角线。
Qt::FDiagPattern从左上到右下的对角线。
Qt::CrossPattern交叉对角线。
Qt::DiagCrossPattern双交叉对角线。
Qt::LinearGradientPattern线性渐变填充。
Qt::ConicalGradientPattern锥形渐变填充。
Qt::RadialGradientPattern径向渐变填充。
Qt::TexturePattern纹理填充。

示例:画笔的使用

#include "widget.h"
#include <QPainter>
#include <QPen>
#include <QColor>Widget::Widget(QWidget *parent): QWidget(parent)
{resize(400, 300);
}void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象 this:表示的是在当前窗口中绘图,即绘图设备QPainter painter(this);// 设置画笔QPen pen(QColor(255, 0, 0));// 设置画笔宽度pen.setWidth(5);// 设置画笔风格pen.setStyle(Qt::DashLine);// 让画家使用画笔painter.setPen(pen);// 绘制圆painter.drawEllipse(QPoint(100, 100), 50, 50);
}

实现效果:在窗口中绘制出一个红色、虚线(线宽 5)的圆形。

设置画刷

画刷,顾名思义,是用 QBrush 类来描述,画刷大多用于填充。QBrush 定义了 QPainter 的填充模式,具有样式、颜色、渐变以及纹理等属性。QBrush 的 style 枚举中,默认值是 Qt::NoBrush,也就是不进行任何填充。可以通过 Qt 的样式,使用 QBrush

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

示例

#include "widget.h"
#include <QPainter>
#include <QBrush>
#include <QColor>Widget::Widget(QWidget *parent): QWidget(parent)
{resize(400, 300);
}void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象 this:表示的是在当前窗口中绘图,即绘图设备QPainter painter(this);// 设置画刷QBrush brush(QColor(0, 255, 255));// 设置画刷风格brush.setStyle(Qt::Dense4Pattern);// 让画家使用画刷painter.setBrush(brush);// 设置画笔QPen pen(QColor(255, 0, 0));pen.setWidth(5);painter.setPen(pen);// 绘制椭圆painter.drawEllipse(QPoint(100, 100), 50, 50);// 绘制矩形painter.drawRect(200, 20, 100, 80);
}

绘制图片

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

绘制简单图片

新建 Qt 项目:基类选择 QWidget,项目名称为 QPainter。在 “widget.h” 头文件中声明绘图事件,如下图示:

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();// 声明绘画事件void paintEvent(QPaintEvent *event);
};#endif // WIDGET_H

添加资源文件:首先准备一些图片资源文件,并将这些图片资源文件放在同一个文件夹中,将该文件夹复制到项目中。

选中项目文件,鼠标右键 -----> add new...

点击 “add new...” 之后,出现如下界面:在弹出的窗口中,选择 “Qt” 分类下的 “Qt Resource File”,点击 “Choose...”。

选择 “Choose...” 之后,给资源文件重命名:输入资源文件名称,点击 “下一步”。

点击 “下一步”,出现如下界面,点击 “完成”

给资源文件添加前缀,并将资源文件添加至项目中

将所有的资源文件添加到项目中,方便后续使用

点击 “构建并运行” 按钮,将资源文件添加到项目中

在 “widget.cpp” 文件中实现图片功能

void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象QPainter painter(this);// 画图片painter.drawPixmap(0, 0, QPixmap(":/mei.webp"));
}

实现效果:在窗口中显示指定的图片。

平移图片

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

void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象QPainter painter(this);// 平移painter.translate(100, 100);// 画图片painter.drawPixmap(0, 0, QPixmap(":mei.jpg"));
}

实现效果:图片相对于原来的位置发生了平移。

缩放图片

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

void Widget::paintEvent(QPaintEvent *event)
{(void)event;QPainter painter(this);QPixmap pixmap(":/mei.jpg");painter.drawPixmap(0, 0, pixmap);painter.drawPixmap(100, 100, 500, 300, pixmap);
}

实现效果:窗口中显示了原图和缩放后的图片。

旋转图片

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

void Widget::paintEvent(QPaintEvent *event)
{// 实例化画家对象QPainter painter(this);// 让旋转的中心为(100,100)painter.translate(100, 100);painter.rotate(90); // 顺时针旋转90度painter.translate(-100, -100); // 恢复坐标// 画.drawPixmap(图片QPixmap pixmap(":/mei.jpg");painter.drawPixmap(0, 0, pixmap);
}

实现效果:图片发生了旋转。

其他设置

移动画家位置

有时在绘制多个图形时,若使用同一坐标位置,绘制出的图形可能会重合。此时,可通过移动画家位置来避免图形重合。

示例 1:未移动画家位置

#include "widget.h"
#include <QPainter>Widget::Widget(QWidget *parent): QWidget(parent)
{
}void Widget::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 实例化画家对象// 画圆painter.drawEllipse(QPoint(100, 200), 50, 50); // 画圆,使用同一坐标位置,两个圆会重合,保证就只有一个圆painter.drawEllipse(QPoint(100, 200), 50, 50); 
}

实现效果:窗口中只有一个圆。

示例 2:移动画家位置使用 translate 移动画家所在位置。

#include "widget.h"
#include <QPainter>Widget::Widget(QWidget *parent): QWidget(parent)
{
}void Widget::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 实例化画家对象// 画圆painter.drawEllipse(QPoint(100, 200), 50, 50); // 移动画家painter.translate(200, 0);// 画圆,此时有两个圆painter.drawEllipse(QPoint(100, 200), 50, 50); 
}

实现效果:窗口中有两个圆。

保存 / 加载画家的状态

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

save() 函数原型如下:

void QPainter::save();

restore() 函数原型如下:

void QPainter::restore();

示例

void Widget::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 实例化画家对象// 画圆painter.drawEllipse(QPoint(100, 200), 50, 50); // 往画家移动painter.translate(200, 0);painter.save(); // 保存画家状态// 画圆painter.drawEllipse(QPoint(100, 200), 50, 50); painter.translate(200, 0); // 往画家移动painter.restore(); // 恢复画家状态// 画圆,第二个和第三个重合为一个painter.drawEllipse(QPoint(100, 200), 50, 50); 
}

实现效果:第二个和第三个圆重合为一个。

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

特殊的绘图设备

前面的代码中我们是使用 QWidget 作为绘图设备,在 Qt 中还存在下列三个比较特殊的绘图设备,此处我们只做个简单的介绍。

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

QPixmap 特点:

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

示例

#include "widget.h"
#include <QPainter>
#include <QPixmap>Widget::Widget(QWidget *parent): QWidget(parent)
{this->resize(600, 500);
}void Widget::paintEvent(QPaintEvent *event)
{// /xp/pic.png(给图片各段仅尺寸为 50*50)QPixmap pixmap(":/xp/pic.png");QPainter painter(this); // 实例化画家对象painter.drawEllipse(QPoint(100, 100), 50, 50); // 设置圆心和半径// 保存绘制的图片pixmap.save("C:\\Users\\lenovo\\Desktop\\Test\\pic.png");
}
QImage

QImage 特点:

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

代码示例QImage 绘图时对像素的修改

头文件中声明事件:

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();// 声明绘画事件void paintEvent(QPaintEvent *event);
};#endif // WIDGET_H

源文件中实现事件,使用 QImage 对图片的像素进行修改:

#include "widget.h"
#include <QPainter>
#include <QImage>Widget::Widget(QWidget *parent): QWidget(parent)
{
}Widget::~Widget()
{
}void Widget::paintEvent(QPaintEvent *event)
{QPainter painter(this); // 实例化画家对象QImage img(":/picture/3.jpg"); // 加载图片// 修改像素点for(int i = 100; i < 200; i++){for(int j = 100; j < 200; j++){img.setPixelColor(i, j, Qt::blue);}}painter.drawImage(0, 0, img);
}

效果如下

  • 没有修改像素之前:显示原始图片。
  • 修改像素之后:图片对应区域像素变为蓝色。
QPicture

QPicture 特性:

  • 使用 QPainter 直接在上面进行绘制图形。
  • 能通过文件路径加载并显示图片。
  • 独立于硬件的系统,能够在不同系统之上提供一致的显示。
  • QPicture 加载的必须是自身的存文件,而不能是任意的 jpgpng 等图片文件。
  • QPicture 类似于很多游戏的 Replay 功能。

示例

#include "widget.h"
#include <QPainter>
#include <QPicture>Widget::Widget(QWidget *parent): QWidget(parent)
{
}void Widget::paintEvent(QPaintEvent *event)
{QPicture pic;QPainter painter;pic.open("C:\\Users\\lenovo\\Desktop\\Test\\pic.pic"); // 设置要保存的路径painter.begin(&pic); // 把图画到 pic 上painter.setPen(Qt::red); // 设置画笔颜色painter.drawEllipse(QPoint(100, 100), 50, 50); // 画圆painter.end();QPainter painter2(this); // 实例化画家对象painter2.drawPicture(0, 0, pic); // 绘制图片
}

Qt 对于界面的美化,还涉及到很多其他的话题,大家未来在工作中如果涉及到了,再针对性学习即可。

  • Qt 动画
  • Qt 3D
  • Qt Quick 三控件
  • Qt 串口
  • Qt 数据库……
http://www.dtcms.com/a/418309.html

相关文章:

  • MySQL Online DDL:高性能表结构变更指南
  • 操作系统:进程调度,创建和终止
  • Kafka09-速答-尚硅谷
  • Jenkins与GitLab-CI的技术对比分析
  • 2025 年 AI 智能体(Agent)发展全景:技术突破、场景落地与产业重构
  • 电子商务的网站建设名词解释网站设计的流程简答题
  • Spark源码中的线程池
  • Kafka06-进阶-尚硅谷
  • TDengine 时序函数 IRATE 用户手册
  • 网站模板源码下载广告网站建设
  • 一键部署 Spring Boot 到远程 Docker 容器
  • Docker 入门:容器化开发的强大工具
  • iOS 26 全景揭秘,新界面、功能创新、兼容挑战与各种工具在新版系统中的定位
  • 北京交易中心网站电商网站建设需要
  • 【ansible/K8s】K8s的自动化部署源码分享
  • C++STL之list
  • CentOS 7安装部署RabbitMQ
  • 本地怎么远程调试服务器
  • AndroidID重置功能开发
  • 【Byte 类型】编程基石:揭开 `Byte`(字节)的神秘面纱
  • 天津做网站哪家服务好北京正邦品牌设计公司
  • 外贸搜素网站android studio开发app实例
  • 5. Prompt 提示词
  • android 自定义样式 Toast 实现(兼容 Android 4.1+~Android 16(API 16))
  • android SharedPreferences 工具类 * 兼容 Android 16+ (API 16)
  • 宁波易通建设网站网站备案信息代码
  • 阿里云OpenLake及行业解决方案年度发布,助力千行百业Data+AI一体化融合
  • 独立站收款方式有哪些
  • 2025 年 Python 数据分析全栈学习路线:从入门到精通的进阶指南
  • 行业类网站应如何建设网站怎么建设以及维护