【问题标题】:Dealing with the "depends on non-NOTIFYable properties" warning处理“取决于不可通知​​的属性”警告
【发布时间】:2015-12-17 16:20:12
【问题描述】:

我有一个暴露给 QML 的 C++ 对象,它具有“某种”只读属性,除了该属性仍需要从 QML 设置,因此它定义了一个 WRITE 方法,但除了初始 强制设置它永远不会改变,所以我觉得NOTIFY 是多余的,因为它在使用时已经设置了该值,并且永远不会改变。

但是,QML 并不认同我的感受,而且不管它是否发出“表达式依赖于不可通知​​的属性”警告。

由于使用属性实例化对象的方式是设置该值的唯一适用方式,因此不可能使用可调用的设置器,因为这样会要求对象已经“完成”,没有它就无法真正完成那个值。因此需要属性机制和WRITE 方法,不幸的是这导致Qt 相信属性会改变。我尝试将属性设置为CONSTANT,但这似乎与使用WRITE 方法并不一致。

我已经设置了一个虚拟信号作为临时措施来处理大量警告,但如果有更优雅的方法来处理这个问题,我更愿意。同样,该设计实际上消除了在初始设置后设置此值的可能性,但由于它是 C++ 对象并且需要基于每个对象设置值,因此不适用于使用 QML readonly property任何一个。是否有不涉及冗余信号的解决方法?


澄清一下,这里的根本问题是,在当前状态下,QML 几乎没有办法初始化对象的只读属性,用 C++ 实现。这样的属性没有设置器(如果有,那么它会发出“取决于...”警告),并且无法将值传递给对象的构造函数。正如我在下面的回答中所描述的,这方面有一些技巧,但它们要么在应用上受到限制,要么相当不方便。在 C++ 中,您甚至可以通过将值传递给在初始化器列表中执行初始化的构造函数,而不是在 QML 中,对每个实例进行 const 成员的初始化。即使在纯 QML 中,只读属性也必须在现场初始化,不可能让它们未初始化(readonly property type name 没有: statement)并将其延迟到实例化并获得每个实例的初始化(Obj { name : initValue })。你可以这样做:

Item { // T.qml
  readonly property int r : w
  property int w
}
...
T { w: value }

但是它......有点违背了目的......并且考虑到一个属性只能在其实例化的主体中“绑定”一次,我认为这可以被视为一种初始化行为,同时进一步分配或命令式重新绑定 (Qt.binding()) 被禁止。没有冗余通知信号的设置器可以解决问题,但是 qtquick 的实现设计假设如果它有一个设置器,它将发生变化,因此会抱怨它没有通知信号,这是 IMO 的设计疏忽——也就是说,不提供任何方法来在每个实例的基础上初始化 QML 端的只读属性。目前,正如我已经提到的,这 3 种可能的解决方案要么带来开销(有一个通知信号)、限制(在对象创建之前设置值 - 仅适用于动态实例化)或主要的不便(使用 setter 和 getter 插槽而不实现实际属性接口)。

【问题讨论】:

    标签: c++ qt properties notifications qml


    【解决方案1】:

    这有点取决于你需要的价值。

    如果在任何属性绑定中使用了该属性,那么您将需要通知,否则您最终会处于依赖对象实例化和绑定评估顺序的情况。

    例如如果它在其他地方的绑定中使用而没有关联的通知信号,那么您依赖于在首次评估任何此类绑定之前发生的初始设置。

    另一方面,如果该值仅用于初始化 C++ 端,但从未在其他地方的 QML 中使用,则可以考虑使用“setter”函数/插槽。

    【讨论】:

    • 如果该值从未改变,则无需通知。当然,这要求进行初始设置,问题是无论如何,如果它没有通知信号,Qt 就会抱怨它。这只是我遇到的众多 qml 设计疏忽之一。就我而言,这是一个在会话中持续存在的唯一 ID。一旦设置它永远不会改变......永远。不幸的是,qtquick 没有“它会被​​设置一次并且永远不会改变”的概念,因此它要求使用虚假的通知信号,或者将其作为插槽获取器而不是属性来实现。
    • 永不改变和被设定是矛盾的。它可以是固定值,也可以设置。前者不需要通知信号,因为属性可以标记为常量,后者需要一种在值更改时通知引擎的方法。即使是一个变化也是一个变化
    • 如果永远无法设置固定值,那么它完全没有用和毫无意义,因为您永远无法将其设置为任何有意义的值,它只会是不确定的和不确定的。 const 值的不同之处不是不能设置,而是只能设置一次。在 C++ 中,您可以在构造函数初始值设定项列表中设置 const 成员。因此,我说“设计监督”,就像 QML 一样,您无法将数据传递给构造函数,并且几乎没有办法在 QML 中初始化 C++ 对象的只读属性。
    • 属性可以改变也可以不改变。一个可以更改的,即使它只更改一次,也需要能够通知 QML 引擎有关该更改。我认为在声明性环境中依赖执行顺序是不明智的,例如将属性标记为 CONSTANT 并依赖于在评估包含它的任何绑定之前对其进行设置。保持属性可通知会更安全:无论值是在初始绑定评估之前还是之后设置,最终值都是正确的
    • 除了它会产生不必要的开销。让我们在这里尝试客观一点,并从您可以理解的偏见中抽象出来 - 无法在 QML 端初始化只读属性是一个问题和设计缺陷。老实说,如果它是一些单一的例外,我会理解假装它不是,但是当 qtquick 受到许多此类限制的困扰时,拒绝承认这些问题并不能真正使产品看起来更好。两周前,我在一天之内就在 qml 中发现了 5 个不同的错误。这就是我对质量的看法......
    【解决方案2】:

    一种可能的解决方案是仅将 setter 和 getter 函数暴露给 QML,但没有实际属性。它有点不那么优雅而且更冗长(一个额外的()),但至少它处理了控制台中的警告垃圾邮件。它最终可能会更有效,因为除了消除冗余信号之外,实例化的绑定表达式重新评估观察者可能更少。

    还有另一种方法,但它仅适用于动态创建对象 - 如果您以声明方式创建对象,它将不起作用。这种方法涉及在每个对象创建之前调用一个函数,该函数设置一个静态成员值,对象的构造函数将其用作隐式参数来初始化只读属性。因此很明显为什么这只能用于动态创建的对象,在其他情况下,您无法在调用对象的构造函数之前调用函数并设置值。

    还有一些股票 API 似乎也带有没有通知的属性。例如FolderListModelrootFolder 属性,因此每次在绑定中使用时都会生成警告。删除这些的解决方案是使用额外的代理属性并将FolderListModel 属性绑定到它。

    【讨论】:

      猜你喜欢
      • 2011-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      • 2022-08-24
      • 1970-01-01
      相关资源
      最近更新 更多