如何在多显示器设置中获取无边界的子窗口以重新缩放到当前屏幕?

时间:2018-08-09 00:51:00

标签: windows qt qml

我的应用程序有一个主窗口,该主窗口使用Window {}创建并打开QML createObject()的子类的实例。此窗口的flags:设置为无边界窗口(我添加了代码,以便可以对其进行拖动和拖动)。

当我将显示器连接到笔记本电脑并将其字体比例因子设置为125%(或150%)时,将主窗口拖到第二个显示器上时,您会看到它突然“捕捉”到更大的尺寸当它到达中点时。同样,当我将其拖回到笔记本电脑屏幕上时,再次滑过一半时,它会再次“缩小”到较小的尺寸(此行为正是我想要的,所以这里没有问题)。

我的问题是,当我将创建的无边界窗口拖到监视器中时,它将保留原始的100%比例因子,而不会“捕捉”到更大的尺寸。如果将主窗口拖到监视器上,它将变大,但无边界窗口将保持较小的比例;只有当我抓住无边界窗口并稍微移动它时,它才会突然“捕捉”到更大的比例尺。反之亦然-如果我将无边界窗口拖回笔记本电脑,它将保持较大尺寸,直到我将主窗口向后拖动,然后将无边界窗口略微移动(此时,它突然“ s”到较小的尺寸。)

因此,似乎创建的Window使用了创建它的父窗口窗口当前所在的屏幕的比例因子,即使它本身在另一个屏幕中也是如此。

这是因为Window是无边界的吗? (我将对此进行测试,但是我的构建过程非常缓慢)或者是否有任何方法可以设置此无边界Window,以便它检测到它正在跨入新屏幕并自行重新缩放(在就像我的主窗口一样)?

更新:我刚刚运行了一个测试,为我的Window提供了一个本机标题栏,有了标题栏,窗口立即采用(“捕捉到”)无论发生哪个屏幕的比例因子就像我的主窗口一样(与主窗口的比例因子无关)。

那么有什么方法可以与无边界窗口复制这种自动缩放窗口的行为?我需要调用某些标志,或者需要调用某些方法来使操作系统重新缩放窗口?

更新2:我尝试了Felix的SetWindowPos解决方案。它确实可以移动窗口,但是不能解决缩放问题-无框架窗口的行为是相同的,并且仍然无法正确拾取其所在屏幕的缩放因子。

我正在使用MoveWindow而不是SetWindowPos进行测试,以查看是否会影响任何内容[ edit: MoveWindow也不起作用-同样的问题]。然后,我将尝试使用SendMessagePostMessage以及NoBugz对WM_DPICHANGED消息的建议。

欢迎其他任何建议。

更新3:我刚刚创建了一个快速的C#应用​​程序(winforms),以查看是否发生了同样的问题,并且没有发生-将C#应用程序中的无边界表单拖到上面时进入另一个监视器,它立即获取比例因子的变化。因此看来这是一个Qt问题。

更新4:请参阅下面的答案,以获取针对该问题的有效解决方案(如果有一点破解的话)。

3 个答案:

答案 0 :(得分:6)

据我了解,您当前的目标是通过WIN-API移动窗口。 您将必须通过C ++这样做。方法是:

  1. 将QML窗口传递给以QQuickWindow暴露给QML的C ++方法(如文档中所示,QML窗口实例化该类型)
  2. 使用QWindow::winId获取本地HWND
  3. 调用SetWindowPos WIN-API方法将其移动

代码示例(C ++部分):

// the method
void moveWindow(QQuickWindow *window, int x, int y) {
    HWND handle = (HWND)window->winId();
    Q_ASSERT(handle);
    ::SetWindowPos(handle, HWND_TOP,
                   x, y, 0, 0,
                   SWP_NOSIZE | SWP_NOZORDER);
}

// assuming "moveWindow" is a member of "MyClass"
qmlEngine->rootContext()->setContextProperty("mover", new MyClass(qmlEngine));

代码示例(QML部分):

// call this method as soon as the drag has finished, with the new positions
mover.moveWindow(idOfWindow, xPos, yPos);

注意:我建议您仅在拖动完成后尝试调用此方法(并像现在一样移动窗口,直到那时为止)。如果可行,您可以尝试在拖动过程中调用此方法,而不用更改窗口的x / y。

答案 1 :(得分:1)

我想出了一种相对简单的方法来解决此问题。由于Qt中的无框架窗口是从创建它的窗口中获取其缩放因子的,所以窍门是创建另一个窗口(具有标题栏,但用户不可见)并在那里创建无框架窗口,然后向其中添加代码无框窗口,用于在用户拖动隐藏窗口时将其保留在其下方。当无框窗口拖动到另一个屏幕时,隐藏的窗口随之移动,拾取新的比例因子(因为它具有标题栏),然后无框窗口也立即获得新屏幕的比例因子。

这是示例解决方案代码:

// HiddenWindow.qml
Window {
    id: hiddenWindow

    // note: just making window visible: false does not work. 
    opacity: 0
    visible: true
    flags: Qt.Tool | Qt.WindowTitleHint | Qt.WindowTransparentForInput | 
           Qt.WindowStaysOnTopHint // Qt.Tool keeps this window out of the 
                 // taskbar

    function createVisibleWindow() {
        var component = Qt.createComponent("VisibleWindow.qml")
        if (component.status === Component.Ready) {
            var win = component.createObject(hiddenWindow)
            return win
        }
    }
}

// VisibleWindow.qml
Window {
    id: visibleWindow
    property var creatorWindow: undefined

    flags: Qt.FramelessWindowHint

    onXChanged: {
        creatorWindow.x = x
    }
    onYChanged: {
        creatorWindow.y = y
    }
    onWidthChanged: {
        creatorWindow.width = width
    }
    onHeightChanged: {
        creatorWindow.height = height
    }
}

然后在主窗口QML中使用这些类:

property var hiddenWindow: undefined
property var visibleWindow: undefined

Component.onCompleted: {

    var component = Qt.createComponent("HiddenWindow.qml")
    if (component.status === Component.Ready) {
        hiddenWindow = component.createObject(null)
    }
    visibleWindow = hiddenWindow.createVisibleWindow()
    visibleWindow.creatorWindow = hiddenWindow

    visibleWindow.show()

}

答案 2 :(得分:0)

当窗口移至其他屏幕时,您需要调整窗口大小

MouseArea {
    anchors.fill: parent
    acceptedButtons: Qt.LeftButton
    onPressed: {
        movePos = Qt.point(mouse.x, mouse.y)
        isDoubleClicked = false
        lastWindowWidth = mainWindow.width
        lastWindowHeight = mainWindow.height
    }
    onPositionChanged: {
        if (!isDoubleClicked) {
            const delta = Qt.point(mouse.x - movePos.x, mouse.y - movePos.y)
            if (mainWindow.visibility !== Window.Maximized) {
                mainWindow.x = mainWindow.x + delta.x
                mainWindow.y = mainWindow.y + delta.y
                mainWindow.width = lastWindowWidth
                mainWindow.height = lastWindowHeight
            }
        }
    }
}