代码之家  ›  专栏  ›  技术社区  ›  MusiGenesis

如何在多监视器设置中获得无边界子窗口以重新缩放到当前屏幕?

  •  14
  • MusiGenesis  · 技术社区  · 6 年前

    我的应用程序有一个主窗口,它创建并打开一个qml子类的实例。 Window {} 使用 createObject() . 这个窗户有 flags: 设置为无边界窗口(我添加了代码以便可以抓取和拖动它)。

    当我将显示器连接到笔记本电脑上并将其字体比例系数设置为125%(或150%)时,当我将主窗口拖到第二个显示器上时,当它到达中间点时,您会看到它突然“捕捉”到较大的尺寸。同样,当我把它拖回到我的笔记本电脑屏幕上时,当我走到一半的时候,它又会“快速”变小(这种行为是我想要的,所以这里没有问题)。

    我的问题是,当我把创建的无边界窗口拖到监视器上时,它会保持原来的100%比例因子,不会“捕捉”到更大的尺寸。如果我把主窗口拖到显示器上,它会变大,但无边界窗口的比例仍然较小;只有当我抓住无边界窗口并稍微移动它时,它才会突然“捕捉”到较大的比例大小。反过来也会发生同样的事情——如果我把无边界窗口拖回到笔记本电脑上,它会保持较大的尺寸,直到我把主窗口拖回来,然后稍微移动无边界窗口(这时它会突然“捕捉”到较小的尺寸)。

    所以这似乎创造了 Window 使用屏幕的比例因子 父窗口 创建它的窗口当前在中,即使它在不同的屏幕中。

    发生这种情况是因为 窗口 是无边界的吗?(我将要测试这个,但是我的构建过程非常缓慢)或者有什么方法可以设置这个无边界的 窗口 向上,以便它检测到它正在进入一个新屏幕,并重新缩放自身(与主窗口的方式相同)?

    更新: 我刚做了一个测试 窗口 一个本地的标题栏,和标题栏,窗口立即采用(“捕捉”)的比例因子,无论它是在哪个屏幕,就像我的主窗口(独立于主窗口的比例因子)。

    那么,是否有任何方法可以用无边界窗口复制这种自动缩放窗口行为?我需要调用某个标志,还是需要调用某个方法来让操作系统重新缩放窗口?

    更新2: 我试过费利克斯的 SetWindowPos 解决方案。它确实移动了窗口,但这并不能解决缩放问题——无框窗口的行为是相同的,它仍然不能正确地拾取它所在屏幕的缩放系数。

    我正在用 MoveWindow 而不是 设置窗口位置 看看这是否影响到什么[ 编辑: 移动窗口 也不起作用-同样的问题]。那我就试试看 SendMessage PostMessage 以及Nobugz关于wm_dpichanged消息的建议。

    欢迎提出其他建议。

    更新3: 我刚刚创建了一个快速的C应用程序(winforms),看看是否也出现了同样的问题,但它没有——当C应用程序中的无边界窗体被拖到另一个监视器中时,它会立即接收比例因子的变化。所以这似乎是一个qt问题。

    更新4: 关于这个问题的有效解决方案,请参阅下面的我的答案(如果有点黑客)。

    2 回复  |  直到 6 年前
        1
  •  6
  •   Felix    6 年前

    据我所知,您当前的目标是通过win-api移动窗口。 你将不得不通过C++来这样做。方法是:

    1. 将QML窗口传递给一个被暴露为QML的C++方法 QQuickWindow (如文档中所示,qml窗口将显示该类型)
    2. 使用 QWindow::winId 获取本地硬件
    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,会发生什么。

        2
  •  1
  •   MusiGenesis    6 年前

    我想出了一个相对简单的方法来解决这个问题。由于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()
    
    }