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函数。
上面的两点在代码的注释中有写,是为重点注意项。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/761586.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙应用更新跳转到应用市场

鸿蒙没有应用下载安装&#xff0c;只支持跳转到应用市场更新 gotoMarket(){try {const request: Want {parameters: {// 此处填入要加载的应用包名&#xff0c;例如&#xff1a; bundleName: "com.huawei.hmsapp.appgallery"bundleName: com.huawei.hmos.maps.app}}…

昇思25天学习打卡营第8天|模型训练

昇思25天学习打卡营第8天|模型训练 前言模型训练构建数据集定义神经网络模型定义超参、损失函数和优化器超参损失函数优化器 训练与评估 个人任务打卡&#xff08;读者请忽略&#xff09;个人理解与总结 前言 非常感谢华为昇思大模型平台和CSDN邀请体验昇思大模型&#xff01;从…

PFA滴定管带阀门耐酸碱本底值低

一、产品介绍 酸式滴定管为一细长的管状容器&#xff0c;一端具有活栓开关用来控制滴定的速度&#xff0c;其上具有刻度指示量度&#xff0c;是分析化学中常用的滴定仪器。可用于进行酸碱中和滴定试验等&#xff0c;量取对橡皮有侵蚀作用的液体。 我司生产的PFA酸式滴定管是用…

全球3DMAX插件界又更新了什么?

“3DMAX插件界”不这样称呼又叫他们什么呢&#xff1f;顾名思义就是开发3dmax插件的那个圈子。现在的3D类软件越来越多&#xff0c;但是&#xff0c;3dmax的地位仍然举足轻重。3dmax软件之所以受欢迎&#xff0c;不仅仅是因为自身的功能强大&#xff0c;还有其良好的可扩展性&a…

哪个麦克风音质好,麦克风哪种好,2024年热门家用麦克风推荐

​在这个信息爆炸的时代&#xff0c;网络直播和短视频成为了人们获取信息、娱乐和社交的重要方式。作为自媒体人&#xff0c;拥有一款优秀的领夹式无线麦克风是必不可少的。它不仅能够帮助你在各种环境中保持清晰的声音&#xff0c;还能提升你的作品质量和专业度。然而&#xf…

Parade接口芯片选型和应用,点击查看!

01 常见数据 / 媒体接口电路 接口电路是电子设备之间&#xff0c;电子设备与外围设备之间&#xff0c;电子设备内部部件之间起连接作用的逻辑电路&#xff0c;接口电路是设备处理器与外部设备进行信息交互的桥梁。 图1&#xff1a;常见高速数据/多媒体接口 1.1 USB接口 从最早…

思维模型:看透本质的思维框架,和它组合个个是王炸(非常详细)零基础入门到精通, 收藏这一篇就够了

为什么要从「为什么」开始&#xff1f; 如何想到又做到&#xff0c;提高行动力&#xff1f; 知行合一的途径&#xff1f;有用的工具&#xff1f; 剧透一下&#xff0c;读完本篇&#xff0c;你会收获一些王炸组合。 01 黄金思维圈 Why→How→What 黄金思维圈是西蒙斯涅克…

算法类学习笔记 ———— 车道线检测

文章目录 介绍基于传统计算机视觉地车道线检测基于道路特征的检测方法基于颜色特征的检测方法基于灰度特征的检测方法 基于彩色特征的检测方法基于纹理特征的检测方法基于多特征融合的检测方法 基于道路模型地检测方法直线模型曲线模型 基于深度学习的车道线检测LaneNet H-Net…

windows离线安装显卡驱动解决方案

前言 我们说这个离线泛指计算机无公网环境&#xff0c;而我们需要将显卡驱动打上&#xff0c;既然没有公网&#xff0c;我们就无法使用联网的方式&#xff08;傻瓜式安装&#xff09;&#xff0c;受各种原因限制&#xff0c;也不可以把主机搬走连上互联网进行安装。总之…

动手学深度学习(Pytorch版)代码实践 -计算机视觉-38实战Kaggle比赛:图像分类 (CIFAR-10)

38实战Kaggle比赛&#xff1a;图像分类 (CIFAR-10) 比赛链接&#xff1a;CIFAR-10 - Object Recognition in Images | Kaggle 导入包 import os import glob import pandas as pd import numpy as np import torch import torchvision from torch.utils.data import Dataset…

Mac多线程下载管理器:Neat Download Manage 最新版

Neat Download Manager&#xff08;NDM&#xff09;是一款功能强大的下载管理软件&#xff0c;它可以帮助用户更有效地管理和下载网络资源。这款软件支持多种浏览器和协议&#xff0c;可以提升下载速度&#xff0c;恢复中断的下载任务&#xff0c;以及自动化下载过程。在使用任…

如何学好AI绘画?点这里有答案!

前言 地狱难度的求职模式下&#xff0c;“掌握一门技术”的那部分求职者&#xff0c;远比其他人更有竞争力&#xff1b;而拥有出色技术和技能的设计师、以及未来想做设计师的小伙伴们&#xff0c;怎么才能更好实现工作自由&#xff1f; 只有两个字&#xff1a;学习。 学习新…

【Go】excelize库实现excel导入导出封装(四),导出时自定义某一列或多列的单元格样式

大家好&#xff0c;这里是符华~ 查看前三篇&#xff1a; 【Go】excelize库实现excel导入导出封装&#xff08;一&#xff09;&#xff0c;自定义导出样式、隔行背景色、自适应行高、动态导出指定列、动态更改表头 【Go】excelize库实现excel导入导出封装&#xff08;二&…

uniapp运行到小程序Vue.use注册全局组件不起作用

真想吐槽一下小程序&#xff0c;uniapp运行到小程序使用Vue.use注册全局组件根本不起作用&#xff0c;也不报错&#xff0c;这只是其中一个问题&#xff0c;其他还有很多问题&#xff0c;比如vue中正常使用的没问题的语法&#xff0c;运行到小程序就不行&#xff0c;又是包太大…

第一后裔延迟高怎么办?快速降低第一后裔延迟

第一后裔/The First Descendant一款射击游戏&#xff0c;融合了刷宝、角色扮演、团队合作、剧情等元素&#xff0c;让每个玩家都能在自己的角度上&#xff0c;找到切入点&#xff0c;并不断地成长&#xff0c;一步步解开后裔身上隐藏的秘密。近期该作正式上线&#xff0c;很多玩…

如何选择适合您业务需求的多语言跨境电商系统源码

随着互联网技术的飞速发展和全球市场的日益融合&#xff0c;多语言跨境电商已经成为许多企业进军国际市场的重要战略。在这个竞争激烈的时代&#xff0c;拥有一个适合自己业务需求的多语言跨境电商系统源码至关重要。本篇文章将为您揭秘如何选择适合您业务需求的多语言跨境电商…

接口自动化测试-项目实战

什么是接口自动化测试&#xff1a;使用工具或代码代替人对接口进行测试 测试项目结构&#xff08;python包&#xff09; 1、接口api包 2、script:业务脚本 3、data:数据 4、config.py :配置文件 5、reporter:报告 错误问题&#xff1a; 1、未打印任何东西。添加pip ins…

浅谈定时器之JSR223 定时器

浅谈定时器之JSR223 定时器 JSR223 定时器作为JMeter提供的众多定时器之一&#xff0c;以其高度的灵活性和可编程性脱颖而出&#xff0c;允许用户通过脚本自定义延时逻辑。本文将详细介绍JSR223定时器的特性和使用方法。 JSR223 定时器简介 JSR223 定时器利用了Java平台的JS…

家政小程序的开发,带动市场快速发展,提高家政服务质量

当下生活水平逐渐提高&#xff0c;也增加了年轻人的工作压力&#xff0c;同时老龄化也在日益增加&#xff0c;使得大众对家政的需求日益提高&#xff0c;能力、服务质量高的家政人员能够有效提高大众的生活幸福指数。 但是&#xff0c;传统的家政服务模式存在着效率低、用户与…

Unity 解包工具(AssetStudio/UtinyRipper)

文章目录 1.UtinyRipper2.AssetStudio 1.UtinyRipper 官方地址&#xff1a; https://github.com/mafaca/UtinyRipper/ 下载步骤&#xff1a; 2.AssetStudio 官方地址&#xff1a; https://github.com/Perfare/AssetStudio 下载步骤&#xff1a;