Qt学习笔记(三十):Qt 中的绘图


一、QPainter:绘图对象

Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制。整个绘图系统基于 QPainter,QPainterDevice 和 QPaintEngine 三个类。

QPainter 用来执行绘制的操作(相当于画家);

QPaintDevice 是一个绘图设备,允许 QPainter 在其上面进行绘制,也就是 QPainter 工作的空间(相当于画板);

QPaintEngine 提供了 QPainter 在不同的设备上进行绘制的统一的接口(相当于一种协议)。

QPaintEngine 类应用于 QPainter 和 QPaintDevice 之间,通常对开发人员是透明的。除非你需要自定义一个设备,否则你是不需要关心 QPaintEngine 这个类的。我们可以把 QPainter 理解成画家;把 QPaintDevice 理解成画板,比如纸张、屏幕等都可以作为画板;而对于纸张、屏幕而言,肯定要使用不同的画笔绘制,为了统一使用一种画笔,Qt 设计了 QPaintEngine 类,这个类让不同的纸张、屏幕都能使用一种画笔。

下图给出了这三个类之间的层次结构:

上面的示意图告诉我们,Qt 的绘图系统实际上是,使用 QPainter 在 QPainterDevice 上进行绘制,它们之间使用QPaintEngine 进行通讯(也就是翻译 QPainter 的指令)。

 

二、画图测试:

新建一个基于 QWidget 的项目,在头文件中重写 paintEvent() 事件方法:

protected:void paintEvent(QPaintEvent *); // 重写绘图事件

然后在源文件中实现 paintEvent() 事件方法,paintEvent() 就是当窗口发生重绘时触发的事件方法,那么我们需要画图的代码就应该写在 paintEvent() 事件方法中。

1、画一条直线:

// 窗体发生重绘时触发的事件
void Widget::paintEvent(QPaintEvent *)
{// 创建一个局部变量 QPainter 对象,即每次运行 paintEvent() 方法的时候都会重新创建该对象。// QPainter 接收一个 QPaintDevice 指针作为参数,QPaintDevice 表示绘图设备,// 即指定我们在哪个设备上进行绘图。this 表示当前主窗口对象,即在当前窗口上进行绘图。QPainter painter(this);// 画一条直线:参数为两个点(两点决定一条直线)painter.drawLine(QPoint(10, 10), QPoint(100, 100));
}

2、画一个矩形,并指定矩形边框的颜色:

void Widget::paintEvent(QPaintEvent *)
{QPainter painter(this);// 设置一个红色的画笔painter.setPen(Qt::red);// 画一个矩形painter.drawRect(QRect(10, 10, 100, 100));
}

3、画一个椭圆形,指定边框的颜色和粗细,并指定图形的填充颜色:

void Widget::paintEvent(QPaintEvent *)
{QPainter painter(this);// 设置一个绿色的画笔,并指定画笔的宽度为 8painter.setPen(QPen(Qt::green, 8));// 设置一个蓝色的画刷,用来填充绘制的图形painter.setBrush(Qt::blue);// 画一个椭圆:指定圆心,宽和高painter.drawEllipse(QPoint(100, 100), 80, 50);
}

 

三、QPaintDevice:绘图设备

绘图设备是指继承 QPainterDevice 的子类。

Qt一共提供了四个这样的类,分别是 QPixmap、QBitmap、QImage 和 QPicture。

  • QPixmap:专门为图像在屏幕上的显示做了优化;

  • QBitmap:是 QPixmap 的子类,它的色深限定为 1,可以使用 QPixmap 的 isQBitmap() 方法来确定这个 QPixmap 是不是一个 QBitmap;

  • QImage:专门为图像的像素级访问做了优化;

  • QPicture:可以记录和重现 QPainter 的各条指令;

  •  

1、QPixmap:可以接收一个字符串作为一个文件的路径来显示这个文件,使用 QPinter 的 drawPixmap() 方法可以把这个文件绘制到一个指定的绘图设备上。QPixmap 是针对屏幕进行特殊优化的,因此,它与实际的底层显示设备息息相关。注意,这里说的显示设备并不是硬件,而是操作系统提供的原生的绘图引擎。所以,在不同的操作系统平台下,QPixmap 的显示可能会有所差别。

void Widget::paintEvent(QPaintEvent *)
{// 创建一个绘图对象,指定绘图设备为当前主窗口QPainter painter(this);// 绘制一个图像:参数为显示图像的开始 x、y 坐标,和要显示的图像// 显示的是图像的原始尺寸大小painter.drawPixmap(10, 10, QPixmap(":/Image/Luffy.png"));
}

还可以自己指定显示图像的大小:

void Widget::paintEvent(QPaintEvent *)
{// 创建一个绘图对象,指定绘图设备为当前主窗口QPainter painter(this);// 绘制一个图像:参数为显示图像的开始 x、y 坐标,和显示图像的宽度和高度,以及要显示的图像painter.drawPixmap(10, 10, 100, 100, QPixmap(":/Image/Luffy.png"));
}

还可以在指定的控件上绘制图像,我最开始的做法如下:

void Widget::paintEvent(QPaintEvent *)
{// 创建一个 QLabel 对象QLabel *label = new QLabel(this);label->setGeometry(10, 10, 200, 200);// 创建一个绘图对象,指定绘图设备为 QLabelQPainter painter(label);// 绘制一个图像painter.drawPixmap(0, 0, 200, 200, QPixmap(":/Image/Luffy.png"));
}

程序运行的时候报错:

QWidget::paintEngine: Should no longer be called

QPainter::begin: Paint device returned engine == 0, type: 1

后来测试发现,好像是想要在哪个控件上绘制,就要在哪个控件类中重写 paintEvent() 方法才行。比如想要在 QLabel 上绘图,就需要新建一个类继承于 QLabel,然后重写 paintEvent() 方法,如下所示:

mylabel.h:

#include <QLabel>class MyLabel : public QLabel
{Q_OBJECT
public:explicit MyLabel(QWidget *parent = 0);protected:void paintEvent(QPaintEvent *); // 重写绘图事件
};

mylabel.cpp:

#include "mylabel.h"
#include <QPainter>MyLabel::MyLabel(QWidget *parent) : QLabel(parent)
{
}// 在控件发生重绘时触发的事件
void MyLabel::paintEvent(QPaintEvent *)
{// 创建一个绘图对象,指定绘图设备为 QLabelQPainter painter(this);// 绘制一个图像painter.drawPixmap(0, 0, this->width(), this->height(), QPixmap(":/Image/Luffy.png"));
}

在主窗口程序中调用 MyLabel:  

Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);// 调用 MylabelMyLabel *label = new MyLabel(this);label->setGeometry(10, 10, 100, 100);
}

 

2、QBitmap:继承自 QPixmap,因此具有 QPixmap 的所有特性。QBitmap 的色深始终为 1,色深这个概念来自于计算机图形学,是指用于表现颜色的二进制位数。我们知道,计算机中的数据都是使用二进制来表示的。为了表示一种颜色,我们也会使用二进制。比如我们要表示 8 种颜色,需要用 3 个二进制位,这时我们就说色深是 3。因此,所谓色深为 1,也就是使用 1 个二进制表示颜色。一个二进制数只有两种状态,0 和 1,因此他所表示的颜色只有两种,黑和白。所以,QBitmap 实际上是只有黑白两色的图像数据。

由于 QBitmap 色深小,因此只占用很少的内存空间,所以适合做光标文件和笔刷。

下面我们来看同一个图像文件在 QPixmap 和 QBitmap 下的不同表现:

void Widget::paintEvent(QPaintEvent *)
{// 创建两个 QPixmap 图像QPixmap pixmap(":/Image/butterfly.png");QPixmap pixmap1(":/Image/butterfly1.png");// 创建两个 QBitmap 图像QBitmap bitmap(":/Image/butterfly.png");QBitmap bitmap1(":/Image/butterfly1.png");QPainter painter(this);// 绘图painter.drawPixmap(0, 0, pixmap);painter.drawPixmap(200, 0, pixmap1);painter.drawPixmap(0, 130, bitmap);painter.drawPixmap(200, 130, bitmap1);
}

显示效果如下:

我们使用了两张图片,butterfly.png 具有透明色的背景,butterfly1.png 是没有透明色的纯白背景。使用 QPixmap 加载出的图像是正常效果;但是使用 QBitmap 加载,可以看到 butterfly.png 的透明色背景变成了黑色,而 butterfly1.png 的纯白色背景反而消失了,变成了透明色。至于图像的其他颜色,则是使用点的疏密程度来表现的。

 

3、QImage: QPixmap 使用底层平台绘制系统进行绘制,无法提供像素级别的操作;而 QImage 则是使用独立于硬件的绘制系统,实际上是自己绘制自己,因此提供了像素级别的操作,并且能够在不同系统之上提供一个一致的显示形式。

我们声明一个 QImage 对象,大小是 200x200,颜色模式是 RGB32,即使用 32 位数值表示一个颜色的 RGB 值,也就是说每种颜色使用 8 位。然后我们为每个像素进行颜色赋值,从而构成了这个图像。

void Widget::paintEvent(QPaintEvent *)
{QPainter painter(this);// 创建一个 QImage 对象QImage image(200, 200, QImage::Format_RGB32);QRgb value;// 将图片背景填充为绿色image.fill(Qt::green);// 改变指定区域的像素点的值for (int i = 50; i < 100; i++){for(int j = 50; j < 100; j++){value = qRgb(255, 0, 0);        // 红色image.setPixel(i, j, value);    // 设置每个像素点的值}}// 将图像绘制到窗口中painter.drawImage(10, 10, image);
}

QImage 与 QPixmap 的区别:

  • QPixmap 主要用于绘图,针对屏幕显示而最佳化设计,QImage 主要是为图像 I/O、图片访问和像素修改而设计的。

  • QPixmap 依赖于所在平台的绘图引擎,顾例如反锯齿等一些效果在不同的平台上可能会有不同的效果,QImage 使用 Qt 自身的绘图引擎,可在不同平台上具有相同的显示效果。

  • 由于 QImage 是独立于硬件的,也是一种 QPaintDevie,因此我们可以在另一个线程中对其进行绘制,而不需要在 GUI 线程中处理,使用这一方式可以很大程序提高 UI 响应速度。

  • QImage 可通过 setPixel() 和 pixel() 等方法直接存取指定的像素。

QImage 和 QPixmap 的相互转换:

	// 将 QImage 转换成 QPixmap:使用 QPixmap 的静态成员函数 fromImage();QPixmap pixmap = QPixmap::fromImage(image);// 将 QPixmap 转换成 QImage:使用 QPixmap 类的成员函数 toImage();QImage img = pixmap.toImage();

 

4、QPicture:这是一个可以记录和重现 QPainter 命令的绘图设备。QPicture 将 QPainter 的命令序列化到一个 IO 设备,保存为一个平台独立的文件格式。这种格式有时候会是 “元文件(mate-files)”。Qt 的这种格式是二进制的,不同于某些本地的元文件,Qt 的 pictures 没有内容上的限制,只要是能够被 QPainter 绘制的元素,不论是字体,还是 pixmap,或者是变换,都可以保存在一个 picture 文件中。

QPicture 是平台无关的,因此他可以使用在多种设备之上,比如 svg、pdf、ps、打印机或者屏幕。QPicture 使用系统的分辨率,并且可以调整 QPainter 来消除不同设备之间的显示差异。

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

void Widget::paintEvent(QPaintEvent *)
{QPicture picture;QPainter painter;// 将图像绘制到 QPicture 中,并保存到文件// 开始记录painter.begin(&picture);// 开始绘图painter.drawEllipse(10, 10, 100, 50);       // 画一个椭圆painter.fillRect(10, 70, 100, 50, Qt::red); // 填充一个矩形// 结束记录painter.end();// 保存文件picture.save("D://drawing.pic");// 加载保存的绘图动作picture.load("D://drawing.pic");// 将保存的绘图重新绘制到设备上 painter.begin(this);painter.drawPicture(10, 10, picture);painter.end();
}

 

四、手动更新窗口

如下图所示:实现一个当按钮被点击时,图片向右移动的功能,使用绘图事件实现。

widget.h:

class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();private:Ui::Widget *ui;// 声明一个全局变量int x = 0;protected:void paintEvent(QPaintEvent *); // 重写绘图事件private slots:void on_btnMove_clicked();
};

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QPainter>Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);
}Widget::~Widget()
{delete ui;
}void Widget::paintEvent(QPaintEvent *)
{// 在主窗口上画一个图片QPainter painter(this);    painter.drawPixmap(x, 100, QPixmap(":/Image/LuffyQ.png"));
}// 点击一下移动按钮,重绘窗口,将图片向右移动
void Widget::on_btnMove_clicked()
{// 绘制图片的 x 坐标增加,向右移动x += 20;// 如果图片移动超出窗口宽度,则恢复 x 坐标为初始值if (x > this->width()){x = 0;}// 重绘窗口,整个窗口都会重绘
//    this->update();// 重绘指定的区域this->update(x - 20, 100, 150, 150);
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Qt入门第16篇】 2D绘图(六)坐标系统

导语 前面一节我们讲解了图片的显示&#xff0c;其中很多地方都用到了坐标的变化。这一节我们将讲解Qt的坐标系统&#xff0c;分为两部分来讲解&#xff1a;第一部分主要讲解前面一节的那几个函数&#xff0c;它们分别是translate()平移变换、scale()比例变换、rotate()旋转变…

使用Qt进行程序开发的过程记录

目录 一、qt的安装 二、resources文件的使用 三、Qt中各种设置问题 四、在子页面的简单图表显示 五、Qt发布独立运行的exe的方法 六、Qt创建子页面的方法 七、Qt中使用菜单栏的方法 八、Qt联合C进行文件读取、写入操作 九、Qt关闭窗口函数 十、提示窗口与调试功能的使…

Qt开发技术:Qt绘图系统(二)QPainter详解

若该文为原创文章&#xff0c;未经允许不得转载 原博主博客地址&#xff1a;https://blog.csdn.net/qq21497936 原博主博客导航&#xff1a;https://blog.csdn.net/qq21497936/article/details/102478062 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/…

Qt绘图与信号事件

Qt应用开发的基本模式(面向对象) 继承QDailog gkdialog.h #ifndef GK_DIALOG_H #define GK_DIALOG_H #include <QtWidgets/QDialog>class GKDialog: public QDialog{ public:GKDialog(QWidget *parent0);~GKDialog(); }; #endifgkdialog.cpp #include "gkdialog…

我的Qt作品(9)Qt使用Cairo绘图引擎提升抗锯齿效果【开源】

之前写了一篇文章《Qt使用GDI绘图&#xff08;仅Windows平台&#xff09;》 Qt使用GDI绘图&#xff08;仅Windows平台&#xff09;_libaineu2004的博客-CSDN博客 本篇重点介绍Cairo绘图引擎&#xff0c;Qt自带的QPainter的绘图效率和抗锯齿效果都没有cairo的好。cairo 的目标…

基于Qt的多媒体综合应用程序设计(2)

框架结构图等 1.多媒体综合应用程序设计思路 首先创建一个Qt Widgets Application程序,在头文件中完成类、函数及变量的设置,在程序窗口中添加相应的控件,利用控件的属性和方法分别实现文字处理程序跳转、绘图程序跳转、图像处理程序跳转、音频播放器跳转、视频播放器跳转和…

VS+Qt+鼠标框选后掩膜去除背景

前言&#xff1a; 实际采集的图片背景比较复杂&#xff0c;所以我想着采用框选矩形框掩膜的方式简单粗暴的消除背景对图片有用区域的干扰。网上搜索了一圈尝试了几种不同的方式去框选&#xff0c;最后发现还是Qt的鼠标事件比较容易上手。 效果图&#xff1a; 主要思路&#x…

QT—3D绘图

OpenGL是一个跨平台的、用来渲染3D图形的标准API,Qt对OpenGL提供了强大的支持。Qt4时代的QtOpenGL模块在Qt5中已经不再建议使用,OpenGL相关的类被移到了Qt GUI模块。Qt Widgets模块中的QOpenGLWidget类提供了一个可以渲染OpenGL图形的部件&#xff0c;通过该部件可以轻松地将Op…

【Qt】Qt环境配置与入门案例

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Qt环境配置与入门。 学其所用&#xff0c;用其所学。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次更新不迷路&…

(三)NI采集卡应用学习:在Qt中实现多路数据的同时采集、显示、绘图及存储

1、使用NI MAX创建多路输入的测量任务 本处设置Dev1/ai0:3四路电压输入&#xff0c;采样率均为100&#xff0c;差分连接。 2、代码 // 设置地址 void Widget::on_add_pushButton_clicked() {address_txt QFileDialog::getExistingDirectory(this, tr("Open Directory&q…

linux程序框架设计,《Linux与Qt程序设计》知识框架

本文主要是通过一本书来大致了解Qt开发的框架,不对具体内容做详细分析。 1.首先弄清楚概念:定义->以自己的话理解是什么-> 实现的是什么功能->用在哪些地方 2.前面认识到的知识点的特点-> 代码实现-> 工程代码分析 第一部分 Linux基础知识 第二部分 Qt程序基础…

QT开发应用程序(11)--图形绘制和文本输出

代码演示&#xff1a; #include <QPainter> MyDraw::MyDraw(QWidget *parent) :QWidget(parent) {QLinearGradient linearGradient(0,0,400,400);linearGradient.setColorAt(0.0,Qt::white);linearGradient.setColorAt(0.2,QColor(0,0,0));linearGradient.setColorAt(1.…

chatgpt赋能python:使用Python捕获错误:为您的代码添加可靠性

使用Python捕获错误&#xff1a;为您的代码添加可靠性 在编写Python代码时&#xff0c;错误很常见。您可能会因输入无效参数而收到TypeError&#xff0c;或者因无法连接到数据库而收到ConnectionError。当这些错误发生时&#xff0c;您的代码可能会崩溃或产生不正确的结果。尽…

chatgpt赋能python:关闭Python:如何优雅地退出一个Python程序

关闭Python&#xff1a;如何优雅地退出一个Python程序 Python作为一门开发语言&#xff0c;可以广泛应用于众多领域&#xff0c;如数据科学、人工智能、Web应用开发等等。但有时候&#xff0c;我们需要关闭一个正在运行的Python程序。本文将介绍如何优雅地退出一个Python程序&…

chatgpt赋能python:Python异常捕获存在的问题

Python 异常捕获存在的问题 作为一门广受欢迎、应用广泛的编程语言&#xff0c;Python 在处理异常方面有着比较完善的设计。Python 提供了 try…except…finally 这样的异常处理机制&#xff0c;通过这些机制&#xff0c;开发者可以捕获、处理程序中产生的异常&#xff0c;从而…

yagmail——快速发送邮件

yagmail——快速发送邮件 目标&#xff1a;如何利用python中的yagmail包&#xff0c;快速发送邮件。 python写的一个简单demo脚本代码如下&#xff1a; import yagmail import pandas as pd from loguru import logger# 保存excel文件 contents pd.DataFrame() contents[&quo…

电脑和微信怎么发邮件到别人邮箱,企业邮箱如何发email给别人

邮箱已成为主流的办公软件&#xff0c;除了日常的电脑办公使用以外&#xff0c;你知道还有哪些便捷的方式使用邮箱吗&#xff1f;现在是互联网时代&#xff0c;许多邮箱都研发了手机邮箱配合使用&#xff0c;那TOM企业邮箱有哪些便捷的服务呢&#xff1f;一起看看吧&#xff01…

怎么发送电子邮件到别人邮箱?手把手教你!

在日常生活中&#xff0c;我们在工作学习的时候都离不开电子邮件&#xff0c;无论两人的距离有多远&#xff0c;都是可以通过电子邮件的方式完成信息的即时传递。这样不仅可以传输文字信息&#xff0c;还能传输图片、视频等多种格式的信息。那么&#xff0c;怎么发送电子邮件到…

chatgpt赋能Python-pythonapp自动化

Python App自动化&#xff1a;优化SEO的终极解决方案 随着互联网的发展&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;变得日益重要。对于任何网站或应用程序开发人员来说&#xff0c;SEO应该是一个非常重要的考虑因素。为了帮助开发人员和企业提高其在线可见性&#x…

chatgpt赋能python:Python自动化断言介绍

Python 自动化断言介绍 软件业的各种应用程序不可避免地需要与用户进行交互。测试团队应该确保这些应用程序快速&#xff0c;可靠地响应用户交互&#xff0c;并注重高质量测试的策略和工具。Python自动化断言是一种测试策略&#xff0c;可帮助团队实现高品质和高效率的测试。P…