1

So, I checked the following questions, which seemed most similar to my existing problem:

QML: Using cpp signal in QML always results in "Cannot assign to non-existent property"

Unfortunately that did not help. (Nor did any other solution that I could find on stackoverflow/google/qt forums, etc)

I kept getting the following two errors:

qrc:/view.qml:30:9: QML Connections: Cannot assign to non-existent property "onNewFrameReceived" qrc:/view.qml:31: ReferenceError: imageProvide is not defined

Here is my code (edited down to make it, into a 'Minimum Working Example').

The only files important should be:

  1. main.cpp
  2. view.qml
  3. imageprovidervm.cpp
  4. imageprovidervm.h

I included the imagesource class, just to be complete, in case someone wants to compile this on his own as well.

Q1. So, I don't understand why even after setting the context property in main.cpp, the following error appears.

qrc:/view.qml:31: ReferenceError: imageProvide is not defined

What is funny is that, intellisense/autocomplete seems to detect imageProvide completely correctly.

Q2. Even though in my imageprovider.h, I added properties (newimage) and signals (newFrameReceived) that should be seen in the qml file, still I get the following error. Also, the Qt intellisense/autocomplete fails to show my defined signal (onNewFrameReceived) here.

qrc:/view.qml:30:9: QML Connections: Cannot assign to non-existent property "onNewFrameReceived"

  • Additional info: Debugging and stopping on a break point in the qml file at line 31, shows in the "locals and expressions" of the qtcreator that I have only 2 signals available here, namely "objectNameChanged" and "targetChanged". Why ???

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "imageprovidervm.h"
#include "imagesource.h"

    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);

        QQmlApplicationEngine engine;
        QQmlContext *context = new QQmlContext(engine.rootContext());

        auto model = std::make_shared<ImageSource>();

        auto vm = new ImageProviderVM(model);
        engine.addImageProvider(QLatin1String("imageProvider"), vm);
        context->setContextProperty("imageProvide", vm );

        model->generateImages();

        engine.load(QUrl(QStringLiteral("qrc:/view.qml")));

        return app.exec();
    }

view.qml

import QtQuick 2.5
import QtQuick.Controls 1.4

import QtQml.Models 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("&Open")
                onTriggered: console.log("Open action triggered");
            }
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }

    Rectangle {
        Connections {
            target: imageProvide
            onNewFrameReceived: image.reload();
        }
        anchors.fill: parent
        Column {
            Image {
                id: image
                source: "image://imageProvider/images.jpeg?id=" + Math.random()
                cache: false
                asynchronous: true
                function reload() {
                        var oldSource = source;
                        source = "";
                        source = oldSource;
                    }
            }
        }
    }
    Label {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

imageprovidervm.h

#ifndef IMAGEPROVIDERVM_H
#define IMAGEPROVIDERVM_H

#include <QQuickImageProvider>
#include <QObject>
#include "imagesource.h"

class ImageProviderVM : public QObject, public QQuickImageProvider
{
    Q_OBJECT
public:
    ImageProviderVM(std::shared_ptr<ImageSource> model);
    ~ImageProviderVM();

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

    // Properties
    Q_PROPERTY(QImage newimage READ getNewImage NOTIFY newFrameReceived)

    // Signals
signals:
    void newFrameReceived();

private:
    QImage getNewImage() const;

    QPixmap m_pixmap;
    QImage m_image;

    std::shared_ptr<ImageSource> m_model;
};

#endif // IMAGEPROVIDERVM_H

imageprovidervm.cpp

#include "imageprovidervm.h"
#include <functional>

#include <QPixmap>
#include <QDebug>

ImageProviderVM::ImageProviderVM()
    : QQuickImageProvider(QQuickImageProvider::Image)
{
}

ImageProviderVM::ImageProviderVM(std::shared_ptr<ImageSource> model)
    : QQuickImageProvider (QQuickImageProvider::Image)
    , m_pixmap()
    , m_model(model)
{
    m_model->subscribeNewPixMap([this](QPixmap pixmap) {
        qDebug() << "setting m_pixmap";
        if (pixmap.size().isValid()) {
            m_pixmap = pixmap;
        }
        else
            qDebug() << "is it NULL ??? " << pixmap.isNull();
    });

    m_model->subscribeNewImage([this](QImage image) {
        qDebug() << "setting m_image";
        if (image.size().isValid()) {
            m_image = image;
            emit newFrameReceived();
        }
        else
            qDebug() << "is it NULL ??? " << image.isNull();
    });

    qDebug() << "imageproviderVM constructed";
}

ImageProviderVM::~ImageProviderVM()
{
}

QPixmap ImageProviderVM::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize)
{
    // look into the parameters id, size and requestedSize once the rest of the structure is there
    return m_pixmap;
}

QImage ImageProviderVM::requestImage(const QString & id, QSize * size, const QSize & requestedSize)
{
    return m_image;
}

QQuickTextureFactory * ImageProviderVM::requestTexture(const QString & id, QSize * size, const QSize & requestedSize)
{
//    return QQuickTextureFactory::createTexture();
}

QImage ImageProviderVM::getNewImage() const
{
    return m_image;
}

imagesource.h

#ifndef IMAGESOURCE_H
#define IMAGESOURCE_H

#include <QImage>
#include <boost/signals2.hpp>

class ImageSource
{
public:
    ImageSource();
    void generateImages();
    void generatePixmaps(const QString &id, QSize *size, const QSize &requestedSize);

    typedef boost::signals2::signal<void (QPixmap)> NewPixMapDelegate;
    boost::signals2::connection subscribeNewPixMap(NewPixMapDelegate::slot_function_type f);

    typedef boost::signals2::signal<void (QImage)> NewImageDelegate;
    boost::signals2::connection subscribeNewImage(NewImageDelegate::slot_function_type f);


private:
    NewPixMapDelegate m_newPixMap;
    NewImageDelegate m_newImage;
};

#endif // IMAGESOURCE_H

imagesource.cpp

#include "imagesource.h"

#include <QPixmap>
#include <QPainter>

#include <thread>

ImageSource::ImageSource()
{
}

boost::signals2::connection ImageSource::subscribeNewImage(NewImageDelegate::slot_function_type f)
{
    return m_newImage.connect(f);
}

void ImageSource::generateImages()
{
    std::thread t([this]() {
        auto image = QImage("/home/junaid/testing_ground/fourthtime/images.jpeg");
        m_newImage(image);

        /// useless wait. just simulating that another image comes after sometime and so on onwards.
        int random_wait = 2; //sec
        sleep(random_wait);

        image = QImage("/home/junaid/Downloads/pnggrad16rgb.png");
        m_newImage(image);
    });
    t.detach();
}

boost::signals2::connection ImageSource::subscribeNewPixMap(NewPixMapDelegate::slot_function_type f)
{
    return m_newPixMap.connect(f);
}

void ImageSource::generatePixmaps(const QString &id, QSize *size, const QSize &requestedSize)
{
    int width = 100;
    int height = 50;

    if (size) {
        *size = QSize(width, height);
    }
    QPixmap pixmap(requestedSize.width() > 0 ? requestedSize.width() : width,
                   requestedSize.height() > 0 ? requestedSize.height() : height);
    pixmap.fill(QColor(id).rgba());

    // write the color name
    QPainter painter(&pixmap);
    QFont f = painter.font();
    f.setPixelSize(20);
    painter.setFont(f);
    painter.setPen(Qt::black);
    if (requestedSize.isValid())
        painter.scale(requestedSize.width() / width, requestedSize.height() / height);
    painter.drawText(QRectF(0, 0, width, height), Qt::AlignCenter, id);

    m_newPixMap(pixmap);
}

and here is the CMake file:

cmake_minimum_required(VERSION 2.8.12)

project(non_existent_property LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5 COMPONENTS Core Quick REQUIRED)

file( GLOB SRCS *.cpp *.h )

add_executable(${PROJECT_NAME} "qml.qrc" ${SRCS})

target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick "pthread")
5
  • What happens when you debug the "imageProvide" property? I mean add a console.log(imageProvide) to some QML button or menu and look what is printed in the console. Commented Apr 10, 2017 at 13:09
  • And how should I be able to do that? I followed this link and added a function with f() {} with your console.log statement in it. But how do I call that function? I followed it here: doc.qt.io/qt-5/qtquick-debugging.html But could not figure it out. (I am not a javascript programmer.) Commented Apr 10, 2017 at 13:41
  • 1
    Try engine.rootContext()->setContextProperty(...) instead of creating an "unassociated" child context. Commented Apr 10, 2017 at 17:00
  • @jpnurmi wow! that worked! But I don't understand, why is it unassociated? Is it not just a variable assigned with the value that I get anyways? I should probably check the QQmlContext doc page on the qt website. Thanks a lot. It solved my problem. If you post it as an answer, I will mark it as resolved/'accepted answer' then. Commented Apr 11, 2017 at 7:35
  • 1
    It's just a newly created child context, no object is assigned to it. To help you figure it out, what if you created a second context too? It wouldn't make sense if the objects created in the root context were able to access properties of the 2 child contexts, it's the same for only one. Commented Apr 11, 2017 at 8:48

1 Answer 1

0

So, here is how the working main.cpp looks. I was mislead by the QmlContext that I was creating, and with help from @GrecKo's and jpnurmi's comments, I understood that I had to set the property for the root context.

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "imageprovidervm.h"
#include "imagesource.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    // QQmlContext *context = new QQmlContext(engine.rootContext());

    auto model = std::make_shared<ImageSource>();

    auto vm = new ImageProviderVM(model);
    engine.addImageProvider(QLatin1String("imageProvider"), vm);
    engine.rootContext()->setContextProperty("imageProvide", vm );

    model->generateImages();

    engine.load(QUrl(QStringLiteral("qrc:/view.qml")));

    return app.exec();
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.