-1

I have a class that has a Q_PROPERTY called rotationVal, and I'm exposing that class using qmlRegisterType in main.cpp.

I'm receiving an int value from Java side which I'm using to set the value of rotationVal (eg: setRotationVal(rotationVal)).

This works perfectly fine when I access rotationVal in QML, but I'm told to use the setRotationVal() from Qt's "UI thread". Can anyone explain?

imageorientation.h

#ifndef IMAGEORIENTATION_H
#define IMAGEORIENTATION_H

#include <QObject>

class ImageOrientation:public QObject
{
    Q_OBJECT

    Q_PROPERTY(int rotation READ rotation WRITE setRotation NOTIFY rotationChanged)

public:
    explicit ImageOrientation(QObject* parent = nullptr);

    int rotation() const;
    void setRotation(int newRotation);

signals:
    void rotationChanged();

private:
    int m_rotation;

};

#endif // IMAGEORIENTATION_H

imageorientation.cpp

#include "imageorientation.h"
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QJniObject>
#include <QDebug>
#include <QObject>

ImageOrientation::ImageOrientation(QObject *parent) : QObject{parent}
{
    QJniObject javaClass = QNativeInterface::QAndroidApplication::context();
    javaClass.callMethod<void>("setPointer","(J)V",(long long)(ImageOrientation*)this);
    setRotation(0);
}

extern "C" {

JNIEXPORT void JNICALL Java_com_example_myapplication_MainActivity_setNativeRotation
    (JNIEnv *, jobject, jint rotation, jlong ptr){

    ImageOrientation* orientation = reinterpret_cast<ImageOrientation*>(ptr);

    if(rotation == 90)
        rotation = 270;
    else if(rotation == 270)
        rotation = 90;
    orientation->setRotation(rotation);
    qDebug() << rotation;
}

}

int ImageOrientation::rotation() const
{
    return m_rotation;
}

void ImageOrientation::setRotation(int newRotation)
{

    if (m_rotation == newRotation)
        return;
    m_rotation = newRotation;
    emit rotationChanged();
}

Main.qml

import QtQuick
import QtQuick.Window
import ImageOrientationpackage

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Image Rotation")

    ImageOrientation {
        id: imgOrientation
    }

    Rectangle {
        width: parent.width
        color: "grey"
        height: 40
        Text {
            text: "Image Rotation"
            font.pointSize: 32
            color: "white"
        }
    }

    Image {
        id: image
        source: "qrc:/images/exampleimg.png"
        width:  200
        height: 200
        anchors.centerIn: parent
        rotation: imgOrientation.rotation
    }

}
sash
  • 101
  • 7

2 Answers2

2

Java calls to setRotation() outside the Qt Ui Thread may crash your application because this call runs in another thread and both threads change the property rotation. Or you end up in a race condition (which value is set last? Unpredictable behaviour).

A solution to this problem could be to send the JAVA setRotation() call throught the QT event queue by adding a signal with a queued connection (Read e.g. Description of QT connection types for more information about that). This will look somehow like this (untested):

imageorientation.h

signals:
    void rotationChanged();
    void emitRotationChanged();

imageorientation.cpp

connect(this, &ImageOrientation::emitRotationChanged, this,
        &ImageOrientation::setRotation, Qt::QueuedConnection);

setRotation has to be changed to be a slot.

The emitRotationChanged signal is what you have to call then in the JAVA method:

emit orientation->emitRotationChanged(rotation);

There might even be a way to achieve the queuing mechanism with the Q_PROPERTY macro directly.

Now, the JAVA calls to setRotation are going over the QT Event Queue like the other QML calls. Still, the order of the calls is not guaranteed by the Queue!

Jürgen Lutz
  • 329
  • 1
  • 1
  • 10
  • What do you think about this solution? https://stackoverflow.com/a/34136511/20197985 using QMetaObject::invokeMethod – sash May 24 '23 at 08:22
  • Your ImageOrientation is a QObject, so no need to use the invokeMethod variant. This is more or less a SIGNAL/SLOT solution in case you have one side not beeing a QObject. – Jürgen Lutz May 24 '23 at 08:30
  • Thanks! Should I be calling the connect method inside the ImageOrientation constructor? – sash May 24 '23 at 08:53
  • You can call it whereever you want. CTOR is quite common...or a seperate init method – Jürgen Lutz May 24 '23 at 09:57
1

you can call orientation->setRotation(rotation) like this:


QMetaObject::invokeMethod(orientation, [=]() {orientation->setRotation(rotation); });