【问题标题】:Change children property when parent property changes in QML当 QML 中的父属性更改时更改子属性
【发布时间】:2017-08-03 16:36:57
【问题描述】:

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

ma​​in.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 发送信号并更新 main 中的 name 属性。

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 中按“返回”时,我将被带回 main.qml(频道名称和 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
            }
        }
    }
}

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

【问题讨论】:

    标签: c++ qt qml qt5 qt-creator


    【解决方案1】:

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

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

    由于channelName 和channelURL 是在qml 文件的根对象中声明的,因此它们应该可以从嵌套在树上的对象中访问,因为它们是动态范围的。

    所以在你发布了相关代码之后,你就有了:

            Text{
                id:channelName
    

    在您的ChannelInfo 对象中,它隐藏了main.qml 中声明的channelName 属性。养成一致的命名约定的习惯是个好主意。例如,由于这是一个 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"
        }
      }
    }
    

    【讨论】:

    • 我更喜欢信号,因为您应该努力避免依赖动态范围。双向绑定可能会更好,但恕我直言,这是一个好的开始。
    • “你应该努力避免依赖动态范围” - 为什么?
    • 您在信息中有一个id:channelName 用于Text 元素,这就是您认为要绑定的频道名称的阴影。
    • 那么现在也有ChannelsModel了?
    • @derM 动态范围即使在这种情况下也应该可以工作,但是是的,这是该代码的众多设计问题之一,它几乎是一团糟:)
    【解决方案2】:

    作为属性

    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.qml 和 ChannelViewer.qml 的根节点中创建属性 channelName 和 channelURL。然后,在这些文件中的每个位置,您使用完全限定标识符:id.propertyName,例如ChannelInfo.qml 中的channelinfo.channelName,ChannelViewer.qml 中的root.channelName 以及您需要设置的 id(例如再次root)在你的Guid.qml -> root.channelName。

    • 使用完全限定标识符进行绑定,始终包含idOfTheObject.propertyName 有助于避免问题。在某些情况下(定位、锚定、调整大小)parent 是可以的,但您可能不知道父项到底是什么)
    • 如果您确切知道代码的使用方式和位置(例如如果它本质上是一个更大对象的部分定义,并且永远不会在另一个上下文中使用。但是这里需要知道,如果父文件更改了内部api,则需要对子文件进行相应的适配。如果您认为该文件可能是为了重复使用,请避免动态范围,仅引用文件中定义的内容。

    【讨论】:

    • 不幸的是,我之前尝试过这种方法,但没有奏效。它说 channelName 是一个不存在的属性。可能我做错了什么?
    • 哦,是的,你是对的。您的ChannelViewer.qml 中没有channelName,Guide.qml 中也没有。您只能设置文件根节点的属性。
    • 您是否有指向ChannelInfo、VlcVideoPlayer 的代码或更好的文档的链接?尽量避免绑定到未在与您设置绑定的文件相同的文件中声明的属性。在文件根节点中创建属性,您将内部属性绑定到该节点。然后就可以在使用组件的时候正确设置了。
    • ChannelInfo 是我自己的代码,我将编辑我的原始帖子并将其包含在内。 VLCVidoPlayer,这是它的 github github.com/vlc-qt/vlc-qt 我尝试在根节点中设置所有内容并遇到同样的问题。
    • 完成了您编辑的操作,但仍然存在同样的问题。快把我逼疯了。
    猜你喜欢
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 2021-08-25
    • 2023-04-05
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 2013-12-10
    相关资源
    最近更新 更多