Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Toggle Switch in Qt

I am trying to use an element which is the equivalent of Android Switches in Qt. I have found a ToggleSwitch in QML, but nothing in the actual C++ Qt libs. Am I just missing something or will I have to reimplement this widget myself?

like image 216
ElCraneo Avatar asked Feb 08 '13 20:02

ElCraneo


People also ask

What is a toggle switch?

Toggle Switches have an operating lever that can be pushed up and down or left and right to switch an electrical circuit. A “toggle” is a small wooden rod that is used as a clothing fastener in the place of buttons.

Can I use JavaScript in Qt?

The Qt platform itself contains a powerful JavaScript engine, enabling the behaviors behind the declared Qt Quick UI controls to be written in pure JavaScript.


1 Answers

Here is an example:

switch.h:

#pragma once #include <QtWidgets>  class Switch : public QAbstractButton {     Q_OBJECT     Q_PROPERTY(int offset READ offset WRITE setOffset)     Q_PROPERTY(QBrush brush READ brush WRITE setBrush)  public:     Switch(QWidget* parent = nullptr);     Switch(const QBrush& brush, QWidget* parent = nullptr);      QSize sizeHint() const override;      QBrush brush() const {         return _brush;     }     void setBrush(const QBrush &brsh) {         _brush = brsh;     }      int offset() const {         return _x;     }     void setOffset(int o) {         _x = o;         update();     }  protected:     void paintEvent(QPaintEvent*) override;     void mouseReleaseEvent(QMouseEvent*) override;     void enterEvent(QEvent*) override;  private:     bool _switch;     qreal _opacity;     int _x, _y, _height, _margin;     QBrush _thumb, _track, _brush;     QPropertyAnimation *_anim = nullptr; }; 

switch.cpp:

Switch::Switch(QWidget *parent) : QAbstractButton(parent), _height(16), _opacity(0.000), _switch(false), _margin(3), _thumb("#d5d5d5"), _anim(new QPropertyAnimation(this, "offset", this)) {     setOffset(_height / 2);     _y = _height / 2;     setBrush(QColor("#009688")); }  Switch::Switch(const QBrush &brush, QWidget *parent) : QAbstractButton(parent), _height(16), _switch(false), _opacity(0.000), _margin(3), _thumb("#d5d5d5"), _anim(new QPropertyAnimation(this, "offset", this)) {     setOffset(_height / 2);     _y = _height / 2;     setBrush(brush); }  void Switch::paintEvent(QPaintEvent *e) {     QPainter p(this);     p.setPen(Qt::NoPen);     if (isEnabled()) {         p.setBrush(_switch ? brush() : Qt::black);         p.setOpacity(_switch ? 0.5 : 0.38);         p.setRenderHint(QPainter::Antialiasing, true);         p.drawRoundedRect(QRect(_margin, _margin, width() - 2 * _margin, height() - 2 * _margin), 8.0, 8.0);         p.setBrush(_thumb);         p.setOpacity(1.0);         p.drawEllipse(QRectF(offset() - (_height / 2), _y - (_height / 2), height(), height()));     } else {         p.setBrush(Qt::black);         p.setOpacity(0.12);         p.drawRoundedRect(QRect(_margin, _margin, width() - 2 * _margin, height() - 2 * _margin), 8.0, 8.0);         p.setOpacity(1.0);         p.setBrush(QColor("#BDBDBD"));         p.drawEllipse(QRectF(offset() - (_height / 2), _y - (_height / 2), height(), height()));     } }  void Switch::mouseReleaseEvent(QMouseEvent *e) {     if (e->button() & Qt::LeftButton) {         _switch = _switch ? false : true;         _thumb = _switch ? _brush : QBrush("#d5d5d5");         if (_switch) {             _anim->setStartValue(_height / 2);             _anim->setEndValue(width() - _height);             _anim->setDuration(120);             _anim->start();         } else {             _anim->setStartValue(offset());             _anim->setEndValue(_height / 2);             _anim->setDuration(120);             _anim->start();         }     }     QAbstractButton::mouseReleaseEvent(e); }  void Switch::enterEvent(QEvent *e) {     setCursor(Qt::PointingHandCursor);     QAbstractButton::enterEvent(e); }  QSize Switch::sizeHint() const {     return QSize(2 * (_height + _margin), _height + 2 * _margin); } 

main.cpp:

#include "switch.h"      int main(int argc, char *argv[]) {         QApplication a(argc, argv);         QWidget *widget = new QWidget;         widget->setWindowFlags(Qt::FramelessWindowHint);         QHBoxLayout layout;         widget->setLayout(&layout);         Switch *_switch = new Switch;         Switch *_switch2 = new Switch;         _switch2->setDisabled(true);         layout.addWidget(_switch);         layout.addWidget(_switch2);         widget->show();         return a.exec();     } 

enter image description here

Update Aug 20'18

New Material Switch Widget!

style.h

/*  * This is nearly complete Material design Switch widget implementation in qtwidgets module.  * More info: https://material.io/design/components/selection-controls.html#switches  * Copyright (C) 2018 Iman Ahmadvand  *  * This is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  *  * It is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */  #ifndef STYLE_H #define STYLE_H  #include <QtCore/qeasingcurve.h>  #define cyan500 QColor("#00bcd4") #define gray50 QColor("#fafafa") #define black QColor("#000000") #define gray400 QColor("#bdbdbd")  Q_DECL_IMPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); // src/widgets/effects/qpixmapfilter.cpp  namespace Style {      using Type = QEasingCurve::Type;      struct Animation {         Animation() = default;         Animation(Type _easing, int _duration) :easing{ _easing }, duration{ _duration } {          }          Type easing;         int duration;     };      struct Switch {         Switch() :             height{ 36 },             font{ QFont("Roboto medium", 13) },             indicatorMargin{ QMargins(8, 8, 8, 8) },             thumbOnBrush{ cyan500 },             thumbOnOpacity{ 1 },             trackOnBrush{ cyan500 },             trackOnOpacity{ 0.5 },             thumbOffBrush{ gray50 },             thumbOffOpacity{ 1 },             trackOffBrush{ black },             trackOffOpacity{ 0.38 },             thumbDisabled{ gray400 },             thumbDisabledOpacity{ 1 },             trackDisabled{ black },             trackDisabledOpacity{ 0.12 },             textColor{ black },             disabledTextOpacity{ 0.26 },             thumbBrushAnimation{ Animation(Type::Linear, 150) },             trackBrushAnimation{ Animation(Type::Linear, 150) },             thumbPosAniamtion{ Animation(Type::InOutQuad, 150) } {          }          int height;         QFont font;         QMargins indicatorMargin;         QColor thumbOnBrush;         double thumbOnOpacity;         QColor trackOnBrush;         double trackOnOpacity;         QColor thumbOffBrush;         double thumbOffOpacity;         QColor trackOffBrush;         double trackOffOpacity;         QColor thumbDisabled;         double thumbDisabledOpacity;         QColor trackDisabled;         double trackDisabledOpacity;         QColor textColor;         double disabledTextOpacity;         Animation thumbBrushAnimation;         Animation trackBrushAnimation;         Animation thumbPosAniamtion;     };      inline QPixmap drawShadowEllipse(qreal radius, qreal elevation, const QColor& color) {         auto px = QPixmap(radius * 2, radius * 2);         px.fill(Qt::transparent);          { // draw ellipes             QPainter p(&px);             p.setBrush(color);             p.setPen(Qt::NoPen);             p.setRenderHint(QPainter::Antialiasing, true);             p.drawEllipse(QRectF(0, 0, px.size().width(), px.size().height()).center(), radius - elevation, radius - elevation);         }          QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);         tmp.setDevicePixelRatio(px.devicePixelRatioF());         tmp.fill(0);         QPainter tmpPainter(&tmp);         tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);         tmpPainter.drawPixmap(QPointF(), px);         tmpPainter.end();          // blur the alpha channel         QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);         blurred.setDevicePixelRatio(px.devicePixelRatioF());         blurred.fill(0);         {             QPainter blurPainter(&blurred);             qt_blurImage(&blurPainter, tmp, elevation * 4., true, false);         }          tmp = blurred;          return QPixmap::fromImage(tmp);     }  } // namespace Style  #endif // STYLE_H 

switch.h

/*  * This is nearly complete Material design Switch widget implementation in qtwidgets module.  * More info: https://material.io/design/components/selection-controls.html#switches  * Copyright (C) 2018-2020 Iman Ahmadvand  *  * This is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  *  * It is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */  #ifndef SWITCH_H #define SWITCH_H  #include <QtWidgets> #include "style.h"  class Animator final : public QVariantAnimation {     Q_OBJECT     Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject)    public:     Animator(QObject* target, QObject* parent = nullptr);     ~Animator() override;      QObject* targetObject() const;     void setTargetObject(QObject* target);      inline bool isRunning() const {         return state() == Running;     }    public slots:     void setup(int duration, QEasingCurve easing = QEasingCurve::Linear);     void interpolate(const QVariant& start, const QVariant& end);     void setCurrentValue(const QVariant&);    protected:     void updateCurrentValue(const QVariant& value) override final;     void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) override final;    private:     QPointer<QObject> target; };  class SelectionControl : public QAbstractButton {     Q_OBJECT    public:     explicit SelectionControl(QWidget* parent = nullptr);     ~SelectionControl() override;      Qt::CheckState checkState() const;    Q_SIGNALS:     void stateChanged(int);    protected:     void enterEvent(QEvent*) override;     void checkStateSet() override;     void nextCheckState() override;     virtual void toggle(Qt::CheckState state) = 0; };  class Switch final : public SelectionControl {     Q_OBJECT      static constexpr auto CORNER_RADIUS = 8.0;     static constexpr auto THUMB_RADIUS = 14.5;     static constexpr auto SHADOW_ELEVATION = 2.0;    public:     explicit Switch(QWidget* parent = nullptr);     Switch(const QString& text, QWidget* parent = nullptr);     Switch(const QString& text, const QBrush&, QWidget* parent = nullptr);     ~Switch() override;      QSize sizeHint() const override final;    protected:     void paintEvent(QPaintEvent*) override final;     void resizeEvent(QResizeEvent*) override final;     void toggle(Qt::CheckState) override final;      void init();     QRect indicatorRect();     QRect textRect();      static inline QColor colorFromOpacity(const QColor& c, qreal opacity) {         return QColor(c.red(), c.green(), c.blue(), qRound(opacity * 255.0));     }     static inline bool ltr(QWidget* w) {         if (nullptr != w)             return w->layoutDirection() == Qt::LeftToRight;          return false;     }    private:     Style::Switch style;     QPixmap shadowPixmap;     QPointer<Animator> thumbBrushAnimation;     QPointer<Animator> trackBrushAnimation;     QPointer<Animator> thumbPosAniamtion; };  #endif // SWITCH_H 

switch.cpp

/*  * This is nearly complete Material design Switch widget implementation in qtwidgets module.  * More info: https://material.io/design/components/selection-controls.html#switches  * Copyright (C) 2018-2020 Iman Ahmadvand  *  * This is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  *  * It is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */  #include "switch.h"  Animator::Animator(QObject* target, QObject* parent) : QVariantAnimation(parent) {     setTargetObject(target); }  Animator::~Animator() {     stop(); }  QObject* Animator::targetObject() const {     return target.data(); }  void Animator::setTargetObject(QObject* _target) {     if (target.data() == _target)         return;      if (isRunning()) {         qWarning("Animation::setTargetObject: you can't change the target of a running animation");         return;     }      target = _target; }  void Animator::updateCurrentValue(const QVariant& value) {     Q_UNUSED(value);      if (!target.isNull()) {         auto update = QEvent(QEvent::StyleAnimationUpdate);         update.setAccepted(false);         QCoreApplication::sendEvent(target.data(), &update);         if (!update.isAccepted())             stop();     } }  void Animator::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) {     if (target.isNull() && oldState == Stopped) {         qWarning("Animation::updateState: Changing state of an animation without target");         return;     }      QVariantAnimation::updateState(newState, oldState);      if (!endValue().isValid() && direction() == Forward) {         qWarning("Animation::updateState (%s): starting an animation without end value", targetObject()->metaObject()->className());     } }  void Animator::setup(int duration, QEasingCurve easing) {     setDuration(duration);     setEasingCurve(easing); }  void Animator::interpolate(const QVariant& _start, const QVariant& end) {     setStartValue(_start);     setEndValue(end);     start(); }  void Animator::setCurrentValue(const QVariant& value) {     setStartValue(value);     setEndValue(value);     updateCurrentValue(currentValue()); }    SelectionControl::SelectionControl(QWidget* parent) : QAbstractButton(parent) {     setObjectName("SelectionControl");     setCheckable(true); }  SelectionControl::~SelectionControl() {  }  void SelectionControl::enterEvent(QEvent* e) {     setCursor(Qt::PointingHandCursor);     QAbstractButton::enterEvent(e); }  Qt::CheckState SelectionControl::checkState() const {     return isChecked() ? Qt::Checked : Qt::Unchecked; }  void SelectionControl::checkStateSet() {     const auto state = checkState();     emit stateChanged(state);     toggle(state); }  void SelectionControl::nextCheckState() {     QAbstractButton::nextCheckState();     SelectionControl::checkStateSet(); }    void Switch::init() {     setFont(style.font);     setObjectName("Switch");     /* setup animations */     thumbBrushAnimation = new Animator{ this, this };     trackBrushAnimation = new Animator{ this, this };     thumbPosAniamtion = new Animator{ this, this };     thumbPosAniamtion->setup(style.thumbPosAniamtion.duration, style.thumbPosAniamtion.easing);     trackBrushAnimation->setup(style.trackBrushAnimation.duration, style.trackBrushAnimation.easing);     thumbBrushAnimation->setup(style.thumbBrushAnimation.duration, style.thumbBrushAnimation.easing);     /* set init values */     trackBrushAnimation->setStartValue(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity));     trackBrushAnimation->setEndValue(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity));     thumbBrushAnimation->setStartValue(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity));     thumbBrushAnimation->setEndValue(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity));     /* set standard palettes */     auto p = palette();     p.setColor(QPalette::Active, QPalette::ButtonText, style.textColor);     p.setColor(QPalette::Disabled, QPalette::ButtonText, style.textColor);     setPalette(p);     setSizePolicy(QSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Fixed)); }  QRect Switch::indicatorRect() {     const auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right();     return ltr(this) ? QRect(0, 0, w, style.height) : QRect(width() - w, 0, w, style.height); }  QRect Switch::textRect() {     const auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right();     return ltr(this) ? rect().marginsRemoved(QMargins(w, 0, 0, 0)) : rect().marginsRemoved(QMargins(0, 0, w, 0)); }  Switch::Switch(QWidget* parent) : SelectionControl(parent) {     init(); }  Switch::Switch(const QString& text, QWidget* parent) : Switch(parent) {     setText(text); }  Switch::Switch(const QString& text, const QBrush& brush, QWidget* parent) : Switch(text, parent) {     style.thumbOnBrush = brush.color();     style.trackOnBrush = brush.color(); }  Switch::~Switch() {  }  QSize Switch::sizeHint() const {     auto h = style.height;     auto w = style.indicatorMargin.left() + style.height + style.indicatorMargin.right() + fontMetrics().width(text());      return QSize(w, h); }  void Switch::paintEvent(QPaintEvent*) {     /* for desktop usage we do not need Radial reaction */      QPainter p(this);      const auto _indicatorRect = indicatorRect();     const auto _textRect = textRect();     auto trackMargin = style.indicatorMargin;     trackMargin.setTop(trackMargin.top() + 2);     trackMargin.setBottom(trackMargin.bottom() + 2);     QRectF trackRect = _indicatorRect.marginsRemoved(trackMargin);      if (isEnabled()) {         p.setOpacity(1.0);         p.setPen(Qt::NoPen);         /* draw track */         p.setBrush(trackBrushAnimation->currentValue().value<QColor>());         p.setRenderHint(QPainter::Antialiasing, true);         p.drawRoundedRect(trackRect, CORNER_RADIUS, CORNER_RADIUS);         p.setRenderHint(QPainter::Antialiasing, false);         /* draw thumb */         trackRect.setX(trackRect.x() - trackMargin.left() - trackMargin.right() - 2 + thumbPosAniamtion->currentValue().toInt());         auto thumbRect = trackRect;          if (!shadowPixmap.isNull())             p.drawPixmap(thumbRect.center() - QPointF(THUMB_RADIUS, THUMB_RADIUS - 1.0), shadowPixmap);          p.setBrush(thumbBrushAnimation->currentValue().value<QColor>());         p.setRenderHint(QPainter::Antialiasing, true);         //        qDebug() << thumbRect << thumbPosAniamtion->currentValue();         p.drawEllipse(thumbRect.center(), THUMB_RADIUS - SHADOW_ELEVATION - 1.0, THUMB_RADIUS - SHADOW_ELEVATION - 1.0);         p.setRenderHint(QPainter::Antialiasing, false);          /* draw text */         if (text().isEmpty())             return;          p.setOpacity(1.0);         p.setPen(palette().color(QPalette::Active, QPalette::ButtonText));         p.setFont(font());         p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, text());     } else {         p.setOpacity(style.trackDisabledOpacity);         p.setPen(Qt::NoPen);         // draw track         p.setBrush(style.trackDisabled);         p.setRenderHint(QPainter::Antialiasing, true);         p.drawRoundedRect(trackRect, CORNER_RADIUS, CORNER_RADIUS);         p.setRenderHint(QPainter::Antialiasing, false);         // draw thumb         p.setOpacity(1.0);         if (!isChecked())             trackRect.setX(trackRect.x() - trackMargin.left() - trackMargin.right() - 2);         else             trackRect.setX(trackRect.x() + trackMargin.left() + trackMargin.right() + 2);         auto thumbRect = trackRect;          if (!shadowPixmap.isNull())             p.drawPixmap(thumbRect.center() - QPointF(THUMB_RADIUS, THUMB_RADIUS - 1.0), shadowPixmap);          p.setOpacity(1.0);         p.setBrush(style.thumbDisabled);         p.setRenderHint(QPainter::Antialiasing, true);         p.drawEllipse(thumbRect.center(), THUMB_RADIUS - SHADOW_ELEVATION - 1.0, THUMB_RADIUS - SHADOW_ELEVATION - 1.0);          /* draw text */         if (text().isEmpty())             return;          p.setOpacity(style.disabledTextOpacity);         p.setPen(palette().color(QPalette::Disabled, QPalette::ButtonText));         p.setFont(font());         p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, text());     } }  void Switch::resizeEvent(QResizeEvent* e) {     shadowPixmap = Style::drawShadowEllipse(THUMB_RADIUS, SHADOW_ELEVATION, QColor(0, 0, 0, 70));     SelectionControl::resizeEvent(e); }  void Switch::toggle(Qt::CheckState state) {     if (state == Qt::Checked) {         const QVariant posEnd = (style.indicatorMargin.left() + style.indicatorMargin.right() + 2) * 2;         const QVariant thumbEnd = colorFromOpacity(style.thumbOnBrush, style.thumbOnOpacity);         const QVariant trackEnd = colorFromOpacity(style.trackOnBrush, style.trackOnOpacity);          if (!isVisible()) {             thumbPosAniamtion->setCurrentValue(posEnd);             thumbBrushAnimation->setCurrentValue(thumbEnd);             trackBrushAnimation->setCurrentValue(trackEnd);         } else {             thumbPosAniamtion->interpolate(0, posEnd);             thumbBrushAnimation->interpolate(colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity), thumbEnd);             trackBrushAnimation->interpolate(colorFromOpacity(style.trackOffBrush, style.trackOffOpacity), trackEnd);         }     } else { // Qt::Unchecked         const QVariant posEnd = 0;         const QVariant thumbEnd = colorFromOpacity(style.thumbOffBrush, style.thumbOffOpacity);         const QVariant trackEnd = colorFromOpacity(style.trackOffBrush, style.trackOffOpacity);          if (!isVisible()) {             thumbPosAniamtion->setCurrentValue(posEnd);             thumbBrushAnimation->setCurrentValue(thumbEnd);             trackBrushAnimation->setCurrentValue(trackEnd);         } else {             thumbPosAniamtion->interpolate(thumbPosAniamtion->currentValue().toInt(), posEnd);             thumbBrushAnimation->interpolate(colorFromOpacity(style.thumbOnBrush, style.thumbOnOpacity), thumbEnd);             trackBrushAnimation->interpolate(colorFromOpacity(style.trackOnBrush, style.trackOnOpacity), trackEnd);         }     } } 

main.cpp

#include "switch.h"  int main(int argc, char *argv[]) {     QApplication application(argc, argv);     QWidget container;     QVBoxLayout mainLayout;     container.setLayout(&mainLayout);      Switch* switch1 = new Switch("SWITCH");     mainLayout.addWidget(switch1);     Switch* switch2 = new Switch("SWITCH");     mainLayout.addWidget(switch2);     switch2->setDisabled(true);     Switch* switch3 = new Switch("SWITCH");     mainLayout.addWidget(switch3);     switch3->setLayoutDirection(Qt::RightToLeft);     Switch* switch4 = new Switch("SWITCH");     mainLayout.addWidget(switch4);     switch4->setLayoutDirection(Qt::RightToLeft);     switch4->setChecked(true);     switch4->setDisabled(true);      QButtonGroup bg;     Switch* item1 = new Switch("ITEM1");     Switch* item2 = new Switch("ITEM2");     bg.addButton(item1);     bg.addButton(item2);     mainLayout.addWidget(item1);     mainLayout.addWidget(item2);     mainLayout.setMargin(100);      container.show();     return application.exec(); } 

Result:

enter image description here

like image 148
IMAN4K Avatar answered Oct 31 '22 06:10

IMAN4K