如何在Loader的项目中连接信号?

时间:2015-12-10 16:51:51

标签: qt signals qml loader qt-quick

我希望将来自QObject的一个信号连接到各种页面,由“Loader”qml元素加载。我的问题类似于Dead QML elements receiving signals?,但是在调用“onDestruction”方法之前已经破坏了已加载的项目。 例如下面,如果在控制台中从page1切换到page2:

"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"

每一秒。因此,无法断开与信号的连接,因为父对象已被破坏。

如何在加载的项目中处理来自QObject(root)的信号?或如何从已卸载的页面断开信号?

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"
}

3 个答案:

答案 0 :(得分:6)

Loader documenation中很好地描述了这一点。它写着:

  

可以使用Connections元素接收从加载项发出的任何信号。

还有一个例子,为了清楚起见,我在下面复制它:

// 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!")
    }
}

显然,如果item被销毁,则会忽略任何信号处理程序,直到再次重新创建目标。

答案 1 :(得分:2)

我的回答是:不要使用" Loader",通过JS创建子对象并将其销毁为不需要的,例如:

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和page2.qml未更改。

答案 2 :(得分:0)

我明白了。我的设置:

  • qml文件以显示ListViews
  • 定义Listviews的几个qml文件,每个文件都使用不同SQL表的不同列。该模型来自C ++

所以这是缩短的代码:

Dialog {
    id: dialog
    width: 1000; height: 400

    property Component listViewItem

    signal newDatabaseEntry( string text ) [1]

    contentItem: Rectangle {

        [...]
        TextInputWithButton { [3]
            id: newRecords
            onInputAccepted: { newDatabaseEntry( text ) } [1]
        }
    }

    [...]

    Loader {
        id: listViewPlaceholder
        anchors.fill: parent
        sourceComponent: dialog.listViewItem
        onLoaded: {
            if( typeof listViewPlaceholder.item.insertRecord === "function" )
                // newRecords.inputAccepted.connect( listViewPlaceholder.item.insertRecord ) [1]
                dialog.newDatabaseEntry.connect( listViewPlaceholder.item.insertRecord ) [2]
        }

上面的代码是ListViews的一般视图。信号往返[1]是必要的,否则不传递数据。如何链接信号在这里描述:

http://doc.qt.io/qt-5/qtqml-syntax-signals.html#connecting-signals-to-methods-and-signals

输入按钮[3]提供要插入数据库的已确认数据。

传递给上述函数的ListView如下所示: DialogSqlSingleColumnEdit {

listViewItem: ListView {

    function insertRecord( text ) {
        console.log( "done:" +  text )
        sqlModel.insertRecord( text )
    }
[...]

调用insertRecord将文本转发到sql-C ++模型。