Skip to content

Commit ca642e4

Browse files
committedApr 5, 2016
Add easy methods for temporarily blocking signals
- QgsSignalBlocker: RAII signal blocking class. Used for temporarily blocking signals from a QObject for the lifetime of QgsSignalBlocker object. - easy shortcut "whileBlocking( QObject* )" function. Temporarily blocks signals from a QObject while calling a single method from the object. Usage: whileBlocking( checkBox )->setChecked( true ); whileBlocking( spinBox )->setValue( 50 ); No signals will be emitted when calling these methods. based on Boojum's code from http://stackoverflow.com/questions/3556687/prevent-firing-signals-in-qt
1 parent 882f6f8 commit ca642e4

File tree

6 files changed

+143
-74
lines changed

6 files changed

+143
-74
lines changed
 

‎src/app/composer/qgscomposer.cpp

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -936,9 +936,7 @@ void QgsComposer::updateStatusZoom()
936936
//current zoomLevel
937937
double zoomLevel = mView->transform().m11() * 100 / scale100;
938938

939-
mStatusZoomCombo->blockSignals( true );
940-
mStatusZoomCombo->lineEdit()->setText( tr( "%1%" ).arg( zoomLevel, 0, 'f', 1 ) );
941-
mStatusZoomCombo->blockSignals( false );
939+
whileBlocking( mStatusZoomCombo )->lineEdit()->setText( tr( "%1%" ).arg( zoomLevel, 0, 'f', 1 ) );
942940
}
943941

944942
void QgsComposer::statusZoomCombo_currentIndexChanged( int index )
@@ -948,9 +946,7 @@ void QgsComposer::statusZoomCombo_currentIndexChanged( int index )
948946
{
949947
mView->setZoomLevel( selectedZoom );
950948
//update zoom combobox text for correct format (one decimal place, trailing % sign)
951-
mStatusZoomCombo->blockSignals( true );
952-
mStatusZoomCombo->lineEdit()->setText( tr( "%1%" ).arg( selectedZoom * 100.0, 0, 'f', 1 ) );
953-
mStatusZoomCombo->blockSignals( false );
949+
whileBlocking( mStatusZoomCombo )->lineEdit()->setText( tr( "%1%" ).arg( selectedZoom * 100.0, 0, 'f', 1 ) );
954950
}
955951
}
956952

@@ -1087,9 +1083,7 @@ void QgsComposer::on_mActionAtlasPreview_triggered( bool checked )
10871083
tr( "Atlas in not currently enabled for this composition!" ),
10881084
QMessageBox::Ok,
10891085
QMessageBox::Ok );
1090-
mActionAtlasPreview->blockSignals( true );
1091-
mActionAtlasPreview->setChecked( false );
1092-
mActionAtlasPreview->blockSignals( false );
1086+
whileBlocking( mActionAtlasPreview )->setChecked( false );
10931087
mStatusAtlasLabel->setText( QString() );
10941088
return;
10951089
}
@@ -1217,9 +1211,7 @@ void QgsComposer::atlasPageComboEditingFinished()
12171211

12181212
if ( !ok || page > mComposition->atlasComposition().numFeatures() || page < 1 )
12191213
{
1220-
mAtlasPageComboBox->blockSignals( true );
1221-
mAtlasPageComboBox->setCurrentIndex( mComposition->atlasComposition().currentFeatureNumber() );
1222-
mAtlasPageComboBox->blockSignals( false );
1214+
whileBlocking( mAtlasPageComboBox )->setCurrentIndex( mComposition->atlasComposition().currentFeatureNumber() );
12231215
}
12241216
else if ( page != mComposition->atlasComposition().currentFeatureNumber() + 1 )
12251217
{
@@ -1573,9 +1565,7 @@ void QgsComposer::dockVisibilityChanged( bool visible )
15731565
{
15741566
if ( visible )
15751567
{
1576-
mActionHidePanels->blockSignals( true );
1577-
mActionHidePanels->setChecked( false );
1578-
mActionHidePanels->blockSignals( false );
1568+
whileBlocking( mActionHidePanels )->setChecked( false );
15791569
}
15801570
}
15811571

@@ -4136,9 +4126,7 @@ void QgsComposer::setAtlasFeature( QgsMapLayer* layer, const QgsFeature& feat )
41364126
{
41374127
mComposition->setAtlasMode( QgsComposition::PreviewAtlas );
41384128
//update gui controls
4139-
mActionAtlasPreview->blockSignals( true );
4140-
mActionAtlasPreview->setChecked( true );
4141-
mActionAtlasPreview->blockSignals( false );
4129+
whileBlocking( mActionAtlasPreview )->setChecked( true );
41424130
mActionAtlasFirst->setEnabled( true );
41434131
mActionAtlasLast->setEnabled( true );
41444132
mActionAtlasNext->setEnabled( true );

‎src/app/composer/qgscomposerattributetablewidget.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -545,9 +545,7 @@ void QgsComposerAttributeTableWidget::atlasToggled()
545545
if ( !mComposerTable )
546546
return;
547547

548-
mSourceComboBox->blockSignals( true );
549-
mSourceComboBox->setCurrentIndex( mSourceComboBox->findData( mComposerTable->source() ) );
550-
mSourceComboBox->blockSignals( false );
548+
whileBlocking( mSourceComboBox )->setCurrentIndex( mSourceComboBox->findData( mComposerTable->source() ) );
551549

552550
if ( !atlasEnabled && mComposerTable->filterToAtlasFeature() )
553551
{
@@ -646,9 +644,7 @@ void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
646644

647645
void QgsComposerAttributeTableWidget::setMaximumNumberOfFeatures( int n )
648646
{
649-
mMaximumRowsSpinBox->blockSignals( true );
650-
mMaximumRowsSpinBox->setValue( n );
651-
mMaximumRowsSpinBox->blockSignals( false );
647+
whileBlocking( mMaximumRowsSpinBox )->setValue( n );
652648
}
653649

654650
void QgsComposerAttributeTableWidget::on_mShowOnlyVisibleFeaturesCheckBox_stateChanged( int state )

‎src/app/composer/qgscomposerimageexportoptionsdialog.cpp

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,20 +157,12 @@ void QgsComposerImageExportOptionsDialog::clipToContentsToggled( bool state )
157157

158158
if ( state )
159159
{
160-
mWidthSpinBox->blockSignals( true );
161-
mWidthSpinBox->setValue( 0 );
162-
mWidthSpinBox->blockSignals( false );
163-
mHeightSpinBox->blockSignals( true );
164-
mHeightSpinBox->setValue( 0 );
165-
mHeightSpinBox->blockSignals( false );
160+
whileBlocking( mWidthSpinBox )->setValue( 0 );
161+
whileBlocking( mHeightSpinBox )->setValue( 0 );
166162
}
167163
else
168164
{
169-
mWidthSpinBox->blockSignals( true );
170-
mWidthSpinBox->setValue( mImageSize.width() * mResolutionSpinBox->value() / 25.4 );
171-
mWidthSpinBox->blockSignals( false );
172-
mHeightSpinBox->blockSignals( true );
173-
mHeightSpinBox->setValue( mImageSize.height() * mResolutionSpinBox->value() / 25.4 );
174-
mHeightSpinBox->blockSignals( false );
165+
whileBlocking( mWidthSpinBox )->setValue( mImageSize.width() * mResolutionSpinBox->value() / 25.4 );
166+
whileBlocking( mHeightSpinBox )->setValue( mImageSize.height() * mResolutionSpinBox->value() / 25.4 );
175167
}
176168
}

‎src/core/qgis.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,54 @@ inline void ( *cast_to_fptr( void *p ) )()
278278
}
279279
#endif
280280

281+
/** RAII signal blocking class. Used for temporarily blocking signals from a QObject
282+
* for the lifetime of QgsSignalBlocker object.
283+
* @see whileBlocking()
284+
* @note added in QGIS 2.16
285+
* @note not available in Python bindings
286+
*/
287+
// based on Boojum's code from http://stackoverflow.com/questions/3556687/prevent-firing-signals-in-qt
288+
template<class Object> class QgsSignalBlocker
289+
{
290+
public:
291+
292+
QgsSignalBlocker( Object* object )
293+
: mObject( object )
294+
, mPreviousState( object->blockSignals( true ) )
295+
{}
296+
297+
~QgsSignalBlocker()
298+
{
299+
mObject->blockSignals( mPreviousState );
300+
}
301+
302+
Object* operator->() { return mObject; }
303+
304+
private:
305+
306+
Object* mObject;
307+
bool mPreviousState;
308+
309+
};
310+
311+
/** Temporarily blocks signals from a QObject while calling a single method from the object.
312+
*
313+
* Usage:
314+
* whileBlocking( checkBox )->setChecked( true );
315+
* whileBlocking( spinBox )->setValue( 50 );
316+
*
317+
* No signals will be emitted when calling these methods.
318+
*
319+
* @note added in QGIS 2.16
320+
* @see QgsSignalBlocker
321+
* @note not available in Python bindings
322+
*/
323+
// based on Boojum's code from http://stackoverflow.com/questions/3556687/prevent-firing-signals-in-qt
324+
template<class Object> inline QgsSignalBlocker<Object> whileBlocking( Object* object )
325+
{
326+
return QgsSignalBlocker<Object>( object );
327+
}
328+
281329
//
282330
// return a string representation of a double
283331
//

‎src/gui/symbology-ng/qgssizescalewidget.cpp

Lines changed: 10 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,6 @@
3232

3333
#include <limits>
3434

35-
///@cond PRIVATE
36-
37-
// RAII class to block a QObject signal until destroyed
38-
struct SignalBlocker
39-
{
40-
SignalBlocker( QObject * object )
41-
: mObject( object )
42-
{
43-
mObject->blockSignals( true );
44-
}
45-
~SignalBlocker()
46-
{
47-
mObject->blockSignals( false );
48-
}
49-
private:
50-
QObject * mObject;
51-
52-
SignalBlocker( const SignalBlocker& rh );
53-
SignalBlocker& operator=( const SignalBlocker& rh );
54-
55-
56-
57-
};
58-
59-
///@endcond
60-
6135
void QgsSizeScaleWidget::setFromSymbol()
6236
{
6337
if ( !mSymbol )
@@ -82,20 +56,18 @@ void QgsSizeScaleWidget::setFromSymbol()
8256
{
8357
if ( scaleMethodComboBox->itemData( i ).toInt() == int( expr.type() ) )
8458
{
85-
( SignalBlocker( scaleMethodComboBox ), scaleMethodComboBox->setCurrentIndex( i ) );
59+
whileBlocking( scaleMethodComboBox )->setCurrentIndex( i );
8660
break;
8761
}
8862
}
8963

90-
// the (,) is used to create the Blocker first, then call the setter
91-
// the unamed SignalBlocker is destroyed at the end of the line (semicolumn)
92-
( SignalBlocker( mExpressionWidget ), mExpressionWidget->setField( expr.baseExpression() ) );
93-
( SignalBlocker( minValueSpinBox ), minValueSpinBox->setValue( expr.minValue() ) );
94-
( SignalBlocker( maxValueSpinBox ), maxValueSpinBox->setValue( expr.maxValue() ) );
95-
( SignalBlocker( minSizeSpinBox ), minSizeSpinBox->setValue( expr.minSize() ) );
96-
( SignalBlocker( maxSizeSpinBox ), maxSizeSpinBox->setValue( expr.maxSize() ) );
97-
( SignalBlocker( nullSizeSpinBox ), nullSizeSpinBox->setValue( expr.nullSize() ) );
98-
( SignalBlocker( exponentSpinBox ), exponentSpinBox->setValue( expr.exponent() ) );
64+
whileBlocking( mExpressionWidget )->setField( expr.baseExpression() );
65+
whileBlocking( minValueSpinBox )->setValue( expr.minValue() );
66+
whileBlocking( maxValueSpinBox )->setValue( expr.maxValue() );
67+
whileBlocking( minSizeSpinBox )->setValue( expr.minSize() );
68+
whileBlocking( maxSizeSpinBox )->setValue( expr.maxSize() );
69+
whileBlocking( nullSizeSpinBox )->setValue( expr.nullSize() );
70+
whileBlocking( exponentSpinBox )->setValue( expr.exponent() );
9971
}
10072
updatePreview();
10173
}
@@ -329,8 +301,8 @@ void QgsSizeScaleWidget::computeFromLayerTriggered()
329301
min = qMin( min, value );
330302
}
331303
}
332-
( SignalBlocker( minValueSpinBox ), minValueSpinBox->setValue( min ) );
333-
( SignalBlocker( maxSizeSpinBox ), maxValueSpinBox->setValue( max ) );
304+
whileBlocking( minValueSpinBox )->setValue( min );
305+
whileBlocking( maxValueSpinBox )->setValue( max );
334306
updatePreview();
335307
}
336308

‎tests/src/core/testqgis.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <QObject>
1717
#include <QString>
1818
#include <QApplication>
19+
#include <QCheckBox>
1920

2021
//qgis includes...
2122
#include <qgis.h>
@@ -37,6 +38,7 @@ class TestQGis : public QObject
3738
void permissiveToInt();
3839
void doubleToString();
3940
void qgsround();
41+
void signalBlocker();
4042

4143
private:
4244
QString mReport;
@@ -151,5 +153,76 @@ void TestQGis::qgsround()
151153
QCOMPARE( qgsRound( -1.5 ), -2. );
152154
}
153155

156+
void TestQGis::signalBlocker()
157+
{
158+
QScopedPointer< QCheckBox > checkbox( new QCheckBox() );
159+
160+
QSignalSpy spy( checkbox.data(), SIGNAL( toggled( bool ) ) );
161+
162+
//first check that signals are not blocked
163+
QVERIFY( !checkbox->signalsBlocked() );
164+
checkbox->setChecked( true );
165+
QCOMPARE( spy.count(), 1 );
166+
QCOMPARE( spy.last().at( 0 ).toBool(), true );
167+
168+
//block signals
169+
{
170+
QgsSignalBlocker< QCheckBox > blocker( checkbox.data() );
171+
QVERIFY( checkbox->signalsBlocked() );
172+
173+
checkbox->setChecked( false );
174+
QVERIFY( !checkbox->isChecked() );
175+
176+
//should be no new signals
177+
QCOMPARE( spy.count(), 1 );
178+
QCOMPARE( spy.last().at( 0 ).toBool(), true );
179+
checkbox->setChecked( true );
180+
}
181+
182+
//blocker is out of scope, blocking should be removed
183+
QVERIFY( !checkbox->signalsBlocked() );
184+
checkbox->setChecked( false );
185+
QCOMPARE( spy.count(), 2 );
186+
QCOMPARE( spy.last().at( 0 ).toBool(), false );
187+
188+
// now check that initial blocking state is restored when QgsSignalBlocker goes out of scope
189+
checkbox->blockSignals( true );
190+
{
191+
QgsSignalBlocker< QCheckBox > blocker( checkbox.data() );
192+
QVERIFY( checkbox->signalsBlocked() );
193+
}
194+
// initial blocked state should be restored
195+
QVERIFY( checkbox->signalsBlocked() );
196+
checkbox->blockSignals( false );
197+
198+
// nested signal blockers
199+
{
200+
QgsSignalBlocker< QCheckBox > blocker( checkbox.data() );
201+
QVERIFY( checkbox->signalsBlocked() );
202+
{
203+
QgsSignalBlocker< QCheckBox > blocker2( checkbox.data() );
204+
QVERIFY( checkbox->signalsBlocked() );
205+
}
206+
QVERIFY( checkbox->signalsBlocked() );
207+
}
208+
QVERIFY( !checkbox->signalsBlocked() );
209+
210+
// check whileBlocking function
211+
checkbox->setChecked( true );
212+
QCOMPARE( spy.count(), 3 );
213+
QCOMPARE( spy.last().at( 0 ).toBool(), true );
214+
215+
QVERIFY( !checkbox->signalsBlocked() );
216+
whileBlocking( checkbox.data() )->setChecked( false );
217+
// should have been no signals emitted
218+
QCOMPARE( spy.count(), 3 );
219+
// check that initial state of blocked signals was restored correctly
220+
QVERIFY( !checkbox->signalsBlocked() );
221+
checkbox->blockSignals( true );
222+
QVERIFY( checkbox->signalsBlocked() );
223+
whileBlocking( checkbox.data() )->setChecked( true );
224+
QVERIFY( checkbox->signalsBlocked() );
225+
}
226+
154227
QTEST_MAIN( TestQGis )
155228
#include "testqgis.moc"

0 commit comments

Comments
 (0)
Failed to load comments.