自定义的Qt开关按钮

这个控件比较简单,模仿现代应用程序常见的开关按钮绘制。通过这个案例你可以了解Qt动画的使用。在VS2015和Qt5.9上简单测试通过。下面是效果图:

下面给出代码,头文件:

class MSwitch : public QWidget
{
    Q_OBJECT

public:
    MSwitch(QWidget* parent = 0);
    QSize sizeHint() const override;
    bool isOpened() const;

signals:
    void clicked(bool);

private:
    void setButtonPos(float where);
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    void enterEvent(QEvent *event) override;
    void leaveEvent(QEvent *event) override;

private:
    bool opened;
    bool hover;
    float atWhere; /* [0, 1] */
    QPoint pressPt;
};

CPP文件(代码中变量atWhere是用来配合绘制动画的。另外需要指出的是在事件mouseReleaseEvent中播放动画的操作,没有考虑用户连续快速点击按钮的情况。在这种情况下会连续new QVariantAnimation,导致同时存在多个QVariantAnimation对象。不过经过我的测试,这也没有什么影响。如果读者觉得需要优化可以改一改。):

MSwitch::MSwitch(QWidget* parent) :
    QWidget(parent)
{
    opened = false;
    hover = false;
    atWhere = 0;
}

void MSwitch::setButtonPos(float where)
{
    atWhere = where;
    update();
}

bool MSwitch::isOpened() const
{
    return opened;
}

void MSwitch::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    qreal rad = height() * 0.5; /* 圆半径 */
    int xleft = rad;
    int xright = width() - rad;
    int xpos = xleft + (xright - xleft) * atWhere;
    painter.setPen(Qt::NoPen);
    painter.setBrush((xpos == xright) ? QColor(0, 170, 255) : QColor(173, 173, 173));
    painter.drawRoundedRect(0, 0, width(), height(), rad, rad);
    if (hover) /* 鼠标悬停稍微提亮一点 */
    {
        painter.setBrush(QColor(255, 255, 255, 63));
        painter.drawRoundedRect(0, 0, width(), height(), rad, rad);
    }
    painter.setPen(QColor(213, 213, 213));
    painter.setBrush(QColor(243, 243, 243));
    painter.drawEllipse(QPointF(xpos, rad), rad - 1, rad - 1);
}

void MSwitch::mousePressEvent(QMouseEvent *event)
{
    pressPt = event->pos();
}

void MSwitch::mouseReleaseEvent(QMouseEvent *event)
{
    if (pressPt == event->pos())
    {
        opened = !opened;
        emit clicked(opened);
        QVariantAnimation* ani = new QVariantAnimation(this);
        ani->setStartValue(0.0f);
        ani->setEndValue(1.0f);
        ani->setDuration(200);
        ani->setDirection(opened ? QVariantAnimation::Forward : QVariantAnimation::Backward);
        connect(ani, &QVariantAnimation::valueChanged, this,
            [this](const QVariant& value) { setButtonPos(value.toFloat()); });
        ani->start(QAbstractAnimation::DeleteWhenStopped);
    }
}

void MSwitch::enterEvent(QEvent *event)
{
    hover = true;
    update();
}

void MSwitch::leaveEvent(QEvent *event)
{
    hover = false;
    update();
}

QSize MSwitch::sizeHint() const
{
    return QSize(42, 21);
}

 

posted @ 2022-11-25 19:33  兜尼完  阅读(475)  评论(0编辑  收藏  举报