1
votes

I've got class that inherits from QQuickItem and inside I'm operating on its width height properties. I'm also exposing my own properties.

class MyQQuickItem : public QQuickItem
{
    Q_OBJECT
    Q_PROPERTY(QUrl source MEMBER m_source NOTIFY sourceChanged)

    public:
    explicit MyQQuickItem(QQuickItem *a_parent = Q_NULLPTR);
     ~PDFPage();

    signals:
    void sourceChanged(const QUrl a_source);

    private:
    QUrl m_source;
};

In qml:

...
MyQQuickItem
{
    width: parent.width / 2
    height: parent.height
    source: "someSource"
    Component.onCompleted
    {
        console.log("component onCompleted");
    }
}

When sourceChanged is emmited I'm working on width height properties, but with the first emit they are not initialized yet, and Component.onCompleted is called after my NOTIFY signal.

I can check if component is ready before getting width and height by calling isComponentComplete and execute my code when it is, but I can't find out when it happens and the initial value of source is skipped when it is not.

Of course I could create slot 'itemReady' and call it from Component.onCompleted, but I have many QMLs using my class and I will create more in the future, so I don't want to make copy-paste job.

I also do not want to set the source in Component.onCompleted.

Connecting to widthChanged, and heightChanged signals isn't also good idea, because I'm resizing my item very frequently, and I don't want to execute my code then.

Is there any way to get completed signal from C++?

EDIT:

I did as @Mitch said - overriten componentComplete method and inside, called it's base class equavilent. isComponentComplete() returns true, but I'm still getting 0 from width() and height() methods:

void MyQQuickItem::componentComplete()
{
    QQuickItem::componentComplete();
    if(isComponentComplete())
    {
        qDebug() << "c++: oncompleted, width,height: " << width() << height(); //gives 0,0
    }

}

Actually I figured out that Component.onCompleted in QML prints 0's too:

...
MyQQuickItem
{
    width: parent.width / 2
    height: parent.height
    source: "someSource"
    Component.onCompleted
    {
        console.log("component onCompleted width, height: ", width, height); //it gives 0 too
    }
}

@derM Component.onCompleted is not emmited by QQuickItem::componentComplete() directly, because it prints its log after the qDebug in c++ above.

So, although it is ready, properties are not initialized.

EDIT2:

Finally I've solved it. The Window was guilty, because it uses contentItem as the parent of his childrens, and in MyQQuickItem Component.onCompleted width and height of contentItem(his parent) are 0.

When I do so:

Window
{
    id: wnd
    width: 640
    height: 480
    Component.onCompleted:
    {
        console.log("Window completed: ", this, width, height);
        console.log("windowContentItem: ", contentItem, width, height);
    }

    MyQQuickItem
    {
        width: parent.width
        height: parent.height
        Component.onCompleted:
        {
            console.log("MyQQuickItem completed: ", this, width, height);
            console.log("MyQQuickItem parent: ", parent, parent.width, parent.height);
        }
    }
}

It prints:

qml: Window completed:  QQuickWindowQmlImpl(0x345b5eb4d0) 640 480
qml: windowContentItem:  QQuickRootItem(0x345e2cdec0) 640 480
qml: MyQQuickItem completed:  MyQQuickItem(0x345b5b99e0) 0 0
qml: MyQQuickItem parent:  QQuickRootItem(0x345e2cdec0) 0 0

So MyQQuickItem parent is Window's contentItem. When I replace

width: parent.width
height: parent.height

to:

width: wnd.width
height: wnd.height

It works perfectly, and in my void MyQQuickItem::componentComplete() I've got width and height initialized as expected.

One thing that I do not understand is why in Window onCompleted, contentItem size is correct, but in MyQQuickItem onCompleted the size is 0,0. If anyone could explain it to me, I would be grateful :P

2
The MCVE is still missing. It would include a complete QML file, so that the whole setup actually reproduces the problem.derM
@derM Check EDIT2Macias

2 Answers

3
votes

Qt code uses componentComplete() like so:

void QQuickControl::componentComplete()
{
    Q_D(QQuickControl);
    QQuickItem::componentComplete();
    d->resizeContent();
    // ...
}

You can do something similar, replacing resizeContent() with your function that works on the width and height. Assuming that function is also called whenever source changes, you'd need the isComponentComplete() check. With this approach, you shouldn't have any issues.

-1
votes

You can inherit QQmlParserStatus and implement componentComplete().

class MyQQuickItem : public QQuickItem, public QQmlParserStatus
{
    Q_OBJECT
    Q_PROPERTY(QUrl source MEMBER m_source NOTIFY sourceChanged)

public:
    explicit MyQQuickItem(QQuickItem *a_parent = Q_NULLPTR);
    ~PDFPage();

    void classBegin();
    void componentComplete();

signals:
    void sourceChanged(const QUrl a_source);

private:
    QUrl m_source;
};

componentComplete() will be called automatically by the QML-engine.