2015-12-10 2 views
3

Я хочу подключить один сигнал от QObject к различным страницам, загруженным элементом qml «Loader». Моя проблема аналогична Dead QML elements receiving signals?, но загруженные предметы уничтожены до вызова метода onDestruction. В приведенном ниже примере, если переход от page1 к PAGE2 в консоли writed:Как правильно подключить сигнал в пункте Loader?

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

ответ

1

Мой ответ: Не используйте "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); 
     } 
    } 
} 

страница1.qml и страница2.qml не изменен.

3

Вот хорошо описано в Loaderdocumenation. Он гласит:

Любые сигналы, излучаемые загруженным предметом, могут быть получены с использованием элемента Подключения.

Существует также пример, я скопировать его ниже для ясности:

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

Нужно получать сигналы в загруженном элементе от загрузчика, а не от загруженного элемента! –

+0

Из-за свойства соединения вы можете попробовать использовать его во время создания вместо прямого вызова для подключения/отключения. Он должен прекрасно справиться с отключением после того, как дочерний элемент будет уничтожен. – skypjack

+0

@AndreyReeshkov вы все еще можете использовать компонент Connections для эффективного подключения к родительским сигналам страницы, и это более чистое решение, чем ручное управление созданием/подключением. –

0

У меня есть. Моя установка: файл

  • QML для отображения ListViews
  • Несколько QML файлов, определяющих ListViews, каждый берет другого столбца различных таблиц 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. Сигнал roundtrip [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++.