当父 属性 在 QML 中更改时更改子 属性

Change children property when parent property changes in QML

我有 3 个文件。 main.qml、Guide.qml 和 ChannelViewer.qml 我的主要 class 包含 2 个组件和一个加载程序,这里是代码

main.qml

import QtQuick 2.0

Rectangle {
    id:loader
    color: "black"
    property string channelName
    property string channelURL

    Component{
        id:tv
        ChannelViewer{}
    }

    Component{
        id:guide
        Guide{}
    }

    Loader
    {
        id: pageLoader
        anchors.fill:parent
        focus:true
        sourceComponent: tv
    }

    Connections{
        target:pageLoader.item
        onChangeChannel:{
            channelName=name
            channelURL=url
        }
    }

    Keys.onPressed: {
        event.accepted = true;
        if (event.key === Qt.Key_I) {
            pageLoader.sourceComponent = tv;
        }
        else if(event.key === Qt.Key_G) {
            pageLoader.sourceComponent = guide;
        }
    }
}

现在,如果我按 "G",我将毫无问题地移至指南文件 在我的指南页面中,我可以向 main.qml 发送信号并更新名称 属性在主要。

Guide.qml

Item {
    signal changeChannel(string url, string name)
    Loader {
        id: pageLoader
        anchors.fill:parent
        sourceComponent: guide
        focus:true
    }

    Keys.onPressed: {
        if(event.key === Qt.Key_Escape) {
            pageLoader.source = "main.qml";
        }
        event.accepted = true;
    }

    Component {
        id:guide

        Rectangle {
            color:"lightblue"

            Keys.onPressed: {
                if(event.key === Qt.Key_Return) {
                    changeChannel(menuContent.currentItem.ch_url, menuContent.currentItem.ch_name)
                    pageLoader.source = "main.qml";
                }
                event.accepted = true;
            }
        }
    }
}

但是现在当我在 Guide.qml 中按 "Return" 时,我将被带回 main.qml(Channelname 和 ChannelURL 将成功更新),而我的 main.qml现在将带我到 ChannelViewer.qml,问题是我的 ChannelViewer.qml 将不会收到更新的 channelName 和 channelURL。而且我不确定我做错了什么。

ChannelViewer.qml

import QtQuick 2.0
import VLCQt 1.0

Rectangle {
    id:root
    width: 640
    height: 480
    color: "black"
    focus:true

    Loader
    {
        id: pageLoader
        anchors.fill:parent
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log(channelURL)
        }
    }

    Keys.onPressed: {
        if (event.key === Qt.Key_I) {
            event.accepted = true;
            if(channelInfo.visible === true) {
                channelInfo.visible=false;
            }
            else {
                channelInfo.visible=true;
            }
        }
    }

    VlcVideoPlayer {
        id: vidwidget
        anchors.fill: parent
        url:channelURL

        ChannelInfo{
            id:channelInfo
            anchors.bottom: parent.bottom
            anchors.bottomMargin: ((parent.height*5)/100)
            anchors.horizontalCenter: parent.horizontalCenter
            width:parent.width - ((parent.width*10)/100)
            height: (parent.height*20)/100
            backgroundOpacity: 0.7
            radius:10
            channelNameProp: channelName
            channelNumberProp: "1"
            headerIcon: "imgs/television_32x32.png"
        }
    }
}

编辑: 我的 ChannelInfo.qml

的代码
import QtQuick 2.0

Item {
    id:channelinfo
    property color backgroundColor: "blue"
    property color headerBackgroundColor: "lightblue"
    property color headerNameColor: "black"
    property color borderColor: "black"
    property color channelNameColor: "white"
    property color channelNumberColor: "white"
    property real borderWidth:0
    property real radius:0
    property real backgroundOpacity: 0.5
    property string menuTitle : "TV Channels"
    property string channelNameProp
    property string channelNumberProp
    property url headerIcon: "imgs/television.png"

    visible:false

    Rectangle{
        id:root
        width:channelinfo.width
        height:channelinfo.height
        color:channelinfo.backgroundColor
        border.color:channelinfo.borderColor
        border.width: channelinfo.borderWidth
        radius:channelinfo.radius
        opacity:channelinfo.backgroundOpacity
        visible: parent.visible

        Rectangle{
            id:header
            anchors.top:parent.top
            //            width:(parent.width*40)/100
            width: parent.width
            height: (parent.height*30)/100
            radius: channelinfo.radius
            color:channelinfo.headerBackgroundColor
            Image{
                source:channelinfo.headerIcon
                anchors.left: parent.left
                anchors.leftMargin: 10
                anchors.verticalCenter: parent.verticalCenter
                anchors.verticalCenterOffset: -4
            }

            Text{
                id:headerTitle
                anchors.left: parent.left
                anchors.leftMargin: 50
                anchors.verticalCenter: parent.verticalCenter
                width:parent.width
                wrapMode: Text.WordWrap
                color:channelinfo.headerNameColor
                text:menuTitle
                font.pixelSize: Math.round(parent.height/2)
                font.bold: true
            }
        }

        Rectangle{
            id:content
            anchors.bottom: parent.bottom
            width:parent.width
            height:parent.height-header.height
            color:"transparent"
            Text{
                id:channelName
                anchors.left: parent.left
                anchors.leftMargin: 50
                anchors.verticalCenter: parent.verticalCenter
                color:channelinfo.channelNameColor
                text:channelNameProp
                font.pixelSize: Math.round(parent.height/4)
                font.bold: true
            }
            Text{
                id:channelNumber
                anchors.right: parent.right
                anchors.rightMargin: 20
                anchors.verticalCenter: parent.verticalCenter
                color:channelinfo.channelNumberColor
                text:channelNumberProp
                font.pixelSize: Math.round(parent.height/4)
                font.bold: true
            }
        }
    }
}

Github VLCPlayer 页面 https://github.com/vlc-qt/

作为属性

property string channelName
property string channelURL

有更改信号,因此支持 属性 绑定,我认为最简单的方法是将第 9-17 行更改为

Component{
    id:tv
    ChannelViewer {
        id: channelViewer
        channelName: loader.channelName
        channelURL: loader.channelURL
    }
}

Component{
    id:guide
    Guide {
        id: guide
        channelName: loader.channelName
        channelURL: loader.channelURL
    }
}

如果 Guide 更改了频道名称,您需要确保在 loader 中进行更改。您可以使用 Binding-objects 使绑定在分配 (=) 中存活。

如此有效,您需要在 Guide.qmlChannelViewer.qml 的根节点中创建属性 channelNamechannelURL。然后,在这些文件中的每个地方,您使用完全限定的标识符:id.propertyName,例如 ChannelInfo.qml 中的 channelinfo.channelNameChannelViewer.qml 中的 root.channelName以及您需要在 Guid.qml -> root.channelName.

中设置的 ID(例如再次 root)
  • Usage of fully qualified identifiers for bindings, which always include the idOfTheObject.propertyName helps to avoid problems. In some cases (positioning, anchoring, sizing) parent is ok, but you might not know what exactly is the parent)
  • Dynamic scoping is a blessing if you know exactly how and where the code will be used, e.g. if it is essentially a partial definition of a larger object, and will never be used in another context. But here you need to know that if the parent file changes the internal api, you need to adapt the child file accordingly. If you think, the file might be for reuse, avoid dynamic scoping and only reference what is defined inside the file.

如果你要有这样一个固定的结构,为什么还要费心信号,你可以简单地:

 Keys.onPressed: {
                if(event.key === Qt.Key_Return) {
                    channelName = menuContent.currentItem.ch_name
                    channelURL = menuContent.currentItem.ch_url
                    pageLoader.source = "main.qml";
                }
                event.accepted = true;
            }

然后去掉不需要的部分:

Connections{
    target:pageLoader.item
    onChangeChannel:{
        channelName=name
        channelURL=url
    }
}

由于 channelNamechannelURL 是在 qml 文件的根对象中声明的,因此由于动态作用域,它们应该可以从嵌套在树上更远的对象中访问。

所以在你发布相关代码后,你有一个:

        Text{
            id:channelName

在您的 ChannelInfo 对象中,它隐藏 channelName 属性,在 main.qml 中声明。养成一致命名约定的习惯是个好主意。例如,由于这是一个 id,我个人会使用 id: _cName,这样可以最大限度地减少发生此类冲突的几率。

更新:

我能想到为什么它不起作用的唯一其他原因是您在某处通过执行 channelNameProp = something.

之类的操作来破坏 channelNameProp: channelName 绑定

这里有一个简单的例子来说明动态作用域 是有效的(只要你不隐藏任何东西),即使在涉及动态更改加载器项目的情况下也是如此:

// main.qml
ApplicationWindow {
  id: _cName
  visible: true
  width: 640
  height: 480
  property int value: 0
  Loader {
    id: loader
    source: "Obj.qml"
  }
}

// Rect.qml
Rectangle {
  id: rectangle
  width: 50; height: 100
  color: "red"
  Text {
    anchors.centerIn: parent
    text: value
  }
  MouseArea {
    anchors.fill: parent
    onClicked: {
      loader.source = "Obj.qml"
    }
  }
}

// Obj.qml
Rectangle {
  id: rectangle
  width: 50; height: 100
  color: "blue"
  MouseArea {
    anchors.fill: parent
    onClicked: {
      value++
      loader.source = "Rect.qml"
    }
  }
}