【QT入门】 无边框窗口设计之综合运用,实现WPS的tab页面

往期回顾:

【QT入门】 无边框窗口设计之实现窗口阴影-CSDN博客

【QT入门】 无边框窗口设计之实现圆角窗口-CSDN博客

【QT入门】 无边框窗口设计综合运用之自定义标题栏带圆角阴影的窗口-CSDN博客

 【QT入门】 无边框窗口设计之综合运用,实现WPS的tab页面

一、最终效果

 实现自增tab页面,不同tab页面之间的切换等,同时右键单击tab页面会弹出菜单栏

二、主界面类设计

主界面类,负责整合标题栏和下面的widget,整合很简单,创建一个tab类添加进来就ok

WPSDemo::WPSDemo(QWidget* parent): QWidget(parent)
{ui.setupUi(this);setWindowFlags(Qt::FramelessWindowHint);//设置为无边框窗口setStyleSheet("background-color:#E3E4E7");//设置背景颜色tabbrowser* pTab = new tabbrowser(this); QHBoxLayout* pHLay = new QHBoxLayout(this);pHLay->addWidget(pTab);pHLay->setContentsMargins(6, 6, 6, 6);setLayout(pHLay);connect(pTab, &tabbrowser::sig_close, this, &WPSDemo::on_close);
}

三、Tab类设计

 tab类负责创建tab,实现相关逻辑,重点还是在这里。主要是创建、删除tab标签,以及样式切换。还有就是单击右键显示菜单这几个点需要注意。

1、定义tab标签样式

首先,定义了两个字符串变量 qss0 和 qss1,分别存储了两种不同的 QSS 样式表,用于设置标签页的外观。当tab标签的数量超过一定范围时,会出现一个切换按钮,也就需要切换不同样式。

QString qss0 = "QTabBar::tab{ \font: 75 12pt Arial; \text-align:left; \width:184px; \height:32; \background:#FFFFFF; \border:2px solid #FFFFFF; \border-bottom-color:#FFFFFF; \border-top-left-radius:4px; \border-top-right-radius:4px; \padding:2px; \margin-top:0px; \margin-right:1px; \margin-left:1px;  \margin-bottom:0px;} \QTabBar::tab:selected{  \color:#333333; /*文字颜色*/  \background-color:#FFFFFF;} \QTabBar::tab:!selected{ \color:#B2B2B2; \border-color:#FFFFFF;} \QTabBar::scroller{width: 0px;}";
QString qss1 = "QTabBar::tab{ \font: 75 12pt Arial; \text-align:left; \width:184px; \height:32; \background:#FFFFFF; \border:2px solid #FFFFFF; \border-bottom-color:#FFFFFF; \border-top-left-radius:4px; \border-top-right-radius:4px; \padding:2px; \margin-top:0px; \margin-right:1px; \margin-left:1px;  \margin-bottom:0px;} \QTabBar::tab:selected{  \color:#333333; /*文字颜色*/  \background-color:#FFFFFF;} \QTabBar::tab:!selected{ \color:#B2B2B2; \border-color:#FFFFFF;} \QTabBar::scroller{width: 36px;}";

2、初始化标签页并设置属性

 在 tabbrowser 类的构造函数中,初始化一个标签页,并设置了一些标签页的属性,如滚动、可关闭、可移动等。然后调用了initTabWidget()函数初始化标签页部件,并设置了初始的标签页操作标志为 NORMAL。

tabbrowser::tabbrowser(QWidget* parent) :QTabWidget(parent)
{this->addTab(new QWidget, u8"稻壳");  //定义最开始的tabthis->setUsesScrollButtons(true);  //滚动鼠标可切换tabthis->setTabsClosable(true);       //显示tab右侧的关闭按钮this->setMovable(true);            //tab可移动位置initTabWidget(); //初始化tabsetTabBarFlag(NORMAL); this->setStyleSheet(qss0);connect(this, &QTabWidget::tabCloseRequested, this, &tabbrowser::on_closeTab);
}

这里我们一共定义了4种标签页操作标志:

        //tab操作标志enum TAB_FLAG{NEW, //新建标签页的操作标志CLOSE, //关闭标签页的操作标志NORMAL, //普通标签页的操作标志SPECIAL //特殊标签页的操作标志,比如当只剩下一个标签页时};

然后需要根据传入的标志值计算标签页的总宽度,并根据总宽度是否超过当前窗口宽度来切换不同的 QSS 样式表。 

3、创建和删除tab标签

1、创建tab标签

用一个 on_newTab() 函数来新建标签页,获取当前标签页数量并转换为字符串作为标题,然后添加新的标签页,并根据需要设置标签页为可关闭状态。

void tabbrowser::on_newTab()
{int nCount = count(); //获取当前标签页的数量。QString  title = QString::number(nCount); //将当前标签页数量转换为字符串。title = "Page" + title; //将标题设置为 "Page" 后接当前标签页数量的字符串// 这里写的有问题,应该是 insertTabthis->addTab(new QWidget, title); //检查标签页是否可关闭,如果不可关闭,则设置标签页为可关闭状态。if (!tabsClosable()){setTabsClosable(true);}setTabBarFlag(NEW);
}
2、删除tab标签

 用一个on_closeTab() 函数来关闭指定索引的标签页,删除对应的窗口部件,并根据剩余标签页数量设置标签栏的标志,当只剩下一个标签页时,将标签页设置为不可关闭状态。

//关闭指定索引的标签页,并根据情况设置标签栏的标志
void tabbrowser::on_closeTab(int index)
{//删除指定索引的标签页的对应窗口部件widget(index)->deleteLater();setTabBarFlag(CLOSE); //设置标签栏的标志为 CLOSE//当只剩下1个tab时if (count() == 1){setTabsClosable(false); //设置标签页为不可关闭状态setTabBarFlag(SPECIAL); //设置标签栏的标志为 SPECIAL}
}

参考一下删除一个tab是怎么做的,是根据鼠标点击获取的index来delete,基于删除一个tab标签的操作,那么删除整个标签栏的操作自然也就很简单。我直接for循环delete所有,只剩下初始的那一个即可。

void tabbrowser::on_closeAllTab()
{for (int i = count(); i > 0; i--){//删除指定索引的标签页的对应窗口部件widget(i)->deleteLater();setTabBarFlag(CLOSE); //设置标签栏的标志为 CLOSE//当只剩下1个tab时if (count() == 1){setTabsClosable(false); //设置标签页为不可关闭状态setTabBarFlag(SPECIAL); //设置标签栏的标志为 SPECIAL}}
}

4、单击右键显示菜单

由于都是只创建菜单,大部分的具体实现都没做,所以还是很简单的,

//右键单击tab出现菜单栏
void tabbrowser::createTabMenu()
{m_pTabMenu = new QMenu(this);QAction* pAcSave = new QAction(QIcon(":/WPSDemo/resources/save.png"), u8"保存", m_pTabMenu);m_pTabMenu->addAction(pAcSave);connect(pAcSave, &QAction::triggered, [=] {QMessageBox::information(this, u8"提示", u8"你点击了 保存");}); //简单响应了一个保存的,其他的都还没做QAction* pAcSaveAs = new QAction(QString(u8"另存为"), m_pTabMenu);m_pTabMenu->addAction(pAcSaveAs);m_pTabMenu->addSeparator(); //这个是QAction之间的横线QAction* pAcShareDoc = new QAction(QIcon(":/WPSDemo/resources/share.png"), QString(u8"分享文档"), m_pTabMenu);m_pTabMenu->addAction(pAcShareDoc);QAction* pAcSendToDevice = new QAction(QString(u8"发送到设备"), m_pTabMenu);m_pTabMenu->addAction(pAcSendToDevice);m_pTabMenu->addSeparator();QAction* pAcNewName = new QAction(QString(u8"重命名"), m_pTabMenu);m_pTabMenu->addAction(pAcNewName);QAction* pAcSaveToWPSCloud = new QAction(QString(u8"保存到WPS云文档"), m_pTabMenu);m_pTabMenu->addAction(pAcSaveToWPSCloud);QAction* pAcCloseAll = new QAction(QString(u8"关闭所有文件"), m_pTabMenu);m_pTabMenu->addAction(pAcCloseAll);//删除所有和删除一个是差不多的connect(pAcCloseAll, &QAction::triggered, this, &tabbrowser::on_closeAllTab);
}

四、标题栏和tab标签的结合

负责标题栏右边的按钮实现,最后和tab结合,这个很简单的,看看代码,已经是很熟悉了,在这个类里不仅进行ui设计,还实现标题栏拖拽功能,绘图功能等。

#include "CTabTitleWidget.h"
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QStyleOption>
#include <QPainter>#ifdef Q_OS_WIN
#include <qt_windows.h>
#pragma comment(lib, "user32.lib")
#endifCTabTitleWidget::CTabTitleWidget(QWidget* parent)
{setStyleSheet("background-color:#E3E4E7");m_pAddBtn = new QPushButton(this);m_pAddBtn->setFlat(true);m_pAddBtn->setFixedSize(32, 32);m_pAddBtn->setStyleSheet("background-image:url(:/WPSDemo/resources/add.svg)");m_pEmptyWidget = new QWidget(this);m_pUserBtn = new QPushButton(this);m_pUserBtn->setFlat(true);m_pUserBtn->setFixedSize(32, 32);m_pUserBtn->setStyleSheet("background-image:url(:/WPSDemo/resources/user)");m_pMinBtn = new QPushButton(this);m_pMinBtn->setFlat(true);m_pMinBtn->setFixedSize(32, 32);m_pMinBtn->setStyleSheet("background-image:url(:/WPSDemo/resources/min.svg)");m_pMaxBtn = new QPushButton(this);m_pMaxBtn->setFlat(true);m_pMaxBtn->setFixedSize(32, 32);m_pMaxBtn->setStyleSheet("background-image:url(:/WPSDemo/resources/max.svg)");m_pCloseBtn = new QPushButton(this);m_pCloseBtn->setFlat(true);m_pCloseBtn->setFixedSize(32, 32);m_pCloseBtn->setStyleSheet("background-image:url(:/WPSDemo/resources/close.svg)");QHBoxLayout* pHLay = new QHBoxLayout(this);pHLay->addWidget(m_pAddBtn);pHLay->addWidget(m_pEmptyWidget);this->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);pHLay->addWidget(m_pUserBtn);pHLay->addSpacing(8);pHLay->addWidget(m_pMinBtn);pHLay->addWidget(m_pMaxBtn);pHLay->addWidget(m_pCloseBtn);pHLay->setContentsMargins(1, 0, 1, 3);setLayout(pHLay);connect(m_pAddBtn, &QPushButton::clicked, this, &CTabTitleWidget::on_Clicked);connect(m_pMinBtn, &QPushButton::clicked, this, &CTabTitleWidget::on_Clicked);connect(m_pMaxBtn, &QPushButton::clicked, this, &CTabTitleWidget::on_Clicked);connect(m_pCloseBtn, &QPushButton::clicked, this, &CTabTitleWidget::on_Clicked);
}CTabTitleWidget::~CTabTitleWidget()
{
}void CTabTitleWidget::setEmptyWidgetWidth(int w)
{m_pEmptyWidget->setMinimumWidth(w);
}void CTabTitleWidget::paintEvent(QPaintEvent* event)
{QStyleOption opt;opt.init(this);QPainter p(this);style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);QWidget::paintEvent(event);
}void CTabTitleWidget::mousePressEvent(QMouseEvent* event)
{if (ReleaseCapture()){QWidget* pWindow = this->window();if (pWindow->isTopLevel()){SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);}}event->ignore();
}void CTabTitleWidget::mouseDoubleClickEvent(QMouseEvent* event)
{emit m_pMaxBtn->clicked();
}void CTabTitleWidget::on_Clicked()
{QPushButton* pButton = qobject_cast<QPushButton*>(sender());QWidget* pWindow = this->window();if (pWindow->isTopLevel()){if (pButton == m_pAddBtn){emit sig_addtab();}else if (pButton == m_pMinBtn){pWindow->showMinimized();}else if (pButton == m_pMaxBtn){pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();}else if (pButton == m_pCloseBtn){emit sig_close();}}
}

都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt加载.css/.qss文件设置控件的QSS样式(支持程序运行时修改且立即生效类似换肤效果)

初学Qt时&#xff0c;你是如何设置QWidget&#xff0c;QPushButton等原生基础控件的样式的&#xff1f;是不是主要是两种方法&#xff1f; 1.直接在可视化的.ui文件中直接添加qss语句。 2.在代码中通过setStyleSheet(QString qss)来设置qss语句。 上述两种方法&#xff0c;在程…

微信小程序短链接工具推荐

现在微信小程序大行其道&#xff0c;但工作中大部分人选择了短链接的方式来推广微信小程序&#xff0c;那么微信小程序短链接工具哪个好?今天就分享一篇从网上看到的关于《微信小程序短链接工具推荐》文&#xff0c;作者是souki&#xff0c;一起来看看吧! 一、缩链 1、生成方…

虚幻UE5对接物联网教程

一、背景 这几年&#xff0c;智慧城市/智慧交通/智慧水利等飞速发展&#xff0c;骑士特意为大家做了一个这块的学习路线。 二、这是学习大纲 1.给虚幻UE5初学者准备的智慧城市/数字孪生蓝图开发教程 https://www.bilibili.com/video/BV1894y1u78G 2.UE5数字孪生蓝图开发教学…

基于架构的软件开发方法_1.概述和相关概念及术语

1.体系结构的设计方法概述 基于体系结构的软件设计&#xff08;Architecture-Based Software Design&#xff0c;ABSD&#xff09;方法。ABSD方法是由体系结构驱动的&#xff0c;即指由构成体系结构的商业、质量和功能需求的组合驱动的。 使用ABSD方法&#xff0c;设计活动可以…

Vue项目中引入html页面(vue.js中引入echarts数据大屏html [静态非数据传递!] )

在项目原有vue&#xff08;例如首页&#xff09;基础上引入html页面 1、存放位置 vue3原有public文件夹下 我这边是新建一个static文件夹 专门存放要用到的html文件 复制拖拽过来 index为html的首页 2、更改路径引入到vue中 这里用到的是 iframe 方法 不同于vue的 component…

WebAuthn:更好地保护线上敏感信息

1. 引言 2023年知乎博客 WebAuthn: 真正的无密码身份认证 总结得很赞。 在数字时代&#xff0c;密码已成为人们日常生活和在线活动中不可或缺的一部分。尽管互联网已经发展了 20 多年&#xff0c;许多方面都有了巨大的改进&#xff0c;但只有密码&#xff0c;还是 20 年前的用…

银行业架构网络BIAN (Banking IndustryArchitecture Network)详细介绍

BIAN ( The Banking Industry Architecture Network) 是一个业界多方协作的非营利性组织&#xff0c;由全球领先银行、技术提供商、顾问和学者组成&#xff0c;定义了一个用以简化和标准化核心银行体系结构的银行技术框架。这一框架基于面向服务的架构 (SOA) 原则&#xff0c;银…

java线程的几种状态

目录 正文&#xff1a; 1.JConsole 2.新建状态(New) 3.运行状态(Runnable) 4.阻塞状态(Blocked) 5.等待状态(Waiting) 6.计时等待状态(Timed Waiting) 7.终止状态(Terminated) 总结&#xff1a; 正文&#xff1a; 1.JConsole JConsole是Java监控和管理控制台工具&…

【深度优先】【树上倍增 】2846. 边权重均等查询

本文涉及知识点 深度优先 树上倍增 LeetCode2846. 边权重均等查询 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ui, vi, wi] 表示树中存在一条位于节点 ui…

【JavaScript 漫游】【053】Reflect

文章简介 本篇文章为【JavaScript 漫游】专栏的第 053 篇文章&#xff0c;记录了 ES6 规范中关于 Reflect 的知识点。 概述 Reflect 对象与 Proxy 对象一样&#xff0c;也是 ES6 为了操作对象而提供的新 API。Reflect 对象的设计目的有这样几个。 &#xff08;1&#xff09…

设计模式 --5观察者模式

观察者模式 观察者模式的优缺点 优点 当一个对象改变的时候 需要同时改变其他对象的相关动作的时候 &#xff0c;而且它不知道有多少具体的对象需要改变 应该考虑使用观察者模式 。观察者模式的工作就是解除耦合 让耦合双方都依赖与抽象 而不是具体 是的各自改变都不会影响另…

聚类算法的先验基础知识

聚类算法的先验基础知识 1. 瑞利商2. 谱定理3. 联合概率4. 条件概率分布5. 边缘分布6. 贝叶斯定理7. 有向图8. 拉格朗日乘子定理 下一篇将介绍整理各种聚类算法&#xff0c;包括k-means&#xff0c;GMM(Guassian Mixture Models, 高斯混合)&#xff0c;EM(Expectation Maximiza…