I implemented a debounce connnect to prevent sending signals too frequently, please help to check whether this code will cause bugs.
-
#include <QTimer> #include <type_traits> #include <tuple> class Debounce { template <typename T> struct lambda_traits : lambda_traits<decltype(&T::operator())> {}; template <typename ClassType, typename ReturnType, typename... Args> struct lambda_traits<ReturnType(ClassType::*)(Args...) const> { static const std::size_t arity = sizeof...(Args); }; template<typename Func, std::size_t... Is, typename... Args> void static call_slot(Func slot, std::index_sequence<Is...>, Args&&... args) { slot(std::get<Is>(std::forward_as_tuple(std::forward<Args>(args)...))...); } public: template <typename Func1, typename Func2> static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal, const QObject* context, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) { QTimer* timer = new QTimer((QObject*)sender); timer->setSingleShot(true); auto func3 = new std::function<void()>; QObject::connect(sender, signal, timer, [=](auto&&... args) {*func3 = [=]() {call_slot(slot, std::make_index_sequence<lambda_traits<Func2>::arity>(), (args)...);}; timer->start(200); }); return QObject::connect(timer, &QTimer::timeout, context, [=]() {(*func3)(); }, type); } }; #include <QApplication> #include <QSlider> #include <QDoubleSpinBox> int main(int argc, char* argv[]) { QApplication app(argc, argv); auto w = new QWidget(); auto sl = new QSlider(Qt::Horizontal, w); auto dsp = new QDoubleSpinBox(w); w->show(); sl->move(30, 30); w->resize(200, 100); Debounce::connect(sl, &QSlider::valueChanged, sl, []() {printf("QSlider1 \n "); }); Debounce::connect(sl, &QSlider::valueChanged, sl, [](int v) {printf("QSlider2 %d \n", v); }); Debounce::connect(dsp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), dsp, [=](double v) {printf("QDoubleSpinBox %f \n", v); emit sl->rangeChanged(0, v); }); Debounce::connect(sl, &QSlider::rangeChanged, sl, [](int v0, int v1) {printf("rangeChanged %d %d\n", v0, v1); }); return app.exec(); } -
I haven’t had a look at your code.
But I am using KDSignalThrottler for signal debouncing. Works great so far and maybe it is of use for you. -
This will most likely fully debounce your signals. You should think about this if this is actually what you want. If there is no pause of at least 200ms between emitted signals, the slot will never be executed. A common solution is to use a second timer with a longer timeout which will not be restarted for every signal emitted, but only if it is not running. On timeout, the second timer will also stop your current timer, such that the slot is not executed twice. It is up to you, which kind of debounce you want.
-
This will most likely fully debounce your signals. You should think about this if this is actually what you want. If there is no pause of at least 200ms between emitted signals, the slot will never be executed. A common solution is to use a second timer with a longer timeout which will not be restarted for every signal emitted, but only if it is not running. On timeout, the second timer will also stop your current timer, such that the slot is not executed twice. It is up to you, which kind of debounce you want.
@SimonSchroeder I added the Throttle class to implement low-frequency send signals
#include <type_traits> #include <tuple> #include <QTimer> #include <QCoreApplication> namespace QtUtility { template <typename T> struct lambda_traits : lambda_traits<decltype(&T::operator())> {}; template <typename ClassType, typename ReturnType, typename... Args> struct lambda_traits<ReturnType(ClassType::*)(Args...) const> { static const std::size_t arity = sizeof...(Args); }; template<typename Func, std::size_t... Is, typename... Args> void static call_slot(Func slot, std::index_sequence<Is...>, Args&&... args) { slot(std::get<Is>(std::forward_as_tuple(std::forward<Args>(args)...))...); } class MyTimer : public QTimer { public: using QTimer::QTimer; std::function<void()> func; }; } class Debounce : QObject { public: int interval = 200; Debounce(int v, QObject* parent = NULL) { interval = v; setParent(parent ? parent : QCoreApplication::instance()); } template <typename Func1, typename Func2> inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal, const QObject* context, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) { using namespace QtUtility; auto timer = new MyTimer((QObject*)sender); timer->setSingleShot(true); QObject::connect(sender, signal, timer, [=](auto&&... args) {timer->func = [=]() {call_slot(slot, std::make_index_sequence<lambda_traits<Func2>::arity>(), (args)...); }; timer->start(interval); }); return QObject::connect(timer, &QTimer::timeout, context, [=]() {timer->func(); }, type); } }; class Throttle : QObject { public: int interval = 200; Throttle(int v, QObject* parent = NULL) { interval = v; setParent(parent ? parent : QCoreApplication::instance()); } template <typename Func1, typename Func2> inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object* sender, Func1 signal, const QObject* context, Func2 slot, Qt::ConnectionType type = Qt::AutoConnection) { using namespace QtUtility; auto timer = new MyTimer((QObject*)sender); timer->setSingleShot(true); QObject::connect(sender, signal, timer, [=](auto&&... args) {timer->func = [=]() {call_slot(slot, std::make_index_sequence<lambda_traits<Func2>::arity>(), (args)...); }; if (!timer->isActive()) timer->start(interval); }); return QObject::connect(timer, &QTimer::timeout, context, [=]() {timer->func(); }, type); } }; #include <QApplication> #include <QSlider> #include <QDoubleSpinBox> #include <QWheelEvent> int main(int argc, char* argv[]) { QApplication app(argc, argv); auto w = new QWidget(); auto sl = new QSlider(Qt::Horizontal, w); auto dsp = new QDoubleSpinBox(w); w->show(); sl->move(30, 30); w->resize(200, 100); { auto db = new Debounce(200); auto thr = new Throttle(200); QObject::connect(dsp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), dsp, [=](double v) { emit sl->rangeChanged(0, v); }); db->connect(dsp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), dsp, [=](double v) { printf("QDoubleSpinBox %f \n", v); }); db->connect(sl, &QSlider::valueChanged, sl, []() {printf("QSlider1 \n "); }); db->connect(sl, &QSlider::valueChanged, sl, [](int v) {printf("QSlider2 %d \n", v); }); thr->connect(sl, &QSlider::rangeChanged, sl, [](int v0, int v1) {printf("rangeChanged %d %d\n", v0, v1); }); } return app.exec(); }