Qt保存和恢复页面布局

一、前言

很多软件在界面上的操作是很灵活的,例如PS,它的主面板是一个个吸附窗口堆叠起来的,用户可以自由拖拽、吸附,并且关闭软件再次打开软件,发现之前的拖拽、吸附完全保存下来了,所以用户可以自由的构建自己舒适的办公环境(软件界面),那这个效果是怎么实现的呢?

在Qt中使用saveState()restoreState(QByteArray)就可以完美的实现这种效果。

QByteArray QMainWindow::saveState(int version = 0) const;
bool QMainWindow::restoreState(const QByteArray &state, int version = 0);

二、效果展示

1、自由调整布局
请添加图片描述
2、重新打开软件
请添加图片描述
在这里插入图片描述


三、关键步骤

在程序的出口处,使用saveState保存界面布局

QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
QFile file(strPath);
if(file.open(QIODevice::WriteOnly)) {
	QDataStream outfile(&file);
	QByteArray ba = this->saveState();
    outfile<<ba;
    file.close();
}

在程序的入口处,使用restoreState恢复界面布局

QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
QFile file(strPath);
if(file.open(QIODevice::ReadOnly)) {
	QByteArray ba;
    QDataStream in(&file);
    in>>ba;
    file.close();
    this->restoreState(ba);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

	//程序入口处
	QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
	QFile file(strPath);
	if(file.open(QIODevice::ReadOnly)) {
		QByteArray ba;
    	QDataStream in(&file);
    	in>>ba;
    	file.close();
    	this->restoreState(ba);
	}
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    //程序出口处
    QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
    QFile file(strPath);
    if(file.open(QIODevice::WriteOnly))
    {
        QDataStream outfile(&file);
        QByteArray ba = this->saveState();
        outfile<<ba;
        file.close();
    }
}

四、详细代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QCloseEvent>
#include <QDataStream>
#include <QByteArray>
#include <QCoreApplication>
#include <QMenuBar>
#include <QToolBar>
#include <QDockWidget>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void CreateMenuBar();
    void CreateToolBar();
    void CreateStatusBar();

protected:
    void closeEvent(QCloseEvent *event);

private:
    Ui::MainWindow *ui;

    QDockWidget *dock_Image;    // 图像窗口
    QDockWidget* dock_Tool;// 工具箱窗口
    QDockWidget* dock_Geom;// 几何变换窗口
    QDockWidget* dock_Gray;// 灰度变换窗口
    QDockWidget* dock_Enhance;// 图像增强窗口
    QDockWidget* dock_Morp;// 形态学处理窗口
    QDockWidget* dock_Color;// 颜色模型窗口
    QDockWidget* dock_Prop;// 属性窗口
    QDockWidget* dock_Output;// 输出窗口
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QWidget* p = takeCentralWidget();   //删除中央窗体
    if (p) {
        delete p;
    }

    setDockNestingEnabled(true);        //允许嵌套dock

    //------------------------------------------------------------------------------
    //创建菜单栏、工具栏、状态栏
    CreateMenuBar();
    CreateToolBar();
    CreateStatusBar();

    //------------------------------------------------------------------------------
    //创建DockWidget
    QDockWidget* dock_Image = new QDockWidget(tr("图像"), this);
    dock_Image->setObjectName("dock_Image");
    dock_Image->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);         // 设置为可移动可浮动,但不可关闭
    dock_Image->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);  // 可移动范围:左右
    dock_Image->setMinimumSize(600, 600);   // 设置最小宽高

    dock_Tool = new QDockWidget(tr("工具箱"), this);            // 工具箱窗口,若想设置特征或移动范围,方法同上。
    dock_Tool->setObjectName("dock_Tool");

    dock_Geom = new QDockWidget(tr("几何变换"), this);          // 几何变换窗口
    dock_Geom->setObjectName("dock_Geom");

    dock_Gray = new QDockWidget(tr("灰度变换"), this);          // 灰度变换窗口
    dock_Gray->setObjectName("dock_Gray");

    dock_Enhance = new QDockWidget(tr("图像增强"), this);       // 图像增强窗口
    dock_Enhance->setObjectName("dock_Enhance");

    dock_Morp = new QDockWidget(tr("形态学处理"), this);        // 形态学处理窗口
    dock_Morp->setObjectName("dock_Morp");

    dock_Color = new QDockWidget(tr("颜色模型"), this);         // 颜色模型窗口
    dock_Color->setObjectName("dock_Color");

    dock_Prop = new QDockWidget(tr("属性"), this);             // 属性窗口
    dock_Prop->setObjectName("dock_Prop");

    dock_Output = new QDockWidget(tr("输出"), this);           // 输出窗口
    dock_Output->setObjectName("dock_Output");

    // 进行布局
    setCentralWidget(dock_Image);       // 指定为中心窗口
    addDockWidget(Qt::LeftDockWidgetArea, dock_Tool);
    addDockWidget(Qt::BottomDockWidgetArea, dock_Output);
    addDockWidget(Qt::RightDockWidgetArea, dock_Geom);
    addDockWidget(Qt::RightDockWidgetArea, dock_Gray);
    addDockWidget(Qt::RightDockWidgetArea, dock_Enhance);
    addDockWidget(Qt::RightDockWidgetArea, dock_Morp);
    addDockWidget(Qt::RightDockWidgetArea, dock_Color);
    addDockWidget(Qt::RightDockWidgetArea, dock_Prop);

    // 分割窗口
    splitDockWidget(dock_Tool, dock_Image, Qt::Horizontal);     // 水平
    splitDockWidget(dock_Geom, dock_Output, Qt::Vertical);      // 垂直

    // 合并窗口
    tabifyDockWidget(dock_Geom, dock_Gray);
    tabifyDockWidget(dock_Gray, dock_Enhance);
    tabifyDockWidget(dock_Enhance, dock_Morp);
    tabifyDockWidget(dock_Morp, dock_Color);

    tabifyDockWidget(dock_Output, dock_Prop);

    dock_Geom->raise();             // raise()函数可使指定窗口置于最前

    //-------------------------------------------------------------------------
    //程序入口处
    QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
    QFile file(strPath);
    if(file.open(QIODevice::ReadOnly))
    {
        QByteArray ba;
        QDataStream in(&file);
        in>>ba;
        file.close();
        this->restoreState(ba);
    }
}

MainWindow::~MainWindow()
{

}

void MainWindow::closeEvent(QCloseEvent *event)
{
    //程序出口处
    QString strPath = QCoreApplication::applicationDirPath() + "/UILayout.ini";
    QFile file(strPath);
    if(file.open(QIODevice::WriteOnly))
    {
        QDataStream outfile(&file);
        QByteArray ba = this->saveState();
        outfile<<ba;
        file.close();
    }
}

void MainWindow::CreateMenuBar()
{
    QMenuBar * menuBar = new QMenuBar(this);

    QMenu * menuFile = new QMenu("File", this);
    QAction * newFile = new QAction(QIcon(), "NewFile", this);
    QAction * openFile = new QAction(QIcon(), "OpenFile", this);
    QAction * save = new QAction(QIcon(), "Save", this);
    menuFile->addAction(newFile);
    menuFile->addAction(openFile);
    menuFile->addAction(save);

    QMenu * menuEdit = new QMenu("Edit", this);
    QAction * cut = new QAction(QIcon(), "Cut", this);
    QAction * copy = new QAction(QIcon(), "Copy", this);
    QAction * paste = new QAction(QIcon(), "Paste", this);
    menuEdit->addAction(cut);
    menuEdit->addAction(copy);
    menuEdit->addAction(paste);

    menuBar->addMenu(menuFile);
    menuBar->addMenu(menuEdit);

    this->setMenuBar(menuBar);
}

void MainWindow::CreateToolBar()
{
    QToolBar * toolBar1 = new QToolBar(this);
    toolBar1->setObjectName("toolBar1");
    QAction * action1_1 = new QAction("action1_1", this);
    QAction * action1_2 = new QAction("action1_2", this);
    QAction * action1_3 = new QAction("action1_3", this);
    QAction * action1_4 = new QAction("action1_4", this);
    toolBar1->addAction(action1_1);
    toolBar1->addAction(action1_2);
    toolBar1->addAction(action1_3);
    toolBar1->addAction(action1_4);

    QToolBar * toolBar2 = new QToolBar(this);
    toolBar2->setObjectName("toolBar2");
    QAction * action2_1 = new QAction("action2_1", this);
    QAction * action2_2 = new QAction("action2_2", this);
    QAction * action2_3 = new QAction("action2_3", this);
    QAction * action2_4 = new QAction("action2_4", this);
    toolBar2->addAction(action2_1);
    toolBar2->addAction(action2_2);
    toolBar2->addAction(action2_3);
    toolBar2->addAction(action2_4);

    QToolBar * toolBar3 = new QToolBar(this);
    toolBar3->setObjectName("toolBar3");
    QAction * action3_1 = new QAction("action3_1", this);
    QAction * action3_2 = new QAction("action3_2", this);
    QAction * action3_3 = new QAction("action3_3", this);
    QAction * action3_4 = new QAction("action3_4", this);
    toolBar3->addAction(action3_1);
    toolBar3->addAction(action3_2);
    toolBar3->addAction(action3_3);
    toolBar3->addAction(action3_4);

    this->addToolBar(toolBar1);
    this->addToolBar(toolBar2);
    this->addToolBar(toolBar3);
}

void MainWindow::CreateStatusBar()
{
    //创建状态栏
    QStatusBar *status = new QStatusBar(this);
    status->setObjectName("status");
    status->setObjectName("状态栏");
    status->setStyleSheet("QStatusBar::item{border: 0px}"); //设置不显示label的边框

    //主窗口添加状态栏
    this->setStatusBar(status);

    //创建标签
    QLabel *statusLabel = new QLabel("我是状态栏", this);

    //状态栏添加信息
    status->showMessage("我在3秒后会消失", 3000);//显示在左侧,并且3秒后自动消失

    status->addPermanentWidget(statusLabel);//添加右侧标签(永久性)
}
Qt 中隐藏一个完整的布局可以通过多种方式实现,具体取决于你希望如何控制布局及其包含的控件的可见性。Qt 并没有直接提供“隐藏整个布局”的方法,但可以通过操作布局中的控件或父容器来达到目的。 ### 使用 QWidget 控制布局的可见性 一种常见的做法是将整个布局放入一个 `QWidget` 容器中,然后通过调用该容器的 `hide()` `show()` 方法来控制其可见性[^1]。例如: ```cpp // 创建一个容器 widget 并设置其布局 QWidget *container = new QWidget; container->setLayout(myLayout); // 隐藏整个布局 container->hide(); ``` 这种方式的优点是简单直观,并且可以轻松地与信号槽机制结合使用,实现动态显示隐藏。 ### 递归隐藏布局中的子控件 如果不想引入额外的容器 widget,也可以手动遍历布局中的所有子控件并调用它们的 `hide()` 方法。这种方法更复杂,需要编写额外的递归函数来处理嵌套布局的情况: ```cpp void hideLayout(QLayout *layout) { for (int i = 0; i < layout->count(); ++i) { QLayoutItem *item = layout->itemAt(i); if (item->widget()) { item->widget()->hide(); } else if (item->layout()) { hideLayout(item->layout()); // 递归处理子布局 } } } ``` 需要注意的是,这种做法不会影响布局本身的几何排列,只是隐藏了其中的控件。因此,在某些情况下可能会导致界面布局出现空隙或不对齐的现象。 ### 使用 QStackedLayout 切换布局 另一种替代方案是使用 `QStackedLayout`,它可以管理多个页面(即不同的布局),并通过索引切换当前显示的页面。这在实现向导或多步骤表单时非常有用: ```cpp QStackedLayout *stackedLayout = new QStackedLayout; stackedLayout->addWidget(layout1); // 添加第一个布局 stackedLayout->addWidget(layout2); // 添加第二个布局 // 切换到指定布局 stackedLayout->setCurrentIndex(1); // 显示第二个布局 ``` ### 注意事项 - **性能问题**:频繁地隐藏显示大量控件可能会影响性能,特别是在嵌套布局较深的情况下。 - **布局重排**:当某个控件被隐藏后,父布局会自动调整其余控件的位置,这可能导致界面重新计算大小位置,进而引发重绘事件。 - **信号槽连接**:确保在隐藏布局之前保存必要的状态信息,以便在重新显示时恢复用户交互体验。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值