2016-06-07 97 views
11

我们有一个相当大的QtQuick应用程序,其中有很多模态对话框。所有这些模式共享一致的外观和行为,并留下了按钮,右键,内容和其他警告小部件。我们用下面的基类(PFDialog.qml):关闭应用程序时出现QQuickItem析构函数/ changeListeners崩溃(Qt 5.6)

Window { 
    property alias content: contentLayout.children 
    ColumnLayout { 
     id: contentLayout 
    } 
} 

,并宣布以下列方式对话框(main.qml):

Window { 
    visible: true 
    property var window: PFDialog { 
     content: Text { text: "Foobar" } 
    } 
} 

的问题是,当应用程序被关闭,一个段错误发生在QQuickItem析构函数中。这段错误很难重现,但这是一个绝妙的实现方式:在调试模式下,Visual Studio将释放的内存填充0xDDDDDDD,每次触发段错误。

完整的示例应用程序可以在这里找到:https://github.com/wesen/testWindowCrash

坠机发生在QQuickItem::~QQuickItem

for (int ii = 0; ii < d->changeListeners.count(); ++ii) { 
    QQuickAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); 
    if (anchor) 
     anchor->clearItem(this); 
} 

这样做的原因是,我们的对话的内容(在上面的例子中,文本项)是主窗口的QObject子元素,但是是对话窗口的可视子元素。关闭应用程序时,首先销毁对话窗口,并且在删除文本项目时,对话窗口(仍然注册为changeListener)已过时。

现在我的问题是:

  • 这是一个QtQuick错误?如果对话框注销自己作为它的孩子的变化侦听器,当它被销毁时(我认为应该)
  • 是我们的property alias content: layout.children模式正确,还是有更好的方法来做到这一点?在声明默认属性别名时也会发生这种情况。

为了完整起见,以下是我们如何在我们的应用程序中对其进行修补。当内容发生变化时,我们将所有项目重新分配给布局项目。优雅之一,你会大家都同意。

function reparentTo(objects, newParent) { 
    for (var i = 0; i < objects.length; i++) { 
     qmlHelpers.qml_SetQObjectParent(objects[i], newParent) 
    } 
} 
onContentChanged: reparentTo(content, contentLayout) 
+0

请问您可以使用自包含的测试用例编辑问题吗? –

+0

对不起,我不确定我关注你。这与我所能做到的完全一样(顶部的8行是整个应用程序)。你可以克隆存储库,如果你想有一些内置的东西。 –

+0

哦,好吧,对不起,我没有注意到这一切。谢谢! –

回答

5

我有这个问题很多次,我不不认为这是一个错误,更像是一个设计限制。你得到的隐含行为越少,你拥有的控制力就越少,导致不合适的对象销毁顺序和访问悬空引用。

有很多情况下,这可能发生在“你自己”,因为你超出了“qml应用程序”这本书的范围,但在你的情况下,你是谁在做这件事。

如果你想正确的所有权,不使用此:

property var window: PFDialog { 
    content: Text { text: "Foobar" } 
} 

而是使用此:

property Window window: dlg // if you need to access it externally 
PFDialog { 
    id: dlg 
    content: Text { text: "Foobar" } 
} 

这里是一个很好的理由:

property var item : Item { 
    Item { 
    Component.onCompleted: console.log(parent) // qml: QQuickItem(0x4ed720) - OK 
    } 
} 
// vs 
property var item : Item { 
    property var i: Item { 
    Component.onCompleted: console.log(parent) // qml: null - BAD 
    } 
} 

孩子与财产不一样。物业仍然收集,但他们没有父亲。

作为实现 “动态内容” 的thingie,我有良好的结果ObjectModel

Window { 
    property ObjectModel layout 
    ListView {    
     width: contentItem.childrenRect.width // expand to content size 
     height: contentItem.childrenRect.height 
     model: layout 
     interactive: false // don't flick 
     orientation: ListView.Vertical 
    } 
} 

然后:

PFDialog { 
    layout: ObjectModel { 
     Text { text: "Foobar" } 
     // other stuff 
    } 
} 

最后,对于结束前做明确清理的缘故该应用程序在您的主QML文件上可以实现处理程序:

onClosing: { 
    if (!canExit) doCleanup() 
    close.accepted = true 
} 

这确保窗口不会首先进行清理而被销毁。

最后:

是我们的财产别名内容:layout.children模式是正确的,或者是 有更好的方法来做到这一点?声明 默认属性别名时也会发生这种情况。

这不是我最后一次看到它,但它至少在几年前。将对象声明为儿童实际上成为其他对象的孩子当然是很好的,但在当时这是不可能的,而且可能不是。因此需要涉及对象模型和列表视图的稍微更详细的解决方案。如果你调查此事并找到不同的东西,留下评论让我知道。

2

我相信你不能在var中声明一个窗口对象。在我的测试中,SubWindow从不打开,有时会在启动时崩溃。

窗口可以在一个Item内部或另一个窗口内声明;在这种情况下,内窗口将自动成为外窗口“瞬时” 见:http://doc.qt.io/qt-5/qml-qtquick-window-window.html

修改代码以这样的:

Window { 
    visible: true 
    PFDialog { 
     content: Text { text: "Foobar" } 
    } 
} 
+0

是的,这是我们期望的行为,实际上我们在我们的主应用程序中使用Dialog作为类。我认为这不是问题,确实可以用一个简单的Item代替Window,我只是想模仿我们的主应用程序。感谢您的洞察力。 –