如何更改QML对话框/窗口的临时父代?

mhc*_*rvo 5 qt qml qt5 qt5.3

我正在开发一个Qt(5.3)桌面应用程序(带有QML ui的C++核心),其主窗口是一个ApplicationWindow,并且在某些时候启动Dialogs.由于在Windows和Mac OS X之间使用对话框模式存在差异(例如,关于对话框在Mac OS X上很少是模态的,但在Windows上几乎总是模态的),而且在呈现一些对话框的内容方面,我也是我们改变了设计,允许实现特定于平台的对话框版本.

为此我创建了以下DialogLoader:

Loader {
    id: dialogFactory
    property string dialogName
    function platformFolder() {
        if (Qt.platform.os === "osx")
            return "osx"
        return "win"
    }
    onDialogNameChanged: { source = platformFolder() + "/" + dialogName + ".qml" }
    onStatusChanged: {
        if (dialogFactory.status === Loader.Error)
            console.log("DialogFactory: failed to load file: " + source);
        else if (dialogFactory.status === Loader.Ready)
            console.log("DialogFactory: file \"" + source + "\" loaded")
    }
}
Run Code Online (Sandbox Code Playgroud)

我用的如下:

ApplicationWindow {
    // …
    property alias aboutDialog: aboutDialogLoader.item
    // …
    DialogLoader { id: aboutDialogLoader; dialogName: "AboutDialog" }
    // …
    Action { text: qsTr("About..."); onTriggered: aboutDialog.show() }
    // …
}
Run Code Online (Sandbox Code Playgroud)

这种方法工作正常,它符合我的需要,除了一件事:Windows上的模态对话框的行为与我ApplicationWindow直接声明它们时的行为不同.如果应用程序在打开模态窗口并且再次授予焦点时失去焦点,则模式窗口将出现在主窗口后面,这会导致应用程序无法运行.

经过一些研究后,我意识到问题的原因在于,使用加载器方法ApplicationWindow并不是作为瞬态父级的Dialog.

我已经找到了一个解决方法,通过在发生这种情况时手动将Dialog置于前面:

MainWindow {
    // ...
    onActiveChanged: {
        if (Qt.platform.os === "windows") {
            if (active) {
                // ...
                if (aboutDialog.visible) {
                    aboutDialog.requestActivate()
                }
                // ...
            }
        }
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

但我认为如果可能的话,最好避免这种解决方法.在没有任何运气的情况下深入研究Qt文档之后,我决定发布这个问题,可以恢复如下:是否可以更改QML窗口的瞬态父级?如果是这样,请你指出如何?

我们欢迎有关避免重复使用的不同方法的建议.

Sha*_*arm 3

我想这是不可能的。至少在 Qt 5.4 中是这样。

来自文档(QML 类型“Window”的默认属性“data”)

data : list data 属性允许您在一个 Window 中自由混合可视子项、资源和其他 Windows。

如果将另一个窗口分配给数据列表,则嵌套窗口将成为外部窗口的“瞬态”。

如果将一个 Item 分配给数据列表,它将成为 Window 的 contentItem 的子项,以便它出现在窗口内。该项目的父级将是窗口的 contentItem,它是该窗口内项目所有权树的根。

如果您分配任何其他对象类型,它将作为资源添加。

通常不需要引用 data 属性,因为它是默认值

Window 的属性,因此所有子项都会自动分配给该属性。

由于 QML 绑定限制,似乎所有“窗口”的动态创建都有错误的瞬态父级。所有 QML 列表元素均不可修改。因此,不建议使用具有属性绑定的列表。如果它发生变化,您将永远不会收到通知信号。因此,我猜想,QML 引擎永远不知道新的 QML 元素是“Window”类型,并且有必要将其添加到 Windows 层次结构中。

来自文档(https://www.ics.com/files/qtdocs/qml-list.html):

不能以任何其他方式修改列表属性。无法通过 JavaScript 操作动态地将项目添加到列表或从列表中删除;列表上的任何 push() 操作都只会修改列表的副本,而不是实际的列表。(这些当前限制是由于涉及列表的属性绑定的限制造成的。)

也许这是一个错误,也许是一个功能。无论如何,目前不可能使用适当的瞬态父级动态加载“窗口”类型。

我发现的最好的解决方法 - 使用模态:Qt.ApplicationModal进行对话框。