文章目录

  • 背景
    • 场景复现
    • `QLabel` 设置图片方法
  • 操作
    • 初步验证
    • 初步验证的结果
  • 源码分析
    • QPixmap
  • 补充

背景

在上篇文章【Qt】png和jpg格式的图片(一) - 掘金 (juejin.cn)中笔者就 jpgpng 两种格式进行了说明,但是关于 Qt 打开改后缀文件名之后图片的问题依然没有说明。要探究Qt为何不能打开改了后缀的图片文件,这个还是得从多方面去定位。前文说了,通过三种方式设置了 QLabel 的图片。

场景复现

这里还是先说明一下设置不成功的场景如何复现:

  1. 找一张 .jpg 的图片,修改后缀也就是文件属性为 .png
  2. 通过以下三种方式中的任意一种去设置 QLabel 为图片。

Qt 版本是 5.9

编译器试了 MSVCmingw 都不好使。

QLabel 设置图片方法

  1. 通过 QPixmap设置 QLabel 的图片
QPixmap img(":/Win11.png");ui->label->setPixmap(img);
ui->label->setScaledContents(true);
  1. 通过 QImage 设置 QLabel 的图片
QImage img;
img.load(":/Win11.png");
ui->label->setPixmap(QPixmap::fromImage(img))
  1. 通过 QLabel.setStyleSheet() 的图片
ui->label->setStyleSheet(QString("QLabel{""border-image:url(:/Win11.png) 4 4 4 4 stretch stretch;""}"));

当然还有第四种方法,通过 QSvgRenderer 设置:

#include <QSvgRenderer>QSvgRenderer svgRender(QString(":/Win11.svg"));
QPixmap pixmap(20,20);
QPainter painter(&pixmap);
svgRender.render(&painter);
ui->label->setPixmap(pixmap);

场景是描述完了,可以动手尝试了

操作

接着就是去查看几种实现方式中的源码是如何是设置的了,这里先猜一下结论

就是 pngjpg 格式的算法不同,格式问题导致的读取的算法不一致,因此Qt内部实现的读取图片的算法只能根据图片文件的后缀所对应的算法去读取算法,而 setStyleSheet 算法也是基于这么一个逻辑,因此三种读取方式都不成功。

关于图片算法的问题,在上篇文章中也略微提到,这个我们不深做研究,只需知道 jpgpng 不是同一种算法,也不通用即可。

初步验证

因为上述几种步骤笔者都做过尝试,因此在验证过程中我们不纠结于使用哪一种方式,直接看结果。

  1. 那如何去验证呢,我们还是看代码,这次我们在 Qt 的 qrc 文件中,去掉图片的后缀名,不带后缀属性去读取图片看看其是否可以读取成功。

运行结果: QLabel 读取成功

如上图所示,在 qrc 文件中去掉图片的后缀,读取图片设置到 QLabel 依然是成功的。

  1. 我们接着操作,在代码中修改图片后缀为 png,看看这次能不能读取成功。

运行结果:读取失败

如上图所示,添加后缀后反而还展示不成功了。

初步验证的结果

这就基本上说明了:

Qt 的内部有很大的几率是通过文件的后缀去判断调用哪个图片读取算法的。也就是说,当你人为的修改了 png->jpg 时,在 Qt 中就会出现设置图片失败的问题。目前看来,在代码没有问题,但是图片设置后不显示的情况下,最好的方式是,就是去掉图片的后缀,让 Qt 自己去判断调用哪个算法读取图片。

源码分析

这里有关于 Qt 图片 I\O 的描述可以看看Qt帮助手册中关于图像文件读写的文档。

源码部分,我们只需查看两个Qt 类就行,个人感觉看一个就知道了。两者识别图片的算法应该是一致的。包括 setStyleSheet()接口中设置图片的接口应该都是一致的。

  1. QPixmap依赖于所在的平台的绘图引擎,故例如反锯齿等一些效果在不同的平台上可能会有不同的显示效果,QImage使用Qt自身的绘图引擎,可在不同平台上具有相同的显示效果
  2. 目前的Qt会把QPixmap都存储在graphics memory中,QImage是存储在客户端的,是独立于硬件的。在X11, Mac 以及 Symbian平台上,QPixmap 是存储在服务器端,而QImage则是存储在客户端,在Windows平台上,QPixmap和QImage都是存储在客户端,并不使用任何的GDI资源。
  3. 由于QImage是独立于硬件的,也是一种QPaintDevice,因此我们可以在另一个线程中对其进行绘制,而不需要在GUI线程中处理,使用这一方式可以很大幅度提高UI响应速度。

QPixmap

查询到QPixmap的源文件,一般存放在 Qt 安装目录下 ${安装目录}\5.9.9\Src\qtbase\src\gui\image,文件名为 qpixmap.h qpixmap.cpp

先看一下 QPixmap 的 构造函数中读取图片文件的方法。

  1. QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)的源码

    QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags): QPaintDevice(){doInit(0, 0, QPlatformPixmap::PixmapType);if (!qt_pixmap_thread_test())return;load(fileName, format, flags); // 这里看到调用了load()的接口,接着查看load是如何实现的}
    
  2. load()函数源码

    /*!Loads a pixmap from the file with the given \a fileName. Returnstrue if the pixmap was successfully loaded; otherwise invalidatesthe pixmap and returns \c false.The loader attempts to read the pixmap using the specified \aformat. If the \a format is not specified (which is the default),the loader probes the file for a header to guess the file format.The file name can either refer to an actual file on disk or to oneof the application's embedded resources. See the\l{resources.html}{Resource System} overview for details on how toembed pixmaps and other resource files in the application'sexecutable.If the data needs to be modified to fit in a lower-resolutionresult (e.g. converting from 32-bit to 8-bit), use the \a flags tocontrol the conversion.Note that QPixmaps are automatically added to the QPixmapCachewhen loaded from a file; the key used is internal and can notbe acquired.\sa loadFromData(), {QPixmap#Reading and Writing ImageFiles}{Reading and Writing Image Files}
    *//* 翻译过来就是
    从给定的文件名的文件中加载一个像素图。如果pixmap成功加载,则为True;否则返回无效pixmap并返回\c false。加载器尝试使用指定的\a读取pixmap格式。如果没有指定\a格式(这是默认的),
    加载器探测文件的头,以猜测文件的格式。文件名可以指向磁盘上的实际文件,也可以指向磁盘上的实际文件应用程序的嵌入式资源。看到\l{resources.html}{Resource System}概述如何嵌入pixmap和其他资源文件在应用程序的可执行文件。如果数据需要修改以适应低分辨率结果(例如从32位转换到8位),使用\a标志来控制转换。注意,qpixmap会自动添加到QPixmapCache中
    当从文件加载时;使用的密钥是内部的,不能被收购。\sa loadFromData(), {QPixmap#读写图像读写图像文件}
    */// loadFromdata的源码我也补充到了文末
    bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
    {if (!fileName.isEmpty()) {QFileInfo info(fileName);// Note: If no extension is provided, we try to match the// file against known plugin extensionsif (info.completeSuffix().isEmpty() || info.exists()) {QString key = QLatin1String("qt_pixmap")% info.absoluteFilePath()% HexString<uint>(info.lastModified().toSecsSinceEpoch())% HexString<quint64>(info.size())% HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType);if (QPixmapCache::find(key, this))return true;data = QPlatformPixmap::create(0, 0, data ? data->pixelType() : QPlatformPixmap::PixmapType);if (data->fromFile(fileName, format, flags)) {QPixmapCache::insert(key, *this);return true;}}}if (!isNull()) {if (isQBitmap())*this = QBitmap();elsedata.reset();}return false;
    }
    

一看源码是不是就清晰多了。在 load()函数的实现中:①判断文件名是不是为空;②不为空时,首先就是读取文件的后缀。这里我们可以细细查一下第二个 if 判断中 QString 类型的 key 到底是进行了一个什么操作。

QString key = QLatin1String("qt_pixmap")% info.absoluteFilePath() // 返回文件名的绝对路径% HexString<uint>(info.lastModified().toSecsSinceEpoch()) // 返回文件最后一次修改的日期和时间% HexString<quint64>(info.size()) // 返回文件的大小% HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType); // 这里的data是成员变量,就是说如果设置了data的pixelType的值就读取,没设置的话就是默认值 QPlatformPixmap::PixmapType

上述的 HexString<type>就是 ASCII 的数组形式,16进制 的数组。上述代码中的 % 操作如果不是求余的话会是什么呢。如果是求余,QString 会报错才是呀???

这里最终还是通过设置和查看 QPixmap 的内置变量 data 中的标志去查看当前文件是不是图片,如果是图片,则会修改当前的 QPixmap 指针,如果不是就会返回 false

if (QPixmapCache::find(key, this))return true;

这里补充一个QPixmapCache的demo:

/*在缓存中查找与给定键关联的缓存pixmap。如果找到了pixmap,函数将pixmap设置为该pixmap并返回true;否则,它将保留pixmap并返回false。*/
QPixmap pm;
if (!QPixmapCache::find("my_big_image", &pm)) {pm.load("bigimage.png");QPixmapCache::insert("my_big_image", pm);
}
painter->drawPixmap(0, 0, pm);

最终判断当前文件是不是图片应该是在 data->fromFile() 中实现的。

这个data的定义:

QExplicitlySharedDataPointer<QPlatformPixmap> data;

下一篇,我们接着研究Qt内部读取图片的代码。

补充

补充一下

load()的函数有必要读一下。

QPixmap::loadFromData() 的源码。

/*!\fn bool QPixmap::loadFromData(const uchar *data, uint len, const char *format, Qt::ImageConversionFlags flags)Loads a pixmap from the \a len first bytes of the given binary \adata.  Returns \c true if the pixmap was loaded successfully;otherwise invalidates the pixmap and returns \c false.The loader attempts to read the pixmap using the specified \aformat. If the \a format is not specified (which is the default),the loader probes the file for a header to guess the file format.If the data needs to be modified to fit in a lower-resolutionresult (e.g. converting from 32-bit to 8-bit), use the \a flags tocontrol the conversion.\sa load(), {QPixmap#Reading and Writing Image Files}{Reading andWriting Image Files}
*/bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)
{if (len == 0 || buf == 0) {data.reset();return false;}data = QPlatformPixmap::create(0, 0, QPlatformPixmap::PixmapType);if (data->fromData(buf, len, format, flags))return true;data.reset();return false;
}

【Qt】png和jpg格式的图片(二)相关推荐

  1. QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(二)

    这篇承接上篇,主要记录的是代码,关于mp3ID3V2的简要介绍可以跳转到上一篇: QT 读取mp3ID3V2 获取mp3专辑图片.专辑名称.标题.作者(一) 前提说明:没有使用任何的外部库,纯代码实现 ...

  2. BMP格式知识之二:16位,24位,32位的BMP图片算法是如何运算的

    BMP格式知识之二:16位,24位,32位的BMP图片算法是如何运算的 原文:http://blog.csdn.net/qq445803843/article/details/46476433 这段代 ...

  3. QT显示Raw格式的图片

    在使用QT进行图像显示时,对于如jpg和png等图片格式的实现可以借助QT很方便的就实现,但是使用QT显示Raw数据时,则相对麻烦一点,主要的原因在于Raw格式的图片不包含关于图片的高度.宽度和数据类 ...

  4. 【Qt】png和jpg格式的图片(一)

    文章目录 `jpg` 和 `png` 图片格式 `jpg`格式 `png`格式 区别:`png` 和 `jpg` jpg 和 png 图片格式 在写这篇文章之前,笔者在编写一个简单的 Qt 程序时遇到 ...

  5. 【Qt】2D绘图之绘制图像(二)

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 绘制QImage图像 04. 绘制QPixmap图像 05. 绘制QPicture图像 06. 综合对比 07. 附录 0 ...

  6. 搭建Android+QT+OpenCV环境,实现“单色图片着色”效果

    OpenCV是我们大家非常熟悉的图像处理开源类库:在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档.这样我们就能够非常方便地利用OpenCV实现一些属于DeepLe ...

  7. QT 读取mp3ID3V2 获取mp3专辑图片、专辑名称、标题、作者(一)

    ID3V2是目前主流的mp3标签格式,特别是做为车载音乐播放器等一些无法连接到互联网的应用场景上,我们无法通过网络获得歌曲的信息,因此,读取ID3V2或者ID3V1标签就是一个很好的解决方案,这里主要 ...

  8. base64格式的图片数据如何转成图片

    base64格式的图片数据如何转成图片 一.总结 一句话总结:不仅要去掉前面的格式串,还需要base64_decode()解码才行. 1 // $base_img是获取到前端传递的值 2 $base_ ...

  9. android 图片二维码识别和保存(二)

    续上一篇,开发图片二维码识别功能后,我们对功能进行性能分析内存占用显著提高了,不使用该功能内存占用大约是147M,使用这个功能多次以后,高达203M. 因此对功能进行研究,发现每次生成的图片没有即时的 ...

最新文章

  1. mac通过tree源码编译安装tree
  2. 万字长文!DeepMind科学家总结2021年的15个高能研究
  3. PHP管理员登陆、验证与添加(前端验证)
  4. Vue2.0环境安装
  5. VTK:PolyData之TransformFilter
  6. MATLAB常用算法与应用实例分享来袭!
  7. 英语中的开音节和闭音节
  8. linux for循环套for循环格式_4.20 for循环 break、continue关键字
  9. 《JavaScript设计模式 张》整理
  10. 两种数据仓库分层实例
  11. 话里话外:从信息系统两大特性理解信息化的实施难度
  12. confluence 4.2 升级至 6.10.x 记录
  13. HTML5求自动在闪,HTML5 重复而不停闪烁的团状物
  14. 83.删除排序链表中的重复元素(力扣leetcode) 博主可答疑该问题
  15. C++跨平台开发——SOCKET网络编程中实现客户端对聊
  16. 免费搭建私人云盘+内网渗透+不限速无限存储
  17. 用jq实现轮播图片的效果
  18. mysql之mysql.sock文件
  19. SAR,SHR,SAL,SHL区别
  20. 关于选牛和选马的问题

热门文章

  1. 【MySQL连表多对多】操作举例之【学生选课】
  2. 电脑怎么截图?怎么录屏?8种常见的截图和录屏方法!
  3. kubelet 认证
  4. Python scikit-learn特征提取讲解
  5. QGIS.exe图标不显示问题
  6. P8002 Alice and Bob are playing a Normal Game
  7. 【AAAI 2020】全部接受论文列表(四)
  8. leetcode系列-394. 字符串解码
  9. error: array type ‘char [x]‘ is not assignable
  10. 如何应用设计模式设计你的足球引擎(第三、四部分)完