当前位置:   article > 正文

QStackedWidget 的高级使用方法_qstackedwidget用法

qstackedwidget用法

QStackedWidget 的高级使用方法

Qt 开发中,构建复杂的 GUI 应用常常面临一个挑战:如何优雅地管理多个页面,并在它们之间进行切换?今天,我将介绍一种基于 QStackedWidget 和工厂模式的解决方案,帮助你轻松实现多页面应用。

效果如下:
在这里插入图片描述

QStackedWidget:堆叠式容器
QStackedWidget 是 Qt 框架中一个强大的容器小部件,它允许你将多个子窗口堆叠在一起,但只显示最上面的一个。这种特性类似于实体世界中的卡片堆叠,每次只能看到最顶部的卡片。

工厂模式:构建窗口的“工厂”

当页面数量众多且复杂时,手动创建和管理每个窗口会变得繁琐且容易出错。这时,我们可以引入工厂模式,将窗口的创建过程封装起来,实现解耦和复用。

1. 基类

首先,我们需要定义一个基类 BaseWindow,作为所有子窗口的父类。基类中包含一个抽象方法 initLoadData,用于加载每个窗口所需的数据。

#ifndef BASEWINDOW_H
#define BASEWINDOW_H

#include <QWidget>

// 这个基类里面有个抽象方法 initLoadData,用来加载子窗口所需数据
class BaseWindow : public QWidget
{
    Q_OBJECT
public:
    explicit BaseWindow(QWidget *parent = nullptr);
    /**
     * @brief 初始化加载数据
     * @param reset
     */
    virtual void initLoadData() = 0;
};
#endif // BASEWINDOW_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2. 工厂类

WindowFactory 类充当创建窗口的“工厂”。它使用一个 QMap 存储窗口名称和对应的创建函数。通过注册机制,我们可以将每个窗口类与一个创建函数关联起来。

#ifndef WINDOWFACTORY_H
#define WINDOWFACTORY_H

#include <QMap>
#include "basewindow.h"

class WindowFactory
{
public:
    using Creator = BaseWindow * (*)(QWidget *);
    static WindowFactory &instance()
    {
        static WindowFactory instance;
        return instance;
    }
    
    void registerWindow(const QString &name, Creator creator)
    {
        creators[name] = creator;
    }
    
    BaseWindow *createWindow(const QString &name, QWidget *parent = nullptr)
    {
        if (creators.contains(name))
        {
            return creators[name](parent);
        }
        return nullptr;
    }
private:
    QMap<QString, Creator> creators;

    WindowFactory() = default;
    ~WindowFactory() = default;
    WindowFactory(const WindowFactory &) = delete;
    WindowFactory &operator=(const WindowFactory &) = delete;
};

#endif // WINDOWFACTORY_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

3. 注册宏

为了简化窗口注册过程,我们定义了一个宏 REGISTER_WINDOW_CLASS。使用该宏,只需传入窗口名称和类名,即可自动注册窗口到工厂中。

#ifndef WINDOWREGISTRATION_H
#define WINDOWREGISTRATION_H

#include "windowfactory.h"

#define REGISTER_WINDOW_CLASS(NAME, CLASS) \
class CLASS##Registrator { \
public: \
    CLASS##Registrator() { \
        WindowFactory::instance().registerWindow(NAME, CLASS##Registrator::createInstance); \
    } \
    static BaseWindow* createInstance(QWidget* parent) { \
        return new CLASS(parent); \
    } \
}; \
static CLASS##Registrator global_##CLASS##Registrator;

#endif // WINDOWREGISTRATION_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4. 使用方法

在主窗口中,我们定义一个 QMap 来存储已经创建的窗口实例,确保每个窗口只创建一次。当需要切换到某个窗口时,只需通过窗口名称从工厂中获取实例,并将其设置为 QStackedWidget 的当前页面即可。

// 示例代码,展示如何使用工厂模式和 QStackedWidget

QMap<QString, BaseWindow*> windowInstances;
QStackedWidget *stackedWidget = new QStackedWidget;

// windowName 窗口名称
void switchToWindow(const QString &windowName)
{
	BaseWindow *window = nullptr;
    if (!windowInstances.contains(windowName))
    {
        BaseWindow *window = WindowFactory::instance().createWindow(windowName);
        if (window)
        {
            windowInstances[windowName] = window;
            stackedWidget->addWidget(window);
        }
    }
    window = windows[windowName];
	if (window)
	{
	    //加载数据
	    window->initLoadData();
	    ui->stackedWidget->setCurrentWidget(window);
	}
    stackedWidget->setCurrentWidget(windowInstances[windowName]);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

浏览器历史记录

为了提供更好的用户体验,我们还可以实现窗口历史记录功能。通过 BrowserHistory 模板类,我们可以轻松地管理历史记录和前进后退操作。

#ifndef BROWSERHISTORY_H
#define BROWSERHISTORY_H

#include <QStack>
#include <QWidget>

template <typename T>
class BrowserHistory
{
public:
    BrowserHistory() {}
    /**
     * @brief 访问新页面,并清空栈
     * @param page 页面
     */
    void visit(const T &page)
    {
        if (backStack.contains(page))
        {
            QStack<T> tempStack;
            while (backStack.top() != page)
            {
                tempStack.push(backStack.pop());
            }
            backStack.pop();
            while (!tempStack.isEmpty())
            {
                backStack.push(tempStack.pop());
            }
        }
        backStack.push(page);
        forwardStack.clear();
        setNavigationButtonState(backStack.size() > 1, false);
    }
    /**
     * @brief 返回上一页,将当前页移到前进栈,并返回新的当前页
     * @return
     */
    T back()
    {
        if (backStack.size() > 1)
        {
            T page = backStack.pop();
            forwardStack.push(page);
            setNavigationButtonState(backStack.size() >= 2, forwardStack.size() >= 1);
            return backStack.top();
        }
        setNavigationButtonState(false, forwardStack.size() >= 1);
        return T();
    }
    /**
     * @brief 前进到下一页,将前进栈的栈顶页移到历史记录栈,并返回新的当前页
     * @return
     */
    T forward()
    {
        if (!forwardStack.isEmpty())
        {
            T page = forwardStack.pop();
            backStack.push(page);
            setNavigationButtonState(backStack.size() >= 2, forwardStack.size() >= 1);
            return page;
        }
        setNavigationButtonState(backStack.size() >= 2, false);
        return T();
    }
    /**
     * @brief 获取当前页
     * @return
     */
    T current() const
    {
        if (!backStack.isEmpty())
        {
            return backStack.top();
        }
        return T();
    }
    /**
     * @brief 设置导航按钮
     * @param forward 前进按钮
     * @param back 后退按钮
     */
    void setNavigationButton(QWidget *forward, QWidget *back)
    {
        this->m_forward = forward;
        this->m_back = back;
        //初始全部禁用
        setNavigationButtonState(false, false);
    }
private:
    void setNavigationButtonState(bool forwardEnabled, bool backEnabled)
    {
        if (m_forward&&m_back)
        {
            m_forward->setEnabled(forwardEnabled);
        	m_back->setEnabled(backEnabled);
        }
    }
private:
    QStack<T> backStack;     // 存储历史记录
    QStack<T> forwardStack;  // 存储前进记录
    QWidget *m_forward; //前进组件
    QWidget *m_back; //后退组件
};


#endif // BROWSERHISTORY_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

浏览记录使用方法

// 示例代码,展示如何使用浏览记录

BrowserHistory<QString> history; //记录
bool isRecord = true;//用于防止浏览记录的时候记录

// windowName 窗口名称
void switchToWindow(const QString &windowName)
{
	BaseWindow *window = nullptr;
    if (!windowInstances.contains(windowName))
    {
        BaseWindow *window = WindowFactory::instance().createWindow(windowName);
        if (window)
        {
            windowInstances[windowName] = window;
            stackedWidget->addWidget(window);
        }
    }
    window = windows[windowName];
	if (window)
	{
	    //加载数据
	    window->initLoadData();
	    ui->stackedWidget->setCurrentWidget(window);
	    if (isRecord)
        {
            history.visit(windowName);
        }
        else
        {
            isRecord = true;
        }
	}
    stackedWidget->setCurrentWidget(windowInstances[windowName]);
}

//点击事件
void Widget::forwardButtonClick()
{
    QString windowName = history.back();
    if (!windowName.isEmpty())
    {
        isRecord = false;
        switchToWindow(windowName);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/961228
推荐阅读
相关标签
  

闽ICP备14008679号