引言
之前写了一个手动切换多个布局的程序,下面来记录一下。
程序运行效果如下:
示例
需求
通过点击程序界面上不同的布局按钮,使主工作区呈现出不同的页面布局,多个布局之间可以通过点击不同布局按钮切换。支持的最多的窗口为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函数。
上面的两点在代码的注释中有写,是为重点注意项。