Reputation: 1134
I have a writen a QML application with shows QImages from an image provider. I wrote a class which inherits from QQuickImageProvider. In QML I have two Image objects.
ColumnLayout {
RowLayout {
Image {
source: "image://backend/1"
width: parent.width/2
}
Image {
source: "image://backend/1"
width: parent.width/2
}
And this is the code of the provider:
QImage qmlProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
QSize req(requestedSize.width()<0?100:requestedSize.width(),
requestedSize.height()<0?100:requestedSize.height());
try {
QImage ret=assign.at(id)->scaled(req,Qt::KeepAspectRatio);
*size=ret.size();
return ret;
}
catch( out_of_range ) {
QImage ret(req,QImage::Format_RGB16);
*size=ret.size();
ret.fill(QColor(qrand()/(RAND_MAX/255),qrand()/(RAND_MAX/255),qrand()/(RAND_MAX/255)).rgba());
return ret;
}
}
It works so far which means that the picture ist shown. Now I want that these pictures are scaled. I want each picture to fill one half of my application window, but it doesn't work. Each width I set is ignored and image provider get every time an invalid requested size.
What do I have to do to achive this?
Upvotes: 0
Views: 923
Reputation: 16691
Example of correct implementation of (caching) image provider:
// videoplayerimageprovider.hpp:
#pragma once
#include <QtQuick>
Q_DECLARE_LOGGING_CATEGORY(videoPlayerImageProviderCategory)
class VideoPlayerImageProvider
: public QQuickImageProvider
{
Q_DECLARE_TR_FUNCTIONS(VideoPlayerImageProvider)
public :
VideoPlayerImageProvider(QQmlEngine * const engine);
QImage requestImage(const QString & id, QSize * size, const QSize & requestedSize) Q_DECL_OVERRIDE;
private :
QQmlEngine * const engine;
QHash< QString, QImage > images;
};
// videoplayerimageprovider.cpp:
#include "videoplayerimageprovider.hpp"
Q_LOGGING_CATEGORY(videoPlayerImageProviderCategory, "videoPlayerImageProvider")
VideoPlayerImageProvider::VideoPlayerImageProvider(QQmlEngine * const engine)
: QQuickImageProvider{QQuickImageProvider::Image}
, engine{engine}
{ ; }
QImage VideoPlayerImageProvider::requestImage(const QString & id, QSize * size, const QSize & requestedSize)
{
const auto selector = QQmlFileSelector::get(engine)->selector();
const auto imagePath = selector->select(QStringLiteral(":/images/videoplayer/%1").arg(id));
#if 0
qDebug().noquote()
<< tr("Selected filepath: %1. Installed file selectors: %2")
.arg(imagePath, selector->allSelectors().join(QStringLiteral(", ")));
#endif
QImage image;
if (!images.contains(imagePath)) {
QImageReader imageReader{imagePath};
image = imageReader.read();
if (image.isNull()) {
qCCritical(videoPlayerImageProviderCategory).noquote()
<< tr("Unable to read image from %1: %3 (%2)")
.arg(imagePath, imageReader.errorString()).arg(imageReader.error());
} else {
images.insert(imagePath, image);
}
} else {
image = images[imagePath];
}
if (size) { // should be assigned size of original image
*size = image.size();
}
if (requestedSize.isEmpty() || (image.size() == requestedSize)) { // avoid returning invalid QImage
return image;
}
return image.scaled(requestedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
If you don't need file selector for your image file, then you may remove engine
and selector
.
I note, that during loading of the scene there are not a single call to the same id
. First of them are with invalid sizes.
Upvotes: 1
Reputation: 12854
Since your item's size managed by Layout
you should set Layout's preferred size instead of item's size:
RowLayout {
anchors.fill: parent
Image {
source: "image://backend/1"
Layout.preferredHeight: parent.height
Layout.preferredWidth: parent.width / 2
}
Image {
source: "image://backend/1"
Layout.preferredHeight: parent.height
Layout.preferredWidth: parent.width / 2
}
}
More info could be found here
Upvotes: 3