Russian Qt Forum
Ноябрь 24, 2020, 15:51 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Сигналы. Аккумулирующее соедиение (QAccumulatingConnection)  (Прочитано 3849 раз)
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« : Декабрь 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;
}
 
« Последнее редактирование: Декабрь 09, 2010, 12:40 от navrocky » Записан

Гугль в помощь
SABROG
Гость
« Ответ #1 : Декабрь 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"
 
 
Записан
ритт
Гость
« Ответ #2 : Декабрь 08, 2010, 17:59 »

насколько я знаю, в кьют планируется подобный функционал для событий (что-то вроде QCompressibleEvent) и для соединений...
а пока что - полезная вещь, спасибо.
Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #3 : Декабрь 09, 2010, 12:41 »

Нашел баг с пропущенным вызовом конструктора QObject.
Дописал еще пару полезных методов.

Код обновил.
Записан

Гугль в помощь
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.142 секунд. Запросов: 22.