Skip to content

Commit 34f00d1

Browse files
committedSep 23, 2014
[FEATURE][composer] Add checkbox for showing unique records only in composer
attribute tables. (Sponsored by my OCD)
1 parent d37f75f commit 34f00d1

11 files changed

+231
-24
lines changed
 

‎python/core/composer/qgscomposerattributetablev2.sip

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,21 @@ class QgsComposerAttributeTableV2 : QgsComposerTableV2
146146
* @see setMaximumNumberOfFeatures
147147
*/
148148
int maximumNumberOfFeatures() const;
149+
150+
/**Sets attribute table to only show unique rows.
151+
* @param uniqueOnly set to true to show only unique rows. Duplicate rows
152+
* will be stripped from the table.
153+
* @see uniqueRowsOnly
154+
*/
155+
void setUniqueRowsOnly( const bool uniqueOnly );
149156

157+
/**Returns true if the table is set to show only unique rows.
158+
* @returns true if table only shows unique rows and is stripping out
159+
* duplicate rows.
160+
* @see setUniqueRowsOnly
161+
*/
162+
bool uniqueRowsOnly() const;
163+
150164
/**Sets attribute table to only show features which are visible in a composer map item. Changing
151165
* this setting forces the table to refetch features from its vector layer, and may result in
152166
* the table changing size to accommodate the new displayed feature attributes.

‎python/core/composer/qgscomposertablev2.sip

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,11 @@ class QgsComposerTableV2: QgsComposerMultiFrame
358358
*/
359359
void recalculateTableSize();
360360

361+
/**Checks whether a table contents contains a given row
362+
* @param contents table contents to check
363+
* @param row row to check for
364+
* @returns true if contents contains rows
365+
*/
366+
bool contentsContainsRow( const QgsComposerTableContents &contents, const QgsComposerTableRow &row ) const;
367+
361368
};

‎src/app/composer/qgsattributeselectiondialog.cpp

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -383,15 +383,21 @@ void QgsAttributeSelectionDialog::on_mRemoveColumnPushButton_clicked()
383383
{
384384
//remove selected row from model
385385
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
386-
int selectedRow = viewSelection.indexes().at( 0 ).row();
387-
mColumnModel->removeRow( selectedRow );
386+
if ( viewSelection.length() > 0 )
387+
{
388+
int selectedRow = viewSelection.indexes().at( 0 ).row();
389+
mColumnModel->removeRow( selectedRow );
390+
}
388391
}
389392
if ( mComposerTableV1 )
390393
{
391394
//remove selected row from model
392395
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
393-
int selectedRow = viewSelection.indexes().at( 0 ).row();
394-
mColumnModelV1->removeRow( selectedRow );
396+
if ( viewSelection.length() > 0 )
397+
{
398+
int selectedRow = viewSelection.indexes().at( 0 ).row();
399+
mColumnModelV1->removeRow( selectedRow );
400+
}
395401
}
396402

397403
}
@@ -416,15 +422,21 @@ void QgsAttributeSelectionDialog::on_mColumnUpPushButton_clicked()
416422
{
417423
//move selected row up
418424
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
419-
int selectedRow = viewSelection.indexes().at( 0 ).row();
420-
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftUp );
425+
if ( viewSelection.size() > 0 )
426+
{
427+
int selectedRow = viewSelection.indexes().at( 0 ).row();
428+
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftUp );
429+
}
421430
}
422431
else if ( mComposerTableV1 )
423432
{
424433
//move selected row up
425434
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
426-
int selectedRow = viewSelection.indexes().at( 0 ).row();
427-
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftUp );
435+
if ( viewSelection.size() > 0 )
436+
{
437+
int selectedRow = viewSelection.indexes().at( 0 ).row();
438+
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftUp );
439+
}
428440
}
429441
}
430442

@@ -434,15 +446,21 @@ void QgsAttributeSelectionDialog::on_mColumnDownPushButton_clicked()
434446
{
435447
//move selected row down
436448
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
437-
int selectedRow = viewSelection.indexes().at( 0 ).row();
438-
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftDown );
449+
if ( viewSelection.size() > 0 )
450+
{
451+
int selectedRow = viewSelection.indexes().at( 0 ).row();
452+
mColumnModel->moveRow( selectedRow, QgsComposerAttributeTableColumnModelV2::ShiftDown );
453+
}
439454
}
440455
else if ( mComposerTableV1 )
441456
{
442457
//move selected row down
443458
QItemSelection viewSelection( mColumnsTableView->selectionModel()->selection() );
444-
int selectedRow = viewSelection.indexes().at( 0 ).row();
445-
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftDown );
459+
if ( viewSelection.size() > 0 )
460+
{
461+
int selectedRow = viewSelection.indexes().at( 0 ).row();
462+
mColumnModelV1->moveRow( selectedRow, QgsComposerAttributeTableColumnModel::ShiftDown );
463+
}
446464
}
447465

448466
}
@@ -498,8 +516,11 @@ void QgsAttributeSelectionDialog::on_mAddSortColumnPushButton_clicked()
498516
void QgsAttributeSelectionDialog::on_mRemoveSortColumnPushButton_clicked()
499517
{
500518
//remove selected rows from sort order widget
501-
502519
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
520+
if ( sortSelection.length() < 1 )
521+
{
522+
return;
523+
}
503524
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
504525
int rowToRemove = selectedIndex.row();
505526

@@ -536,6 +557,10 @@ void QgsAttributeSelectionDialog::on_mSortColumnUpPushButton_clicked()
536557
{
537558
//find selected row
538559
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
560+
if ( sortSelection.length() < 1 )
561+
{
562+
return;
563+
}
539564
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
540565

541566
if ( mComposerTable )
@@ -562,10 +587,13 @@ void QgsAttributeSelectionDialog::on_mSortColumnUpPushButton_clicked()
562587

563588
void QgsAttributeSelectionDialog::on_mSortColumnDownPushButton_clicked()
564589
{
565-
566-
567590
//find selected row
568591
QItemSelection sortSelection( mSortColumnTableView->selectionModel()->selection() );
592+
if ( sortSelection.length() < 1 )
593+
{
594+
return;
595+
}
596+
569597
QModelIndex selectedIndex = sortSelection.indexes().at( 0 );
570598

571599
if ( mComposerTable )

‎src/app/composer/qgscomposerattributetablewidget.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,7 @@ void QgsComposerAttributeTableWidget::updateGuiElements()
491491
mComposerMapLabel->setEnabled( false );
492492
}
493493

494+
mUniqueOnlyCheckBox->setChecked( mComposerTable->uniqueRowsOnly() );
494495
mIntersectAtlasCheckBox->setChecked( mComposerTable->filterToAtlasFeature() );
495496
mFeatureFilterEdit->setText( mComposerTable->featureFilter() );
496497
mFeatureFilterCheckBox->setCheckState( mComposerTable->filterFeatures() ? Qt::Checked : Qt::Unchecked );
@@ -595,6 +596,7 @@ void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
595596
mGridStrokeWidthSpinBox->blockSignals( b );
596597
mShowGridGroupCheckBox->blockSignals( b );
597598
mShowOnlyVisibleFeaturesCheckBox->blockSignals( b );
599+
mUniqueOnlyCheckBox->blockSignals( b );
598600
mIntersectAtlasCheckBox->blockSignals( b );
599601
mFeatureFilterEdit->blockSignals( b );
600602
mFeatureFilterCheckBox->blockSignals( b );
@@ -640,6 +642,26 @@ void QgsComposerAttributeTableWidget::on_mShowOnlyVisibleFeaturesCheckBox_stateC
640642
mComposerMapLabel->setEnabled( state == Qt::Checked );
641643
}
642644

645+
void QgsComposerAttributeTableWidget::on_mUniqueOnlyCheckBox_stateChanged( int state )
646+
{
647+
if ( !mComposerTable )
648+
{
649+
return;
650+
}
651+
652+
QgsComposition* composition = mComposerTable->composition();
653+
if ( composition )
654+
{
655+
composition->beginMultiFrameCommand( mComposerTable, tr( "Table remove duplicates changed" ) );
656+
}
657+
mComposerTable->setUniqueRowsOnly( state == Qt::Checked );
658+
mComposerTable->update();
659+
if ( composition )
660+
{
661+
composition->endMultiFrameCommand();
662+
}
663+
}
664+
643665
void QgsComposerAttributeTableWidget::on_mIntersectAtlasCheckBox_stateChanged( int state )
644666
{
645667
if ( !mComposerTable )

‎src/app/composer/qgscomposerattributetablewidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private
7373
void on_mEmptyModeComboBox_currentIndexChanged( int index );
7474
void on_mEmptyMessageLineEdit_editingFinished();
7575
void on_mIntersectAtlasCheckBox_stateChanged( int state );
76+
void on_mUniqueOnlyCheckBox_stateChanged( int state );
7677

7778
/**Inserts a new maximum number of features into the spin box (without the spinbox emitting a signal)*/
7879
void setMaximumNumberOfFeatures( int n );

‎src/core/composer/qgscomposerattributetablev2.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition* compos
104104
, mCurrentAtlasLayer( 0 )
105105
, mComposerMap( 0 )
106106
, mMaximumNumberOfFeatures( 5 )
107+
, mShowUniqueRowsOnly( false )
107108
, mShowOnlyVisibleFeatures( false )
108109
, mFilterToAtlasIntersection( false )
109110
, mFilterFeatures( false )
@@ -293,6 +294,18 @@ void QgsComposerAttributeTableV2::setMaximumNumberOfFeatures( const int features
293294
emit changed();
294295
}
295296

297+
void QgsComposerAttributeTableV2::setUniqueRowsOnly( const bool uniqueOnly )
298+
{
299+
if ( uniqueOnly == mShowUniqueRowsOnly )
300+
{
301+
return;
302+
}
303+
304+
mShowUniqueRowsOnly = uniqueOnly;
305+
refreshAttributes();
306+
emit changed();
307+
}
308+
296309
void QgsComposerAttributeTableV2::setDisplayOnlyVisibleFeatures( const bool visibleOnly )
297310
{
298311
if ( visibleOnly == mShowOnlyVisibleFeatures )
@@ -555,8 +568,12 @@ bool QgsComposerAttributeTableV2::getTableContents( QgsComposerTableContents &co
555568
currentRow << value;
556569
}
557570
}
558-
contents << currentRow;
559-
++counter;
571+
572+
if ( !mShowUniqueRowsOnly || !contentsContainsRow( contents, currentRow ) )
573+
{
574+
contents << currentRow;
575+
++counter;
576+
}
560577
}
561578

562579
//sort the list, starting with the last attribute
@@ -644,6 +661,7 @@ bool QgsComposerAttributeTableV2::writeXML( QDomElement& elem, QDomDocument & do
644661
QDomElement composerTableElem = doc.createElement( "ComposerAttributeTableV2" );
645662
composerTableElem.setAttribute( "source", QString::number(( int )mSource ) );
646663
composerTableElem.setAttribute( "relationId", mRelationId );
664+
composerTableElem.setAttribute( "showUniqueRowsOnly", mShowUniqueRowsOnly );
647665
composerTableElem.setAttribute( "showOnlyVisibleFeatures", mShowOnlyVisibleFeatures );
648666
composerTableElem.setAttribute( "filterToAtlasIntersection", mFilterToAtlasIntersection );
649667
composerTableElem.setAttribute( "maxFeatures", mMaximumNumberOfFeatures );
@@ -698,6 +716,7 @@ bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QD
698716
mCurrentAtlasLayer = mComposition->atlasComposition().coverageLayer();
699717
}
700718

719+
mShowUniqueRowsOnly = itemElem.attribute( "showUniqueRowsOnly", "0" ).toInt();
701720
mShowOnlyVisibleFeatures = itemElem.attribute( "showOnlyVisibleFeatures", "1" ).toInt();
702721
mFilterToAtlasIntersection = itemElem.attribute( "filterToAtlasIntersection", "0" ).toInt();
703722
mFilterFeatures = itemElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;

‎src/core/composer/qgscomposerattributetablev2.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,20 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
170170
*/
171171
int maximumNumberOfFeatures() const { return mMaximumNumberOfFeatures; }
172172

173+
/**Sets attribute table to only show unique rows.
174+
* @param uniqueOnly set to true to show only unique rows. Duplicate rows
175+
* will be stripped from the table.
176+
* @see uniqueRowsOnly
177+
*/
178+
void setUniqueRowsOnly( const bool uniqueOnly );
179+
180+
/**Returns true if the table is set to show only unique rows.
181+
* @returns true if table only shows unique rows and is stripping out
182+
* duplicate rows.
183+
* @see setUniqueRowsOnly
184+
*/
185+
bool uniqueRowsOnly() const { return mShowUniqueRowsOnly; }
186+
173187
/**Sets attribute table to only show features which are visible in a composer map item. Changing
174188
* this setting forces the table to refetch features from its vector layer, and may result in
175189
* the table changing size to accommodate the new displayed feature attributes.
@@ -278,6 +292,9 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
278292
/**Maximum number of features that is displayed*/
279293
int mMaximumNumberOfFeatures;
280294

295+
/**True if only unique rows should be shown*/
296+
bool mShowUniqueRowsOnly;
297+
281298
/**Shows only the features that are visible in the associated composer map (true by default)*/
282299
bool mShowOnlyVisibleFeatures;
283300

‎src/core/composer/qgscomposertablev2.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,3 +801,15 @@ void QgsComposerTableV2::recalculateTableSize()
801801
//fixed and minimum frame sizes
802802
recalculateFrameRects();
803803
}
804+
805+
bool QgsComposerTableV2::contentsContainsRow( const QgsComposerTableContents &contents, const QgsComposerTableRow &row ) const
806+
{
807+
if ( contents.indexOf( row ) >= 0 )
808+
{
809+
return true;
810+
}
811+
else
812+
{
813+
return false;
814+
}
815+
}

‎src/core/composer/qgscomposertablev2.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,15 @@ class CORE_EXPORT QgsComposerTableV2: public QgsComposerMultiFrame
428428
/**Recalculates and updates the size of the table and all table frames.
429429
*/
430430
void recalculateTableSize();
431+
432+
/**Checks whether a table contents contains a given row
433+
* @param contents table contents to check
434+
* @param row row to check for
435+
* @returns true if contents contains rows
436+
*/
437+
bool contentsContainsRow( const QgsComposerTableContents &contents, const QgsComposerTableRow &row ) const;
438+
439+
friend class TestQgsComposerTableV2;
431440
};
432441

433442
#endif // QGSCOMPOSERTABLEV2_H

‎src/ui/qgscomposerattributetablewidgetbase.ui

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
<x>0</x>
4848
<y>0</y>
4949
<width>392</width>
50-
<height>1048</height>
50+
<height>1076</height>
5151
</rect>
5252
</property>
5353
<layout class="QVBoxLayout" name="mainLayout">
@@ -158,14 +158,14 @@
158158
</property>
159159
</widget>
160160
</item>
161-
<item row="1" column="0" colspan="2">
161+
<item row="2" column="0" colspan="2">
162162
<widget class="QCheckBox" name="mShowOnlyVisibleFeaturesCheckBox">
163163
<property name="text">
164164
<string>Show only features visible within a map</string>
165165
</property>
166166
</widget>
167167
</item>
168-
<item row="2" column="0">
168+
<item row="3" column="0">
169169
<widget class="QLabel" name="mComposerMapLabel">
170170
<property name="text">
171171
<string>Composer map</string>
@@ -178,17 +178,17 @@
178178
</property>
179179
</widget>
180180
</item>
181-
<item row="2" column="1">
181+
<item row="3" column="1">
182182
<widget class="QComboBox" name="mComposerMapComboBox"/>
183183
</item>
184-
<item row="4" column="0">
184+
<item row="5" column="0">
185185
<widget class="QCheckBox" name="mFeatureFilterCheckBox">
186186
<property name="text">
187187
<string>Filter with</string>
188188
</property>
189189
</widget>
190190
</item>
191-
<item row="4" column="1">
191+
<item row="5" column="1">
192192
<layout class="QHBoxLayout" name="horizontalLayout">
193193
<item>
194194
<widget class="QLineEdit" name="mFeatureFilterEdit"/>
@@ -206,13 +206,20 @@
206206
</item>
207207
</layout>
208208
</item>
209-
<item row="3" column="0" colspan="2">
209+
<item row="4" column="0" colspan="2">
210210
<widget class="QCheckBox" name="mIntersectAtlasCheckBox">
211211
<property name="text">
212212
<string>Show only features intersecting atlas feature</string>
213213
</property>
214214
</widget>
215215
</item>
216+
<item row="1" column="0" colspan="2">
217+
<widget class="QCheckBox" name="mUniqueOnlyCheckBox">
218+
<property name="text">
219+
<string>Remove duplicate rows from table</string>
220+
</property>
221+
</widget>
222+
</item>
216223
</layout>
217224
<zorder>mShowOnlyVisibleFeaturesCheckBox</zorder>
218225
<zorder>mComposerMapLabel</zorder>
@@ -221,6 +228,7 @@
221228
<zorder>mMaximumRowsSpinBox</zorder>
222229
<zorder>mMaxNumFeaturesLabel</zorder>
223230
<zorder>mIntersectAtlasCheckBox</zorder>
231+
<zorder>mUniqueOnlyCheckBox</zorder>
224232
</widget>
225233
</item>
226234
<item>

‎tests/src/core/testqgscomposertablev2.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ class TestQgsComposerTableV2: public QObject
5555
void attributeTableRepeat();
5656
void attributeTableAtlasSource(); //test attribute table in atlas feature mode
5757
void attributeTableRelationSource(); //test attribute table in relation mode
58+
void contentsContainsRow(); //test the contentsContainsRow function
59+
void removeDuplicates(); //test removing duplicate rows
5860

5961
private:
6062
QgsComposition* mComposition;
@@ -535,6 +537,74 @@ void TestQgsComposerTableV2::attributeTableRelationSource()
535537
delete table;
536538
}
537539

540+
void TestQgsComposerTableV2::contentsContainsRow()
541+
{
542+
QgsComposerTableContents testContents;
543+
QgsComposerTableRow row1;
544+
row1 << QVariant( QString( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QString( "string 2" ) );
545+
QgsComposerTableRow row2;
546+
row2 << QVariant( QString( "string 2" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QString( "string 2" ) );
547+
//same as row1
548+
QgsComposerTableRow row3;
549+
row3 << QVariant( QString( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.5 ) << QVariant( QString( "string 2" ) );
550+
QgsComposerTableRow row4;
551+
row4 << QVariant( QString( "string 1" ) ) << QVariant( 2 ) << QVariant( 1.7 ) << QVariant( QString( "string 2" ) );
552+
553+
testContents << row1;
554+
testContents << row2;
555+
556+
QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row1 ) );
557+
QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row2 ) );
558+
QVERIFY( mComposerAttributeTable->contentsContainsRow( testContents, row3 ) );
559+
QVERIFY( !mComposerAttributeTable->contentsContainsRow( testContents, row4 ) );
560+
}
561+
562+
void TestQgsComposerTableV2::removeDuplicates()
563+
{
564+
QgsVectorLayer* dupesLayer = new QgsVectorLayer( "Point?field=col1:integer&field=col2:integer&field=col3:integer", "dupes", "memory" );
565+
QVERIFY( dupesLayer->isValid() );
566+
QgsFeature f1( dupesLayer->dataProvider()->fields(), 1 );
567+
f1.setAttribute( "col1", 1 );
568+
f1.setAttribute( "col2", 1 );
569+
f1.setAttribute( "col3", 1 );
570+
QgsFeature f2( dupesLayer->dataProvider()->fields(), 2 );
571+
f2.setAttribute( "col1", 1 );
572+
f2.setAttribute( "col2", 2 );
573+
f2.setAttribute( "col3", 2 );
574+
QgsFeature f3( dupesLayer->dataProvider()->fields(), 3 );
575+
f3.setAttribute( "col1", 1 );
576+
f3.setAttribute( "col2", 2 );
577+
f3.setAttribute( "col3", 3 );
578+
QgsFeature f4( dupesLayer->dataProvider()->fields(), 4 );
579+
f4.setAttribute( "col1", 1 );
580+
f4.setAttribute( "col2", 1 );
581+
f4.setAttribute( "col3", 1 );
582+
dupesLayer->dataProvider()->addFeatures( QgsFeatureList() << f1 << f2 << f3 << f4 );
583+
584+
QgsComposerAttributeTableV2* table = new QgsComposerAttributeTableV2( mComposition, false );
585+
table->setSource( QgsComposerAttributeTableV2::LayerAttributes );
586+
table->setVectorLayer( dupesLayer );
587+
table->setMaximumNumberOfFeatures( 50 );
588+
QCOMPARE( table->contents()->length(), 4 );
589+
590+
table->setUniqueRowsOnly( true );
591+
QCOMPARE( table->contents()->length(), 3 );
592+
593+
//check if removing attributes in unique mode works correctly (should result in duplicate rows,
594+
//which will be stripped out)
595+
table->columns()->removeLast();
596+
table->refreshAttributes();
597+
QCOMPARE( table->contents()->length(), 2 );
598+
table->columns()->removeLast();
599+
table->refreshAttributes();
600+
QCOMPARE( table->contents()->length(), 1 );
601+
table->setUniqueRowsOnly( false );
602+
QCOMPARE( table->contents()->length(), 4 );
603+
604+
mComposition->removeMultiFrame( table );
605+
delete dupesLayer;
606+
}
607+
538608
QTEST_MAIN( TestQgsComposerTableV2 )
539609
#include "moc_testqgscomposertablev2.cxx"
540610

0 commit comments

Comments
 (0)
Please sign in to comment.