如何使非模态对话框窗口始终置顶?

11
我在应用程序中使用一个Dialog {}实例来显示一个小的控制器窗口,用户可以与之交互以影响主窗口中的功能(类似于遥控器)。我可以使这个对话框模态(modality: Qt.WindowModalmodality: Qt.ApplicationModal),或者我可以使用modality: Qt.NonModal使其非模态。

我的问题是,我需要使它非模态,但始终位于主窗口的顶部。如果我使用Qt.NonModal,我仍然可以点击主窗体,但我的对话框就会被隐藏在后面。Dialog类似乎没有flags:属性,所以我不能只将其设置为Qt.WindowsStaysOnTopHint

有没有办法仅从QML端设置此对话框的标志?或者是否可以编写一个简单的C++实用程序方法,我可以从我的对话框的Component.onCompleted:中调用并传递对话框以在那里设置其窗口标志?

更新:为了说明我在谈论什么,这里是我的对话框位于主窗口顶部的示例:

enter image description here

这是我的对话框位于主窗口下方:

enter image description here

我希望我的对话框不会像这样被主窗口覆盖,但我仍然希望能够点击和与我的主窗口交互。换句话说,我想要我的对话框是非模态的,但始终保持在最上面。


也许你可以使用MouseArea过滤掉对话框外的点击事件? - sk2212
@sk2212:我不认为那样会奏效。我希望用户能够点击并与主窗口或浮动的远程进行交互。基本上只是标准的工具窗口行为。 - MusiGenesis
嗯...你能否从你的窗口和对话框中创建一个简单的截图?我还是不明白你的意思。 - sk2212
主窗口是“对话框”的父窗口吗?这应该足以使“对话框”保持在顶部——我想。 - G.M.
@G.M.:主窗口是对话框的父窗口(我认为,它目前是内联定义的),但它仍然在主窗口下面。 - MusiGenesis
@sk2212:已添加屏幕截图,但这只是标准工具窗口行为:非模态但始终置顶。我只是不知道如何在QML中实现这一点。 - MusiGenesis
3个回答

10
请尝试使用Window而不是Dialog,这样您就可以访问flags属性。
您可以将flags设置为Qt.WindowStaysOnTopHint,以使您的窗口始终位于其他窗口之上。您可以在此处找到标志列表:这里。(在QML中别忘了用.替换::) Main.qml :
import QtQuick 2.5
import QtQuick.Controls 2.0
import QtQuick.Dialogs 1.2

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")


    Button {
        id: btn
        width: 100 ; height : 40
        text: "click me"
    }

    Text {
        anchors.top : btn.bottom
        text: "Button currently pressed"
        visible: btn.pressed
    }

    DialogOnTop {

    }
}

DialogOnTop.qml :

import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4

Window {
    id: myWindow

    width: 200
    height: 200

    flags:  Qt.Window | Qt.WindowSystemMenuHint
            | Qt.WindowTitleHint | Qt.WindowMinimizeButtonHint
            | Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint


    visible: true
    modality: Qt.NonModal // no need for this as it is the default value


    Rectangle {
        color: "lightskyblue"
        anchors.fill: parent
        Text {
            text: "Hello !"
            color: "navy"
            anchors.centerIn: parent
        }
    }
}

结果:

置顶窗口


所以我的早期尝试使用Window在createComponent上出现了无法解释的失败(这是关于不存在的标志和模态属性的错误),但是当我根据你的示例创建了一个新类时,Window完美地工作了。我正在开发的应用程序是从其他开发人员继承的代码库,他们似乎在C++中添加了一些神奇的底层代码,用他们自己的版本替换了QML中真正的Window类(因此没有公开暴露标志和模态性),因此导致了错误。一旦我在我的类中注释掉他们的import语句,Window就能够工作了... - MusiGenesis
3
完美地、非模态地并且总在我的主窗口之上。有时候我讨厌我的同行开发者,但是我喜欢。我将在这个问题上添加一个悬赏,你将得到这个悬赏(我本来就打算悬赏帮助我找到答案)。 - MusiGenesis

2
通常,您不仅想使用Dialog创建新窗口,而是要使用其实现的功能和接口...。 Dialog没有继承WindowApplicationWindow的原因很明显:只要它没有open(),就没有窗口。但是一旦打开了,就有了一个ApplicationWindow(来自QtQuick.Controls 1.4)。
现在,在文档中,我们找到了这个很好的附加属性:ApplicationWindow,它对每个Item都可用,并且方便地允许我们访问窗口。然后,我们只需要找到一种方法,在ApplicationWindow可用时设置正确的标志,例如当我们收到信号visibleChanged时。
由于Dialog也不是Item,所以我们需要使用其contentItem来访问此附加属性。
当我们把所有这些放在一起时,结果可能如下所示: NonModalDialogThatStaysOnTop.qml // 我不擅长命名
import QtQuick 2.3
import QtQuick.Controls 1.4 // You need this, to have access to the `ApplicationWindow`-attatched property
import QtQuick.Dialogs 1.2

Dialog {
    onVisibleChanged: {
        if (visible) contentItem.ApplicationWindow.window.flags |= Qt.WindowStaysOnTopHint
    }
    modality: "NonModal"
}

现在您拥有了自己喜欢的Dialog,它会保持在顶部,但不是模态的。

它可以工作!但是,它只能与QtQuick.Controls第一代导入一起使用(而不是第二代)。此外,我还必须添加一对其他标志才能使标题和关闭按钮可见:Qt.WindowTitleHint和Qt.WindowCloseButtonHint。 - Artem Zh.

1

好的,您只需创建一个对话框(或类似对话框的组件),并希望与主窗口和对话框窗口进行交互。

请尝试以下操作:

main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0

ApplicationWindow {
    id: rootWindow
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    color: "green"

    Rectangle {
        id: behind
        anchors.fill: parent
        color: Qt.rgba(0, 0, 0,  0.7)
        visible: false
    }

    MouseArea {
        enabled: behind.visible
        anchors.fill: parent

        onClicked: {
            console.log("Root Window")
        }
    }

    Button {

        text: "Open Dialog"

        onClicked: {
            behind.visible = true;
            var comp = Qt.createComponent("qrc:/MyDialog.qml");
            // var comp = Qt.createComponent("qrc:/DialogQt.qml");
            var obj1 = comp.createObject(rootWindow, {});
            obj1.z = 2;
        }
    }
}

MyDialog.qml

import QtQuick 2.7

Rectangle {
    id: modalWindow
    width: 200
    height: 200
    color: "red"

    anchors.centerIn: parent

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.log("Modal Window")
        }
    }
}

点击“打开对话框”按钮将创建并打开“模态”对话框,位于您的主窗口组件的顶部。

当然,您必须自己调整“MyDialog.qml”文件以适应您的设计要求。

然而,像G.M在评论部分已经指出的那样,将其用作“真正的”对话框也可以工作(对我而言)。

DialogQt.qml

Dialog {
    visible: true
    title: "Blue sky dialog"

    modality : Qt.ApplicationModal

    contentItem: Rectangle {
        color: "lightskyblue"
        anchors.fill: parent
        Text {
            text: "Hello blue sky!"
            color: "navy"
            anchors.centerIn: parent
        }
    }

}

我想我没有很好地传达我想做的事情。我可以轻松地在我的主窗口中执行你在这里回答的操作 - 弹出一个矩形并与它或窗口下面的内容交互。这只是标准的QML布局。我正在尝试使用单独的窗口来实现这一点 - 这也是你的DialogQt.qml无法实现的,因为它是模态的(我需要一个单独的窗口,它是非模态的但始终位于我的主窗口之上)。 - MusiGenesis
@MusiGenesis 你必须使用 Dialog 吗?使用 Window 代替会起作用吗?我在我的应用程序中使用 Window 做了完全相同的事情(非模态但始终置顶)。 - Blabdouze
@Blabdouze:我之前尝试使用Window而不是Dialog,但是createComponent调用一直失败,说在Window上没有flags:modality:这样的属性,我必须设置这两个属性才能使其工作(我猜)。我使用的是Qt 5.5,也许这就是我的问题所在?你能否发布一个使用Window的答案? - MusiGenesis
@MusiGenesis 我认为版本不是问题,如果我没记错的话,当时我使用的是5.3版本,并且我有一个带有标志的Window。我发布了一个对我有效的解决方案(在5.7上运行,但我认为这不是版本问题)。 - Blabdouze

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