文章目录

  • 1、绘画
  • 2、压缩
  • 3、保存
  • 4、读取

1、绘画

Qt提供了QPainter类来对图片进行绘画,提供了QImage类对图片进行压缩和保存。

如果图片显示在UI上,我们就能够很正常的使用QPaintEvent函数对图片进行绘画的操作,但有时候,我们需要批量处理图片,并且在处理这些图片的过程中图片是不可现的。那么我们就需要开启线程去处理这些图片。

前段时间在线程处理图片时踩了一些坑,将这些坑分享一下。

刚开始,按照惯性思维,画图只能在QWidget类中的QPaintEvent中实现,因此在设置画布时继承了QLabel类,毕竟在以前的通过界面画图的时候就是这么干的。。

class PictureHelper : public QLabel
{Q_OBJECTQ_PROPERTY(PictureType pictureType READ pictureType WRITE setpictureType DESIGNABLE true)
public:static PictureHelper* Instance();        //获取单实例static void            Release();        //释放单实例;void setpictureType(PictureType nType);PictureType pictureType() const;
protected:void paintEvent(QPaintEvent* event) override;
private:explicit PictureHelper(QObject *parent = Q_NULLPTR);~PictureHelper();void drawPoint(QPainter* pPainter);void drawText(QPainter* pPainter, QRect dRect);
private:static PictureHelper* m_pSelf;QImage m_Image;QPoint  m_hot;QColor m_pointColor;
};
void PictureHelper::paintEvent(QPaintEvent *)
{QPainter dPainter(this);QPen dCapture = QPen(m_pointColor, 1, Qt::SolidLine, Qt::FlatCap);dPainter.setPen(dCapture);dPainter.drawPixmap(0, 0, m_Image);    //画布drawPoint(&dPainter);  //画点}

​ 当实现整个工程之后发现,函数 paintEvent(QPaintEvent* event) 根本没有调用,然后开始疯狂的各种找原因,找原因的过程中也了解到了很多知识。

​ 函数 paintEvent(QPaintEvent* event) 调用的时机:

  • 在窗口部件第一次显示时,系统会自动产生一个绘图事件,从而强制绘制这个窗口部件;
  • 重新调整窗口部件的大小时,系统也会产生一个绘制事件;
  • 窗口部件隐藏部分重新显示时,隐藏的部分会被重新产生一个绘制事件;
  • 手动调用QWidget::update() 或QWidget::repaint()强制产生一个重绘事件;两者的区别是:repaint会产生一个即时的事件,而update是在Qt下一次处理事件时才产生事件,因此update可以防窗口抖动。

按照上述方法,需要画图时手动调用了update甚至repaint都不能使paintEvent函数调用,紧接着去找paintEvent函数不调用的原因。
​ 函数 paintEvent(QPaintEvent* event) 不调用的原因有两种:

  • update 是被禁用的
  • 绘画的widget被隐藏了
  • 设置了QWidget 的 setAttribute(Qt::WA_TranslucentBackground,true); 属性,会引起很多刷新问题

​ 逐一排除原因,可以保证update函数没有被禁用,也没有设置 Qt::WA_TranslucentBackground 属性,那么就只剩下第二条了,可是第二条与我们的目标是相冲的,这肯定是不现实的。

​ 到这时候,突然发现paintEvent函数不能调用,那么是不是可以试一下不用这个函数呢?

​ 然后修改了函数:

void PictureHelper::paint()
{QPainter dPainter(this);QPen dCapture = QPen(m_pointColor, 1, Qt::SolidLine, Qt::FlatCap);dPainter.setPen(dCapture);dPainter.drawPixmap(0, 0, m_Image);    //画布drawPoint(&dPainter);  //画点
}

在需要画图的时候手动调用pait()函数进行画图,是不是就能够保证画图正常运行了。悲催的是修改之后有新问题:

QPainter::begin: Paint device returned engine == 0, type: 3
QPainter::save: Painter not activeqpainter setpen painter not active

报这个错的位置是 QPainter dPainter(this); 然后查找了下这个报错的原因:

没有在QPainterEvent中绘图,而是在其他处(如果想在其他位置使用QPainter,建议使用双缓冲机制,也就是用paintEvent函数)

​ 得,又回到原点了。

查找QPainter的源码发现

d->engine = pd->paintEngine();
​if (!d->engine) {qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType());return false;
}

查看Qt文档

QPaintEngine *QPainter::paintEngine() constReturns the paint engine that the painter is currently operating on if the painter is active; otherwise 0.See also isActive().
bool QPainter::isActive() constReturns true if begin() has been called and end() has not yet been called; otherwise returns false.See also begin() and QPaintDevice::paintingActive().

追踪pd->devType():

int QImage::devType() const
{return QInternal::Image;
}
class Q_CORE_EXPORT QInternal {public:enum PaintDeviceFlags {UnknownDevice = 0x00,Widget        = 0x01,Pixmap        = 0x02,Image         = 0x03,Printer       = 0x04,Picture       = 0x05,Pbuffer       = 0x06,    // GL pbufferFramebufferObject = 0x07, // GL framebuffer objectCustomRaster  = 0x08,MacQuartz     = 0x09,PaintBuffer   = 0x0a,OpenGL        = 0x0b};};

至此,说明painter是为激活的,后面的type表明了绘画的基础类型,没什么具体的含义。
到现在,感觉已经无力回天了,那么,就需要换个方向重新走走看。所以回到了最初的设定,查看了QPainter的构造:

QPainter::QPainter(QPaintDevice *device)
Constructs a painter that begins painting the paint device immediately.This constructor is convenient for short-lived painters, e.g. in a QWidget::paintEvent() and should be used only once. The constructor calls begin() for you and the QPainter destructor automatically calls end().Here's an example using begin() and end():

再看看QPaintDevice类:

Header:#include <QPaintDevice> qmake:QT += guiInherited By:QImage, QOpenGLPaintDevice, QPagedPaintDevice, QPaintDeviceWindow, QPicture, and QPixmapList of all members, including inherited members

QPaintDevice继承了QImage,那么我们为什么需要多加一步QPainter dPainter(this);呢?
马上修改自定义类,取消继承QLabel,然后画图函数通过手动调用。

class PictureHelper
{Q_OBJECT...
};
void PictureHelper::paint()
{QPainter dPainter(m_Image);QPen dCapture = QPen(m_pointColor, 1, Qt::SolidLine, Qt::FlatCap);dPainter.setPen(dCapture);drawPoint(&dPainter);  //画点
}

发现图片能够被正常绘画。

**总结一下:**出现问题时,我们总会在自己的舒适圈中找问题的答案,经常是撞得头破血流,如果跳出舒适圈,就会发现答案可能就在离你3米远的臭水沟。。。

2、压缩

下面说下图片的压缩,大多都知道,Qt提供了图片的压缩接口,使用QImage或者QPixmap的scaled函数能够实现图片的压缩,首先通过Qt文档查看下scaled这个函数:

QImage QImage::scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio, Qt::TransformationMode transformMode = Qt::FastTransformation) constThis is an overloaded function.
Returns a copy of the image scaled to a rectangle with the given width and height according to the given aspectRatioMode and transformMode.
If either the width or the height is zero or negative, this function returns a null image.

下面看下enum Qt::AspectRatioMode:
This enum type defines what happens to the aspect ratio when scaling an rectangle.

type value infomation infomation
Qt::IgnoreAspectRatio 0 The size is scaled freely. The aspect ratio is not preserved. 大小自由缩放,长宽比不保留
Qt::KeepAspectRatio 1 The size is scaled to a rectangle as large as possible inside a given rectangle, preserving the aspect ratio. 在给定的矩形内,大小被缩放到一个尽可能大的矩形,保持高宽比。
Qt::KeepAspectRatioByExpanding 2 The size is scaled to a rectangle as small as possible outside a given rectangle, preserving the aspect ratio. 大小被缩放到一个矩形,在给定的矩形外尽可能小,保持高宽比。

具体的效果请看下图:

下面看下enum Qt::TransformationMode:
This enum type defines whether image transformations (e.g., scaling) should be smooth or not.

type value infomation infomation
Qt::FastTransformation 0 The transformation is performed quickly, with no smoothing. 快速压缩,图片质量不高
Qt::SmoothTransformation 1 The resulting image is transformed using bilinear filtering. 平滑压缩,图片质量较高

3、保存

通过上面的函数,按照一定的大小压缩,可以压缩图片,但压缩比例可能会不尽人意。(亲测1.25M的bmp图片按照原先长宽的1/2压缩之后的大小为960K)
因此我们还需要QImage类的save函数进行二次压缩。

bool QImage::save(const QString &fileName, const char *format = Q_NULLPTR, int quality = -1) const

最后一个参数quality的取值决定保存的图片的大小,取值范围[0-100],值越小,压缩比例越大。保存图片的格式也会影响压缩的比例。

4、读取

下面再看下压缩之后图片的读取,以1280 * 1024的图片为例,压缩之后的大小640 * 512,文件大小114K。

假设我们使用QImage类将图片读到内存:

QImage image(filepath);uchar* ba = image.bits();int size = image.byteCount();

得到的结果为:size = 640 * 512 = 327680,但其实图片的文件大小为:114 * 1024 = 116736,并且重新将ba中的数据保存为图片时,图片是损坏的。

因此压缩之后图片是有部分失真的,我们不能按照正常的图片读取,应该按照文件二进制流的形式读取。

QFile file(pic.path);
if (!file.open(QIODevice::ReadOnly))
{return;
}
char *pBuff = Q_NULLPTR;
do
{pBuff = new char[pic.size + 1];
} while (pBuff == Q_NULLPTR);QDataStream in(&file);
int buffSize = in.readRawData(pBuff, pic.size);... //to do somethingif(pBuff != Q_NULLPTR)
{delete[] pBuff;pBuff = Q_NULLPTR;
}

Qt线程处理图片(绘画、压缩、保存、压缩图片的读取)相关推荐

  1. java 保存图片_java实现保存文件图片以及读取

    // 文件转换为字节数组 private byte[] getByteFromFile(File file) { byte[] be = null; try { if (file == null) { ...

  2. 【转】c# 图片压缩 (非图片大小变化)----使得显示效果差点,但是图片占用空间需要变小

    转自:http://bbs.csdn.net/topics/270043488 c# 图片压缩 (非图片大小变化)----使得显示效果差点,但是图片占用空间需要变小. [问题点数:60分,结帖人lin ...

  3. java jpeg压缩解码_图片压缩(iOS)

    场景很简单,上传图片前压缩图片,节省流量和发图时间.最近看了看 iOS 的静态图片压缩,这里记个笔记.本人之前没学过 iOS 和 Swift,本文是一篇入门文章,描述不到位之处请大家多多批评斧正. ̄ω ...

  4. 使用Python快速压缩目录中图片

    使用Python快速压缩目录中图片 脚本语言 #coding:utf-8 import Image import os import logging import sqlite3 from PIL i ...

  5. Python模块学习 - 用tinify模块压缩和优化图片

    Python模块学习 - 用tinify模块压缩和优化图片 tinify模块 功能描述:TinyPNG和TinyJPG网站提供了压缩和优化.png和.jpg格式图片的功能.虽然可以很轻松地使用网页版进 ...

  6. 如何转换并压缩png格式图片

    我们常用的图片格式大概就是jpg.png.bmp这些,但是可能用途不一样需要的格式就不一样,那对于常用的图片格式怎么转换呢?如何转换并压缩png格式图片呢? 1.对于常用的图片格式,比如jpg格式以及 ...

  7. Golang PDF转图片 拼接长图 压缩PDF及图片 输出JPEG

    PDF转PNG主要是用到Xpdf的库 根据操作系统选择命令行工具下载- Download the Xpdf command line tools: 在bin64目录下可以找到PDFTOPNG可执行文件 ...

  8. C#图片截取压缩(百分比压缩/大小压缩)实现代码

    原文链接:http://www.jb51.net/article/33622.htm   脚本之家 为了娱乐写了个截取图片和压缩图片你的小工具:按照百分比压缩/制定大小压缩等等,感兴趣的朋友可以了解下 ...

  9. [Python模块学习]用tinify模块压缩和优化图片

    tinify模块 by: uizuizui 功能描述:TinyPNG和TinyJPG网站提供了压缩和优化.png和.jpg格式图片的功能.虽然可以很轻松地使用网页版进行操作,但是在搭建个人博客的时候, ...

最新文章

  1. python序列类型-什么是序列,Python序列详解(包括序列类型和常用操作)
  2. python安装教程mac-Mac安装python3的方法步骤
  3. ora-01740: 标识符中缺失双引号_sql语句中单引号的使用
  4. EA出品的java射击类游戏,盘点五款射击类RPG游戏:你喜欢这类型游戏吗?
  5. python最新面试题_2018年最新Python面试题及答案
  6. 前端学习(489):文本格式化
  7. 异常检测算法之LOF
  8. 52 SD配置-定价配置-定义定价程序确定
  9. ezmorph-1.0.6.jar的作用
  10. 一文快速掌握前端开发必备技能
  11. mysql 通过ssh通道安全连接数据库
  12. 多媒体计算机与一般计算机,计算机和多媒体计算机的区别
  13. Unity3D Terrain 变成粉色(紫色/洋红色)解决方案!
  14. 互联网和大数据是什么意思_互联网包括大数据吗 大数据与互联网的关系是什么...
  15. 通过IMAP方式迁移U-Mail邮件到Exchange 2013之Exchange 2007 系统搭建!
  16. VisualEffectGraph概述
  17. c语言数组文曲星猜数游戏编程,第7章 数组-8数组的其他应用——文曲星猜数游戏.pdf...
  18. linux 中断子系统
  19. MacOS上BeyondCompare无限试用的方法
  20. Java+MySQL基于ssm的物理竞赛管理系统

热门文章

  1. 目标主机DNS服务允许递归查询漏铜处理
  2. Python中Json对象处理的jsonpath-rw
  3. 也谈用户体验这件小事
  4. python - 用input()接收同行输入的多个参数
  5. 在字节跳动工作是怎样的?
  6. 经典游戏,计算24点,简单实现
  7. monkeyrunner2
  8. excel批量删除前后缀
  9. MathType 7.4下载链接
  10. 重庆师范大学计算机技术排名,2021世界一流学科排行榜,重庆62个入榜,两所部属高校占三分之二...