2013-02-08 253 views
11

我试图使用一个相当于Qt中的Android Switches的元素。我在QML中找到了一个ToggleSwitch,但在实际的C++ Qt库中没有任何内容。我是否错过了一些东西,或者我会不得不重新实现这个小部件?在Qt中切换开关

回答

10

@ piccy的建议是我以前为这种切换开关所做的。有一些调整寿。

我们必须模拟类似于iOS开/关开关的行为。这意味着你需要一个渐进的移动,你不会有滑块与0-1的限制没有外部动画。

因此,我所做的是将滑块的值范围设置为与滑块的最大宽度相同。

然后连接滑块释放信号,并检查该值是否小于最大值的一半,如果是,则将滑块值设置为0,否则将滑块值设置为最大值。

这会给你一个很好的拖动效果,并在释放鼠标时剪辑到极端。

如果您希望滑块刚好在没有任何拖动的情况下在另一侧单击时切换,请连接滑块值更改信号并检查新值是否接近任一极值,并将其设置为滑块值不处于其停机状态。如果滑块关闭,请勿更改滑块值,因为您可能会破坏之前的拖动动作。

+1

太棒了!不错的“快照结束”效果是通过你在这里分享的技巧实现的!我已经添加了相同的代码,以便人们可以更好,更好地理解它。 – zeFree 2013-10-22 18:36:16

2

那么,你将不得不使用QCheckBox。这不是一个切换开关,但它做同样的事情。如果你真的想要不同的视觉,你必须创建自定义窗口小部件

2

达维塔是正确的in his answer它涉及复选框。如果你正在寻找类似于第三个例子的东西(开/关开关),那么你可以简单地使用两个QPushButton,并将它们设置为checkable。让他们在同一时间autoexclusive,你应该很好去。

用一点视觉样式using a stylesheet你应该能够关闭,如果没有发现。

+0

你会说并排安排两个PushButton并修改样式表会比引入QML对象更优雅吗?我试图看看什么才是最好的方式来获得等效的切换开关的外观和感觉。 – ElCraneo 2013-02-08 20:36:37

+0

更优雅,我真的不能告诉你。我对Qt的QML部分没有太多的经验。 QPushbutton解决方案应该是微不足道的,所以这将是我的出路。但这只是基于我对Qt(C++方面)的经验以及对QML缺乏经验。 – Bart 2013-02-08 20:39:47

+0

从这样的开关,人们可能会期望一个动画过渡,这两个QPushButtons不提供。 (而不是他们自己带来的,在这种情况下会引起混淆) – 2013-02-09 08:15:28

2

你也可以用一个QSlider控件在水平方向上做到这一点,范围为0到1.你可能想要将它的最大宽度设置为50左右,以防止它横跨对话框的宽度。然后,您可以使用样式表对其进行调整以改善外观,或将其子类化并自行绘制控件。它可能并不需要太多的代码来使它看起来不错。

0

我知道这个帖子已经过时了,但我仍然在这个特定的问题上挣扎了很多,尽管被Viv给了一个非常好的提示。

无论如何,我想我会分享我在这里提出的解决方案,也许它会帮助别人。

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); 
} 

一点解释:actionTriggered将每一个滑块被点击或用键盘移动时调用。当它被拖动时,它会发出信号'7'。为了避免立即捕捉,动作7被阻止。

当向右移动时,在键盘上点击'right'(或'down')时,它会发出3,同时点击1并发出1,这就是为什么当它不是偶数时我们正在向右移动。

当向左移动时,会发出2个或4

sliderReleased()将被调用,一旦你放手后拖动鼠标按钮的,但在这一刻,滑块仍然有它的旧值(它绊倒了我相当多)。所以,为了得到正确的位置来对齐我查询sliderPosition而不是value就是这样。

我希望这可以帮助别人。

13

下面是一个例子:

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

这个类很好,但它需要调用 'QAbstractButton :: mouseReleaseEvent(e);'在void'Switch :: mouseReleaseEvent(QMouseEvent * e)'末尾。这将有助于按钮在点击时发出所有正确的信号。 – Brandon 2017-05-01 22:51:13

+0

是的,调用基类实现是个好习惯。谢谢 – IMAN4K 2017-05-02 08:08:08