Qt實(shí)現(xiàn)手動(dòng)切換多種布局的完美方案
引言
之前寫了一個(gè)手動(dòng)切換多個(gè)布局的程序,下面來(lái)記錄一下。
程序運(yùn)行效果如下:

示例
需求
通過(guò)點(diǎn)擊程序界面上不同的布局按鈕,使主工作區(qū)呈現(xiàn)出不同的頁(yè)面布局,多個(gè)布局之間可以通過(guò)點(diǎn)擊不同布局按鈕切換。支持的最多的窗口為9個(gè)。不同布局下窗口數(shù)隨之變化。
開發(fā)環(huán)境
使用的QtCreator12.0.2,基于Qt5.15.2庫(kù)開發(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_HObjectPooling.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_HManulLayoutDialog.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ù)組中去除,刪除窗口原來(lái)的布局,同時(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語(yǔ)言實(shí)現(xiàn)數(shù)字炸彈小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)數(shù)字炸彈小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11
vs2019+qt工程中如何生成dump文件及調(diào)試詳解
這篇文章主要介紹了vs2019+qt工程中如何生成dump文件及調(diào)試的相關(guān)資料,包括了調(diào)試前置條件、異常處理函數(shù)的區(qū)別、可復(fù)用代碼、調(diào)試步驟等內(nèi)容,需要的朋友可以參考下2026-01-01
C++11 lambda表達(dá)式在回調(diào)函數(shù)中的使用方式
這篇文章主要介紹了C++11 lambda表達(dá)式在回調(diào)函數(shù)中的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
虛函數(shù)與純虛函數(shù)(C++與Java虛函數(shù)的區(qū)別)的深入分析
本篇文章是對(duì)虛函數(shù)與純虛函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
C語(yǔ)言實(shí)現(xiàn)車輛出租管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)車輛出租管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
C語(yǔ)言調(diào)用攝像頭實(shí)現(xiàn)生成yuv未壓縮圖片
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言如何調(diào)用攝像頭實(shí)現(xiàn)生成yuv未壓縮圖片,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以參考一下2023-11-11
C語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的掃雷游戲
掃雷是電腦上很經(jīng)典的游戲,特意去網(wǎng)上玩了一會(huì),幾次調(diào)試之后,發(fā)現(xiàn)這個(gè)比三子棋要復(fù)雜一些,尤其是空白展開算法上和堵截玩家有的一拼,與實(shí)際游戲差別較大,不能使用光標(biāo),下面來(lái)詳解每一步分析2021-10-10

