Display QImage in QML

Currently I am developing an application that captures images and should display them via a Qt’s QML frontend. I thought that would be an easy task because I am familiar with QML and collected my own experiences in the last months.

So I got the QImages continously from my capturing thread. I updated my QImage member variable and wanted to display it via a QML image structure via the source tag and recognised this does not work….After a research session an trying out different ways to solve this problem I want to share my knowledge with all of you out there that do have the same problem. The order of the solutions is also the ranking, beginning from the best one (for me):

1. QPainter
Here I use a user defined QML item called ImageItem and paint the content of the image by a QPainter. For me it worked best but you have to take care by yourself about the image output.

// ImageItem.h
#ifndef IMAGEITEM_H
#define IMAGEITEM_H
#include <QQuickPaintedItem>
#include <QQuickItem>
#include <QPainter>
#include <QImage>

class ImageItem : public QQuickPaintedItem
{
Q_OBJECT
    Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
    ImageItem(QQuickItem *parent = nullptr);
    Q_INVOKABLE void setImage(const QImage &image);
    void paint(QPainter *painter);
    QImage image() const;
signals:
    void imageChanged();
private:
    QImage current_image;
};
#endif // IMAGEITEM_H
// ImageItem.cpp
#include "ImageItem.h"

ImageItem::ImageItem(QQuickItem *parent) : QQuickPaintedItem(parent)
{    
this->current_image = QImage(":/images/no_image.png");
}

void ImageItem::paint(QPainter *painter)
{
    QRectF bounding_rect = boundingRect();
    QImage scaled = this->current_image.scaledToHeight(bounding_rect.height());
    QPointF center = bounding_rect.center() - scaled.rect().center();

    if(center.x() < 0)
        center.setX(0);
    if(center.y() < 0)
        center.setY(0);
   painter->drawImage(center, scaled);
}

QImage ImageItem::image() const
{    return this->current_image;
}

void ImageItem::setImage(const QImage &image)
{
    this->current_image = image;
    update();
}

Don’t forget to register the user defined item:

main.cpp
...
qmlRegisterType<ImageItem>("myextension", 1, 0, "ImageItem");
...

Then use it in your QML:

// main.qml
import myextension 1.0
...
ImageItem {
  id: liveImageItem
  height: parent.height
  width: parent.width
}
...

2. QImageProvider
Here you create your own QQuickImageProvider that is used to display the image. This should be the preferred solution but caused flickering output errors on animations of my user interface. So it got the good 2nd place.

// LiveImageProvider.h
#ifndef LIVEIMAGEPROVIDER_H
#define LIVEIMAGEPROVIDER_H

#include <QImage>
#include <QQuickImageProvider>

class LiveImageProvider : public QObject, public QQuickImageProvider
{
    Q_OBJECT
public:
    LiveImageProvider();

    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;

public slots:
    void updateImage(const QImage &image);

signals:
    void imageChanged();

private:
    QImage image;
    QImage no_image;
};

#endif // LIVEIMAGEPROVIDER_H
// LiveImageProvider.cpp
#include "LiveImageProvider.h"
#include <QDebug>
/**
* @brief Image provider that is used to handle the live image stream in the QML viewer.
 */
LiveImageProvider::LiveImageProvider() : QQuickImageProvider(QQuickImageProvider::Image)
{
    this->no_image = QImage(":/images/no_image.png");
    this->blockSignals(false);
}

/**
 * @brief Delivers image. The id is not used.
 * @param id The id is the requested image source, with the "image:" scheme and provider identifier removed.
 * For example, if the image source was "image://myprovider/icons/home", the given id would be "icons/home".
 * @param size In all cases, size must be set to the original size of the image. This is used to set the
 * width and height of the relevant Image if these values have not been set explicitly.
 * @param requestedSize The requestedSize corresponds to the Image::sourceSize requested by an Image item.
 * If requestedSize is a valid size, the image returned should be of that size.
 * @return
 */
QImage LiveImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    QImage result = this->image;

    if(result.isNull()) {
        result = this->no_image;
    }

    if(size) {
        *size = result.size();
    }

    if(requestedSize.width() > 0 && requestedSize.height() > 0) {
        result = result.scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio);
    }

    return result;
}

/**
 * @brief Update of the current image.
 * @param image The new image.
 */
void LiveImageProvider::updateImage(const QImage &image)
{
    if(this->image != image) {
        this->image = image;
        emit imageChanged();
    }
}

Keep attention to the reload() function because to update the scene graph that finally displays the content you need to change the (dummy) source.

// main.qml
...
Image {
  id: liveImage
  property bool counter: false

  asynchronous: true
  source: "image://live/image"
  anchors.fill: parent
  fillMode: Image.PreserveAspectFit
  cache: false


  function reload() {
    counter = !counter
    source = "image://live/image?id=" + counter
  }
  }
...

Dont’t forget to register the new image provider.

// main.cpp
...
QScopedPointer<LiveImageProvider> liveImageProvider(new LiveImageProvider());
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("liveImageProvider", liveImageProvider.data());
engine.addImageProvider("live", liveImageProvider.data());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
...

3. QString
This solution creates a string out of the QImage. This string is set to the source property of the QML image tag. Why this is the worst solution: a lot of conversions and copies, need prior knowledge about the image type (jpg/png/…).

QString ImageViewer::imageData() const
 {
 QByteArray bArray;
 QBuffer buffer(&bArray);
 buffer.open(QIODevice::WriteOnly);
 this->image.save(&buffer, "JPEG");

QString image("data:image/jpg;base64,");
 image.append(QString::fromLatin1(bArray.toBase64().data()));

return image;
 }

 

Image DateTime Updater

I got a new Android phone and had the problem that during copy process from the old to the new smartphone the file dates of my images changed to the current date. The gallery app showed them all under the same month… The following script helped me to overcome this problem, just follow the instructions presented in the readme file.

Here is the code: https://code.huber.xyz/pub/image-date-handler

Windows Qt Maintenance Tool Stucks Solution

Sometimes it happens, that the maintenance tool of Qt is running and running and nothing is happening. It stucks at 99% and I waited hours of hours. In my case the following helped me to overcome this:

  1. Open Windows Explorer and enable the following option: show hidden files and folders
  2. Be sure that the maintenance tool is not running in the background (TaskManager)
  3. Now clear the following folder (it should contain ‘remoterepo-*’ – folders)
    C:\Users\profile-name\AppData\Local\Temp\
  4. Finished. Now open the Qt Maintenance tool and try again to update or modify your packages.

GitLab SSH Key Windows

Prerequisites:

  1. Download git-scm
  2. GitLab server
  3. Open the C:/user/your-user/.ssh/ and create the SSH key via git-bash:
    >> ssh-keygen.exe -t rsa -b 4096
  4. Add the public key to your GitLab account
    4.1 Enter your GitLab website
    4.2 Click on your profile, then ‘Setting’ and after that select ‘SSH-Keys’
    4.3 Follow the instructions of GitLab for adding a new key
  5. Create a ‘config‘ file (without extension) in C:/user/your-user/.ssh/ and add the following content:
    Host sample_1
        user git
        hostname git.domain.com
        port 22
        identityfile ~/.ssh/generated-private-key-file_1
    
    # For multiple ssh-key you can add more than one host
    Host sample_2
        user git
        hostname git.domain.com
        port 22
        identityfile ~/.ssh/generated-private-key-file_2
  6. Test it with the git-bash console:
    git clone git@sample:project.git
  7. [Optional] If the key could not be found try to add the following argument to your environment variables:
    Name:  HOME
    Value: %USERPROFILE%

Cloning Bootable Linux Disk

I got a new and bigger HDD for my webserver and so I decided to clone my existing one (128GB) to the new one (512GB). The best thing was that I didn’t need to install and configure all my applications again, so here is a short manual that worked in my case.

  1. Download Ubuntu Desktop
  2. Create a bootable USB disk with Rufus
  3. Start from USB disk and choose “Try Linux from bootable media”
  4. After Linux has started open a terminal and enter the following commands to copy the disk layout:
    >> sudo bash [go to superuser mode]
    >> fdisk /dev/sda [source disk]
    >> o [dump disk layout to file]
    >> q [quit]
    >> fdisk /dev/sdb [destination disk]
    >> d [delete]
    >> I [load disk layout from currently saved sda layout]
    >> w [write layout]
  5. Now clone all data from the source disk to the destination disk by following this command:
    >> dd if=/dev/sda1 of=/dev/sdb1 bs=64K conv=noerror,sync [blocksize 64K, don't stop on error and keep syncing] Don't mix up input and output disk!!!
  6. Now you have to make the destination disk bootable again:
    >> mount /dev/sdb1 /mnt
    >> grub-install --root-directory=mnt /dev/sdb
  7. If all worked correctly you can optionally open gparted and resize the cloned partation to your needings.

 

GDAL Python gdal_retile.py

Here is a small installation manual for GDAL and Python:

  1. Install GDAL core
  2. Install Python 3.3 and add the Python main path to the environment variable ‘path’ of your System
  3. Add GDAL_DATA to your environment variables
  4. Install GDAL Python bindings

If all there were no errors during installation GDAL Python scripts should work. Please be aware that all software parts are x64 and do have the same Python Version!