学习C++、QT---30(QT库中如何自定义控件(自定义按钮)讲解)

每日一言

        你比想象中更有韧性,那些看似艰难的日子,终将成为勋章。

自定义按钮

        我们要知道自定义控件就需要我们创建一个新的类加上继承父类,但是我们还要注意一个点,就是如果我们是自己重头开始造控件的话,那么我们就直接可以继承QWidget就可以了,因为这个属于是极致的定制开发,如果是只需要在QT已有的控件上添加QT官方在这个控件上没有的功能的话就需要我们自己创建新的类之后还需要将原有的控件进行提升为这个我们创建的新类,提升成自己的类,那么我们写的新控件就等于是在原有控件上的改变,变的拥有了更多功能,但是可以说又保存了90%的原有控件的功能,这个就是两种不同的自定义控件的方法

好的那我们现在来自定义一个按钮吧(实际上是自定义一个控件,看起来像按钮一样的功能)

就是我们这个说实话不叫自定义按钮,应该是叫做自定义控件,只是看起来像一个按钮,现在我来讲讲怎么搞,首先我们先创建一个C++的类叫做MyButton随后继承这个QWidget,那么就是意味着是全部是自己极致开发,全部自己干,所以我们需要重写的事件有鼠标点击事件、鼠标的进入事件

Widget::enterEvent

MyButton::enterEvent

这个两个是不一样的效果的,一个是在窗口的时候,鼠标进入到窗口就会触发事件,一个是要在Mybutton这个控件上的时候才会触发事件,

每一个控件都有自己的(鼠标、键盘)事件

好的现在我们想要我们的自定义按钮是一个图片的样子我们怎么操作呢?

那么我们这边就需要进行接收新的知识点了

QPixmap  这个类是专门用于画图的

这里有一个load这个函数,就是可以载入我们的资源文件的,值得注意的是我们去看这个函数的参数

涨知识啦

核心是理解 const QString & 作为函数参数的特性—— 它允许直接接收临时对象(比如字符串字面量转换的临时 QString),而不需要显式定义一个变量。我们一步步拆解:

一、const QString & 为什么能直接接收字符串字面量?

当你写 pic.load(":/QTicon/open2.png") 时,发生了这几步:

  1. 字符串字面量 ":/QTicon/open2.png" 是 const char* 类型,Qt 会自动调用 QString 的构造函数,创建一个临时的 QString 对象(这个对象在内存中短暂存在,没有名字)。
  2. 函数参数 const QString &fileName 是常量引用,它可以直接绑定到这个临时 QString 对象上(这是 C++ 标准允许的,const 引用的特殊权限)。

二、为什么不需要先用变量接收?

非 const 引用(如 QString &必须绑定到一个 “有名字的、已存在的变量”,不能绑定临时对象。例如:

void func(QString &s) {}  // 非const引用参数func("hello");  // 报错!不能绑定临时对象

但 const 引用 被设计为可以绑定到:

    • 已存在的变量(如 QString path = "..."; load(path);
    • 临时对象(如字符串字面量转换的临时 QString)
      这是 C++ 的语法规则,目的是方便使用(不需要显式定义变量),同时保证安全性(const 确保不会修改这个临时对象)。

三、const QString & 中 “引用” 的作用是什么?

引用的核心作用是 “避免不必要的拷贝,提高效率”

如果函数参数是 QString fileName(按值传递):
调用时会把传递的字符串(无论是变量还是临时对象)完整拷贝一份到函数内部的 fileName 中。对于长字符串(比如路径很长),拷贝会浪费内存和时间。

如果参数是 const QString &fileName(按 const 引用传递):
不会拷贝,fileName 只是一个 “别名”,直接指向传递进来的对象(可能是变量,也可能是临时对象)。既节省了拷贝的开销,又通过 const 保证不会意外修改这个对象。

四、总结你的代码为什么可行?

pic.load(":/QTicon/open2.png") 的过程:

  1. 字符串字面量被转换为临时 QString 对象(存储路径)。
  2. const QString &fileName 绑定到这个临时对象(不需要显式变量)。
  3. 函数内部通过引用直接访问这个临时对象,避免拷贝,高效且安全。

简单说:const 引用的特性就是 “既能直接用字面量 / 临时对象,又不浪费内存”,所以不需要先定义一个 QString 变量~

那么我们的代码怎么写呢?

我们需要在类这边写QPixmap pic这个对象,还记得这个是什么操作吗,这个就是我们的类的组合的操作,在类中创建其他类的对象,可以实现获得其他类的属性和方法,然后我们去这个cpp中调用他的load函数

我来一个一个讲,这个MyButton的这个里面的  pic.load(":/QTicon/open.png");这个是在构造函数里面写的嘛,说明这个就是我们设置的这个程序第一眼看到的样子,我们毕竟是自定义按钮嘛,这些初始化的设置肯定是在构造函数里面写的

就是这个样子,

其他的几个事件也都是一样的意思,这个图片文件的话是我通过添加Qt Resources文件添加的就像我们给记事本的按钮添加上图片作为图标一样做的前期工作,需要将我们想要的图片先放在资源文件里面

setFixedSize(pic.size());

这个是什么意思呢,这个就是设置我们的控件的尺寸,尺寸为pic.size()调用这个函数,就是表示我们设置这个按钮的尺寸为这个图片的尺寸

后面会发现没有任何的结果啊,不显示啊,这也是因为我们是继承的QWidget这个是为了极致的开发的,什么都自己干,所以连很基础的绘图都没有,因此我们也有一个绘图事件,对于这个事件我们可以去QWidget这个类里面去寻找

我们会找到这个绘制事件,然后我们可以通过这个事件找到QPaint这个类

我们通过这个类创建一个对象,调用这个drawPixmap()这个函数,这个函数的话就是,可以将我们的图片绘制出来,

void MyButton::paintEvent(QPaintEvent *event)

{

    QPainter painter(this);

    painter.drawPixmap(rect(),pic);

}

会发现我们的painter(this)要加上这个才会正常运行,this的意思就是这个Mybutton这个控件,

我们也可以看这个案例,会发现这个用法painter(this)要这样写

目的是:作用:作为 QPainter 的构造参数时,它告诉 QPainter 在哪个控件上进行绘制

对于这个drawPixmap(),他需要一个矩形区域,还有一个就是QPixmap 的对象的引用,说实话有点像我们的之前的那个font对象的那个操作,那个ui->textEdit->setFont(font)

这样的操作都是传的是对象的引用,

好的说一下这个矩形

 rect() 作为目标矩形(控件的完整区域),让图片自适应控件大小:

painter.drawPixmap(rect(), pic); // 图片填满整个控件(可能拉伸)

最后一步就是我们需要在加载完这个图片的时候需要手动更新一下update一下,这个是为什么呢?而且我之前那个设置字体的缩放的时候好像也不需要这样啊?

因为:

  • 内置属性(如字体):Qt 已处理自动更新,无需手动 update()
  • 自定义绘图:必须手动调用 update() 来触发重绘,否则界面不会更新。

这种设计体现了 Qt 的灵活性:内置控件负责基础属性的自动管理,而自定义绘图则交给开发者控制,避免了不必要的性能开销。

其实到最后如果我前面没有讲提升的问题的话,那么我们就需要进行提升

就是因为我们是继承这个QWidget的,所以我们需要拉一个widget就好了

我提出一个问题??是不是就是我由于是继承的QWidget那么就是意味着我需要的是极致的自己开发控件,所以也就是只能去拉一个widget出来,然后我去提升他

是的,你的理解非常准确!我用更简洁的方式总结一下:

为什么必须拉一个 QWidget 并提升?

因为你继承 QWidget 时,相当于从头开始开发一个全新控件,而 Qt Designer 默认没有你的控件。具体逻辑链:

继承 QWidget 的本质
QWidget 是最基础的控件类,几乎什么都没实现(没有按钮外观、没有点击事件)。你继承它,就像 “从零开始造一辆车”,所有功能都要自己写。

“提升控件” 如何解决问题?

“提升” 操作的本质是:告诉 Qt Designer,把这个普通的 QWidget 当成我的 MyButton 来用

具体步骤的意义:
  1. 拖一个普通 QWidget 到界面上:这是因为 Qt Designer 没有 MyButton 控件可选,只能用 widget 占位。就是因为我们是极致开发啊,记住我们这个是自己开发的按钮,不是在QT已有的控件上的开发,另外的是我选择继承的是QWidget,就说明全部自己干,说明就是拖一个Widget就好了
  2. 右键 → 提升为 MyButton
    1. Qt Designer 会记录这个操作,并在生成的代码中使用 MyButton 而非 Widget
    2. 生成的代码会自动包含 mybutton.h 头文件。

最后完成以上所有的操作就可以实现啦

效果为:我鼠标移到上方的时候就是紫色,点击的时候就是黑色,鼠标离开这个控件的时候就是蓝色,一打开程序的时候也是蓝色

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

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

相关文章

【补充】Linux内核链表机制

专题文章:Linux内核链表与Pinctrl数据结构解析 目标: 深入解析Pinctrl子系统中,struct pinctrl如何通过内核链表,来组织和管理其多个struct pinctrl_state。 1. 问题背景:一个设备,多种引脚状态 一个复杂的…

本地部署Dify、Docker重装

需要先安装一个Docker,Docker就像是一个容器,将部署Dify的空间与本地环境隔离,避免因为本地环境的一些问题导致BUG。也确保了环境的统一,不会出现在自己的电脑上能跑但是移植到别人电脑上就跑不通的情况。那么现在就开始先安装Doc…

【每天一个知识点】非参聚类(Nonparametric Clustering)

ChatGPT 说:“非参聚类”(Nonparametric Clustering)是一类不预先设定聚类数目或数据分布形式的聚类方法。与传统“参数聚类”(如高斯混合模型)不同,非参聚类在建模过程中不假设数据来自于已知分布数量的某…

人形机器人CMU-ASAP算法理解

一原文在第一阶段,用重定位的人体运动数据在模拟中预训练运动跟踪策略。在第二阶段,在现实世界中部署策略并收集现实世界数据来训练一个增量(残差)动作模型来补偿动态不匹配。,ASAP 使用集成到模拟器中的增量动作模型对…

next.js刷新页面时二级菜单展开状态判断

在 Next.js 中保持二级菜单刷新后展开状态的解决方案 在 Next.js 应用中,当页面刷新时保持二级菜单的展开状态,可以通过以下几种方法实现: 方法1:使用 URL 参数保存状态(推荐) import { useRouter } from n…

网络基础DAY13-NAT技术

NAT技术internet接入方式:ADLS技术:能够将不同设备的不同信号通过分离器进行打包之后再internet中传输,到另一端的分离器之后再进行分离。传输到不同的设备中去。常见光纤接入方式internet接入认证方式:PPPoE:先认证再…

HBuilderX中设置 DevEco Studio路径,但是一直提示未安装

前言: HBuilderX中设置 DevEco Studio路径,但是一直提示未安装。 报错信息: 检测到鸿蒙工具链,请在菜单“工具->设置->运行配置”中设置鸿蒙开发者工具路径为 DevEco Studio 的安装路径,请参考 报错原因…

什么是GNN?——聚合、更新与循环

在传统的深度学习中,卷积神经网络(CNN)擅长处理网格结构数据(如图像),循环神经网络(RNN)擅长处理序列数据(如文本)。但当数据以图的形式存在时(如…

深入解析 Django REST Framework 的 APIView 核心方法

在 Python 3 中,Django 的 APIView 类是 Django REST Framework(DRF)中用于构建 API 视图的核心基类。它提供了一个灵活的框架来处理 HTTP 请求,并通过一系列方法支持认证、权限检查和请求限制等功能。self.perform_authenticatio…

神经网络——卷积层

目录 卷积层介绍 Conv2d 卷积动画演示 卷积代码演示 综合代码案例 卷积层介绍 卷积层是卷积神经网络(CNN)的核心组件,它通过卷积运算提取输入数据的特征。 基本原理 卷积层通过卷积核(过滤器)在输入数据&…

神经网络——线性层

在机器学习中,线性层(Linear Layer) 是一种基础的神经网络组件,也称为全连接层(Fully Connected Layer) 或密集层(Dense Layer)。 其严格的数学定义为:对输入数据执行线…

大模型高效适配:软提示调优 Prompt Tuning

The Power of Scale for Parameter-Efficient Prompt Tuning ruatishi 软提示向量 具体是什么 《The Power of Scale for Parameter-Efficient Prompt Tuning》中增加的部分是“软提示(soft prompts)”,这是一种针对特定下游任务,添加到输入文本中的可调参数序列。它与传统…

https正向代理 GoProxy

背景: 在安全隔离的内网环境中,部署于内网的应用如需调用公网第三方接口(如支付、短信),可通过正向代理服务实现访问。 GoProxy 下载: https://github.com/snail007/goproxy/releases 使用文档&#xff…

Java IO流体系详解:字节流、字符流与NIO/BIO对比及文件拷贝实践

一、字节流与字符流:如何选择? 1.1 核心区别特性字节流字符流处理单位字节(8位)字符(16位Unicode)适用场景二进制文件(图片/视频)文本文件(TXT/CSV)编码处理需…

QT6 源,七章对话框与多窗体(5) 文件对话框 QFileDialog 篇二:源码带注释

&#xff08;13&#xff09;本源代码定义于头文件 qfiledialog . h &#xff1a; #ifndef QFILEDIALOG_H #define QFILEDIALOG_H#include <QtWidgets/qtwidgetsglobal.h> #include <QtCore/qdir.h> #include <QtCore/qstring.h> #include <QtCore/qurl.h…

关于Ajax的学习笔记

Ajax概念&#xff1a;是一门使用了js语言&#xff0c;可以使用于Javaweb&#xff0c;实现前端代码和后端代码连结的的一种异步同步&#xff08;不需要等待服务器相应&#xff0c;就能够发送第二次请求&#xff09;的一种技术&#xff0c;它主要用于网页内容的局部刷新&#xff…

The Missing Semester of Your CS Education 学习笔记以及一些拓展知识(三)

文章目录The Missing Semester of Your CS Education 学习笔记以及一些拓展知识Vim编辑器笔记部分程序员常用的编辑器Vim的模式Vim的普通模式Vim的插入模式Vim的可视模式Vim的替换模式Vim的命令行模式Vim的高级功能文本对象宏寄存器缓冲区标记代码折叠Vim的常用配置Vim的常用插…

PyTorch常用的简单数学运算

一、基础算术运算1. 逐元素运算a torch.tensor([1, 2, 3]) b torch.tensor([4, 5, 6])# 加减乘除 a b # [5, 7, 9] a - b # [-3, -3, -3] a * b # [4, 10, 18] a / b # [0.25, 0.4, 0.5]# 幂运算、平方根 a ** 2 # [1, 4, 9] torch.sqrt(a) # [1.0, 1.414, 1.732]2. 标…

C++ Lambda 表达式详解:从基础到实战

Lambda 表达式是 C11 引入的重要特性&#xff0c;它允许我们在代码中定义匿名函数&#xff0c;极大地简化了代码编写&#xff0c;尤其是在使用 STL 算法和多线程编程时。本文将详细介绍 Lambda 表达式的语法、特性及实际应用场景。什么是 Lambda 表达式&#xff1f;Lambda 表达…

Spring Boot注解详解

文章目录前言1. 核心启动注解SpringBootApplicationEnableAutoConfigurationSpringBootConfiguration2. 组件注解Component及其衍生注解ComponentServiceRepositoryControllerRestController3. 依赖注入注解AutowiredQualifierPrimary4. Web相关注解请求映射注解RequestMapping…