• Qt实现手动切换多种布局


    引言

    之前写了一个手动切换多个布局的程序,下面来记录一下。
    程序运行效果如下:
    在这里插入图片描述

    示例

    需求

    通过点击程序界面上不同的布局按钮,使主工作区呈现出不同的页面布局,多个布局之间可以通过点击不同布局按钮切换。支持的最多的窗口为9个。不同布局下窗口数随之变化。

    开发环境

    使用的QtCreator12.0.2,基于Qt5.15.2库开发。

    代码实现

    创建基于QApplication的应用程序。
    下面是实现代码:
    main.cpp

    #include "manullayoutdialog.h"
    
    #include 
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        ManulLayoutDialog w;
        w.show();
        ObjectPooling*m_pool = ObjectPooling::getInstance(9);
        return a.exec();
    }
    
    

    ObjectPooling.h

    #ifndef OBJECTPOOLING_H
    #define OBJECTPOOLING_H
    
    #include 
    #include 
    #include 
    
    class ObjectPooling:public QObject
    {
        Q_OBJECT
    private:
        ObjectPooling(qint32 num);
        ObjectPooling(const ObjectPooling &) = delete;
        ObjectPooling& operator=(const ObjectPooling&)=delete;
    public:
        static ObjectPooling *getInstance(qint32 num);
        ~ObjectPooling();
    
        QWidget* takeOut();
        void putIn(QWidget *pWidget);
        int getSize()const;
    private:
        QVector<QWidget*> m_vecWidget;
    };
    
    #endif // OBJECTPOOLING_H
    
    

    ObjectPooling.cpp

    #include "objectpooling.h"
    #include 
    
    ObjectPooling::ObjectPooling(qint32 num):QObject() {
        for(int i = 0; i < num;++i){
            QWidget *pWidget = new QWidget;
            if(pWidget){
                pWidget->setStyleSheet("background-color:back;");
                m_vecWidget.push_back(pWidget);
            }
        }
    }
    
    ObjectPooling *ObjectPooling::getInstance(qint32 num)
    {
        static ObjectPooling instance(num);
        return &instance;
    }
    
    ObjectPooling::~ObjectPooling()
    {
        if(m_vecWidget.size()<1)
            return;
    
        for(auto it = m_vecWidget.begin();it != m_vecWidget.end();++it){
            if(*it){
                delete *it;
                (*it) = nullptr;
            }
        }
        m_vecWidget.clear();
    }
    
    QWidget *ObjectPooling::takeOut()
    {
        if(m_vecWidget.size()){
           QWidget*pWidget = m_vecWidget.back();
    //        qDebug()<<"takeOut-befor : "<
            m_vecWidget.pop_back();
    //       qDebug()<<"takeOut-back : "<
           return pWidget;
        }
        qDebug()<<"对象池没有对象了!!";
        return nullptr;
    }
    
    void ObjectPooling::putIn(QWidget *pWidget)
    {
        m_vecWidget.push_back(pWidget);
    }
    
    int ObjectPooling::getSize() const
    {
        return m_vecWidget.size();
    }
    
    

    ManulLayoutDialog.h

    #ifndef MANULLAYOUTDIALOG_H
    #define MANULLAYOUTDIALOG_H
    
    #include 
    #include "objectpooling.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui {
    class ManulLayoutDialog;
    }
    QT_END_NAMESPACE
    
    class ManulLayoutDialog : public QDialog
    {
        Q_OBJECT
    
    public:
        ManulLayoutDialog(QWidget *parent = nullptr);
        ~ManulLayoutDialog();
    private:
        void initLayout();
        void clearLastLayout(int n);//n——新的布局中窗口的总数
        void threeColumnLayout(int r,int c);//r——行数,c——列数
    private slots:
        void on_pushButton_clicked();
    
        void on_pushButton_2_clicked();
    
        void on_pushButton_3_clicked();
    
        void on_pushButton_4_clicked();
    
        void on_pushButton_5_clicked();
    
    private:
        Ui::ManulLayoutDialog *ui;
        qint32 m_n;//布局中窗口的总个数
        QVector<QWidget*> m_vecWidget;//保存布局中的窗口
        ObjectPooling* m_pool;
    };
    #endif // MANULLAYOUTDIALOG_H
    
    

    ManulLayoutDialog.cpp

    #include "manullayoutdialog.h"
    #include "ui_manullayoutdialog.h"
    #include 
    
    ManulLayoutDialog::ManulLayoutDialog(QWidget *parent)
        : QDialog(parent)
        , ui(new Ui::ManulLayoutDialog)
    {
        ui->setupUi(this);
        initLayout();
    }
    
    ManulLayoutDialog::~ManulLayoutDialog()
    {
        for(QWidget *pWidget:m_vecWidget){
            pWidget->setParent(nullptr);//不设置被回收的窗口父对象为空,会被再次释放
            ObjectPooling::getInstance(9)->putIn(pWidget);
        }
        m_vecWidget.clear();
    
        delete ui;//若被回收的窗口没有设置父对象为空,这里会析构该窗口,回收到对象池后会再次析构
    }
    
    void ManulLayoutDialog::initLayout()
    {
        QHBoxLayout *pHLayout = new QHBoxLayout(ui->widget);
        pHLayout->setContentsMargins(0,0,0,0);
        QWidget *pWidget = ObjectPooling::getInstance(9)->takeOut();
        pHLayout->addWidget(pWidget);
        m_n = 1;
        m_vecWidget.push_back(pWidget);
        m_pool = ObjectPooling::getInstance(9);
    }
    
    void ManulLayoutDialog::clearLastLayout(int n)
    {
        QLayout *pLayout = ui->widget->layout();
    //    qDebug()<<"移除前的窗口数m_vecWidget: "<
        if(m_n>n){
            for(int i =0; i <m_n -n;++i){//趟数
                //移除窗口中的控件,回收到对象池
                QWidget *pWidget = m_vecWidget.back();
                if(pLayout && pWidget){
                    qDebug()<<"准备移除窗口===";
                    pLayout->removeWidget(pWidget);
                    pWidget->setParent(nullptr);
                    m_vecWidget.pop_back();
                }
                // if(pLayout){qDebug()<<"布局不为空 ";}
                // if(pWidget){qDebug()<<"窗口不为空 ";}
                // QWidget *fWidget = pWidget->parentWidget();
                ObjectPooling::getInstance(9)->putIn(pWidget);
                // qDebug()<<"对象池窗口数m_vecWidget: "<getSize();
                // qDebug()<<"移除后的窗口数m_vecWidget: "<
            }
        }else if(m_n <n){
            //为了防止二次重设父对象,先将上一次的父对象清空
            for(auto it = m_vecWidget.begin();it != m_vecWidget.end();++it){
                if(*it){
                    (*it)->setParent(nullptr);
                }
            }
            for(int i = 0; i < n-m_n;++i){
                m_vecWidget.push_back(ObjectPooling::getInstance(9)->takeOut());
            }
        }
    
        //删除窗口原本的布局
        delete pLayout;
        //    ui->widget->setLayout(nullptr);
    }
    
    void ManulLayoutDialog::threeColumnLayout(int r, int c)
    {
        int total = r*c;
        if(m_n == total){
            return ;
        }
    
        clearLastLayout(total);
        QGridLayout *pGridLayout = new QGridLayout(ui->widget);
        pGridLayout->setContentsMargins(0,0,0,0);
        for(int i = 0; i < r;++i){
            for(int j = 0; j < c; ++j){
                pGridLayout->addWidget(m_vecWidget[i+j+2*i],i,j);//找下标对应的位置与元素之间的关系
            }
        }
        m_n = total;
    }
    
    void ManulLayoutDialog::on_pushButton_clicked()
    {
        if(m_n == 1){
            return ;
        }else{
            clearLastLayout(1);
        }
        QHBoxLayout *pHLayout = new QHBoxLayout(ui->widget);
        pHLayout->setContentsMargins(0,0,0,0);
        // qDebug()<<"布局1中的窗口数m_vecWidget: "<
        pHLayout->addWidget(m_vecWidget.back());
        m_n = 1;
    }
    
    
    void ManulLayoutDialog::on_pushButton_2_clicked()
    {
        if(m_n == 2){
            return ;
        }else if(m_n > 2){
            clearLastLayout(2);
            QHBoxLayout *pLayout = new QHBoxLayout(ui->widget);
            pLayout->setContentsMargins(0,0,0,0);
            for(auto it = m_vecWidget.begin();it != m_vecWidget.end();++it){
                pLayout->addWidget(*it);
            }
        }else{
            QLayout *pLayout = ui->widget->layout();
            QWidget *pWidget = ObjectPooling::getInstance(9)->takeOut();
            pLayout->addWidget(pWidget);
            m_vecWidget.push_back(pWidget);
        }
        m_n = 2;
        // qDebug()<<"布局2中的窗口数m_vecWidget: "<
    }
    
    
    void ManulLayoutDialog::on_pushButton_3_clicked()
    {
        if(m_n == 4){
            return ;
        }
    
        clearLastLayout(4);//只能先清理之前的布局,不能与下面的新布局互换位置
        QGridLayout *pGridLayout = new QGridLayout(ui->widget);
        pGridLayout->setContentsMargins(0,0,0,0);
        for(int i = 0; i < 2;++i){
            for(int j = 0; j < 2; ++j){
                pGridLayout->addWidget(m_vecWidget[i+j+i],i,j);//找下标对应的位置与元素之间的关系
            }
        }
        m_n = 4;
    }
    
    
    void ManulLayoutDialog::on_pushButton_4_clicked()
    {
        threeColumnLayout(2,3);
    }
    
    
    void ManulLayoutDialog::on_pushButton_5_clicked()
    {
        threeColumnLayout(3,3);
    }
    
    

    运行结果

    选一种的2行6列布局下的效果的截图。具体的运行效果和文章开始的效果一样。
    在这里插入图片描述

    程序分析

    项目中先创建了一个单例模式下的对象池,负责布局中总窗口的创建、回收,取出、以及存入。同时创建了一个手动布局类ManulLayoutDialog,在该类中实现了在界面上点击不同布局按钮的响应,ObjectPooling类作为手动布局类ManulLayoutDialog的成员函数,两个类之间是一种关联的关系。采用队列存放布局中的窗口,当要切换的布局中的窗口数大于当前的窗口布局中的窗口数,则先清除之前的窗口布局,将布局中的窗口回收到窗口数组中,同时向对象池中取出相差数量的窗口,放入窗口数组,创建新的布局,将窗口数组中的窗口加入新布局;当要切换的布局中的窗口数小于当前的窗口布局中的窗口数,则先从布局中移除相差数量的窗口,将移除的窗口从窗口数组中去除,删除窗口原来的布局,同时将移除的窗口存入对象池中,创建新的布局,将窗口数组中的窗口加入到新的布局。

    注意

    示例中有两个需要注意的点:
    1.对象池中窗口的释放。 可看ManulLayoutDialog的析构函数。
    2.原本布局中窗口的回收。 可看clearLastLayout函数。
    上面的两点在代码的注释中有写,是为重点注意项。

  • 相关阅读:
    Kafka的消息存储机制
    前端开发:在JS中以…为前缀的用法汇总
    【一天一题—Day1】1260. 二维网格迁移
    只会postman单接口测试?这些高级功能你必须掌握
    国产管理软件勒索病毒大爆发
    2022牛客寒假算法基础集训营2
    JAVA毕业设计高校排课管理系统计算机源码+lw文档+系统+调试部署+数据库
    神经网络rnn是什么意思,RNN神经网络基本原理
    第四十三天&jmeter组件及其操作(2)
    MySql存储引擎
  • 原文地址:https://blog.csdn.net/blqzj214817/article/details/140037497