qt开关控件设计(手把手从零开始)
从零开始手把手教你设计自己的qt控件
- 1 说明
- 1.1 显示效果
- 1.2 控件特性
- 1.3 设计方法
- 2 控件需求分析
- 2.1 必要需求
- 2.1 顺带需求(锦上添花)
- 3 功能设计
- 3.1 设计思路(重点内容)
- 3.2 自适应大小
- 3.3 开关动画
- 3.4 控件绘制
- 4 总体代码
1 说明
1.1 显示效果
显示效果看下面的动图:
(1)普通模式,一般都是用这种模式就行
(2)等待模式,有的比较耗时且不确定是否能够开启的操作使用该模式
1.2 控件特性
具体特性如下图所示:
1.3 设计方法
实现自定义控件的方法有很多,除了自己绘制外,还可以使用qt的控件+样式表堆一个新的控件。
我现在只推荐继承qwidget类进行绘制,虽然前期可能麻烦点,原因如下:
- 需要高度自定义,很多细节把控;
- 后续功能和样式拓展方便灵活;
- 执行效率;
- 通用性和独立性;
- 项目如果非常复杂庞大的话,样式表将会是个非常忌讳的东西。
在后文提供一种控件的通用设计思路,可供参考。
2 控件需求分析
需求分析主要是要明确控件做成什么样子,从而在开发中避免做无用功,最主要的是防止设计方向出现偏差导致漏掉的需求很难添加上去。
这个步骤还是非常有必要的,在我们的工作项目中开发做需求分析后,一个是可以对工作上做风险把控,确定工作难度和影响,另外一个就是可以做好时间规划,防止规划工作的时间过长或过短,最后就是这个过程可以顺便把概要设计完成,总之益处多多。
2.1 必要需求
(1)控件开、关状态;
(2)开、关、圆形按钮颜色自定义;
(3)圆形按钮带移动动画;
(4)添加等待模式;
(5)圆形按钮阴影;
(6)可获取开关状态;
(7)状态切换后发出信号。
2.1 顺带需求(锦上添花)
(1)自适应缩放,就是可大可小;
(2)边缘颜色自定义(一般为透明就行);
(3)颜色渐变动画;
(4)添加使、失能状态。
3 功能设计
3.1 设计思路(重点内容)
对于自定义控件实现中,每个功能的支持,在代码上我们一般可总结为接口、操作、数据、绘制几个部分,关系如下图:
使用失能状态为例子:
(1)首先是数据支持,每个功能第一步应该是需要构建好支持的数据,这样就有个中心,接下来围绕这个中心进行开发,从上图可以看出来,最后数据最好有个默认初始状态。
bool mEnable{1}; //使能状态
(2)、读取、写入接口,接口定义可以优于开发的,一般在项目需求分析的时候定义好了。
void setEnabled(bool enable); /// 设置使能状态,default:1bool getEnabled(); /// 获取使能状态
(3)操作,由于这个功能非常简单,所以基本没有什么操作,所以直接写在接口函数内部,如下:
void WBSwitchButton::setEnabled(bool enable){QWidget::setEnabled(enable);mEnable = enable;emit sigEnableChanged(mEnable);update();
}
(3-2)操作相关影响,失能状态下是不可点击切换开关状态的,所以在点击事件中进行过滤,代码如下:
if(!mEnable) return ;
注:这段代码需要写在事件函数最前面。
(4)绘制,这步主要是要在显示上区分使能和失能,我的思路是直接在按钮上面盖上一层暗色的蒙层,表示处于失能状态,代码如下所示:
/// 失能显示,添加一层暗色的蒙层if(!mEnable){QColor disable(Qt::black);disable.setAlphaF(0.5);painter.setBrush(disable);painter.drawRoundedRect(this->rect(),mRadius,mRadius);}
注:这部分代码是写在绘制函数最后面。
失能效果如下,可对比前面的使能状态。
3.2 自适应大小
因为控件按照上面的思路设计进行设计,所以在自适应大小的时候,不涉及到接口和操作,直接调整(·计算)背后支持的数据即可,然后直接update()重新绘制一下。
void WBSwitchButton::resizeEvent(QResizeEvent *event){Q_UNUSED(event)/// 更新按钮大小、圆角大小、动画两个位置int size = qMin(this->width(),this->height());mRadius = size/2;float width = size * 3 / 4;float border = (size - width) / 2;mLeftPos = QPoint(border,border);mRightPos = QPoint(this->width() - border - width,border);mButtonRect.setWidth(width);mButtonRect.setHeight(width);mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);mBackColor = mOnOff ? mBackOnColor : mBackOffColor ;update();
}
3.3 开关动画
对于动画,其实都是属于设计思路中的操作部分,此部分只要使用qt的动画对象动态修改数据即可,位置动画就修改按钮位置,颜色动画就修改颜色位置,具体代码如下:
开关按钮位置移动动画
/// 动画-开关按钮位置QVariantAnimation* posAnimation = new QVariantAnimation(this);posAnimation->setDuration(mAnimationPeriod);posAnimation->setStartValue(mButtonRect.topLeft());posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);connect(posAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){mButtonRect.moveTo(value.toPointF());update();});posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
开关状态颜色渐变动画
/// 动画-背景颜色QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");colorAnimation->setDuration(mAnimationPeriod);colorAnimation->setStartValue(mBackColor);colorAnimation->setEndValue(mOnOff ? mBackOnColor: mBackOffColor);colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
3.4 控件绘制
从设计思路中可以看出,我们的绘制主要按照外观,读取支持的数据直接绘制即可,这样的好处就是绘制中不带复杂的操作逻辑(比如动画)和计算操作,防止每次绘制都会进行没必要的重复计算。
绘制函数只拿数据在必要的时候进行绘制,这样大大提高控件的执行效率和流畅度(本控件可能看不出来,大的复杂控件就能体验出来)。
void WBSwitchButton::paintEvent(QPaintEvent *event){Q_UNUSED(event)QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true); //抗锯齿painter.setPen(Qt::NoPen);/// 绘制边缘颜色QPainterPath path;path.addRect(this->rect());path.addRoundedRect(this->rect(),mRadius,mRadius);path.setFillRule(Qt::OddEvenFill);painter.setBrush(mEdgeColor);painter.drawPath(path);/// 绘制背景颜色painter.setBrush(mBackColor);painter.drawRoundedRect(this->rect(),mRadius,mRadius);/// 绘制圆形按钮painter.setBrush(mButtonColor);painter.drawEllipse(mButtonRect);/// 绘制按钮阴影painter.setBrush(Qt::NoBrush);QColor color(Qt::black);int count = (this->height() - mButtonRect.height())/2;float stepColor = (0.15-0.0)/count;for (int i = mButtonRect.height()/2 + 1; i < this->height()/2; i++){color.setAlphaF(0.15 - stepColor*(i - mButtonRect.height()/2));painter.setPen(color);painter.drawEllipse(mButtonRect.center(),i,i);}/// 失能显示,添加一层蒙层if(!mEnable){QColor disable(Qt::black);disable.setAlphaF(0.5);painter.setBrush(disable);painter.drawRoundedRect(this->rect(),mRadius,mRadius);}
}
注:可以重点看一下上面的绘制阴影实现思路。
4 总体代码
(1)h文件
#ifndef WBSWITCHBUTTON_H
#define WBSWITCHBUTTON_H#include <QWidget>
#include <QPropertyAnimation>
#include <QPainterPath>
#include <QPainter>
#include <QRadialGradient>
#include <QMouseEvent>///
/// \brief 基础控件-Switch开关按钮
///
class WBSwitchButton : public QWidget
{Q_OBJECT
public:Q_PROPERTY(QColor pBackColor MEMBER mBackColor) //新增背景颜色属性,用于动画explicit WBSwitchButton(QWidget *parent = nullptr);bool getSwitch(); /// 获取开关状态public slots:void setSwitch(bool onoff); /// 设置开关状态,default:0void setEnabled(bool enable); /// 设置使能状态,default:1bool getEnabled(); /// 获取使能状态void setAnimationPeriod(int period); /// 设置切换状态周期void setPrecisionClick(bool flag); /// 设置精确点击,即只有点中按钮的时候才开关void setWaitModel(bool flag); /// 设置等待模式,点击后不会主动切换开关,需要setSwitchvoid setSwitchForWaitModel(bool onoff); /// 设置开关状态,default:0void setButtonColor(QColor color); /// 设置开关(圆形按钮)颜色void setBackOnColor(QColor color); /// 设置背景颜色-开void setBackOffColor(QColor color); /// 设置背景颜色-关void setEdgeColor(QColor color); /// 设置边缘颜色,默认透明signals:void sigEnableChanged(bool enable); /// 使能状态变化信号void sigSwitchChanged(bool onoff); /// 开关状态变化信号protected:void paintEvent(QPaintEvent *event);void resizeEvent(QResizeEvent *event);void mouseReleaseEvent(QMouseEvent *event);void enterEvent(QEvent *event);void leaveEvent(QEvent *event);private:bool mOnOff{0}; //开关状态bool mEnable{1}; //使能状态bool mPrecisionClickFlagh{0}; //精确点击标志位bool mWaitSigModel{1}; //等待模式,点击后按钮位置会进行切换,但是颜色需要等待外部信号变动bool mAnimationOnOff{1}; //动画开关,default:1bool mHover{0};QColor mButtonColor{Qt::white}; //开关(圆形按钮)颜色QColor mBackColor{Qt::red};QColor mEdgeColor{Qt::transparent}; //边缘颜色QRectF mButtonRect; //开关按钮rectint mRadius{8}; // 开关外观边缘圆角int mAnimationPeriod{200}; //动画周期QPointF mRightPos; // 动画位置-开QPointF mLeftPos; // 动画位置-关QColor mBackOnColor{Qt::green}; //背景颜色-开QColor mBackOffColor{Qt::darkGray}; //背景颜色-关
};#endif // WBSWITCHBUTTON_H
(2)cpp文件
#include "wbswitchbutton.h"
#include <QDebug>WBSwitchButton::WBSwitchButton(QWidget *parent): QWidget{parent}
{}bool WBSwitchButton::getSwitch(){return mOnOff;
}void WBSwitchButton::setSwitch(bool onoff){if(mWaitSigModel) return ;/// 状态切换mOnOff = onoff;/// 发送信号sigSwitchChanged(mOnOff);/// 动画-背景颜色QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");colorAnimation->setDuration(mAnimationPeriod);colorAnimation->setStartValue(mBackColor);colorAnimation->setEndValue(mOnOff ? mBackOnColor: mBackOffColor);colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除/// 动画-开关按钮位置QVariantAnimation* posAnimation = new QVariantAnimation(this);posAnimation->setDuration(mAnimationPeriod);posAnimation->setStartValue(mButtonRect.topLeft());posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);connect(posAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){mButtonRect.moveTo(value.toPointF());update();});posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除
}void WBSwitchButton::setSwitchForWaitModel(bool onoff)
{if(!mWaitSigModel) return ;if(mOnOff == onoff){/// 表示值未改变先运行按钮位置动画QVariantAnimation* posAnimation = new QVariantAnimation(this);posAnimation->setDuration(mAnimationPeriod);posAnimation->setStartValue(mOnOff ? mLeftPos : mRightPos);posAnimation->setEndValue(mOnOff ? mRightPos : mLeftPos);connect(posAnimation,&QVariantAnimation::valueChanged,[=](const QVariant &value){mButtonRect.moveTo(value.toPointF());update();});posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除return ;}/// 状态切换mOnOff = onoff;/// 发送信号sigSwitchChanged(mOnOff);/// 后运行背景颜色动画QPropertyAnimation * colorAnimation = new QPropertyAnimation(this,"pBackColor");colorAnimation->setDuration(mAnimationPeriod);colorAnimation->setStartValue(mBackColor);colorAnimation->setEndValue(mOnOff ? mBackOnColor: mBackOffColor);colorAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除connect(colorAnimation,&QPropertyAnimation::valueChanged,[=](const QVariant &value){update();});
}void WBSwitchButton::setEnabled(bool enable){QWidget::setEnabled(enable);mEnable = enable;emit sigEnableChanged(mEnable);update();
}void WBSwitchButton::setAnimationPeriod(int period){mAnimationPeriod = period;
}void WBSwitchButton::setPrecisionClick(bool flag){mPrecisionClickFlagh = flag;
}void WBSwitchButton::setWaitModel(bool flag)
{mWaitSigModel = flag;
}void WBSwitchButton::setButtonColor(QColor color){mButtonColor = color;update();
}void WBSwitchButton::setBackOnColor(QColor color){mBackOnColor = color;update();
}void WBSwitchButton::setBackOffColor(QColor color){mBackOffColor = color;update();
}void WBSwitchButton::setEdgeColor(QColor color){mEdgeColor = color;update();
}void WBSwitchButton::paintEvent(QPaintEvent *event){Q_UNUSED(event)QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing, true);painter.setPen(Qt::NoPen);/// 绘制边缘颜色QPainterPath path;path.addRect(this->rect());path.addRoundedRect(this->rect(),mRadius,mRadius);path.setFillRule(Qt::OddEvenFill);painter.setBrush(mEdgeColor);painter.drawPath(path);/// 绘制背景颜色painter.setBrush(mBackColor);painter.drawRoundedRect(this->rect(),mRadius,mRadius);/// 绘制圆形按钮painter.setBrush(mButtonColor);painter.drawEllipse(mButtonRect);/// 绘制按钮阴影painter.setBrush(Qt::NoBrush);QColor color(Qt::black);int count = (this->height() - mButtonRect.height())/2;float stepColor = (0.15-0.0)/count;for (int i = mButtonRect.height()/2 + 1; i < this->height()/2; i++){color.setAlphaF(0.15 - stepColor*(i - mButtonRect.height()/2));painter.setPen(color);painter.drawEllipse(mButtonRect.center(),i,i);}/// 失能显示,添加一层蒙层if(!mEnable){QColor disable(Qt::black);disable.setAlphaF(0.5);painter.setBrush(disable);painter.drawRoundedRect(this->rect(),mRadius,mRadius);}
}void WBSwitchButton::resizeEvent(QResizeEvent *event){Q_UNUSED(event)/// 更新按钮大小、圆角大小、动画两个位置int size = qMin(this->width(),this->height());mRadius = size/2;float width = size * 3 / 4;float border = (size - width) / 2;mLeftPos = QPoint(border,border);mRightPos = QPoint(this->width() - border - width,border);mButtonRect.setWidth(width);mButtonRect.setHeight(width);mButtonRect.moveTo(mOnOff ? mRightPos : mLeftPos);mBackColor = mOnOff ? mBackOnColor : mBackOffColor ;update();
}void WBSwitchButton::mouseReleaseEvent(QMouseEvent *event){if(mWaitSigModel){/// 先运行按钮位置动画QVariantAnimation* posAnimation = new QVariantAnimation(this);posAnimation->setDuration(mAnimationPeriod);posAnimation->setStartValue(mOnOff ? mRightPos : mLeftPos);posAnimation->setEndValue(mOnOff ? mLeftPos : mRightPos);connect(posAnimation,&QVariantAnimation::valueChanged,[=](const QVariant &value){mButtonRect.moveTo(value.toPointF());update();});posAnimation->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped); //停止后删除return ;}if(!mEnable) return ;if(mButtonRect.contains(event->pos()) || !mPrecisionClickFlagh){setSwitch(!mOnOff);}
}void WBSwitchButton::enterEvent(QEvent *event){Q_UNUSED(event)mHover = true;
}void WBSwitchButton::leaveEvent(QEvent *event){Q_UNUSED(event)mHover = false;
}
qt开关控件设计(手把手从零开始)相关推荐
- QT传输函数控件设计8 设计小圆点
首先看一下小圆点: 如图,左图上有很多小圆点,这些小圆点是由蓝色的中心和红边构成.点击小圆点的时候,小圆点的红边会消失,松开鼠标红边又会出现. 我们首先要把这些小圆点能够显示在QGraphicsVie ...
- QT传输函数控件设计10 包含小圆点的图形项
在NodeGraphicsItem小圆点类里面加入新的成员: double lastPos;double currentPos;double nextPos;double opacity;void s ...
- Qt QWidget实现开关控件SwithButton(SlipButton)
前言 Qt做界面的时候常常会用到开关控件,类似于CheckButton有两种状态,只是界面表现形式不一样而已.本文通过QWidget类来实现一个开关控件SwitchBtn(有些平台上又称为SlipBu ...
- Qt工作笔记-自定义开关控件
1.自定义开关控件: 2.点击有动画效果: 3.在动画效果中,不再响应信号: 运行截图如下: 输出响应信号: 源码如下: myonoff.h #ifndef MYONOFF_H #define MYO ...
- 【Qt】qt自定义开关控件
基于QWidget实现一个简单的开关控件,左右动画效果切换,效果如下 样式在paintEvent中绘制 void RdSwitchButton::paintEvent(QPaintEvent *eve ...
- Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例
QT表格控件QTableView简介 表格视图控件QTableView,需要和QStandardItemModel, 配套使用,这套框架是基于MVC设计模式设计的,M(Model)是QStanda ...
- 转大佬--C++语言Qt实现控件拖拽和连线类似可视化操作Simulink仿真类软件 电路仿真软件 和模型驱动等软件
转自–标biao的文章:https://blog.csdn.net/kangkanglhb88008/article/details/120812524 目标:开发一个电路仿真软件. 内部原理:qt编 ...
- Qt常用控件介绍(一)
Qt常用控件介绍 Qt Creator 的使用技巧 Qt Creator的常用快捷键 按钮 QPushButton QToolButton QRadioButton QCheckBox QComman ...
- Qt编写控件属性设计器
一.前言 自从研究Qt编写自定义控件以来,一发不可收拾,越多越多人有类似的需求找我定制控件,陆陆续续写了上百个控件,目前已超过150个,于是逐渐衍生了另外一个需求,提供一个控件属性设计器,类似QtDe ...
最新文章
- EffectiveC++ Item11
- 语义分割源代码_综述 | 基于深度学习的实时语义分割方法:全面调研
- aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉
- snpeff注释变异(variants)
- python 使用全局变量_如何在Python中的不同模块中使用全局变量
- 数据结构+算法+c++学习(写在前面)
- 新手如何使用Docker来搭建PHP开发环境?
- 使用 jquery 的 上传文件插件 uploadify 3.1 配合 java 来做一个简单的文件上次功能。并且在界面上有radio 的选择内容也要上传...
- Git入门(本地使用)
- minkowski sum matlab,Matlab 聚类分析
- 511遇见易语言教程外形框和模仿进度条闪烁效果
- 怎样访问ftp服务器文件夹权限,访问ftp服务器文件夹权限设置
- 为什么现在微信附近人“没人”了
- 动易CMS 实现ctrl+v粘贴图片并上传、word粘贴带图片
- 一次对天翼安全网关的渗透
- 30天自制操作系统-3
- 图解设计模式读书笔记(十三)——Mediator(仲裁者)模式
- 论文阅读--Adapted Dynamic Memory Network for Emotion Recognition in Conversation
- uni app图片预览
- Python实用模块(二十一)base64
热门文章
- 一个简单的问卷调查管理系统
- 怎样使用ping(转载请注明出处,谢谢!!!)
- Questions And Answers About The Swine Flu
- 因为 ‘PRIMARY‘ 文件组已满。请删除不需要的文件、删除文件组中的对象、将其他文件添加到文件组或为文件组中的现有文件启用自动增长
- 串口调试助手 安卓版 附下载地址
- UR5构型机械臂正逆运动学
- 凌思微-LE5010蓝牙开发(五)
- LJJ-C++接口类代码编写规范
- ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
- 7-54 福到了 (15 分)