2013-02-08 9 views
11

Ich versuche, ein Element zu verwenden, das dem Android Switches in Qt entspricht. Ich habe einen ToggleSwitch in QML gefunden, aber nichts in den C++ Qt-Bibliotheken. Fehle ich gerade etwas oder muss ich dieses Widget selbst neu implementieren?Kippschalter in Qt

Antwort

10

@ Piccys Vorschlag ist, was ich für einen solchen Kippschalter zuvor getan habe. Mit ein paar Verbesserungen tho.

Wir mussten das Verhalten ähnlich den iOS-Ein/Aus-Schalter emulieren. Das bedeutet, dass Sie eine schrittweise Bewegung benötigen, die Sie nicht haben werden, wenn der Schieberegler ohne externe Animationen 0-1 beträgt.

Daher wurde der Wertebereich für den Schieberegler so eingestellt, dass er der maximalen Breite des Schiebereglers entspricht.

Schließen Sie dann das freigegebene Slider-Signal an und prüfen Sie, ob der Wert weniger als die Hälfte des Maximums beträgt. Ist dies der Fall, stellen Sie den Schieberegler auf 0 oder den Schieberegler auf max.

Dadurch erhalten Sie einen guten Drag-Effekt und Clip auf Extreme, wenn Sie die Maustaste loslassen.

Wenn Sie möchten, dass der Schieberegler ohne Klicken auf die andere Seite umschaltet, verbinden Sie das veränderte Signal des Schiebereglers und prüfen Sie, ob der neue Wert näher an beiden Extremwerten liegt und setzen Sie den Schieberegler auf nicht in seinem heruntergesetzten Zustand. Ändern Sie den Schiebereglerwert nicht, wenn der Schieberegler nicht gedrückt ist, da Sie dann möglicherweise die vorherige Ziehbewegung unterbrechen.

+1

Ehrfürchtiger Mann! Schöne "Snap-To-End" -Effekte werden durch den Trick erzielt, den Sie hier geteilt haben! Ich habe den Code für das gleiche hinzugefügt, damit die Leute es besser und besser verstehen können. – zeFree

2

Nun, Sie müssen QCheckBox verwenden. Es ist kein Toggle Switch, aber es macht dasselbe. Wenn Sie wirklich unterschiedliche visuelle möchten, müssen Sie benutzerdefinierte erstellen müssen Widget

2

Davita gemacht werden ist richtig in his answer, wo es Checkboxen betrifft. Wenn Sie nach etwas suchen, das dem dritten Beispiel ähnlich ist (die Ein/Aus-Schalter), können Sie einfach zwei QPushButton s dafür verwenden und sie auf checkable setzen. Machen Sie sie gleichzeitig autoexclusive, und Sie sollten gut gehen.

Mit ein wenig visuellen Styling using a stylesheet sollten Sie in der Lage sein, nahe zu kommen, wenn nicht gerade auf.

+0

Würden Sie sagen, dass Sie zwei PushButtons nebeneinander anordnen und das Modifizieren des Stylesheets eleganter sein würde als das Einbringen des QML-Objekts? Ich versuche zu sehen, was der beste Weg wäre, um das gleiche Aussehen und Verhalten des Toggle Switch zu erhalten. – ElCraneo

+0

Eleganter könnte ich dir wirklich nicht sagen. Ich habe nicht viel Erfahrung mit dem QML-Teil von Qt. Die QPushbutton-Lösung sollte jedoch wirklich trivial sein, also wäre es mein Weg zu gehen. Aber das basiert nur auf meiner Erfahrung mit Qt (der C++ Seite) und dem Mangel an Erfahrung mit QML. – Bart

+0

Von einem solchen Schalter könnte man einen animierten Übergang erwarten, den zwei QPushButtons nicht bieten. (Stattdessen bringen sie ihre eigenen mit, was in diesem Zusammenhang verwirrend wäre) –

2

Sie könnten dies auch mit einem QSlider-Steuerelement in einer horizontalen Ausrichtung tun, die einen Bereich von 0 bis 1 hat. Sie möchten wahrscheinlich die maximale Breite auf etwa 50 einstellen, damit es sich nicht über die Breite des Dialogs. Sie können es dann mit einem Stylesheet optimieren, um das Aussehen zu verbessern, oder es unterklassifizieren und die Steuerelemente selbst zeichnen. Es braucht nicht zu viel Code, um es gut aussehen zu lassen.

0

Ich weiß, dass dieser Thread alt ist, aber ich habe ziemlich mit diesem speziellen Problem gekämpft, obwohl ich einen sehr guten Hinweis von Viv bekommen habe.

Wie auch immer, ich dachte mir, ich würde die Lösung teilen, die ich mir hier ausgedacht habe, vielleicht hilft sie jemandem auf dem Weg.

void Switch::on_sldSwitch_actionTriggered(int action) { 
    if(action != 7) ui->sldSwitch->setValue((action%2) ? 100 : 0); 
} 

void Switch::on_sldSwitch_sliderReleased() { 
    ui->sldSwitch->setValue((ui->sldSwitch->sliderPosition() >= 50) ? 100 : 0); 
} 

eine kleine Erklärung: actionTriggered wird jedes Mal aufgerufen werden, der Schieber geklickt wird oder mit der Tastatur bewegt. Wenn es gezogen wird, wird es das Signal "7" ausgeben. Um ein sofortiges Fangen zu vermeiden, wird Aktion 7 blockiert.

Wenn Sie nach rechts bewegen, wird beim Klicken 3 und beim Klicken 1 "rechts" (oder "unten") auf der Tastatur angezeigt, weshalb wir nach rechts springen, wenn es sich nicht um eine gerade Zahl handelt.

Wenn links bewegt, emittiert er 2 oder 4

sliderReleased() wird aufgerufen, wenn Sie in diesem Augenblick Sie die Maustaste loslassen, aber nach dem Ziehen weist der Schieber noch seinen alten Wert (die mich gestolpert ziemlich viel). Also, um die richtige Position zum Einrasten zu bekommen, habe ich sliderPosition statt value abgefragt und das ist das.

Ich hoffe, das hilft jemandem. Hier

13

ein Beispiel:

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

+2

Diese Klasse ist großartig, aber sie benötigt einen Aufruf an 'QAbstractButton :: mouseReleaseEvent (e);' am Ende von void 'Switch :: mouseReleaseEvent (QMouseEvent * e)'. Dies wird dazu beitragen, dass der Knopf alle richtigen Signale aussendet, wenn er angeklickt wird. – Brandon

+0

Ja, es ist gute Praxis, Basisklasse implementation.thanks zu nennen – IMAN4K