Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to propertly connect signal in Loader's item?

I want connect one signal from QObject to various pages, loaded by the "Loader" qml element. My problem similar Dead QML elements receiving signals? but loaded items destroyed before calling the "onDestruction" method. For example below, if switch from page1 to page2 in console writed:

"QML: Loading status:  1  Item:  QDeclarativeRectangle(0x8dcd408, "page2")
QML Item: Loaded QDeclarativeRectangle(0x8dcd408, "page2") 1
qrc:/page1.qml:12: TypeError: Result of expression 'parent' [null] is not an object.
qrc:/page1.qml:15: ReferenceError: Can't find variable: page1text"

every second. So there can't disconnect from signal because parent object is destroyed.

How to handle signals from QObject (root) in loaded items? or How to disconnect signal from unloaded page?

main.qml

import QtQuick 1.1

Rectangle {
    id: root
    objectName: "root"
    width: 360
    height: 360
    state: "page1"
    color: "white"

    Item {
        width: parent.width
        height: parent.height/2
        anchors.top: parent.top
        Loader {
            id: pageLoader
            objectName: "pageLoader"
            anchors.fill: parent
            anchors.centerIn: parent
            signal textMsg(variant params)
            onStatusChanged: console.log("QML: Loading status: ", status, " Item: ", item)
            onLoaded: { console.log("QML Item: Loaded",item,status); }
        }
    }
    states: [
        State {
            name: "page1"
            PropertyChanges { target: pageLoader; source: "qrc:/page1.qml"}
        }
        ,State {
            name: "page2"
            PropertyChanges { target: pageLoader; source: "qrc:/page2.qml"}
        }
    ]
    Timer {
        // simulate signals from QObject
        interval: 1000; running: true; repeat: true
        onTriggered: pageLoader.textMsg({"msg2page1":"test","msg2page2":"test"})
    }
    Rectangle {
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        width: parent.width/2
        height: parent.height/2
        border {
            color: "black"
            width: 1
        }
        color: "yellow"
        Text{
            anchors.fill: parent
            anchors.centerIn: parent
            text: "Set Page 1"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                root.state = "page1";
            }
        }
    }
    Rectangle {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        width: parent.width/2
        height: parent.height/2
        border {
            color: "black"
            width: 1
        }
        color: "red"
        Text{
            anchors.fill: parent
            anchors.centerIn: parent
            text: "Set Page 2"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                root.state = "page2";
            }
        }
    }
}

page1.qml

import QtQuick 1.1

Rectangle {
    id: page1
    objectName: "page1"
    color: "yellow"

    Component.onCompleted: {
        parent.textMsg.connect(msgHandler);
    }
    Component.onDestruction: {
        parent.textMsg.disconnect(msgHandler);
    }
    function msgHandler(params) {
        page1text.text += " "+params.msg2page1;
    }
    Text {
        id: page1text
        anchors.fill: parent
        wrapMode: Text.WordWrap
        text: "page1"
    }
}

page2.qml

import QtQuick 1.1

Rectangle {
    id: page2
    objectName: "page2"
    color: "red"
}
like image 734
Andrey Reeshkov Avatar asked Dec 10 '15 16:12

Andrey Reeshkov


People also ask

How do you use QML signals?

Adding signals to custom QML typesSignals can be added to custom QML types through the signal keyword. The syntax for defining a new signal is: signal <name>[([<type> <parameter name>[, ...]])] A signal is emitted by invoking the signal as a method.

What is component onCompleted in QML?

This can be used to execute script code at startup, once the full QML environment has been established. The onCompleted signal handler can be declared on any object. The order of running the handlers is undefined. Rectangle { Component. onCompleted: console.

What is loader in QML?

Loader is used to dynamically load QML components. Loader can load a QML file (using the source property) or a Component object (using the sourceComponent property).


2 Answers

That's nicely described in Loader documenation. It reads:

Any signals emitted from the loaded item can be received using the Connections element.

There is also an example, I copy it below for the sake of clarity:

// Application.qml

import QtQuick 1.0

Item {
    width: 100; height: 100
    Loader {
        id: myLoader
        source: "MyItem.qml"
    }
    Connections {
        target: myLoader.item
        onMessage: console.log(msg)
    }
}

// MyItem.qml

import QtQuick 1.0

Rectangle {
    id: myItem
    signal message(string msg)
    width: 100; height: 100
    MouseArea {
        anchors.fill: parent
        onClicked: myItem.message("clicked!")
    }
}

Clearly, if item is destroyed, any signal handlers are ignored until the target is recreated again.

like image 149
skypjack Avatar answered Oct 13 '22 09:10

skypjack


My answer is: Don't use the "Loader", create child object by JS and destroy it as no needed, for example:

main.qml

import QtQuick 1.1
import "qrc:/pageloader.js" as Pageloader


Rectangle {
    id: root
    objectName: "root"
    width: 360
    height: 360
    state: "page1"
    color: "white"

    signal textMsg (variant params)

    states: [
        State {
            name: "page1"
            StateChangeScript{ script: Pageloader.createPageObject("qrc:/page1.qml");}
        }
        ,State {
            name: "page2"
            StateChangeScript{ script: Pageloader.createPageObject("qrc:/page2.qml");}
        }
    ]
    Timer {
        // simulate signals from QObject
        interval: 1000; running: true; repeat: true
        onTriggered: textMsg({"msg2page1":"test","msg2page2":"test"})
    }
    Rectangle {
        anchors.left: parent.left
        anchors.bottom: parent.bottom
        width: parent.width/2
        height: parent.height/2
        border {
            color: "black"
            width: 1
        }
        color: "yellow"
        Text{
            anchors.fill: parent
            anchors.centerIn: parent
            text: "Set Page 1"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                root.state = "page1";
            }
        }
    }
    Rectangle {
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        width: parent.width/2
        height: parent.height/2
        border {
            color: "black"
            width: 1
        }
        color: "red"
        Text{
            anchors.fill: parent
            anchors.centerIn: parent
            text: "Set Page 2"
        }
        MouseArea {
            anchors.fill: parent
            onClicked: {
                root.state = "page2";
            }
        }
    }
}

pageloader.js

var component;
var sprite;

function createPageObject(path) {
    if(sprite){
        console.log("sprite.destroy() ",typeof sprite);
        sprite.destroy();
        console.log("component.destroy() ",typeof component);
        component.destroy();
    }
    component = Qt.createComponent(path);
    if (component.status === Component.Ready)
        finishCreation();
    else
        component.statusChanged.connect(finishCreation);
}

function finishCreation() {
    if (component.status == Component.Ready) {
        sprite = component.createObject(root);
        if (sprite == null) {
            // Error Handling
            console.log("Error creating object");
        }
    } else{
        if (component.status === Component.Error) {
        // Error Handling
        console.log("Error loading component:", component.errorString());
        }else{
            console.log("Component status changed:", component.status);
        }
    }
}

page1.qml and page2.qml not changed.

like image 31
Andrey Reeshkov Avatar answered Oct 13 '22 09:10

Andrey Reeshkov