Skip to content

Commit cb33c0d

Browse files
committed
Fix crash when reordering composer items via drag and drop
Caused by internal Qt bug when multiple QSortFilterProxyModels used by widgets are attached to a parent model which calls beginMoveRows. Adds some tests, but none reproduce the crash. Not reproducable on Qt5 builds.
1 parent b0268ef commit cb33c0d

File tree

5 files changed

+149
-0
lines changed

5 files changed

+149
-0
lines changed

src/core/composer/qgscomposermodel.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -958,10 +958,15 @@ QgsComposerProxyModel::QgsComposerProxyModel( QgsComposition *composition, QObje
958958
if ( mComposition )
959959
setSourceModel( mComposition->itemsModel() );
960960

961+
// WARNING: the below code triggers a Qt bug (tested on Qt 4.8, can't reproduce on Qt5) whenever the QgsComposerModel source model
962+
// calls beginInsertRows - since it's non functional anyway it's now disabled
963+
// PLEASE verify that the Qt issue is fixed before reenabling
964+
#if 0
961965
// TODO doesn't seem to work correctly - not updated when item changes
962966
setDynamicSortFilter( true );
963967
setSortLocaleAware( true );
964968
sort( QgsComposerModel::ItemId );
969+
#endif
965970
}
966971

967972
bool QgsComposerProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const

src/core/composer/qgscomposermodel.h

+1
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ class CORE_EXPORT QgsComposerModel: public QAbstractItemModel
289289
void rebuildSceneItemList();
290290

291291
friend class TestQgsComposerModel;
292+
friend class TestQgsComposerGui;
292293
};
293294

294295

tests/src/core/testqgscomposermodel.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class TestQgsComposerModel : public QObject
6262
void reorderToTopWithRemoved(); //test reordering to top with removed items
6363
void reorderToBottomWithRemoved(); //test reordering to bottom with removed items
6464

65+
void proxyCrash();
66+
6567
private:
6668
QgsComposition *mComposition;
6769
QgsMapSettings *mMapSettings;
@@ -610,5 +612,27 @@ void TestQgsComposerModel::reorderToBottomWithRemoved()
610612
QCOMPARE( mComposition->itemsModel()->mItemsInScene.at( 1 ), mItem2 );
611613
}
612614

615+
void TestQgsComposerModel::proxyCrash()
616+
{
617+
// test for a possible crash when using QgsComposerProxyModel and reordering items
618+
QgsMapSettings ms;
619+
QgsComposition* composition = new QgsComposition( ms );
620+
621+
// create a proxy - it's not used, but will be watching...
622+
QgsComposerProxyModel* proxy = new QgsComposerProxyModel( composition );
623+
Q_UNUSED( proxy );
624+
625+
// add some items to composition
626+
QgsComposerLabel* item1 = new QgsComposerLabel( composition );
627+
composition->addItem( item1 );
628+
QgsComposerLabel* item2 = new QgsComposerLabel( composition );
629+
composition->addItem( item2 );
630+
QgsComposerLabel* item3 = new QgsComposerLabel( composition );
631+
composition->addItem( item3 );
632+
633+
// reorder items - expect no crash!
634+
composition->itemsModel()->reorderItemUp( item1 );
635+
}
636+
613637
QTEST_MAIN( TestQgsComposerModel )
614638
#include "testqgscomposermodel.moc"

tests/src/gui/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}
1616
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/gui/raster
1717
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core
1818
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/auth
19+
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/composer
1920
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/geometry
2021
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/raster
2122
${CMAKE_CURRENT_SOURCE_DIR}/../../../src/core/symbology-ng
@@ -141,4 +142,5 @@ ADD_QGIS_TEST(scalecombobox testqgsscalecombobox.cpp)
141142
ADD_QGIS_TEST(spinbox testqgsspinbox.cpp)
142143
ADD_QGIS_TEST(sqlcomposerdialog testqgssqlcomposerdialog.cpp)
143144
ADD_QGIS_TEST(filedownloader testqgsfiledownloader.cpp)
145+
ADD_QGIS_TEST(composergui testqgscomposergui.cpp)
144146

tests/src/gui/testqgscomposergui.cpp

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/***************************************************************************
2+
testqgscomposergui.cpp
3+
----------------------
4+
Date : May 2017
5+
Copyright : (C) 2017 Nyall Dawson
6+
Email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
17+
18+
19+
#include <QtTest/QtTest>
20+
21+
#include "qgsmapsettings.h"
22+
#include "qgscomposition.h"
23+
#include "qgscomposerlabel.h"
24+
#include "qgscomposermap.h"
25+
#include "qgscomposermodel.h"
26+
#include "qgscomposeritemcombobox.h"
27+
28+
#include <QApplication>
29+
#include <QMainWindow>
30+
31+
class TestQgsComposerGui: public QObject
32+
{
33+
Q_OBJECT
34+
private slots:
35+
void initTestCase(); // will be called before the first testfunction is executed.
36+
void cleanupTestCase(); // will be called after the last testfunction was executed.
37+
void init(); // will be called before each testfunction is executed.
38+
void cleanup(); // will be called after every testfunction.
39+
40+
void testProxyCrash();
41+
42+
private:
43+
44+
};
45+
46+
void TestQgsComposerGui::initTestCase()
47+
{
48+
}
49+
50+
void TestQgsComposerGui::cleanupTestCase()
51+
{
52+
}
53+
54+
void TestQgsComposerGui::init()
55+
{
56+
}
57+
58+
void TestQgsComposerGui::cleanup()
59+
{
60+
}
61+
62+
void TestQgsComposerGui::testProxyCrash()
63+
{
64+
// test for a possible crash when using QgsComposerProxyModel and reordering items
65+
QgsMapSettings ms;
66+
QgsComposition* composition = new QgsComposition( ms );
67+
68+
// create a composer item combobox
69+
QgsComposerItemComboBox* cb = new QgsComposerItemComboBox( nullptr, composition );
70+
QgsComposerItemComboBox* cb2 = new QgsComposerItemComboBox( nullptr, composition );
71+
QgsComposerItemComboBox* cb3 = new QgsComposerItemComboBox( nullptr, composition );
72+
QgsComposerItemComboBox* cb4 = new QgsComposerItemComboBox( nullptr, composition );
73+
74+
cb->show();
75+
cb2->show();
76+
77+
// add some items to composition
78+
QgsComposerMap* item1 = new QgsComposerMap( composition );
79+
composition->addItem( item1 );
80+
QgsComposerLabel* item2 = new QgsComposerLabel( composition );
81+
composition->addItem( item2 );
82+
QgsComposerMap* item3 = new QgsComposerMap( composition );
83+
composition->addItem( item3 );
84+
85+
QCOMPARE( cb->count(), 3 );
86+
cb->setItemType( QgsComposerItem::ComposerMap );
87+
QCOMPARE( cb->count(), 2 );
88+
89+
cb4->setItemType( QgsComposerItem::ComposerLabel );
90+
91+
cb->setItem( item1 );
92+
QCOMPARE( cb->currentItem(), item1 );
93+
cb2->setItem( item3 );
94+
QCOMPARE( cb2->currentItem(), item3 );
95+
cb3->setItem( item2 );
96+
QCOMPARE( cb3->currentItem(), item2 );
97+
98+
// reorder items - expect no crash!
99+
// we do this by calling the private members, in order to simulate what
100+
// happens when a drag and drop reorder occurs
101+
composition->itemsModel()->mItemZList.removeOne( item1 );
102+
composition->itemsModel()->mItemZList.insert( 1, item1 );
103+
composition->itemsModel()->rebuildSceneItemList();
104+
105+
QCOMPARE( cb->currentItem(), item1 );
106+
QCOMPARE( cb2->currentItem(), item3 );
107+
108+
composition->itemsModel()->mItemZList.removeOne( item1 );
109+
composition->itemsModel()->mItemZList.insert( 0, item1 );
110+
composition->itemsModel()->rebuildSceneItemList();
111+
112+
delete composition;
113+
}
114+
115+
116+
QTEST_MAIN( TestQgsComposerGui )
117+
#include "testqgscomposergui.moc"

0 commit comments

Comments
 (0)