当前位置: 首页 > news >正文

✍Qt自定义带图标按钮

✍Qt自定义带图标按钮

📝问题引入

近段时间的工作中,有遇到这样一个需求 📝:

一个按钮,有normal、hover、pressed三种状态的样式,并且normal和hover样式下,字体颜色和按钮图标不一样。

分析这个需求,背景色和文字颜色容易实现,复杂的点在于图标和隐藏的一个点——文字和图标的间距以及文字的换行。常规的样式表只能实现背景色、文字颜色,无法设置hover和normal状态下,不同的icon。当然,我们有很多种方式来实现这个需求:例如创建一个继承自QWidget的类,并在里面添加文字label和图标label。但这种方式需要处理按钮的点击事件(毕竟一个按钮不可能光秃秃的放在那里,肯定会有点击操作)。

我采用的方案是:创建一个QPushButton,并创建一个布局,在布局里塞入文字label和图标label,最后在将这个布局设置给创建的QPushButton。代码可能如下:

当然,还要处理一下按钮的hover事件:

// ... 假设你为这个按钮安装了事件过滤器
bool eventFilter(QObject* watched, QEvent* e)
{if (e->type() == QEvent::HoverEnter) {iconLabel->setProperty("LabelStatus", "Hover");textLabel->setProperty("LabelStatus", "Hover");this->style()->unpolish(iconLabel);this->style()->polish(iconLabel);this->style()->unpolish(textLabel);this->style()->polish(textLabel);} else if (e->type() == QEvent::HoverLeave) {iconLabel->setProperty("LabelStatus", "Normal");textLabel->setProperty("LabelStatus", "Normal");this->style()->unpolish(iconLabel);this->style()->polish(iconLabel);this->style()->unpolish(textLabel);this->style()->polish(textLabel);}return QWidget::eventFilter(watched, e);
}

你可以通过样式表来设置图标和文字样式:

QLabel#iconLabel[LabelStatus=Normal]
{border-image: url("xxx");
}QLabel#iconLabel[LabelStatus=Hover]
{border-image: url("xxx_hover");
}QLabel#textLabel[LabelStatus=Normal]
{color: #FFFFFF;
}QLabel#textLabel[LabelStatus=Hover]
{color: #FF0000;
}

这样做一个好处就是你就不需要去单独处理点击事件(虽然都已经处理了HoverEnter和HoverLeave😓),还有一个好处是:在按钮过多时,你可以将按钮加入到QButtonGroup里,统一对按钮的点击事件进行处理

当然,代码还可以优化,你可以将这些内容封装成类:

class MyButton : public QPushButton
{Q_OBJECTpublic:MyButton(QWidget* parent): QPushButton(parent), m_pIconLabel{nullptr}, m_pTextLabel{nullptr}{auto hLayout = new QHBoxLayout();hLayout->setContentMargins(4, 4, 4, 4);hLayout->setSpacing(8);	// 设置文字和图标的间距m_pTextLabel = new QLabel(this);m_pTextLabel->setObjectName("m_pTextLabel");m_pIconLabel = new QLabel(this);m_pIconLabel->setObjectName("m_pIconLabel");hLayout->addWidget(m_pTextLabel);hLayout->addWidget(m_pIconLabel);this->setLayout(hLayout);this->installEventFilter(this);this->setStyleSheet(R"(QLabel#iconLabel[LabelStatus=Normal]{border-image: url("xxx");}QLabel#iconLabel[LabelStatus=Hover]{border-image: url("xxx_hover");}QLabel#textLabel[LabelStatus=Normal]{color: #FFFFFF;}QLabel#textLabel[LabelStatus=Hover]{color: #FF0000;})");}private:bool eventFilter(QObject* watched, QEvent* e) override{if (e->type() == QEvent::HoverEnter) {m_pIconLabel->setProperty("LabelStatus", "Hover");m_pTextLabel->setProperty("LabelStatus", "Hover");this->style()->unpolish(m_pIconLabel);this->style()->polish(m_pIconLabel);this->style()->unpolish(m_pTextLabel);this->style()->polish(m_pTextLabel);} else if (e->type() == QEvent::HoverLeave) {m_pIconLabel->setProperty("LabelStatus", "Normal");m_pTextLabel->setProperty("LabelStatus", "Normal");this->style()->unpolish(m_pIconLabel);this->style()->polish(m_pIconLabel);this->style()->unpolish(m_pTextLabel);this->style()->polish(m_pTextLabel);}return QPushButton::eventFilter(watched, e);}private:QLabel* m_pIconLabel;QLabel* m_pTextLabel;
};

但当把这个按钮加到到实际的界面时,问题出现了:

// ...创建界面
auto hLayout = new QHBoxLayout(this);auto title = new QLabel(this);
title->setText("XXXXXX");auto btn = new MyButton(this);hLayout->addWidget(title);
hLayout->addStrect();
hLayout->addWidget(btn);

我想要的效果是:

在切换语言后,按钮的宽度能够跟随文字的长度变换而相应变宽或变窄。

但实际情况与预期有所不同😕。即使我将按钮或文字的sizePolicy设置为Expanding,效果依然不理想。而原生的QPushButton是可以根据文字宽度自动调整大小的。这是为什么呢🤔?按理说,我已经把自定义按钮内的文字标签等组件放入了一个水平布局中,它应该能够自动调整宽度,但实际上水平布局并未起作用。这种情况让我很好奇,想要去了解Qt的布局管理系统是如何工作的。

🚀Qt布局探秘

在Qt关于布局管理的[官方文档](https://doc.qt.io/qt-5/layout.html)中,有这样一句话:

:::success

Adding Widgets to a Layout

When you add widgets to a layout, the layout process works as follows:
  1. All the widgets will initially be allocated an amount of space in accordance with their QWidget::sizePolicy() and QWidget::sizeHint().

:::

大致意思就是:

所有界面的初始大小将根据其尺寸策略(sizePolicy)和尺寸建议(sizeHint)进行设定。

这让我突然有了一个灵感🌟,是不是因为QPushButton的sizeHint中,计算的文字控件与我所显示的文字控件不是同一个呢?而外层布局又是直接调用QPushButton的sizeHint函数来获取宽度的,进而导致按钮的大小不如人意。为了验证我的想法,我决定到QPushButton的源码中一探究竟。

QSize QPushButton::sizeHint() const
{Q_D(const QPushButton);// ...QString s(text());bool empty = s.isEmpty();if (empty)s = QStringLiteral("XXXX");QFontMetrics fm = fontMetrics();QSize sz = fm.size(Qt::TextShowMnemonic, s);if(!empty || !w)w += sz.width();if(!empty || !h)h = qMax(h, sz.height());opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height// ...d->sizeHint = (style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(w, h), this).expandedTo(QApplication::globalStrut()));return d->sizeHint;
}

事实确实是这样,QPushButton中计算的文本是通过text函数来获取的,而这个文本又是通过调用setText来设置的,我们绕过了这一步,那计算的大小就肯定不是我们想要的了。

🛠️问题的解决

看到这里,相信各位同学也都已经知道解决方法了:**实现自己的sizeHint。**

最终,我们的按钮类变成了:

class MyButton : public QPushButton
{Q_OBJECTpublic:MyButton(QWidget* parent): QPushButton(parent), m_pIconLabel{nullptr}, m_pTextLabel{nullptr}, m_pLayout{nullptr}{m_pLayout = new QHBoxLayout();m_pLayout->setContentMargins(4, 4, 4, 4);m_pLayout->setSpacing(8);	// 设置文字和图标的间距m_pTextLabel = new QLabel(this);m_pTextLabel->setObjectName("m_pTextLabel");m_pIconLabel = new QLabel(this);m_pIconLabel->setObjectName("m_pIconLabel");m_pLayout->addWidget(m_pTextLabel);m_pLayout->addWidget(m_pIconLabel);this->setLayout(m_pLayout);this->installEventFilter(this);this->setStyleSheet(R"(QLabel#iconLabel[LabelStatus=Normal]{border-image: url("xxx");}QLabel#iconLabel[LabelStatus=Hover]{border-image: url("xxx_hover");}QLabel#textLabel[LabelStatus=Normal]{color: #FFFFFF;}QLabel#textLabel[LabelStatus=Hover]{color: #FF0000;})");}QSize sizeHint(){return m_pLayout->sizeHint();}private:bool eventFilter(QObject* watched, QEvent* e) override{if (e->type() == QEvent::HoverEnter) {m_pIconLabel->setProperty("LabelStatus", "Hover");m_pTextLabel->setProperty("LabelStatus", "Hover");this->style()->unpolish(m_pIconLabel);this->style()->polish(m_pIconLabel);this->style()->unpolish(m_pTextLabel);this->style()->polish(m_pTextLabel);} else if (e->type() == QEvent::HoverLeave) {m_pIconLabel->setProperty("LabelStatus", "Normal");m_pTextLabel->setProperty("LabelStatus", "Normal");this->style()->unpolish(m_pIconLabel);this->style()->polish(m_pIconLabel);this->style()->unpolish(m_pTextLabel);this->style()->polish(m_pTextLabel);}return QPushButton::eventFilter(watched, e);}private:QLabel* m_pIconLabel;QLabel* m_pTextLabel;QHBoxLayout* m_pLayout;
};

通过返回内部布局的尺寸,来控制按钮在布局中的尺寸。

相关文章:

✍Qt自定义带图标按钮

✍Qt自定义带图标按钮 📝问题引入 近段时间的工作中,有遇到这样一个需求 📝: 一个按钮,有normal、hover、pressed三种状态的样式,并且normal和hover样式下,字体颜色和按钮图标不一样。 分析…...

【Git】如何在 Git 项目中引用另一个 Git 项目:子模块与子树合并

如何在 Git 项目中引用另一个 Git 项目:子模块与子树合并 在进行软件开发时,我们经常会遇到需要将一个 Git 项目(B 项目)引用到另一个 Git 项目(A 项目)的情况。这种需求通常出现在以下场景: …...

webstorm 打开prettier的项目代码后面会出现红色的波浪线

效果如图所有代码后面都有红色的波浪线。 解决File-Settings 找到Editor下面的inspections ...按照图示取消勾选ESLint再点Apply ok...

用 Python 从零开始创建神经网络(二):第一个神经元的进阶

第一个神经元的进阶 引言1. Tensors, Arrays and Vectors:2. Dot Product and Vector Additiona. Dot Product (点积)b. Vector Addition (向量加法) 3. A Single Neuron with NumPy4. A Layer of Neurons with NumPy5…...

一、文心一言问答系统为什么要分对话,是否回学习上下文?二、文心一言是知识检索还是大模型检索?三、文心一言的词向量、词语种类及多头数量

目录 一、文心一言问答系统为什么要分对话,是否回学习上下文? 二、文心一言是知识检索还是大模型检索? 三、文心一言的词向量、词语种类及多头数量 一、文心一言问答系统为什么要分对话,是否回学习上下文? 文心一言问答系统分对话的原因在于其设计初衷就是提供一个交互…...

C++ 的协程

现代C中的协程(coroutines)是C20引入的一项重大语言特性,它们允许函数在执行过程中可以暂停并稍后从暂停点恢复执行。协程提供了一种控制流机制,使得函数可以包含多个入口点和出口点,这与传统的单入口、单出口的函数模…...

D3的竞品有哪些,D3的优势,D3和echarts的对比

D3 的竞品 ECharts: 简介: ECharts 是由百度公司开发的一款开源的 JavaScript 图表库,提供了丰富的图表类型和高度定制化的配置选项。特点: 易于使用,文档详尽,社区活跃,支持多种图表类型(如折线图、柱状图、饼图、散点…...

大厂计算机网络高频八股文面试题及参考答案(面试必问,持续更新)

目录 请简述 TCP 和 UDP 的区别? TCP 和 UDP 分别对应的常见应用层协议有哪些? UDP 的优缺点是什么?它适用于哪些场景? UDP 如何实现可靠传输? 请简述 HTTP 和 HTTPS 的区别? HTTP 协议的工作原理是什么? HTTP 状态码有哪些常见的类型及其含义? HTTP 哪些常用的…...

【bayes-Transformer-GRU多维时序预测】多变量输入模型。matlab代码,2023b及其以上

% 1. 数据准备 X_train 训练数据输入; Y_train 训练数据输出; X_test 测试数据输入; % 2. 模型构建 inputSize size(X_train, 2); numHiddenUnits 100; numResponses 1; layers [ … sequenceInputLayer(inputSize) biLSTMLayer(numHiddenUnits, ‘OutputMode’, ‘se…...

动手学深度学习69 BERT预训练

1. BERT 3亿参数 30亿个词 在输入和loss上有创新 两个句子拼起来放到encoder–句子对 cls-class分类 sep-seperate 分隔符 分开每个句子 告诉是哪个句子 两个句子给不同的向量 位置编码不用sin cos, 让网络自己学习 bert–通用任务 encoder 是双向的,…...

【2024软考架构案例题】你知道 Es 的几种分词器吗?Standard、Simple、WhiteSpace、Keyword 四种分词器你知道吗?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区&#x…...

Elman 神经网络 MATLAB 函数详解

Elman 神经网络 MATLAB 函数详解 一、引言 Elman 神经网络是一种在时间序列分析和动态系统建模领域广泛应用的递归神经网络(RNN)。MATLAB 提供了一系列强大的函数来创建、训练和应用 Elman 神经网络,使得用户能够方便地利用其处理具有时间序…...

vue el-date-picker 日期选择器禁用失效问题

当value-format"yyyy-MM-dd"的格式不要改为"yyyyMMdd"&#xff0c;否则会导致日期选择器禁用失效问题&#xff0c;因为该组件默认的格式就是yyyy-MM-dd。 <el-col v-for"(item, index) in formData" :key"index" ><el-date-…...

搭建Python2和Python3虚拟环境

搭建Python3虚拟环境 1. 更新pip2. 搭建Python3虚拟环境第一步&#xff1a;安装python虚拟化工具第二步&#xff1a; 创建虚拟环境 3. 搭建Python2虚拟环境第一步&#xff1a;安装虚拟环境模块第二步&#xff1a;创建虚拟环境 4. workon命令管理虚拟机第一步&#xff1a;安装扩…...

【HarmonyOS NEXT】一次开发多端部署(以轮播图、Tab栏、列表为例,配合栅格布局与媒体查询,进行 UI 的一多开发)

关键词&#xff1a;一多、响应式、媒体查询、栅格布局、断点、UI 随着设备形态的逐渐增多&#xff0c;应用界面适配也面临着很大问题&#xff0c;在以往的安卓应用开发过程中&#xff0c;往往需要重新开发一套适用于大屏展示的应用&#xff0c;耗时又耗力&#xff0c;而鸿蒙提供…...

ubontu--cuDNN安装

1. 下载 cuDNN https://developer.nvidia.com/cudnn 2. 拷贝到服务器/home/<username>文件夹下 解压缩到当前文件夹&#xff1a; tar -xvf cudnn-linux-x86_64-9.5.1.17_cuda11-archive.tar.xz复制头文件和库文件到cuda安装目录/usr/local/cuda/ sudo cp /home/usern…...

高项 - 项目范围管理

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 博文更新参考时间点&#xff1a;2024-12 高项 - 章节与知识点汇总&#xff1a;点击跳转 文章目录 高项 - 项目范围管理范围管理ITO规划监控 管理基础产品范围与项目范围管理新实践 5大过程组与范围管理过程概述裁…...

如何获取PostgreSQL慢查询?从小白到高手的实战指南

数据库优化是性能调优的核心&#xff0c;而慢查询则是性能瓶颈的罪魁祸首。如何找到慢查询并优化它们&#xff0c;是每个开发者和DBA都必须掌握的技能。 今天&#xff0c;我们就来聊聊如何在PostgreSQL中快速获取慢查询日志&#xff0c;并结合不同场景进行分析优化。本文风格参…...

golang分布式缓存项目 Day4 一致性哈希

注&#xff1a;该项目原作者&#xff1a;https://geektutu.com/post/geecache-day1.html。本文旨在记录本人做该项目时的一些疑惑解答以及部分的测试样例以便于本人复习 为什么使用一致性哈希 我该访问谁 对于分布式缓存来说&#xff0c;当一个节点接收到请求&#xff0c;如…...

ARM 汇编指令

blr指令的基本概念和用途 在 ARM64 汇编中&#xff0c;blr是 “Branch with Link to Register” 的缩写。它是一种分支指令&#xff0c;主要用于跳转到一个由寄存器指定的地址&#xff0c;并将返回地址保存到链接寄存器&#xff08;Link Register&#xff0c;LR&#xff09;中。…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...