糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > Qt线程处理图片(绘画 压缩 保存 压缩图片的读取)

Qt线程处理图片(绘画 压缩 保存 压缩图片的读取)

时间:2020-11-12 05:31:24

相关推荐

Qt线程处理图片(绘画 压缩 保存 压缩图片的读取)

文章目录

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: 3QPainter::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.

具体的效果请看下图:

下面看下enum Qt::TransformationMode:

This enum type defines whether image transformations (e.g., scaling) should be smooth or not.

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线程处理图片(绘画 压缩 保存 压缩图片的读取)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。