从零开始手把手教你设计自己的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开关控件设计(手把手从零开始)相关推荐

  1. QT传输函数控件设计8 设计小圆点

    首先看一下小圆点: 如图,左图上有很多小圆点,这些小圆点是由蓝色的中心和红边构成.点击小圆点的时候,小圆点的红边会消失,松开鼠标红边又会出现. 我们首先要把这些小圆点能够显示在QGraphicsVie ...

  2. QT传输函数控件设计10 包含小圆点的图形项

    在NodeGraphicsItem小圆点类里面加入新的成员: double lastPos;double currentPos;double nextPos;double opacity;void s ...

  3. Qt QWidget实现开关控件SwithButton(SlipButton)

    前言 Qt做界面的时候常常会用到开关控件,类似于CheckButton有两种状态,只是界面表现形式不一样而已.本文通过QWidget类来实现一个开关控件SwitchBtn(有些平台上又称为SlipBu ...

  4. Qt工作笔记-自定义开关控件

    1.自定义开关控件: 2.点击有动画效果: 3.在动画效果中,不再响应信号: 运行截图如下: 输出响应信号: 源码如下: myonoff.h #ifndef MYONOFF_H #define MYO ...

  5. 【Qt】qt自定义开关控件

    基于QWidget实现一个简单的开关控件,左右动画效果切换,效果如下 样式在paintEvent中绘制 void RdSwitchButton::paintEvent(QPaintEvent *eve ...

  6. Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例

    QT表格控件QTableView简介 ​ 表格视图控件QTableView,需要和QStandardItemModel, 配套使用,这套框架是基于MVC设计模式设计的,M(Model)是QStanda ...

  7. 转大佬--C++语言Qt实现控件拖拽和连线类似可视化操作Simulink仿真类软件 电路仿真软件 和模型驱动等软件

    转自–标biao的文章:https://blog.csdn.net/kangkanglhb88008/article/details/120812524 目标:开发一个电路仿真软件. 内部原理:qt编 ...

  8. Qt常用控件介绍(一)

    Qt常用控件介绍 Qt Creator 的使用技巧 Qt Creator的常用快捷键 按钮 QPushButton QToolButton QRadioButton QCheckBox QComman ...

  9. Qt编写控件属性设计器

    一.前言 自从研究Qt编写自定义控件以来,一发不可收拾,越多越多人有类似的需求找我定制控件,陆陆续续写了上百个控件,目前已超过150个,于是逐渐衍生了另外一个需求,提供一个控件属性设计器,类似QtDe ...

最新文章

  1. EffectiveC++ Item11
  2. 语义分割源代码_综述 | 基于深度学习的实时语义分割方法:全面调研
  3. aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉
  4. snpeff注释变异(variants)
  5. python 使用全局变量_如何在Python中的不同模块中使用全局变量
  6. 数据结构+算法+c++学习(写在前面)
  7. 新手如何使用Docker来搭建PHP开发环境?
  8. 使用 jquery 的 上传文件插件 uploadify 3.1 配合 java 来做一个简单的文件上次功能。并且在界面上有radio 的选择内容也要上传...
  9. Git入门(本地使用)
  10. minkowski sum matlab,Matlab 聚类分析
  11. 511遇见易语言教程外形框和模仿进度条闪烁效果
  12. 怎样访问ftp服务器文件夹权限,访问ftp服务器文件夹权限设置
  13. 为什么现在微信附近人“没人”了
  14. 动易CMS 实现ctrl+v粘贴图片并上传、word粘贴带图片
  15. 一次对天翼安全网关的渗透
  16. 30天自制操作系统-3
  17. 图解设计模式读书笔记(十三)——Mediator(仲裁者)模式
  18. 论文阅读--Adapted Dynamic Memory Network for Emotion Recognition in Conversation
  19. uni app图片预览
  20. Python实用模块(二十一)base64

热门文章

  1. 一个简单的问卷调查管理系统
  2. 怎样使用ping(转载请注明出处,谢谢!!!)
  3. Questions And Answers About The Swine Flu
  4. 因为 ‘PRIMARY‘ 文件组已满。请删除不需要的文件、删除文件组中的对象、将其他文件添加到文件组或为文件组中的现有文件启用自动增长
  5. 串口调试助手 安卓版 附下载地址
  6. UR5构型机械臂正逆运动学
  7. 凌思微-LE5010蓝牙开发(五)
  8. LJJ-C++接口类代码编写规范
  9. ARM hint instruction-WFI(Wait For Interrupt)指令的一些笔记
  10. 7-54 福到了 (15 分)