处理“取决于不可通知属性”的警告

10
我有一个C++对象在QML中暴露出来,其中有一个“类似于”只读属性,除了需要从QML设置该属性之外,该属性仍然需要设置,因此定义了一个WRITE方法,但除了初始的强制设置之外,它永远不会改变,因此我觉得NOTIFY是多余的,因为在使用时它已经设置了该值,并且它永远不会改变。
然而,QML并不分享我的感受,而且无论如何都会产生“表达式取决于不可通知的属性”的警告。
由于对象实例化的方式,使用属性是设置该值的唯一适用方式,不能使用可调用的setter,因为这样需要对象已经“完成”,而没有该值它无法真正完成。因此需要使用属性机制和WRITE方法,这不幸地导致Qt认为该属性将会改变。我尝试将属性设置为CONSTANT,但似乎与具有WRITE方法不兼容。
我已经放置了一个虚假信号作为暂时措施来处理警告,但我更喜欢有一种更优雅的方法来处理这个问题。再次强调,该设计实际上消除了在初始设置之后设置此值的可能性,但由于它是一个C++对象并且需要按对象设置该值,因此不适用于使用QML readonly property。有没有一种解决这个问题的方法,而不涉及冗余信号?
只是为了澄清,这里的根本问题是,在其当前状态下,QML几乎无法初始化在C++中实现的对象的只读属性。这样的属性将没有setter(如果有,则会产生“取决于...”警告),并且没有办法将值传递给对象的构造函数。如下面的答案所述,有一些解决方法,但它们要么应用受限,要么非常不方便。在C++中,您甚至可以对每个实例进行const成员的初始化,通过将值传递给执行初始化的构造函数的初始化器列表,但在QML中不行。即使在纯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())则是被禁止的。没有冗余通知信号的setter可以解决问题,然而qtquick的实现设计假定如果它有一个setter,它将会被改变,因此会抱怨它没有notify信号,我认为这是一个设计上的疏忽 - 也就是说,在QML端提供任何以每个实例为基础初始化只读属性的方式。目前,正如我已经提到的,3个可能的解决方案要么带来额外的开销(有一个notify信号),要么有限制(在对象创建之前设置值 - 只适用于动态实例化),要么带来重大不便(使用setter和getter插槽而不实现实际的属性接口)。

2个回答

1

这有点取决于您需要该值的用途。

如果该属性用于任何属性绑定,则需要通知,否则您将陷入一种情况,即依赖对象实例化和绑定评估的顺序。

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

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


除了它会产生不必要的开销之外。让我们试着客观一点,抽象出你可以理解的偏见——在QML方面无法初始化只读属性是一个问题和设计缺陷。老实说,如果这是某个特殊情况,我可以理解假装它不存在,但当qtquick被许多这样的限制所困扰时,拒绝承认这些问题并不能真正使产品变得更好。两周前,我在qml中发现了整整5个不同的bug,仅仅在一天内。这就是对质量的评判…… - dtech
所以你建议的是有些情况下看起来像属性但实际上并不是属性。 就像ListElement中的“属性”一样。你考虑过为你的类型做同样的事情吗? - Kevin Krammer
就实际的“初始化”而言,通过我的测试,我没有遇到任何表达式在初始值设置之前被评估的情况。我不知道评估顺序有多随机,但我假设如果这些属性在对象体中的第一件事就被初始化,那么对它们的后续引用将已经具有初始化的值。这就是C++构造函数初始化列表的思想——它在构造函数代码之前隐式执行,但即使如此,您仍然必须注意实际的初始化顺序,因为可能存在依赖关系。 - dtech
我不明白你的意思 - ListElement的属性并非固定不变,你可以通过ListModel.set()修改它们。该属性是一个唯一的整数ID,需要从QML中设置,因为每个上下文都有自己持久化的ID管理器。 - dtech
ListElement属性并不是实际的属性,它们只是看起来像属性而已。 这些“属性”被固定为一个简单的值,不能用作属性绑定的目标。 因此,我想知道你是否正在寻找像这样的东西,一个实际上并不是属性,但在语法上看起来像一个属性以进行初始化。它们的值在解析时被固定,这听起来很符合你描述的目标。 - Kevin Krammer
显示剩余7条评论

0

一种可能的解决方案是只向QML公开setter和getter函数,而不是实际属性。这样做略显笨拙且更加冗长(多了一个()),但至少可以解决控制台中的警告信息。这样做可能会更有效率,因为除了消除冗余信号外,可能会减少绑定表达式重新评估观察者的实例化。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接