Qt自定义带前后缀图标的PushButton

写在前面

Qt提供QPushButton不满足带前后缀图标的需求,因此考虑自定义实现带前后缀图标的PushButton,方便后续快速使用。

效果如下:
1

2

3

4
同时可设置前后缀图标和文本之间间隙:
5

代码实现

通过前文介绍的Qt样式表底层实现

可以得知通过setStyleSheet()设置的样式,最终都会到paintEvent中绘制实现。

因此本示例的原理就是:前后缀图标和文本的绘制通过重写paintEvent实现,其他样式设置调用默认的paintEvent()处理。

完整代码如下:

#ifndef MPUSHBUTTON_H
#define MPUSHBUTTON_H
#include <QPushButton>
#include <QIcon>class MPushButton : public QPushButton
{Q_OBJECT
public:explicit MPushButton(QWidget* parent = nullptr);void setPrefixIcons(const QIcon(&icons)[4]);void setSuffixIcons(const QIcon(&icons)[4]);void setPrefixIconSize(const QSize& size);void setSuffixIconSize(const QSize& size);void setPrefixIconTextSpacing(int spacing);void setSuffixIconTextSpacing(int spacing);void hidePrefixIcon(bool bHide);void hideSuffixIcon(bool bHide);void setTextColor(const QColor(&colors)[4]);signals:protected:void paintEvent(QPaintEvent* event) override;private:QIcon m_prefixIcons[4];QIcon m_suffixIcons[4];QSize m_prefixIconSize;QSize m_suffixIconSize;int m_prefixIconTextSpacing;int m_suffixIconTextSpacing;bool m_hide_prefix_icon;bool m_hide_suffix_icon;QColor m_textColor[4];
};#endif // MPUSHBUTTON_H
#include "mpushbutton.h"#include <QPainter>
#include <QStyleOptionButton>
#include <QStylePainter>MPushButton::MPushButton(QWidget* parent): QPushButton{ parent }
{setAttribute(Qt::WA_Hover);m_prefixIconSize = QSize(20, 20);m_suffixIconSize = QSize(20, 20);m_prefixIconTextSpacing = 2;m_suffixIconTextSpacing = 2;m_hide_prefix_icon = true;m_hide_suffix_icon = true;
}void MPushButton::setPrefixIcons(const QIcon(&icons)[4])
{std::copy(std::begin(icons), std::end(icons), std::begin(m_prefixIcons));update();
}void MPushButton::setSuffixIcons(const QIcon(&icons)[4])
{std::copy(std::begin(icons), std::end(icons), std::begin(m_suffixIcons));update();
}void MPushButton::setPrefixIconSize(const QSize& size)
{m_prefixIconSize = size;update();
}void MPushButton::setSuffixIconSize(const QSize& size)
{m_suffixIconSize = size;update();
}void MPushButton::setPrefixIconTextSpacing(int spacing)
{m_prefixIconTextSpacing = spacing;update();
}void MPushButton::setSuffixIconTextSpacing(int spacing)
{m_suffixIconTextSpacing = spacing;update();
}void MPushButton::hidePrefixIcon(bool bHide)
{m_hide_prefix_icon = bHide;update();
}void MPushButton::hideSuffixIcon(bool bHide)
{m_hide_suffix_icon = bHide;update();
}void MPushButton::setTextColor(const QColor(&colors)[4])
{std::copy(std::begin(colors), std::end(colors), std::begin(m_textColor));update();
}void MPushButton::paintEvent(QPaintEvent* event)
{//通过父类QPushButton绘制样式表//注意:通过text-align设置文本对齐会影响图标设置效果QString qsText = text();setText("");	//设置文本为空,即不绘制文本,以便后面自己绘制QPushButton::paintEvent(event);	// 保留样式表的样式setText(qsText);QStylePainter painter(this);QStyleOptionButton option;initStyleOption(&option);//option.initFrom(this);QFontMetrics fm = painter.fontMetrics();QRect contentRect = style()->subElementRect(QStyle::SE_PushButtonContents, &option, this);int idx;if (!isEnabled()){idx = 3;}else if (isDown() || isChecked()){idx = 2;}else if (underMouse()){idx = 1;}else{idx = 0;}QPixmap prefixPixmap = m_prefixIcons[idx].pixmap(m_prefixIconSize);QPixmap suffixPixmap = m_suffixIcons[idx].pixmap(m_suffixIconSize);//自定义图标高度和文本间距。注意:前缀图标、文本、后缀图标要当成一个整体处理,否则无法应用样式表属性(如padding)int totalWidth = 0;int prefixStartX = 0;int contentStartX = 0;int suffixStartX = 0;if (m_hide_prefix_icon && m_hide_suffix_icon){//不显示前后缀图标totalWidth = fm.width(option.text);contentStartX = contentRect.left() + (contentRect.width() - totalWidth) / 2;}else if (!m_hide_prefix_icon && !m_hide_suffix_icon){//显示前后缀图标totalWidth = m_prefixIconSize.width() + fm.width(option.text) + m_suffixIconSize.width() + m_prefixIconTextSpacing + m_suffixIconTextSpacing;prefixStartX = contentRect.left() + (contentRect.width() - totalWidth) / 2;contentStartX = prefixStartX + m_prefixIconSize.width() + m_prefixIconTextSpacing;suffixStartX = prefixStartX + m_prefixIconSize.width() + m_prefixIconTextSpacing + fm.width(option.text) + m_suffixIconTextSpacing;}else if (m_hide_prefix_icon && !m_hide_suffix_icon){//只显示后缀图标totalWidth = fm.width(option.text) + m_suffixIconSize.width() + m_suffixIconTextSpacing;contentStartX = contentRect.left() + (contentRect.width() - totalWidth) / 2;suffixStartX = contentStartX + fm.width(option.text) + m_suffixIconTextSpacing;}else{//只显示前缀图标totalWidth = m_prefixIconSize.width() + fm.width(option.text) + m_prefixIconTextSpacing;prefixStartX = contentRect.left() + (contentRect.width() - totalWidth) / 2;contentStartX = prefixStartX + m_prefixIconSize.width() + m_prefixIconTextSpacing;}int startY = contentRect.top() + (contentRect.height() - fm.height()) / 2;int startPreY = contentRect.top() + (contentRect.height() - m_prefixIconSize.height()) / 2;int startSufY = contentRect.top() + (contentRect.height() - m_suffixIconSize.height()) / 2;if (!m_hide_prefix_icon){//前面已有当前状态判断,后续可扩展维护hover、presse状态的前缀图标painter.drawPixmap(prefixStartX, startPreY, m_prefixIconSize.width(), m_prefixIconSize.height(), prefixPixmap);}//方式一、通过drawText绘制文本// 优点:可自己指定绘制位置,// 缺点:无法应用样式表中字体相关的设置// 处理:自己维护正常、悬浮、点击、禁用时的文本颜色,自己绘制QColor textColor = m_textColor[idx];painter.setPen(textColor);painter.drawText(contentStartX, startY, fm.width(option.text), fm.height(), Qt::AlignCenter, option.text);//方式二、通过drawControl绘制文本//可通过QStylePainter绘制保留样式的文本//优点:可以应用样式表中字体相关的设置//缺点:无法指定绘制位置,遇到text-align或者padding样式属性值时会绘制两次文本,导致文本错位,同时与前后缀图标错位//style()->drawControl(QStyle::CE_PushButtonLabel, &option, &painter, this);if (!m_hide_suffix_icon){//前面已有当前状态判断,后续可扩展维护hover、presse状态的前缀图标painter.drawPixmap(suffixStartX, startSufY, m_suffixIconSize.width(), m_suffixIconSize.height(), suffixPixmap);}
}

使用示例:

#include "mpushbutton.h"
void MyWidget::mpushbutton_test()
{MPushButton* btn = new MPushButton(this);QIcon prefixIcons[4] = {QIcon(":/Image/avatar_normal.svg"), QIcon(":/Image/avatar_hover_pressed.svg"), QIcon(":/Image/avatar_hover_pressed.svg"), QIcon(":/Image/avatar_normal.svg")};btn->setPrefixIcons(prefixIcons);btn->setPrefixIconSize(QSize(16, 16));QIcon suffixIcons[4] = {QIcon(":/Image/avatar_normal.svg"), QIcon(":/Image/avatar_hover_pressed.svg"), QIcon(":/Image/avatar_hover_pressed.svg"), QIcon(":/Image/avatar_normal.svg")};btn->setSuffixIcons(suffixIcons);btn->setSuffixIconSize(QSize(16, 16));//可通过样式表设置背景、边框等样式QString qsBtnCSS = "QPushButton{font-family: Microsoft YaHei; font-size: 14px; font-weight: normal; color: #333333; border: 1px solid #DBDBDB;border-radius: 4px;text-align: left; padding-left: 20px;background: #FAFBFC; }""QPushButton:hover{ background: #EBEBEB;  }""QPushButton:pressed{background: #EBEBEB;  }""QPushButton:disabled{background: #EBEBEB;  }";btn->setStyleSheet(qsBtnCSS);//btn->setPrefixIconTextSpacing(10);		//可设置前后缀图标和文本之间的间隙//btn->setSuffixIconTextSpacing(4);QColor textColor[4] = {QColor("#333333"), QColor("#2982FF"), QColor("#0053D9"), QColor("#BDBDBD")};btn->setTextColor(textColor);btn->hidePrefixIcon(false);btn->hideSuffixIcon(false);btn->resize(96, 30);btn->setText("Admin");btn->move(100, 100);//btn->setEnabled(false);	//验证禁用效果
}

总结

通过自定义QPushButton,重写paintEvent,同时保留setStyleSheet()设置的样式,来实现带前后缀图标的MPushButton,以满足特殊场景使用。

这样实现的问题上面也有提到,自己绘制文本,需要考虑文本相关的样式(如text-align、padding)的影响。

后续也可按需扩展维护hover、pressed、disabled状态的前后缀图标。

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

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

相关文章

【人工智能】AI绘画:科技与艺术交汇的新时代

文章目录 &#x1f34a;AI绘画:开启艺术创作新纪元AI绘画技术发展&#xff1a;算法与艺术的完美交融AI绘画的工作原理与创意生成AI绘画的应用 AI绘画工具介绍 &#x1f34a;AI绘画:开启艺术创作新纪元 人工智能正以前所未有的力量重塑我们的世界&#xff0c;而AI绘画作为这股科…

0725,进程间传递文件描述符,socketpair + sendmsg/recvmsg

我要碎掉了我要碎掉了我要碎掉了 上课喵&#xff1a; pipe匿名管道的问题 #include <func.h>int main() {int fds[2];pipe(fds);pid_t pidfork();if(pid>0){ //fatherclose(fds[0]);//close readint fdopen("file2.txt",O_RDONLY);printf("father: …

如何安全的申请SSL证书

随着数字化时代的快速发展&#xff0c;互联网政务应用已成为政府服务民众、提升治理效能的重要途径。在这个网络日益复杂的时代&#xff0c;政务网站的安全问题显得尤为重要。2024年&#xff0c;国家出台并从2024年7月1日开始实施执行《互联网政务应用安全管理规定》&#xff0…

常见的CSS属性(一)——字体、文本、边框、内边距、外边距、背景、行高、圆角、透明度、颜色值

一、字体 二、文本 三、边框 四、外边距 五、内边距 六、背景 七、行高 八、圆角 九、透明度 九、颜色值 元素的继承性是指给父元素设置了某些属性&#xff0c;子元素或后代元素也会有作用。 一、字体 “font-*”是字体相关的属性&#xff0c;具有继承性。代码如下&a…

Github 2024-07-26 Java开源项目日报 Top10

根据Github Trendings的统计,今日(2024-07-26统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目9HTML项目1TypeScript项目1非开发语言项目1JavaGuide - Java 程序员学习和面试指南 创建周期:2118 天开发语言:Java协议类型:Apache…

Matlab编程资源库(7)图形修饰处理

一、视点处理 MATLAB 提供了 设置视点 的函数 view &#xff0c;其调用格式 为&#xff1a; view(az,el) 其中 az 为 方位角 &#xff0c; el 为 仰角 &#xff0c;它们均以度为单位。 系统缺省的视点定义为方位角-37.5 , 仰角30 。 二、色彩处理 1 &#xff0e;颜色的向量…

【前端学习】CSS三大特性

CSS三大特性 CSS的三大特性是为了化简代码、定位问题并且解决问题 继承性 继承性特点&#xff1a; 子级默认继承父级的文字控制属性。注意&#xff1a;如果标签自己有样式则生效自己的样式&#xff0c;不继承。 <!DOCTYPE html> <html lang"en"><…

windows USB 设备驱动开发- WinUSB 简介

WinUSB 是 Windows 随附的 USB 设备的通用驱动程序。WinUSB 包括&#xff1a; 内核模式驱动程序 (Winusb.sys)&#xff1b;公开 winusb.h 中所述的 WinUSB 函数的用户模式动态链接库 (Winusb.dll)。 借助这些函数&#xff0c;你可以使用用户模式软件管理 USB 设备&#xff1b;…

12 位运算符

位运算符只能用于整数&#xff0c;其内部执行过程为&#xff1a;首先将整数转换为二进制数&#xff0c;然后右对齐&#xff0c;必要时左侧补0&#xff0c;按位进行运算&#xff0c;最后再把计算结果转换为十进制数字返回。 ① 左移&#xff1a;高位丢弃&#xff0c;低位补0&…

Exponential Moving Average (EMA) in Stable Diffusion

1.Moving Average in Stable Diffusion (SMA&EMA) 1.Moving average 2.移动平均值 3.How We Trained Stable Diffusion for Less than $50k (Part 3) Moving Average 在统计学中&#xff0c;移动平均是通过创建整个数据集中不同选择的一系列平均值来分析数据点的计算。 …

数据结构与算法-插入排序

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、插入排…

unity ui toolkit的使用

UIToolkitExamples (github)样例 GitHub - ikewada/UIToolkitExamples: チュートリアル動画「使ってみようUI Toolkit」のためのサンプルプロジェクトです官网 Unity - Manual: UI Toolkit视频教程 使用 UI Toolkit - 上集_哔哩哔哩_bilibili 使用 UI Toolkit - 下集_哔哩哔哩_…

Java | Leetcode Java题解之第283题移动零

题目&#xff1a; 题解&#xff1a; class Solution {public void moveZeroes(int[] nums) {int n nums.length, left 0, right 0;while (right < n) {if (nums[right] ! 0) {swap(nums, left, right);left;}right;}}public void swap(int[] nums, int left, int right)…

赋能未来教育,3DCAT助力深圳鹏程技师学院打造5G+XR实训室

随着国家对教育行业的重视&#xff0c;实训室建设已成为推动教育现代化的关键。《教育信息化2.0行动计划》、《职业教育示范性虚拟仿真实训基地建设指南》等政策文件&#xff0c;明确指出了加强虚拟仿真实训教学环境建设的重要性。 在这一大背景下&#xff0c;教育行业对于实训…

初识C++ · AVL树(1)

目录 前言&#xff1a; 1 AVL树的创建 2 部分成员函数 2.1 查找 2.2 中序遍历 2.3 插入 2.4 左旋转 2.5右旋转 前言&#xff1a; 上文&#xff0c;上上文提到了map set&#xff0c;二叉搜索树&#xff0c;其实都是为了近两文做铺垫的&#xff0c;虽然map的底层是红黑树…

openFeign配置okhttp

原来的项目出现了性能问题&#xff0c;老大不知道怎么的&#xff0c;让我改openFeign线程池为okhttp&#xff0c;说原生的不支持线程池性能比较差。 原openFeign配置文章地址 一、pom文件 <dependency><groupId>org.springframework.cloud</groupId><arti…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的跳格子游戏(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

手写spring简易版本,让你更好理解spring源码

首先我们要模拟spring&#xff0c;先搞配置文件&#xff0c;并配置bean 创建我们需要的类&#xff0c;beandefito&#xff0c;这个类是用来装解析后的bean&#xff0c;主要三个字段&#xff0c;id&#xff0c;class&#xff0c;scop&#xff0c;对应xml配置的属性 package org…

第二讲:NJ网络配置

Ethernet/IP网络拓扑结构 一. NJ EtherNet/IP 1、网络端口位置 NJ的CPU上面有两个RJ45的网络接口,其中一个是EtherNet/IP网络端口(另一个是EtherCAT的网络端口) 2、网络作用 如图所示,EtherNet/IP网络既可以做控制器与控制器之间的通信,也可以实现与上位机系统的对接通…

MySQL --- 表的操作

在对表进行操作时&#xff0c;需要先选定操作的表所在的数据库&#xff0c;即先执行 use 数据库名; 一、创建表 create table 表名( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎 ; 说明&#xff1a…