Qt实现手动切换多种布局的完美方案

 更新时间:2024年07月02日 14:23:00   作者:肩上风骋  
通过点击程序界面上不同的布局按钮,使主工作区呈现出不同的页面布局,多个布局之间可以通过点击不同布局按钮切换,支持的最多的窗口为9个,不同布局下窗口数随之变化,这篇文章主要介绍了Qt实现手动切换多种布局的完美方案,需要的朋友可以参考下

引言

之前写了一个手动切换多个布局的程序,下面来记录一下。
程序运行效果如下:

示例

需求

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

开发环境

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

代码实现

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

#include "manullayoutdialog.h"
#include <QApplication>
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 <QObject>
#include <QWidget>
#include <QVector>
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 <qDebug>
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.size();
        m_vecWidget.pop_back();
//       qDebug()<<"takeOut-back : "<<m_vecWidget.size();
       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 <QDialog>
#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 <QDebug>
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: "<<m_vecWidget.size();
    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: "<<ObjectPooling::getInstance(9)->getSize();
            // qDebug()<<"移除后的窗口数m_vecWidget: "<<m_vecWidget.size();
        }
    }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: "<<m_vecWidget.size();
    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: "<<m_vecWidget.size();
}
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函数。
上面的两点在代码的注释中有写,是为重点注意项。

到此这篇关于Qt实现手动切换多种布局的文章就介绍到这了,更多相关Qt切换多种布局内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现火车订票系统

    C语言实现火车订票系统

    这篇文章主要为大家详细介绍了C语言实现火车订票系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • C++标准C函数在各平台编译结果都相同

    C++标准C函数在各平台编译结果都相同

    今天小编就为大家分享一篇关于C++标准C函数在各平台编译结果都相同,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++ 情怀游戏扫雷的实现流程详解

    C++ 情怀游戏扫雷的实现流程详解

    扫雷是电脑上很经典很经典的传统老游戏,从小编第一次摸到计算机开始就玩过扫雷,虽然当时并不理解玩法原理,但终是第一次玩电脑游戏,下面来从扫雷的前世今生讲起
    2021-11-11
  • C++利用静态成员或类模板构建链表的方法讲解

    C++利用静态成员或类模板构建链表的方法讲解

    这篇文章主要介绍了C++利用静态成员或类模板构建链表的方法讲解,链表是基础的数据结构,而在C++中构件单链表还是稍显复杂,需要的朋友可以参考下
    2016-04-04
  • QT5实现简单的TCP通信的实现

    QT5实现简单的TCP通信的实现

    本文主要介绍了QT5实现简单的TCP通信的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C语言修炼之路初识指针阴阳窍 地址还归大道真上篇

    C语言修炼之路初识指针阴阳窍 地址还归大道真上篇

    指针是指向另一个变量的变量。意思是一个指针保存的是另一个变量的内存地址。换句话说,指针保存的并不是普通意义上的数值,而是另一个变量的地址值。一个指针保存了另一个变量的地址值,就说这个指针“指向”了那个变量
    2022-02-02
  • C语言深入讲解语句与选择结构的使用

    C语言深入讲解语句与选择结构的使用

    这篇文章主要为大家介绍了C语言的语句与选择结构,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 基于C语言实现的扫雷游戏代码

    基于C语言实现的扫雷游戏代码

    这篇文章主要介绍了基于C语言实现的扫雷游戏代码,对于学习游戏开发的朋友有一定的借鉴价值,需要的朋友可以参考下
    2014-08-08
  • C语言实现反弹球消砖块游戏

    C语言实现反弹球消砖块游戏

    这篇文章主要为大家详细介绍了C语言实现反弹球消砖块游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • Cocos2d-x 3.x入门教程(一):基础概念

    Cocos2d-x 3.x入门教程(一):基础概念

    这篇文章主要介绍了Cocos2d-x 3.x入门教程(一):基础概念,本文讲解了Director、Scene、Layer、Sprite等内容,需要的朋友可以参考下
    2014-11-11

最新评论