使用QML保存窗口状态

6

有没有一种不错的方法来记录QtQuick应用程序的窗口状态?文档给出以下方法:

Settings {
    property alias x: mainWindow.x
    property alias y: mainWindow.y
    property alias width: mainWindow.width
    property alias height: mainWindow.height

但是该方法有三个缺陷:

  1. 每次调整窗口大小/移动时都会不断地写入设置文件。
  2. 它不能记住窗口是否被最大化(Notepad++也有这个烦人的缺陷)。
  3. 如果你最大化了窗口,它将无法保存未最大化状态的几何信息。

有没有更好的代码实现?


创建了 https://bugreports.qt.io/browse/QTBUG-96632 来跟踪此问题。 - Mitch
2个回答

2
我已经成功地制作了一个东西,根据我的有限测试结果它运行得非常好。但我确实不得不做一些 hacky 的部分,因为不幸的是Window.visibilityWindow.x/y/width/height更新之后更新,这意味着如果您试图记录窗口几何图形,则无法仅在onXChanged中检查Window.visibility的状态。相反,我必须记录两个先前的值,然后如果窗口被最大化,则丢弃最近的值。

编辑:这并不完美。如果你将窗口最大化,然后关闭应用程序。然后再打开它然后再关闭它。然后再次打开它,当你取消最大化时,它将不会回到正确的窗口大小。我认为修复这个问题的 QML 将会很难看,所以我可能会在 C++ 中实现它,因为那里才是它真正属于的地方。

import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.3
import Qt.labs.settings 1.0

Item {
    property Window window

    // Default properties for the application's first run.
    property int defaultX: 100
    property int defaultY: 100
    property int defaultWidth: 500
    property int defaultHeight: 500
    property bool defaultMaximised: false

    Settings {
        id: windowStateSettings
        category: "WindowState"
        property int x
        property int y
        property int width
        property int height
        property bool maximised
    }

    Component.onCompleted: {
        if (windowStateSettings.width === 0 || windowStateSettings.height === 0)
        {
            // First run, or width/height are screwed up.
            curX = defaultX;
            curY = defaultY;
            curWidth = defaultWidth;
            curHeight = defaultHeight;
            curMaximised = defaultMaximised
        }
        else
        {
            curX = windowStateSettings.x;
            curY = windowStateSettings.y;
            curWidth = windowStateSettings.width;
            curHeight = windowStateSettings.height;
            curMaximised = windowStateSettings.maximised
        }
        window.x = prevX = curX;
        window.y = prevY = curY;
        window.width = prevWidth = curWidth;
        window.height = prevHeight = curHeight;

        if (curMaximised)
            window.visibility = Window.Maximized;
    }

    // Remember the windowed geometry, and whether it is maximised or not.
    // Internal use only.
    property int curX
    property int curY
    property int curWidth
    property int curHeight
    property bool curMaximised

    // We also have to save the previous values of X/Y/Width/Height so they can be restored if we maximise, since we
    // can't tell that the updated X,Y values are because of maximisation until *after* the maximisation.
    property int prevX
    property int prevY
    property int prevWidth
    property int prevHeight

    Connections {
        target: window
        onVisibilityChanged: {
            if (window.visibility === Window.Maximized)
            {
                curMaximised = true;
                // Ignore the latest X/Y/width/height values.
                curX = prevX;
                curY = prevY;
                curWidth = prevWidth;
                curHeight = prevHeight;
            }
            else if (window.visibility === Window.Windowed)
            {
                curMaximised = false;
            }
            else if (window.visibility === Window.Hidden)
            {
                // Save settings.
                windowStateSettings.x = curX;
                windowStateSettings.y = curY;
                windowStateSettings.width = curWidth;
                windowStateSettings.height = curHeight;
                windowStateSettings.maximised = curMaximised;
            }
        }

        // We can't use window.visibility here to ignore the maximised geometry because it changes after the geometry.
        // Instead we cache the two previous values and revert them if maximised.
        onXChanged: {
            prevX = curX;
            curX = window.x;
        }
        onYChanged: {
            prevY = curY;
            curY = window.y;
        }
        onWidthChanged: {
            prevWidth = curWidth;
            curWidth = window.width;
        }
        onHeightChanged: {
            prevHeight = curHeight;
            curHeight = window.height;
        }
    }

}

使用方法如下:

ApplicationWindow {
    id: mainWindow

    WindowStateSaver {
        window: mainWindow
        defaultWidth: 1000 // Or whatever. You can also specify the defaultX/Y if you want.
        defaultHeight: 700
    }

非常感谢您!我知道如何在C++中实现这一点,但是我在QML中遇到了困难!我还修复了您在编辑中提到的情况:当您保存设置时,需要检查curMaximised状态:如果为真,则需要保存先前的状态,否则保存当前状态。当可见性变为窗口化时,我也更新了所有状态,但我不知道是否必要。 - Citron
如果我在外接显示器上关闭窗口,然后拔掉显示器并重新启动应用程序,Windows 会发生什么? 我猜测它会在外接显示器上打开应用程序,因此用户将无法访问它。 - Silex
我正在使用Qt 6。我认为他们已经改变了行为。可见性似乎在x/y/w/h之前更新。我在C++中管理设置。每个都使用“Binding”更新x/y/w/h,“isMaximized”在“onVisibilityChanged”处理程序中更新。IsMaximized始终首先更新,因此我可以使用它来知道不要存储其他设置。 - DanielAbele

0

基于Timmmm的答案,但可能更适合。

import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.Controls 2.3
import Qt.labs.settings 1.0

Item
{
    property Window window
    property string windowName: ""

    Settings
    {
        id: s
        category: windowName
        property int x
        property int y
        property int width
        property int height
        property int visibility
    }

    Component.onCompleted:
    {
        if (s.width && s.height)
        {
            window.x = s.x;
            window.y = s.y;
            window.width = s.width;
            window.height = s.height;
            window.visibility = s.visibility;
        }
    }

    Connections
    {
        target: window
        onXChanged: saveSettingsTimer.restart()
        onYChanged: saveSettingsTimer.restart()
        onWidthChanged: saveSettingsTimer.restart()
        onHeightChanged: saveSettingsTimer.restart()
        onVisibilityChanged: saveSettingsTimer.restart()
    }

    Timer
    {
        id: saveSettingsTimer
        interval: 1000
        repeat: false
        onTriggered: saveSettings()
    }

    function saveSettings()
    {
        switch(window.visibility)
        {
        case ApplicationWindow.Windowed:
            s.x = window.x;
            s.y = window.y;
            s.width = window.width;
            s.height = window.height;
            s.visibility = window.visibility;
            break;
        case ApplicationWindow.FullScreen:
            s.visibility = window.visibility;
            break;
        case ApplicationWindow.Maximized:
            s.visibility = window.visibility;
            break;
        }
    }
}

使用方法。类似于这样:

ApplicationWindow 
{
    id: mainWindow
    WindowStateSaver 
    {
        window: mainWindow
        windowName: "mainWindow"
    }
}

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