Article directory
background
In the previous article 【Qt】Pictures in png and jpg formats (1) - Nuggets (juejin.cn), the author explained jpg
and the two formats, but there is still no explanation about the problem of Qt opening and changing the suffix file name of the picture png
. To explore why Qt cannot open the picture file with the suffix changed, this still needs to be located in many ways. As mentioned above, QLabel
the pictures of are set in three ways.
Scene recurrence
Here is how to reproduce the scene where the setting is unsuccessful:
- Find a
.jpg
picture of and modify the suffix, that is, the file attribute to.png
- Use any of the following three methods to set it
QLabel
as a picture.The Qt version is
5.9
The compiler tried
MSVC
andmingw
did not work.
QLabel
Set picture method
- Image via
QPixmap
settingsQLabel
QPixmap img(":/Win11.png");
ui->label->setPixmap(img);
ui->label->setScaledContents(true);
- Image via
QImage
settingsQLabel
QImage img;
img.load(":/Win11.png");
ui->label->setPixmap(QPixmap::fromImage(img))
- via
QLabel.setStyleSheet()
pictures
ui->label->setStyleSheet(QString("QLabel{"
"border-image:url(:/Win11.png) 4 4 4 4 stretch stretch;"
"}"));
Of course, there is a fourth method, by QSvgRenderer
setting:
#include <QSvgRenderer>
QSvgRenderer svgRender(QString(":/Win11.svg"));
QPixmap pixmap(20,20);
QPainter painter(&pixmap);
svgRender.render(&painter);
ui->label->setPixmap(pixmap);
The scene is described, you can try it yourself
operate
The next step is to check how the source code in several implementation methods is set up. Let’s guess the conclusion first.
That is
png
,jpg
the algorithm of the format is different from that of the format, and the reading algorithm caused by the format problem is inconsistent. Therefore, the algorithm for reading pictures implemented inside Qt can only read the algorithm according to the algorithm corresponding to the suffix of the picture file, and the algorithm is also based on such a logicsetStyleSheet
. , so the three reading methods are all unsuccessful.Regarding the problem of the image algorithm, it was also slightly mentioned in the previous article. We don’t do in-depth research on this, we only need to know that
jpg
andpng
are not the same algorithm, and they are not universal.
preliminary verification
Because the author has tried all the above steps, we don't worry about which method to use during the verification process, and just look at the results.
- Then how to verify it, we still look at the code, this time we remove the suffix name of the picture in the Qt
qrc
file, and read the picture without the suffix attribute to see if it can be read successfully.
operation result: QLabel
read successfully
As shown in the figure above, it is still successful qrc
to remove the suffix of the picture in the file and set it to read the picture .QLabel
- Let's go ahead and modify the image suffix in the code
png
to see if it can be read successfully this time.
operation result: read failed
As shown in the figure above, after adding the suffix, it still fails to display.
The results of the preliminary verification
This basically says:
Inside
Qt
there is a high probability that the file suffix is used to determine which image reading algorithm to call. In other words, when you artificially modify thepng->jpg
,Qt
there will be a problem of failing to set the picture in . At present, it seems that if there is no problem with the code, but the picture is not displayed after setting, the best way is to remove the suffix of the picture and let the user decideQt
which algorithm to call to read the picture.
Source code analysis
Here is a description of Qt
the image I\O
, you can see the documentation on image file reading and writing in the Qt help manual.
In the source code part, we only need Qt
to look at two classes, and I personally feel that we can know just by looking at one. The algorithm for identifying pictures between the two should be the same. The interface including setStyleSheet()
setting pictures in the interface should be consistent.
- QPixmap depends on the drawing engine of the platform where it is located, so some effects such as anti-aliasing may have different display effects on different platforms. QImage uses Qt's own drawing engine to have the same display effect on different platforms.
- The current Qt will store QPixmap in graphics memory, and QImage is stored on the client side, which is independent of hardware. On X11, Mac and Symbian platforms, QPixmap is stored on the server side, while QImage is stored on the client side. On the Windows platform, both QPixmap and QImage are stored on the client side without using any GDI resources.
- Since QImage is independent of hardware and is also a kind of QPaintDevice, we can draw it in another thread without processing it in the GUI thread. Using this method can greatly improve the UI response speed.
QPixmap
Query the source file of QPixmap, which is generally stored in Qt
the installation directory ${安装目录}\5.9.9\Src\qtbase\src\gui\image
, and the file name isqpixmap.h qpixmap.cpp
First look at QPixmap
the method of reading image files in the constructor of .
-
QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)
source codeQPixmap::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是如何实现的 }
-
load()
Function source code/*! Loads a pixmap from the file with the given \a fileName. Returns true if the pixmap was successfully loaded; otherwise invalidates the pixmap and returns \c false. The loader attempts to read the pixmap using the specified \a format. 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 one of the application's embedded resources. See the \l{resources.html}{Resource System} overview for details on how to embed pixmaps and other resource files in the application's executable. If the data needs to be modified to fit in a lower-resolution result (e.g. converting from 32-bit to 8-bit), use the \a flags to control the conversion. Note that QPixmaps are automatically added to the QPixmapCache when loaded from a file; the key used is internal and can not be acquired. \sa loadFromData(), {QPixmap#Reading and Writing Image Files}{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 extensions if (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(); else data.reset(); } return false; }
See if the source code is much clearer. In load()
the implementation of the function: ① judge whether the file name is empty; ② when it is not empty, the first thing is to read the suffix of the file. Here we can check in detail what kind of operation is performed in the second type of if
judgment .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
The above HexString<type>
is the ASCII array form, 16进制
the array. What is the operation in the code above %
if not remainder. If it is asking for a surplus, QString
will it report an error? ? ?
In the end, check whether the current file is a picture by setting and checking the flag in QPixmap
the built-in variable data
of , if it is a picture, the current QPixmap
pointer will be modified, and if not, it will returnfalse。
if (QPixmapCache::find(key, this))
return true;
Here is a QPixmapCache
demo to add:
/*在缓存中查找与给定键关联的缓存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);
The final judgment of whether the current file is a picture should be data->fromFile()
realized in .
The definition of this data
:
QExplicitlySharedDataPointer<QPlatformPixmap> data;
In the next article, we will continue to study the code for reading pictures inside Qt.
Replenish
Add
load()
It is necessary to read the function.
QPixmap::loadFromData()
source code./*! \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 \a data. 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 \a format. 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-resolution result (e.g. converting from 32-bit to 8-bit), use the \a flags to control the conversion. \sa load(), {QPixmap#Reading and Writing Image Files}{Reading and Writing 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; }