Krita Source Code Documentation
Loading...
Searching...
No Matches
KisSignalCompressor Class Reference

#include <kis_signal_compressor.h>

+ Inheritance diagram for KisSignalCompressor:

Public Types

enum  Mode {
  POSTPONE , FIRST_ACTIVE_POSTPONE_NEXT , FIRST_ACTIVE , FIRST_INACTIVE ,
  UNDEFINED
}
 
enum  SlowHandlerMode { PRECISE_INTERVAL , ADDITIVE_INTERVAL }
 

Public Slots

void setDelay (int delay)
 
void start ()
 
void stop ()
 

Signals

void timeout ()
 

Public Member Functions

int delay () const
 
bool isActive () const
 
 KisSignalCompressor ()
 
 KisSignalCompressor (int delay, Mode mode, QObject *parent=0)
 
 KisSignalCompressor (int delay, Mode mode, SlowHandlerMode slowHandlerMode, QObject *parent=0)
 
void setDelay (std::function< bool()> idleCallback, int idleDelay, int timeout)
 
void setMode (Mode mode)
 

Private Slots

void slotTimerExpired ()
 

Private Member Functions

void setDelayImpl (int delay)
 
bool tryEmitOnTick (bool isFromTimer)
 
bool tryEmitSignalSafely ()
 

Private Attributes

std::function< bool()> m_idleCallback
 
int m_isEmitting = 0
 
QElapsedTimer m_lastEmittedTimer
 
Mode m_mode = UNDEFINED
 
int m_sanityIsStarting = 0
 
bool m_signalsPending = false
 
SlowHandlerMode m_slowHandlerMode = PRECISE_INTERVAL
 
int m_timeout = 0
 
QTimer * m_timer = 0
 

Detailed Description

Sets a timer to delay or throttle activation of a Qt slot. One example of where this is used is to limit the amount of expensive redraw activity on the canvas.

There are four behaviors to choose from.

POSTPONE resets the timer after each call. Therefore if the calls are made quickly enough, the timer will never be activated.

FIRST_ACTIVE_POSTPONE_NEXT emits the first signal and postpones all the other actions like in POSTPONE. This mode is used e.g. in move/remove layer functionality. If you remove a single layer, you'll see the result immediately. But if you want to remove multiple layers, you should wait until all the actions are finished.

FIRST_ACTIVE emits the timeout() event immediately and sets a timer of duration delay. If the compressor is triggered during this time, it will wait until the end of the delay period to fire the signal. Further events are ignored until the timer elapses. Think of it as a queue with size 1, and where the leading element is popped every delay ms.

FIRST_INACTIVE emits the timeout() event at the end of a timer of duration delay ms. The compressor becomes inactive and all events are ignored until the timer has elapsed.

The current implementation allows the timeout() to be delayed by up to 2 times delay in certain situations (for details see cpp file).

Definition at line 48 of file kis_signal_compressor.h.

Member Enumeration Documentation

◆ Mode

Enumerator
POSTPONE 
FIRST_ACTIVE_POSTPONE_NEXT 
FIRST_ACTIVE 
FIRST_INACTIVE 
UNDEFINED 

Definition at line 53 of file kis_signal_compressor.h.

53 {
54 POSTPONE, /* Calling start() resets the timer to \p delay ms */
55 FIRST_ACTIVE_POSTPONE_NEXT, /* emits the first signal and postpones all the next ones */
56 FIRST_ACTIVE, /* Emit timeout() signal immediately. Throttle further timeout() to rate of one per \p delay ms */
57 FIRST_INACTIVE, /* Set a timer \p delay ms, Q_EMIT timeout() when it elapses. Ignore all events meanwhile. */
58 UNDEFINED /* KisSignalCompressor is created without an explicit mode */
59 };

◆ SlowHandlerMode

Enumerator
PRECISE_INTERVAL 
ADDITIVE_INTERVAL 

Definition at line 61 of file kis_signal_compressor.h.

61 {
62 PRECISE_INTERVAL, /* Interval of timeout is forced to \p delay ms, whatever time the handler of timeout() takes */
63 ADDITIVE_INTERVAL /* When the handler of timeout() is slow, the timeout delay is increased to the (delay + handler_time) */
64 };

Constructor & Destructor Documentation

◆ KisSignalCompressor() [1/3]

KisSignalCompressor::KisSignalCompressor ( )

KisSignalCompressor will never trigger timeout more often than every delay ms, i.e. delay ms is a given lower limit defining the highest frequency.

The current implementation uses a long-running monitor timer to eliminate the overhead incurred by restarting and stopping timers with each signal. The consequence of this is that the given delay ms is not always exactly followed.

KisSignalCompressor makes the following callback guarantees (0 < err <= 1, with err == 0 if this is the first signal after a while):

POSTPONE:

  • timeout after = [0.5 ... 1.0] * delay ms. FIRST_ACTIVE_POSTPONE_NEXT:
  • first timeout immediately
  • postponed timeout after [0.5 ... 1.0] * delay ms FIRST_ACTIVE:
  • first timeout immediately
  • after that [0.5 ... 1.5] * delay ms FIRST_INACTIVE:
  • timeout after [0.5 ... 1.5] * delay ms

Definition at line 37 of file kis_signal_compressor.cpp.

38 : QObject(0)
39 , m_timer(new QTimer(this))
40{
41 m_timer->setSingleShot(false);
42 connect(m_timer, SIGNAL(timeout()), SLOT(slotTimerExpired()));
43}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))

References connect(), m_timer, slotTimerExpired(), and timeout().

◆ KisSignalCompressor() [2/3]

KisSignalCompressor::KisSignalCompressor ( int delay,
Mode mode,
QObject * parent = 0 )

◆ KisSignalCompressor() [3/3]

KisSignalCompressor::KisSignalCompressor ( int delay,
Mode mode,
SlowHandlerMode slowHandlerMode,
QObject * parent = 0 )

Definition at line 50 of file kis_signal_compressor.cpp.

51 : QObject(parent),
52 m_timer(new QTimer(this)),
53 m_mode(mode),
54 m_slowHandlerMode(slowHandlerMode),
56{
57 m_timer->setSingleShot(false);
58 m_timer->setInterval(delay);
59 connect(m_timer, SIGNAL(timeout()), SLOT(slotTimerExpired()));
60}
SlowHandlerMode m_slowHandlerMode

References connect(), delay(), m_timer, slotTimerExpired(), and timeout().

Member Function Documentation

◆ delay()

int KisSignalCompressor::delay ( ) const

Definition at line 240 of file kis_signal_compressor.cpp.

241{
242 return m_timeout;
243}

References m_timeout.

◆ isActive()

bool KisSignalCompressor::isActive ( ) const

Definition at line 230 of file kis_signal_compressor.cpp.

231{
232 return m_signalsPending && m_timer->isActive();
233}

References m_signalsPending, and m_timer.

◆ setDelay [1/2]

void KisSignalCompressor::setDelay ( int delay)
slot

Definition at line 77 of file kis_signal_compressor.cpp.

78{
80 m_idleCallback = {};
82}
std::function< bool()> m_idleCallback

References delay(), m_idleCallback, m_timeout, and setDelayImpl().

◆ setDelay() [2/2]

void KisSignalCompressor::setDelay ( std::function< bool()> idleCallback,
int idleDelay,
int timeout )

Definition at line 84 of file kis_signal_compressor.cpp.

85{
87 m_idleCallback = idleCallback;
88 setDelayImpl(idleDelay);
89}

References m_idleCallback, m_timeout, setDelayImpl(), and timeout().

◆ setDelayImpl()

void KisSignalCompressor::setDelayImpl ( int delay)
private

Definition at line 62 of file kis_signal_compressor.cpp.

63{
64 const bool wasActive = m_timer->isActive();
65
66 if (wasActive) {
67 m_timer->stop();
68 }
69
70 m_timer->setInterval(delay);
71
72 if (wasActive) {
73 m_timer->start();
74 }
75}

References delay(), and m_timer.

◆ setMode()

void KisSignalCompressor::setMode ( KisSignalCompressor::Mode mode)

Definition at line 235 of file kis_signal_compressor.cpp.

236{
237 m_mode = mode;
238}

References m_mode.

◆ slotTimerExpired

void KisSignalCompressor::slotTimerExpired ( )
privateslot

Definition at line 208 of file kis_signal_compressor.cpp.

209{
212 if (!tryEmitOnTick(true)) {
213 const int calmDownInterval = 5 * m_timeout;
214
215 if (!m_lastEmittedTimer.isValid() ||
216 m_lastEmittedTimer.elapsed() > calmDownInterval) {
217
218 m_timer->stop();
219 }
220 }
221}
bool tryEmitOnTick(bool isFromTimer)
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References KIS_ASSERT_RECOVER_NOOP, KIS_SAFE_ASSERT_RECOVER_NOOP, m_lastEmittedTimer, m_mode, m_sanityIsStarting, m_timeout, m_timer, tryEmitOnTick(), and UNDEFINED.

◆ start

void KisSignalCompressor::start ( )
slot

Definition at line 91 of file kis_signal_compressor.cpp.

92{
94
95 const bool isFirstStart = !m_timer->isActive();
96
99
100
101 switch (m_mode) {
102 case POSTPONE:
103 if (isFirstStart) {
104 m_timer->start();
105 }
106 m_lastEmittedTimer.restart();
107 m_signalsPending = true;
108 break;
110 case FIRST_ACTIVE:
111 if (isFirstStart) {
112 m_timer->start();
114 m_lastEmittedTimer.restart();
115 }
116 m_signalsPending = false;
117 if (!tryEmitSignalSafely()) {
118 m_signalsPending = true;
119 }
121 m_lastEmittedTimer.restart();
122 }
123 } else {
124 if (m_mode == FIRST_ACTIVE) {
125 m_signalsPending = true;
126 tryEmitOnTick(false);
127 } else {
128 m_lastEmittedTimer.restart();
129 m_signalsPending = true;
130 }
131 }
132 break;
133 case FIRST_INACTIVE:
134 if (isFirstStart) {
135 m_timer->start();
136 m_lastEmittedTimer.restart();
137 m_signalsPending = true;
138 } else {
139 m_signalsPending = true;
140 tryEmitOnTick(false);
141 }
142 case UNDEFINED:
143 ; // Should never happen, but do nothing
144 };
145
147
148 KIS_SAFE_ASSERT_RECOVER(m_timer->isActive()) {
149 m_timer->start();
150 }
151}
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128

References ADDITIVE_INTERVAL, FIRST_ACTIVE, FIRST_ACTIVE_POSTPONE_NEXT, FIRST_INACTIVE, KIS_SAFE_ASSERT_RECOVER, KIS_SAFE_ASSERT_RECOVER_NOOP, KIS_SAFE_ASSERT_RECOVER_RETURN, m_lastEmittedTimer, m_mode, m_sanityIsStarting, m_signalsPending, m_slowHandlerMode, m_timer, POSTPONE, PRECISE_INTERVAL, tryEmitOnTick(), tryEmitSignalSafely(), and UNDEFINED.

◆ stop

void KisSignalCompressor::stop ( )
slot

Definition at line 223 of file kis_signal_compressor.cpp.

224{
225 m_timer->stop();
226 m_signalsPending = false;
227 m_lastEmittedTimer.invalidate();
228}

References m_lastEmittedTimer, m_signalsPending, and m_timer.

◆ timeout

void KisSignalCompressor::timeout ( )
signal

◆ tryEmitOnTick()

bool KisSignalCompressor::tryEmitOnTick ( bool isFromTimer)
private

Definition at line 153 of file kis_signal_compressor.cpp.

154{
155 bool wasEmitted = false;
156
157 // we have different requirements for hi-frequency events (the mean
158 // of the events rate must be min(compressorRate, eventsRate)
159 const int realInterval = m_timeout;
160 const int minInterval = realInterval < 100 ? 0.5 * realInterval : realInterval;
161
162 // Enable for debugging:
163 // ENTER_FUNCTION() << ppVar(isFromTimer) << ppVar(m_signalsPending) << m_lastEmittedTimer.elapsed() << ppVar((m_idleCallback && m_idleCallback()));
164
165 if (m_signalsPending &&
166 (m_lastEmittedTimer.elapsed() >= minInterval ||
168
170
172 m_lastEmittedTimer.start();
173 }
174
175 m_signalsPending = false;
176 if (!tryEmitSignalSafely()) {
177 m_signalsPending = true;
178 }
179
181 m_lastEmittedTimer.start();
182 }
183
184 wasEmitted = true;
185 } else if (!isFromTimer) {
186 m_signalsPending = true;
187 }
188
189 return wasEmitted;
190}

References ADDITIVE_INTERVAL, KIS_SAFE_ASSERT_RECOVER_NOOP, m_idleCallback, m_isEmitting, m_lastEmittedTimer, m_signalsPending, m_slowHandlerMode, m_timeout, PRECISE_INTERVAL, and tryEmitSignalSafely().

◆ tryEmitSignalSafely()

bool KisSignalCompressor::tryEmitSignalSafely ( )
private

Definition at line 192 of file kis_signal_compressor.cpp.

193{
194 bool wasEmitted = false;
195
196 m_isEmitting++;
197
198 if (m_isEmitting == 1) {
199 Q_EMIT timeout();
200 wasEmitted = true;
201 }
202
203 m_isEmitting--;
204
205 return wasEmitted;
206}

References m_isEmitting, and timeout().

Member Data Documentation

◆ m_idleCallback

std::function<bool()> KisSignalCompressor::m_idleCallback
private

Definition at line 101 of file kis_signal_compressor.h.

◆ m_isEmitting

int KisSignalCompressor::m_isEmitting = 0
private

Definition at line 99 of file kis_signal_compressor.h.

◆ m_lastEmittedTimer

QElapsedTimer KisSignalCompressor::m_lastEmittedTimer
private

Definition at line 98 of file kis_signal_compressor.h.

◆ m_mode

Mode KisSignalCompressor::m_mode = UNDEFINED
private

Definition at line 95 of file kis_signal_compressor.h.

◆ m_sanityIsStarting

int KisSignalCompressor::m_sanityIsStarting = 0
private

Definition at line 102 of file kis_signal_compressor.h.

◆ m_signalsPending

bool KisSignalCompressor::m_signalsPending = false
private

Definition at line 97 of file kis_signal_compressor.h.

◆ m_slowHandlerMode

SlowHandlerMode KisSignalCompressor::m_slowHandlerMode = PRECISE_INTERVAL
private

Definition at line 96 of file kis_signal_compressor.h.

◆ m_timeout

int KisSignalCompressor::m_timeout = 0
private

Definition at line 100 of file kis_signal_compressor.h.

◆ m_timer

QTimer* KisSignalCompressor::m_timer = 0
private

Definition at line 94 of file kis_signal_compressor.h.


The documentation for this class was generated from the following files: