查看完整版本: [-- Qt编写自定义控件一开关按钮 --]

QTCN开发网 -> Qt 作品展 -> Qt编写自定义控件一开关按钮 [打印本页] 登录 -> 注册 -> 回复主题 -> 发表主题

liudianwu 2016-11-06 16:48

Qt编写自定义控件一开关按钮

从2010年进入互联网+智能手机时代以来,各种各样的APP大行其道,手机上面的APP有很多流行的元素,开关按钮个人非常喜欢,手机QQ360卫士、金山毒霸等,都有很多开关控制一些操作,在Qt widgets应用项目上,在项目中应用些类似的开关按钮,估计也会为项目增添不少新鲜感。
总结了大部分的开关按钮控件,基本上有两大类,第一类是纯代码绘制,这种对代码的掌控度要求比较高,但是灵活性比较好。第二类是贴图,专业的美工做好的各种状态的背景图片,只需要用代码将该图片画到界面上即可。为了能够涵盖两大类的开关按钮,特意将常见的四种类型(圆角矩形/内圆形/外圆形/图片)都集成到了自定义的开关按钮中。

运行效果:
[attachment=15942]


1:纯代码绘制
纯代码绘制开关按钮,可以很灵活的设置各种颜色、间隔、文字等,还可以产生动画过度的滑动效果。
产生滑动效果采用定时器绘制的方式,自动计算滑块的X轴开始坐标,当滑块的X轴开始坐标到达滑块的X轴结束坐标时停止定时器。
  1. void SwitchButton::updateValue()
    {
        if (checked) {
            if (startX < endX) {
                startX = startX + step;
            } else {
                startX = endX;
                timer->stop();
            }
        } else {
            if (startX > endX) {
                startX = startX - step;
            } else {
                startX = endX;
                timer->stop();
            }
        }

        update();
    }



2:贴图绘制
  1. void SwitchButton::drawImage(QPainter *painter)
    {
        painter->save();

        QPixmap pix;

        if (!checked) {
            pix = QPixmap(imageOff);
        } else {
            pix = QPixmap(imageOn);
        }

        //自动等比例平滑缩放居中显示
        int targetWidth = pix.width();
        int targetHeight = pix.height();
        pix = pix.scaled(targetWidth, targetHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);

        int pixX = rect().center().x() - targetWidth / 2;
        int pixY = rect().center().y() - targetHeight / 2;
        QPoint point(pixX, pixY);
        painter->drawPixmap(point, pix);

        painter->restore();
    }


有些人说PS一张精美的图片也不是很容易,需要专业的,这里推荐一个好方法,让你也可以获取到这些图片,其实大部分的APP都可以用解压软件打开,拓展名改为.zip即可,解压出来一般里面都含有绝大部分的图片,发现绝大部分的APP都喜欢用图片作为背景来展示一些效果,而不是原原本本的用代码一点点绘制。腾讯就是腾讯啊,大公司!人家的美工MM设计的图片那真的没得话说,绝对一流,手机QQ每次升级一个版本,我都会下过来将里面的精美图片图标之类的提取出来,以便项目使用。同时还推荐两个网站:http://www.easyicon.net/ 我的所有项目用到的ico图标都是这网站上面的。http://www.ui.cn/专业的设计师集中营,这里面成千上万的精美的设计的图片,可以多多参考。

完整代码:

switchbutton.h
  1. #ifndef SWITCHBUTTON_H
    #define SWITCHBUTTON_H

    /**
    * 作者:feiyangqingyun(QQ:517216493) 2016-11-6
    * 1:可设置开关按钮的样式 圆角矩形/内圆形/外圆形/图片
    * 2:可设置选中和未选中时的背景颜色
    * 3:可设置选中和未选中时的滑块颜色
    * 4:可设置显示的文本
    * 5:可设置滑块离背景的间隔
    * 6:可设置圆角角度
    */

    #include <QWidget>

    class QTimer;

    class SwitchButton: public QWidget
    {
        Q_OBJECT
    public:
        enum ButtonStyle {
            ButtonStyle_Rect = 0,     //圆角矩形
            ButtonStyle_CircleIn = 1, //内圆形
            ButtonStyle_CircleOut = 2,//外圆形
            ButtonStyle_Image = 3     //图片
        };

        SwitchButton(QWidget *parent = 0);
        ~SwitchButton();

    protected:
        void mousePressEvent(QMouseEvent *);
        void resizeEvent(QResizeEvent *);
        void paintEvent(QPaintEvent *);
        void drawBg(QPainter *painter);
        void drawSlider(QPainter *painter);
        void drawText(QPainter *painter);
        void drawImage(QPainter *painter);

    private:
        bool checked;               //是否选中
        ButtonStyle buttonStyle;    //开关按钮样式

        QColor bgColorOff;          //关闭时背景颜色
        QColor bgColorOn;           //打开时背景颜色

        QColor sliderColorOff;      //关闭时滑块颜色
        QColor sliderColorOn;       //打开时滑块颜色

        QColor textColorOff;        //关闭时文本颜色
        QColor textColorOn;         //打开时文本颜色

        QString textOff;            //关闭时显示的文字
        QString textOn;             //打开时显示的文字

        QString imageOff;           //关闭时显示的图片
        QString imageOn;            //打开时显示的图片

        int space;                  //滑块离背景间隔
        int rectRadius;             //圆角角度

        int step;                   //每次移动的步长
        int startX;                 //滑块开始X轴坐标
        int endX;                   //滑块结束X轴坐标
        QTimer *timer;              //定时器绘制

    private slots:
        void updateValue();

    public:
        bool getChecked()const
        {
            return checked;
        }
        ButtonStyle getButtonStyle()const
        {
            return buttonStyle;
        }

        QColor getBgColorOff()const
        {
            return bgColorOff;
        }
        QColor getBgColorOn()const
        {
            return bgColorOn;
        }

        QColor getSliderColorOff()const
        {
            return sliderColorOff;
        }
        QColor getSliderColorOn()const
        {
            return sliderColorOn;
        }

        QColor getTextColorOff()const
        {
            return textColorOff;
        }
        QColor getTextColorOn()const
        {
            return textColorOn;
        }

        QString getTextOff()const
        {
            return textOff;
        }
        QString getTextOn()const
        {
            return textOn;
        }

        QString getImageOff()const
        {
            return imageOff;
        }
        QString getImageOn()const
        {
            return imageOn;
        }

        int getSpace()const
        {
            return space;
        }
        int getRectRadius()const
        {
            return rectRadius;
        }

    public slots:
        //设置是否选中
        void setChecked(bool checked);
        //设置风格样式
        void setButtonStyle(ButtonStyle buttonStyle);

        //设置背景颜色
        void setBgColor(QColor bgColorOff, QColor bgColorOn);
        //设置滑块颜色
        void setSliderColor(QColor sliderColorOff, QColor sliderColorOn);
        //设置文本颜色
        void setTextColor(QColor textColorOff, QColor textColorOn);

        //设置文本
        void setText(QString textOff, QString textOn);

        //设置背景图片
        void setImage(QString imageOff, QString imageOn);

        //设置间隔
        void setSpace(int space);
        //设置圆角角度
        void setRectRadius(int rectRadius);

    signals:
        void checkedChanged(bool checked);
    };

    #endif // SWITCHBUTTON_H

switchbutton.cpp
  1. #include "switchbutton.h"
    #include "qpainter.h"
    #include "qevent.h"
    #include "qtimer.h"
    #include "qdebug.h"

    SwitchButton::SwitchButton(QWidget *parent): QWidget(parent)
    {
        checked = false;
        buttonStyle    = ButtonStyle_Rect;

        bgColorOff = QColor(225, 225, 225);
        bgColorOn = QColor(250, 250, 250);

        sliderColorOff = QColor(100, 100, 100);
        sliderColorOn = QColor(100, 184, 255);

        textColorOff = QColor(255, 255, 255);
        textColorOn = QColor(10, 10, 10);

        textOff = "";
        textOn = "";

        imageOff = ":/image/btncheckoff1.png";
        imageOn = ":/image/btncheckon1.png";

        space = 2;
        rectRadius = 5;

        step = width() / 50;
        startX = 0;
        endX = 0;

        timer = new QTimer(this);
        timer->setInterval(5);
        connect(timer, SIGNAL(timeout()), this, SLOT(updateValue()));

        setFont(QFont("Microsoft Yahei", 10));
    }

    SwitchButton::~SwitchButton()
    {

    }

    void SwitchButton::mousePressEvent(QMouseEvent *)
    {
        checked = !checked;
        emit checkedChanged(checked);

        //每次移动的步长为宽度的 50分之一
        step = width() / 50;

        //状态切换改变后自动计算终点坐标
        if (checked) {
            if (buttonStyle == ButtonStyle_Rect) {
                endX = width() - width() / 2;
            } else if (buttonStyle == ButtonStyle_CircleIn) {
                endX = width() - height();
            } else if (buttonStyle == ButtonStyle_CircleOut) {
                endX = width() - height() + space;
            }
        } else {
            endX = 0;
        }

        timer->start();
    }

    void SwitchButton::resizeEvent(QResizeEvent *)
    {
        //每次移动的步长为宽度的 50分之一
        step = width() / 50;

        //尺寸大小改变后自动设置起点坐标为终点
        if (checked) {
            if (buttonStyle == ButtonStyle_Rect) {
                startX = width() - width() / 2;
            } else if (buttonStyle == ButtonStyle_CircleIn) {
                startX = width() - height();
            } else if (buttonStyle == ButtonStyle_CircleOut) {
                startX = width() - height() + space;
            }
        } else {
            startX = 0;
        }

        update();
    }

    void SwitchButton::paintEvent(QPaintEvent *)
    {
        //绘制准备工作,启用反锯齿
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        if (buttonStyle == ButtonStyle_Image) {
            //绘制图片
            drawImage(&painter);
        } else {
            //绘制背景
            drawBg(&painter);
            //绘制滑块
            drawSlider(&painter);
            //绘制文字
            drawText(&painter);
        }
    }

    void SwitchButton::drawBg(QPainter *painter)
    {
        painter->save();
        painter->setPen(Qt::NoPen);

        if (!checked) {
            painter->setBrush(bgColorOff);
        } else {
            painter->setBrush(bgColorOn);
        }

        if (buttonStyle == ButtonStyle_Rect) {
            painter->drawRoundedRect(rect(), rectRadius, rectRadius);
        } else if (buttonStyle == ButtonStyle_CircleIn) {
            QRect rect(0, 0, width(), height());
            //半径为高度的一半
            int radius = rect.height() / 2;
            //圆的宽度为高度
            int circleWidth = rect.height();

            QPainterPath path;
            path.moveTo(radius, rect.left());
            path.arcTo(QRectF(rect.left(), rect.top(), circleWidth, circleWidth), 90, 180);
            path.lineTo(rect.width() - radius, rect.height());
            path.arcTo(QRectF(rect.width() - rect.height(), rect.top(), circleWidth, circleWidth), 270, 180);
            path.lineTo(radius, rect.top());

            painter->drawPath(path);
        } else if (buttonStyle == ButtonStyle_CircleOut) {
            QRect rect(space, space, width() - space * 2, height() - space * 2);
            painter->drawRoundedRect(rect, rectRadius, rectRadius);
        }

        painter->restore();
    }

    void SwitchButton::drawSlider(QPainter *painter)
    {
        painter->save();
        painter->setPen(Qt::NoPen);

        if (!checked) {
            painter->setBrush(sliderColorOff);
        } else {
            painter->setBrush(sliderColorOn);
        }

        if (buttonStyle == ButtonStyle_Rect) {
            int sliderWidth = width() / 2 - space * 2;
            int sliderHeight = height() - space * 2;
            QRect sliderRect(startX + space, space, sliderWidth , sliderHeight);
            painter->drawRoundedRect(sliderRect, rectRadius, rectRadius);
        } else if (buttonStyle == ButtonStyle_CircleIn) {
            QRect rect(0, 0, width(), height());
            int sliderWidth = rect.height() - space * 2;
            QRect sliderRect(startX + space, space, sliderWidth, sliderWidth);
            painter->drawEllipse(sliderRect);
        } else if (buttonStyle == ButtonStyle_CircleOut) {
            QRect rect(0, 0, width() - space, height() - space);
            int sliderWidth = rect.height();
            QRect sliderRect(startX, space / 2, sliderWidth, sliderWidth);
            painter->drawEllipse(sliderRect);
        }

        painter->restore();
    }

    void SwitchButton::drawText(QPainter *painter)
    {
        painter->save();

        if (!checked) {
            painter->setPen(textColorOff);
            painter->drawText(width() / 2, 0, width() / 2 - space, height(), Qt::AlignCenter, textOff);
        } else {
            painter->setPen(textColorOn);
            painter->drawText(0, 0, width() / 2 + space * 2, height(), Qt::AlignCenter, textOn);
        }

        painter->restore();
    }

    void SwitchButton::drawImage(QPainter *painter)
    {
        painter->save();

        QPixmap pix;

        if (!checked) {
            pix = QPixmap(imageOff);
        } else {
            pix = QPixmap(imageOn);
        }

        //自动等比例平滑缩放居中显示
        int targetWidth = pix.width();
        int targetHeight = pix.height();
        pix = pix.scaled(targetWidth, targetHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);

        int pixX = rect().center().x() - targetWidth / 2;
        int pixY = rect().center().y() - targetHeight / 2;
        QPoint point(pixX, pixY);
        painter->drawPixmap(point, pix);

        painter->restore();
    }

    void SwitchButton::updateValue()
    {
        if (checked) {
            if (startX < endX) {
                startX = startX + step;
            } else {
                startX = endX;
                timer->stop();
            }
        } else {
            if (startX > endX) {
                startX = startX - step;
            } else {
                startX = endX;
                timer->stop();
            }
        }

        update();
    }

    void SwitchButton::setChecked(bool checked)
    {
        if (this->checked != checked) {
            this->checked = checked;
            emit checkedChanged(checked);
            update();
        }
    }

    void SwitchButton::setButtonStyle(SwitchButton::ButtonStyle buttonStyle)
    {
        this->buttonStyle = buttonStyle;
        update();
    }

    void SwitchButton::setBgColor(QColor bgColorOff, QColor bgColorOn)
    {
        this->bgColorOff = bgColorOff;
        this->bgColorOn = bgColorOn;
        update();
    }

    void SwitchButton::setSliderColor(QColor sliderColorOff, QColor sliderColorOn)
    {
        this->sliderColorOff = sliderColorOff;
        this->sliderColorOn = sliderColorOn;
        update();
    }

    void SwitchButton::setTextColor(QColor textColorOff, QColor textColorOn)
    {
        this->textColorOff = textColorOff;
        this->textColorOn = textColorOn;
        update();
    }

    void SwitchButton::setText(QString textOff, QString textOn)
    {
        this->textOff = textOff;
        this->textOn = textOn;
        update();
    }

    void SwitchButton::setImage(QString imageOff, QString imageOn)
    {
        this->imageOff = imageOff;
        this->imageOn = imageOn;
        update();
    }

    void SwitchButton::setSpace(int space)
    {
        this->space = space;
        update();
    }

    void SwitchButton::setRectRadius(int rectRadius)
    {
        this->rectRadius = rectRadius;
        update();
    }


此自定义控件集成在QFramework中。
自定义控件可执行文件下载:http://pan.baidu.com/s/1i5iCfzv

QFramework简介:
QFramework是一套通用的Qt程序开发框架,集成主界面布局、各种自定义控件、数据库处理、excel极速导出、数据打印、串口通信、网络通信、协议解析、全局热键、邮件发送,短信发送,百度地图调用、ffmpeg+vlc处理等功能,将常用的功能封装成类库,提供统一直观的调用接口,方便使用者使用,对应封装的库都有对应的demo程序。QQ:517216493

QFramework基本功能:
1:支持从4.7.0到5.7.0的任何Qt版本,不受版本限制。用了此框架,不会再有Qt版本不同而引起的程序编译通不过的烦恼。
2:极速导出数据到excel,支持表格数据或者查询的数据,不依赖任何组件,支持任何excel、wps等表格软件版本,导出10万行数据8个字段只需要3秒完成。对导出的表格样式可自定义主标题和副标题,可对导出的数据按照指定条件红色突出显示。
3:数据导出到pdf及打印功能,支持表格数据或者查询的数据,支持横向纵向打印,自动分页。
4:数据分页dbapi类,只需传入表格对象,表名,翻页按钮即可。无需再写重复的方法处理翻页。
5:各种自定义控件,例如开关按钮、发光按钮,仪表盘控件、音量控件、温湿度控件、仪表仪器类控件等。
6:全新超级中英双拼输入法,非常适合触摸设备。
7:全局热键处理。
8:串口热敏打印机打印。
9:qcustomplot 2D图形曲线绘制(含鼠标数据跟踪)。
10:多线程邮件发送,支持多个接收邮箱。
11:多线程短信发送,支持多个接收号码及长短信。
12:Qffmpeg+Qvlc视频处理。
13:取字模,字符转LED数据处理。
14:全局日志输出类 applog,可动态挂载和卸载。
15:全局程序控制类 appkey,可控制程序的使用时间、运行时间、设备数量限制等。
16:封装百度地图调用接口,支持设备标注、路线查询、位置显示等。
17:自动清理程序早期数据类 cleanapi,传入要清理的数据库表名,执行间隔,保留的最大记录数即可。这样保证了整个系统存储的都是最新的数据。
18:NTP校时服务程序。
19:全局截图处理,可以很方便的直接在ARM上对程序进行截图。
20:程序存活检测功能 applive,通过udp通信实时发送心跳命令,这样可以保证程序7*24小时运行,在ARM上可采用appdog看门狗程序。
21:已运行时间+当前时间+实时CPU使用率+实时内存使用率等。
22:自定义程序主界面底部信息。
23:Echart图表的交互使用。

nigoole 2016-11-06 18:19
  

尽途 2016-11-06 18:45
如何做到的 ,能发布一下源文件吗?


z278930050 2016-11-06 22:08
支持大神

greedysky 2016-11-07 14:41
用qss很容易实现

liudianwu 2016-11-07 16:47
greedysky:用qss很容易实现 (2016-11-07 14:41) 

qss实现不了其中的动画过渡效果!

stlcours 2016-11-07 17:33
liudianwu:qss实现不了其中的动画过渡效果! (2016-11-07 16:47) 

那过渡动画到底是怎么实现的??

greedysky 2016-11-07 18:37
stlcours:那过渡动画到底是怎么实现的?? (2016-11-07 17:33) 

看代码是定时器,不是动画组件

clickto 2016-11-08 08:36
尽途:如何做到的 ,能发布一下源文件吗?
 (2016-11-06 18:45) 

刘大师已经公布了源码啊

arlyb 2016-11-08 09:06
赞一个!

mu_de_yu 2016-11-12 16:16
首先给刘大师一万个赞
我将其开源代码修改了下,可以实现如下样式(源代码对圆在外算法有点问题):
[attachment=16001][attachment=16002]

修改详细如下:
代码61行和82行,均修改为:    
          endX = width() - height();
代码168 - 170行,修改为:    
          int sliderWidth = this->height();
          Rect sliderRect(startX, 0, sliderWidth, sliderWidth);  
完事,然后就可以做出以上样式按钮了

mu_de_yu 2016-11-12 16:44
对上文的补充,增加修改,第139行改为:
         QRect rect(height()/2, space, width() - height(), height() - space * 2);

一去丶二三里 2016-11-14 08:22
Qt之自定义控件(开关按钮)

xiewangdong 2016-11-15 19:54
好厉害啊

xiewangdong 2016-11-15 19:56
QFramework是第三方库吗?还是QT原生库?
在哪里能下载使用啊?

liudianwu 2016-11-15 21:49
xiewangdong:QFramework是第三方库吗?还是QT原生库?
在哪里能下载使用啊? (2016-11-15 19:56) 

是我整理的一套通用开发库!

wangfei9618 2016-11-18 00:51
QFramework在哪里下载呢

xiaoniede 2016-12-23 18:02
给力,积少成多,多谢大师分享。

cy_ygs 2016-12-27 15:08
好资料,学习下

shf1986 2017-01-11 13:57
向大神学习学习......

殇雪寒 2017-07-07 17:49
刘大师,你好厉害


查看完整版本: [-- Qt编写自定义控件一开关按钮 --] [-- top --]



Powered by phpwind v8.7 Code ©2003-2011 phpwind
Gzip disabled