Приходится сталкиваться с задачей, когда очень часто приходят сигналы от какого-нибудь объекта о том что он изменился, и надо обновлять информацию на экране. Но по каждому сигналу это делать не рационально. Я обычно в таких случаях запускаю таймер, и, допустим обновляю каждую секунду если сигналы от объекта идут.
Предложенный мною класс упрощает реализацию такого подхода. Я честно поискал в Qt наличие подобного функционала, но не нашел..
Использование не намного сложнее обычного QObject::connect:
C++ (Qt)
...
new QAccumulatingConnection(object, SIGNAL(stateChanged()), this, SLOT(updateObjectInfo()), 500, QAccumulatingConnection::Periodically, this);
...
Вышеописанный код создает накапливающее соединение сигнала object->stateChanged() и слота this->updateObjectInfo. Сигналы накапливаются и каждые 500мс (в случае с Periodically) вызывается указанный слот. Соединением владеет this, он его автоматически уничтожит в деструкторе.
В случае с QAccumulatingConnection::Finally сигналы будут накапливаться до тех пор пока они генерируются, по прошествии указанного времени после последней генерации сигнала будет вызван слот. Т.е. такой вид соединения позволяет сгладить "дребезг" сигнала.
Итак код:
qaccumulatingconnection.h
C++ (Qt)
#ifndef QACCUMULATINGCONNECTION_H
#define QACCUMULATINGCONNECTION_H
#include <QObject>
/*! Accumulating connection.
Example:
\code
new QAccumulatingConnection(object, SIGNAL(stateChanged()), this,
SLOT(updateObjectInfo()), 500, QAccumulatingConnection::Periodically, this);
\endcode
*/
class QAccumulatingConnection : public QObject
{
Q_OBJECT
public:
/*! Emit mode */
enum EmitMode
{
/*! Emits a signal after the lapse /p emitInterval ms since last call
to emit slot. */
Finally,
/*! Periodically emits a signal every /p emitInterval ms until slot
is called. */
Periodically
};
/*! Constructs an accumulating connection. */
QAccumulatingConnection(int emitInterval = 100, EmitMode mode = Finally, QObject* parent = NULL);
/*! Constructs an accumulating connection and connects sender signal to receiver slot. */
QAccumulatingConnection(const QObject* sender, const char* signal,
const QObject* receiver, const char* slot, int emitInterval = 100,
EmitMode mode = Finally, QObject* parent = NULL);
/*! Time interval in ms between call emitSignal() and emitting signal(). */
int emitInterval() const {return emitInterval_;}
/*! Current emit mode. */
EmitMode emitMode() const {return emitMode_;}
signals:
/*! Emitted signal after calling emitSignal() method. */
void signal();
public slots:
/*! Call this to emit signal() later. */
void emitSignal();
/*! Call this to emit signal() now, and reset emitting later. */
void emitNow();
/*! Resets emitting of pended signal(). */
void resetEmit();
protected:
virtual void timerEvent(QTimerEvent* e);
private:
int emitInterval_;
int timerId_;
EmitMode emitMode_;
};
#endif
qaccumulatingconnection.cpp
C++ (Qt)
#include <QTimerEvent>
#include "qaccumulatingconnection.h"
QAccumulatingConnection::QAccumulatingConnection(int emitInterval, EmitMode mode, QObject* parent)
: QObject(parent)
, emitInterval_(emitInterval)
, timerId_(0)
, emitMode_(mode)
{
}
QAccumulatingConnection::QAccumulatingConnection(const QObject* sender, const char* signal,
const QObject* receiver, const char* slot, int emitInterval,
EmitMode mode, QObject* parent)
: QObject(parent)
, emitInterval_(emitInterval)
, timerId_(0)
, emitMode_(mode)
{
QObject::connect(sender, signal, this, SLOT(emitSignal()));
QObject::connect(this, SIGNAL(signal()), receiver, slot);
}
void QAccumulatingConnection::emitSignal()
{
if (emitMode_ == Finally && timerId_)
{
killTimer(timerId_);
timerId_ = 0;
}
if (!timerId_)
timerId_ = startTimer(emitInterval_);
}
void QAccumulatingConnection::timerEvent(QTimerEvent* e)
{
if (e->timerId() != timerId_)
return;
killTimer(timerId_);
timerId_ = 0;
emit signal();
}
void QAccumulatingConnection::emitNow()
{
resetEmit();
emit signal();
}
void QAccumulatingConnection::resetEmit()
{
if (!timerId_)
return;
killTimer(timerId_);
timerId_ = 0;
}