gpt4 book ai didi

c++ - Qt 中的切换开关

转载 作者:IT老高 更新时间:2023-10-28 21:37:47 27 4
gpt4 key购买 nike

我正在尝试使用与 Android Switches 等效的元素在 Qt 中。我在 QML 中找到了一个 ToggleSwitch,但在实际的 C++ Qt 库中什么也没有。我只是遗漏了什么,还是我必须自己重新实现这个小部件?

最佳答案

这是一个例子:

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

2018 年 8 月更新

新的 Material 切换小部件!

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

结果:

enter image description here

关于c++ - Qt 中的切换开关,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14780517/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com