Как я могу получить окно с безграничным дочерним окном для повторного масштабирования на текущий экран при настройке нескольких мониторов? - программирование
Подтвердить что ты не робот

Как я могу получить окно с безграничным дочерним окном для повторного масштабирования на текущий экран при настройке нескольких мониторов?

В моем приложении есть главное окно, которое создает и открывает экземпляр подкласса QML Window {} используя createObject(). Это окно имеет свои flags: устанавливается как окно без полей (я добавил код, чтобы его можно было схватить и перетащить).

Когда я прикрепляю монитор к своему ноутбуку и устанавливаю его коэффициент масштабирования на 125% (или 150%), когда я перетаскиваю свое главное окно на второй монитор, вы можете внезапно увидеть "привязку" к большему размеру, когда он достигнет на полпути. Аналогичным образом, когда я перетаскиваю его обратно на экран своего ноутбука, он снова "привязывается" к меньшему размеру, когда я нахожусь на полпути (это поведение - то, что я хочу, поэтому никаких проблем здесь нет).

Моя проблема заключается в том, что когда я перетаскиваю свое созданное окно без полей в монитор, он сохраняет исходный 100% масштабный коэффициент и не "привязывается" к большему размеру. Если я перетащил свое главное окно на монитор, он станет больше, но окно без полей останется в меньшем масштабе; только когда я хватаю окно без полей и немного перемещаю его, он внезапно "привязывается" к размеру большего масштаба. То же самое происходит в обратном порядке - если я затем перетащил окно без полей обратно на ноутбук, он останется в большем размере, пока я не перетащил главное окно назад, а затем немного сдвинуту окно без полей (после чего он внезапно "привязывается" к меньший размер).

Таким образом, похоже, что это созданное Window использует масштабный коэффициент экрана, который родительское окно окно, которое создало его, в настоящее время находится, даже если оно находится на другом экране.

Это происходит потому, что Window без полей? (Я собираюсь протестировать это, но мой процесс сборки невероятно медленный). Или есть способ установить это Window без полей, чтобы он обнаружил, что он пересекает новый экран и перемасштабирует себя (так же, как мое главное окно)?

Обновление: я просто запускал тест, предоставляя моему Window собственный заголовок, и с помощью заголовка окно мгновенно принимает ("привязывается к") масштабный коэффициент того, на каком экране он находится, как и в моем главном окне (и не зависит от коэффициент масштабирования основного окна).

Итак, есть ли способ дублировать это поведение окна автомасштабирования с окном без полей? Некоторый флаг, который мне нужно вызвать, или некоторые методы, которые мне нужно вызвать, чтобы заставить ОС перемасштабировать окно?

Обновление 2: я опробовал решение Felix SetWindowPos. Он перемещает окно, но это не устраняет проблему масштабирования - поведение безрамного окна одно и то же, и оно по-прежнему неправильно отображает коэффициент масштабирования экрана, на котором он находится.

Я запускаю тест, используя MoveWindow вместо SetWindowPos чтобы узнать, не влияет ли это на что-либо [ edit: MoveWindow не работает, либо - та же проблема]. Затем я попытаюсь отправить SendMessage или PostMessage вместе с предложением NoBugz о сообщении WM_DPICHANGED.

Любые другие предложения приветствуются.

Обновление 3: я только что создал быстрое приложение С# (winforms), чтобы узнать, возникает ли с ним одна и та же проблема, и это не так - когда форма без полей в приложении С# перетаскивается на другой монитор, он сразу же поднимает изменение масштабного коэффициента. Таким образом, похоже, что это проблема Qt.

Обновление 4: см. Мой ответ ниже для рабочего решения этой проблемы (если немного взломать).

4b9b3361

Ответ 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

Насколько я понимаю, ваша текущая цель - переместить окно через WIN-API. Вам нужно будет сделать это через C++. Такой подход будет:

  1. Передайте QML-окно в C++ -метод, открытый QML как QQuickWindow (окно QML запускает этот тип, как видно из документации)
  2. Используйте QWindow::winId чтобы получить собственный HWND
  3. Вызовите SetWindowPos WIN-API SetWindowPos чтобы переместить его

Пример кода (C++ -part):

// 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 окна.