Russian Qt Forum

Qt => Кладовая готовых решений => Тема начата: navrocky от Декабрь 03, 2010, 18:10



Название: Сигналы. Аккумулирующее соедиение (QAccumulatingConnection)
Отправлено: navrocky от Декабрь 03, 2010, 18:10
Приходится сталкиваться с задачей, когда очень часто приходят сигналы от какого-нибудь объекта о том что он изменился, и надо обновлять информацию на экране. Но по каждому сигналу это делать не рационально. Я обычно в таких случаях запускаю таймер, и, допустим обновляю каждую секунду если сигналы от объекта идут.

Предложенный мною класс упрощает реализацию такого подхода. Я честно поискал в 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;
}
 


Название: Re: Сигналы. Аккумулирующее соедиение (QAccumulatingConnection)
Отправлено: SABROG от Декабрь 04, 2010, 00:27
Реализовал подобный функционал через машину состояний:

Код
C++ (Qt)
#include <QtCore/QtGlobal>
#include <QtCore/QObject>
 
#include <QtCore/QCoreApplication>
 
#include <QtCore/QTextStream>
 
#include <QtCore/QStateMachine>
#include <QtCore/QSignalTransition>
#include <QtCore/QState>
#include <QtCore/QFinalState>
 
#include <QtCore/QTimer>
 
class DummyObject : public QObject
{
   Q_OBJECT
public:
   DummyObject(QObject* parent = 0) : QObject(parent) {}
   virtual ~DummyObject() {}
 
public slots:
   void dummySlot()
   {
       QTextStream(stdout) << Q_FUNC_INFO << endl;
   }
};
 
int main(int argc, char** argv[])
{
   QCoreApplication a(argc, argv);
 
   DummyObject dummyObject;
 
   QStateMachine stateMachine;
 
   QTimer emitter;
   emitter.start(100);
 
   QTimer accTimer;
   accTimer.setSingleShot(true);
   accTimer.setInterval(5000);
 
   QState* idleState = new QState(&stateMachine);
   QState* accumState = new QState(&stateMachine);
 
   idleState->addTransition(&emitter, SIGNAL(timeout()), accumState);
 
   QObject::connect(accumState, SIGNAL(entered()), &accTimer, SLOT(start()));
   QObject::connect(&accTimer
                    , SIGNAL(timeout()), &dummyObject, SLOT(dummySlot()));
#if 0
   accumState->addTransition(&accTimer, SIGNAL(timeout()), idleState);
#else
   QObject::connect(&accTimer, SIGNAL(timeout())
                    , QCoreApplication::instance()
                    , SLOT(quit()));
#endif
   stateMachine.setInitialState(idleState);
 
   stateMachine.start();
 
   return a.exec();
}
 
#include "moc_main.cpp"
 
 


Название: Re: Сигналы. Аккумулирующее соедиение (QAccumulatingConnection)
Отправлено: ритт от Декабрь 08, 2010, 17:59
насколько я знаю, в кьют планируется подобный функционал для событий (что-то вроде QCompressibleEvent) и для соединений...
а пока что - полезная вещь, спасибо.


Название: Re: Сигналы. Аккумулирующее соедиение (QAccumulatingConnection)
Отправлено: navrocky от Декабрь 09, 2010, 12:41
Нашел баг с пропущенным вызовом конструктора QObject.
Дописал еще пару полезных методов.

Код обновил.