Qt實(shí)現(xiàn)手動(dòng)切換多種布局的完美方案
引言
之前寫了一個(gè)手動(dòng)切換多個(gè)布局的程序,下面來記錄一下。
程序運(yùn)行效果如下:
示例
需求
通過點(diǎn)擊程序界面上不同的布局按鈕,使主工作區(qū)呈現(xiàn)出不同的頁面布局,多個(gè)布局之間可以通過點(diǎn)擊不同布局按鈕切換。支持的最多的窗口為9個(gè)。不同布局下窗口數(shù)隨之變化。
開發(fā)環(huán)境
使用的QtCreator12.0.2,基于Qt5.15.2庫開發(fā)。
代碼實(shí)現(xiàn)
創(chuàng)建基于QApplication的應(yīng)用程序。
下面是實(shí)現(xiàn)代碼:
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()<<"對(duì)象池沒有對(duì)象了??!"; 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——新的布局中窗口的總數(shù) void threeColumnLayout(int r,int c);//r——行數(shù),c——列數(shù) 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;//布局中窗口的總個(gè)數(shù) 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);//不設(shè)置被回收的窗口父對(duì)象為空,會(huì)被再次釋放 ObjectPooling::getInstance(9)->putIn(pWidget); } m_vecWidget.clear(); delete ui;//若被回收的窗口沒有設(shè)置父對(duì)象為空,這里會(huì)析構(gòu)該窗口,回收到對(duì)象池后會(huì)再次析構(gòu) } 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()<<"移除前的窗口數(shù)m_vecWidget: "<<m_vecWidget.size(); if(m_n>n){ for(int i =0; i <m_n -n;++i){//趟數(shù) //移除窗口中的控件,回收到對(duì)象池 QWidget *pWidget = m_vecWidget.back(); if(pLayout && pWidget){ qDebug()<<"準(zhǔn)備移除窗口==="; 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()<<"對(duì)象池窗口數(shù)m_vecWidget: "<<ObjectPooling::getInstance(9)->getSize(); // qDebug()<<"移除后的窗口數(shù)m_vecWidget: "<<m_vecWidget.size(); } }else if(m_n <n){ //為了防止二次重設(shè)父對(duì)象,先將上一次的父對(duì)象清空 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);//找下標(biāo)對(duì)應(yīng)的位置與元素之間的關(guān)系 } } 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中的窗口數(shù)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中的窗口數(shù)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);//找下標(biāo)對(duì)應(yīng)的位置與元素之間的關(guān)系 } } m_n = 4; } void ManulLayoutDialog::on_pushButton_4_clicked() { threeColumnLayout(2,3); } void ManulLayoutDialog::on_pushButton_5_clicked() { threeColumnLayout(3,3); }
運(yùn)行結(jié)果
選一種的2行6列布局下的效果的截圖。具體的運(yùn)行效果和文章開始的效果一樣。
程序分析
項(xiàng)目中先創(chuàng)建了一個(gè)單例模式下的對(duì)象池,負(fù)責(zé)布局中總窗口的創(chuàng)建、回收,取出、以及存入。同時(shí)創(chuàng)建了一個(gè)手動(dòng)布局類ManulLayoutDialog,在該類中實(shí)現(xiàn)了在界面上點(diǎn)擊不同布局按鈕的響應(yīng),ObjectPooling類作為手動(dòng)布局類ManulLayoutDialog的成員函數(shù),兩個(gè)類之間是一種關(guān)聯(lián)的關(guān)系。采用隊(duì)列存放布局中的窗口,當(dāng)要切換的布局中的窗口數(shù)大于當(dāng)前的窗口布局中的窗口數(shù),則先清除之前的窗口布局,將布局中的窗口回收到窗口數(shù)組中,同時(shí)向?qū)ο蟪刂腥〕鱿嗖顢?shù)量的窗口,放入窗口數(shù)組,創(chuàng)建新的布局,將窗口數(shù)組中的窗口加入新布局;當(dāng)要切換的布局中的窗口數(shù)小于當(dāng)前的窗口布局中的窗口數(shù),則先從布局中移除相差數(shù)量的窗口,將移除的窗口從窗口數(shù)組中去除,刪除窗口原來的布局,同時(shí)將移除的窗口存入對(duì)象池中,創(chuàng)建新的布局,將窗口數(shù)組中的窗口加入到新的布局。
注意
示例中有兩個(gè)需要注意的點(diǎn):
1.對(duì)象池中窗口的釋放。 可看ManulLayoutDialog的析構(gòu)函數(shù)。
2.原本布局中窗口的回收。 可看clearLastLayout函數(shù)。
上面的兩點(diǎn)在代碼的注釋中有寫,是為重點(diǎn)注意項(xiàng)。
到此這篇關(guān)于Qt實(shí)現(xiàn)手動(dòng)切換多種布局的文章就介紹到這了,更多相關(guān)Qt切換多種布局內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中constexpr與模板元編程的基礎(chǔ)、常見問題、易錯(cuò)點(diǎn)及其規(guī)避策略
C++編譯時(shí)計(jì)算允許程序在編譯階段完成計(jì)算任務(wù),constexpr與模板元編程是C編譯時(shí)計(jì)算的兩把利劍,它們不僅能夠提升程序的性能,還能增強(qiáng)代碼的健壯性和可維護(hù)性,通過避開本文闡述的易錯(cuò)點(diǎn),開發(fā)者可以更加得心應(yīng)手地運(yùn)用這些特性,編寫出既高效又優(yōu)雅的C代碼2024-06-06標(biāo)準(zhǔn)C++類string的Copy-On-Write技術(shù)
這里,我想從C++類或是設(shè)計(jì)模式的角度為各位揭開Copy-On-Write技術(shù)在string中實(shí)現(xiàn)的面紗,以供各位在用C++進(jìn)行類庫設(shè)計(jì)時(shí)做一點(diǎn)參考2013-11-11