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