Skip to content

Commit 8412960

Browse files
committedMay 12, 2016
Add a QgsSQLComposerDialog GUI to edit QgsSQLStatement
1 parent 9df19e0 commit 8412960

File tree

6 files changed

+1826
-1
lines changed

6 files changed

+1826
-1
lines changed
 

‎src/gui/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ SET(QGIS_GUI_SRCS
276276
qgssearchquerybuilder.cpp
277277
qgsslider.cpp
278278
qgssublayersdialog.cpp
279+
qgssqlcomposerdialog.cpp
279280
qgssvgannotationitem.cpp
280281
qgstablewidgetitem.cpp
281282
qgstextannotationitem.cpp
@@ -416,6 +417,7 @@ SET(QGIS_GUI_MOC_HDRS
416417
qgsscalewidget.h
417418
qgssearchquerybuilder.h
418419
qgsslider.h
420+
qgssqlcomposerdialog.h
419421
qgssublayersdialog.h
420422
qgsunitselectionwidget.h
421423
qgsuserinputdockwidget.h
@@ -595,6 +597,7 @@ SET(QGIS_GUI_HDRS
595597
qgsmaptip.h
596598
qgsnumericsortlistviewitem.h
597599
qgsrubberband.h
600+
qgssqlcomposerdialog.h
598601
qgssvgannotationitem.h
599602
qgstablewidgetitem.h
600603
qgstextannotationitem.h
@@ -673,6 +676,7 @@ SET(QGIS_GUI_UI_HDRS
673676
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsowssourceselectbase.h
674677
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsprojectionselectorbase.h
675678
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgsquerybuilderbase.h
679+
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssqlcomposerdialogbase.h
676680
${CMAKE_CURRENT_BINARY_DIR}/../ui/ui_qgssublayersdialogbase.h
677681
)
678682

‎src/gui/qgssqlcomposerdialog.cpp

Lines changed: 704 additions & 0 deletions
Large diffs are not rendered by default.

‎src/gui/qgssqlcomposerdialog.h

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/***************************************************************************
2+
qgssqlcomposerdialog.cpp
3+
Dialog to compose SQL queries
4+
5+
begin : Apr 2016
6+
copyright : (C) 2016 Even Rouault
7+
email : even.rouault at spatialys.com
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#ifndef QGSSQLCOMPOSERDIALOG_H
20+
#define QGSSQLCOMPOSERDIALOG_H
21+
22+
#include "ui_qgssqlcomposerdialogbase.h"
23+
#include <qgis.h>
24+
#include <qgisgui.h>
25+
#include "qgscontexthelp.h"
26+
27+
#include <QPair>
28+
#include <QStringList>
29+
#include <QSet>
30+
31+
/** SQL composer dialog
32+
* @note not available in Python bindings
33+
*/
34+
class GUI_EXPORT QgsSQLComposerDialog : public QDialog, private Ui::QgsSQLComposerDialogBase
35+
{
36+
Q_OBJECT
37+
38+
public:
39+
40+
//! pair (name, title)
41+
typedef QPair<QString, QString> PairNameTitle;
42+
43+
//! pair (name, type)
44+
typedef QPair<QString, QString> PairNameType;
45+
46+
/** Callback to do actions on table selection
47+
* @note not available in Python bindings
48+
*/
49+
class GUI_EXPORT TableSelectedCallback
50+
{
51+
public:
52+
virtual ~TableSelectedCallback();
53+
//! method called when a table is selected
54+
virtual void tableSelected( const QString& name ) = 0;
55+
};
56+
57+
/** Callback to do validation check on dialog validation.
58+
* @note not available in Python bindings
59+
*/
60+
class GUI_EXPORT SQLValidatorCallback
61+
{
62+
public:
63+
virtual ~SQLValidatorCallback();
64+
//! method should return true if the SQL is valid. Otherwise return false and set the errorReason
65+
virtual bool isValid( const QString& sql, QString& errorReason ) = 0;
66+
};
67+
68+
//! argument of a function
69+
struct Argument
70+
{
71+
//! name
72+
QString name;
73+
//! type, or empty if unknown
74+
QString type;
75+
76+
//! constructor
77+
Argument( const QString& nameIn = QString(), const QString& typeIn = QString() ) : name( nameIn ), type( typeIn ) {}
78+
};
79+
80+
//! description of server functions
81+
struct Function
82+
{
83+
//! name
84+
QString name;
85+
//! return type, or empty if unknown
86+
QString returnType;
87+
//! minimum number of argument (or -1 if unknown)
88+
int minArgs;
89+
//! maximum number of argument (or -1 if unknown)
90+
int maxArgs;
91+
//! list of arguments. May be empty despite minArgs > 0
92+
QList<Argument> argumentList;
93+
94+
//! constructor with name and fixed number of arguments
95+
Function( const QString& nameIn, int args ) : name( nameIn ), minArgs( args ), maxArgs( args ) {}
96+
//! constructor with name and min,max number of arguments
97+
Function( const QString& nameIn, int minArgs, int maxArgsIn ) : name( nameIn ), minArgs( minArgs ), maxArgs( maxArgsIn ) {}
98+
//! default constructor
99+
Function() : minArgs( -1 ), maxArgs( -1 ) {}
100+
};
101+
102+
//! constructor
103+
explicit QgsSQLComposerDialog( QWidget * parent = nullptr, Qt::WindowFlags fl = QgisGui::ModalDialogFlags );
104+
virtual ~QgsSQLComposerDialog();
105+
106+
//! initialize the SQL statement
107+
void setSql( const QString& sql );
108+
109+
//! get the SQL statement
110+
QString sql() const;
111+
112+
//! add a list of table names
113+
void addTableNames( const QStringList& list );
114+
//! add a list of table names
115+
void addTableNames( const QList<PairNameTitle> & listNameTitle );
116+
//! add a list of column names
117+
void addColumnNames( const QStringList& list, const QString& tableName );
118+
//! add a list of column names
119+
void addColumnNames( const QList<PairNameType>& list, const QString& tableName );
120+
//! add a list of operators
121+
void addOperators( const QStringList& list );
122+
//! add a list of spatial predicates
123+
void addSpatialPredicates( const QStringList& list );
124+
//! add a list of spatial predicates
125+
void addSpatialPredicates( const QList<Function>& list );
126+
//! add a list of functions
127+
void addFunctions( const QStringList& list );
128+
//! add a list of functions
129+
void addFunctions( const QList<Function>& list );
130+
//! add a list of API for autocompletion
131+
void addApis( const QStringList& list );
132+
133+
//! set if multiple tables/joins are supported. Default is false
134+
void setSupportMultipleTables( bool );
135+
136+
/** Set a callback that will be called when a new table is selected, so
137+
that new column names can be added typically.
138+
Ownership of the callback remains to the caller */
139+
void setTableSelectedCallback( TableSelectedCallback* tableSelectedCallback );
140+
141+
/** Set a callback that will be called when the OK button is pushed.
142+
Ownership of the callback remains to the caller */
143+
void setSQLValidatorCallback( SQLValidatorCallback* sqlValidatorCallback );
144+
145+
protected:
146+
bool eventFilter( QObject *obj, QEvent *event ) override;
147+
148+
private slots:
149+
void accept() override;
150+
151+
void on_mTablesCombo_currentIndexChanged( int );
152+
void on_mColumnsCombo_currentIndexChanged( int );
153+
void on_mSpatialPredicatesCombo_currentIndexChanged( int );
154+
void on_mFunctionsCombo_currentIndexChanged( int );
155+
void on_mOperatorsCombo_currentIndexChanged( int );
156+
void on_mAddJoinButton_clicked();
157+
void on_mRemoveJoinButton_clicked();
158+
void on_mTableJoins_itemSelectionChanged();
159+
160+
void on_mButtonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
161+
162+
void reset();
163+
void buildSQLFromFields();
164+
void splitSQLIntoFields();
165+
166+
private:
167+
QStringList mApiList;
168+
QSet<QString> mAlreadySelectedTables;
169+
TableSelectedCallback* mTableSelectedCallback;
170+
SQLValidatorCallback* mSQLValidatorCallback;
171+
QObject* mFocusedObject;
172+
bool mAlreadyModifyingFields;
173+
bool mDistinct;
174+
QString mResetSql;
175+
QMap<QString, QString> mapTableEntryTextToName;
176+
QMap<QString, QString> mapColumnEntryTextToName;
177+
QMap<QString, QString> mapSpatialPredicateEntryTextToName;
178+
QMap<QString, QString> mapFunctionEntryTextToName;
179+
180+
void loadTableColumns( const QString& table );
181+
void functionCurrentIndexChanged( QComboBox* combo,
182+
const QMap<QString, QString>& mapEntryTextToName );
183+
void getFunctionList( const QList<Function>& list,
184+
QStringList& listApi,
185+
QStringList& listCombo,
186+
QMap<QString, QString>& mapEntryTextToName );
187+
};
188+
189+
#endif

‎src/ui/qgssqlcomposerdialogbase.ui

Lines changed: 460 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,460 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsSQLComposerDialogBase</class>
4+
<widget class="QDialog" name="QgsSQLComposerDialogBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>747</width>
10+
<height>641</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>SQL query composer</string>
15+
</property>
16+
<layout class="QVBoxLayout" name="verticalLayout_7">
17+
<item>
18+
<layout class="QVBoxLayout" name="verticalLayout_3">
19+
<item>
20+
<layout class="QHBoxLayout" name="horizontalLayout_2">
21+
<item>
22+
<widget class="QLabel" name="mSQLStatementLabel">
23+
<property name="text">
24+
<string>SQL statement</string>
25+
</property>
26+
</widget>
27+
</item>
28+
<item>
29+
<widget class="QgsCodeEditorSQL" name="mQueryEdit" native="true">
30+
<property name="toolTip">
31+
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This is the SQL query editor.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
32+
</property>
33+
</widget>
34+
</item>
35+
</layout>
36+
</item>
37+
</layout>
38+
</item>
39+
<item>
40+
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0,0">
41+
<property name="sizeConstraint">
42+
<enum>QLayout::SetDefaultConstraint</enum>
43+
</property>
44+
<item>
45+
<layout class="QFormLayout" name="formLayout">
46+
<property name="fieldGrowthPolicy">
47+
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
48+
</property>
49+
<item row="0" column="0">
50+
<widget class="QLabel" name="mColumnsLabel">
51+
<property name="text">
52+
<string>Columns</string>
53+
</property>
54+
</widget>
55+
</item>
56+
<item row="0" column="1">
57+
<widget class="QTextEdit" name="mColumnsEditor">
58+
<property name="minimumSize">
59+
<size>
60+
<width>400</width>
61+
<height>0</height>
62+
</size>
63+
</property>
64+
</widget>
65+
</item>
66+
<item row="1" column="0">
67+
<widget class="QLabel" name="mTablesLabel">
68+
<property name="text">
69+
<string>Table(s)</string>
70+
</property>
71+
</widget>
72+
</item>
73+
<item row="1" column="1">
74+
<widget class="QLineEdit" name="mTablesEditor">
75+
<property name="text">
76+
<string/>
77+
</property>
78+
<property name="frame">
79+
<bool>true</bool>
80+
</property>
81+
</widget>
82+
</item>
83+
<item row="2" column="0">
84+
<widget class="QLabel" name="mJoinsLabels">
85+
<property name="text">
86+
<string>Joins</string>
87+
</property>
88+
</widget>
89+
</item>
90+
<item row="2" column="1">
91+
<layout class="QVBoxLayout" name="verticalLayout_8">
92+
<item>
93+
<widget class="QTableWidget" name="mTableJoins">
94+
<property name="sizePolicy">
95+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
96+
<horstretch>0</horstretch>
97+
<verstretch>0</verstretch>
98+
</sizepolicy>
99+
</property>
100+
<property name="minimumSize">
101+
<size>
102+
<width>398</width>
103+
<height>0</height>
104+
</size>
105+
</property>
106+
<property name="frameShape">
107+
<enum>QFrame::NoFrame</enum>
108+
</property>
109+
<property name="rowCount">
110+
<number>0</number>
111+
</property>
112+
<attribute name="horizontalHeaderDefaultSectionSize">
113+
<number>200</number>
114+
</attribute>
115+
<attribute name="horizontalHeaderStretchLastSection">
116+
<bool>false</bool>
117+
</attribute>
118+
<attribute name="verticalHeaderVisible">
119+
<bool>false</bool>
120+
</attribute>
121+
<column>
122+
<property name="text">
123+
<string>Joint layer</string>
124+
</property>
125+
</column>
126+
<column>
127+
<property name="text">
128+
<string>ON condition</string>
129+
</property>
130+
</column>
131+
</widget>
132+
</item>
133+
<item>
134+
<layout class="QHBoxLayout" name="mAddRemoveLayout">
135+
<item>
136+
<widget class="QPushButton" name="mAddJoinButton">
137+
<property name="text">
138+
<string/>
139+
</property>
140+
<property name="icon">
141+
<iconset resource="../../images/images.qrc">
142+
<normaloff>:/images/themes/default/symbologyAdd.png</normaloff>:/images/themes/default/symbologyAdd.png</iconset>
143+
</property>
144+
</widget>
145+
</item>
146+
<item>
147+
<widget class="QPushButton" name="mRemoveJoinButton">
148+
<property name="text">
149+
<string/>
150+
</property>
151+
<property name="icon">
152+
<iconset resource="../../images/images.qrc">
153+
<normaloff>:/images/themes/default/symbologyRemove.png</normaloff>:/images/themes/default/symbologyRemove.png</iconset>
154+
</property>
155+
</widget>
156+
</item>
157+
<item>
158+
<spacer name="mAddRemoveSpacer">
159+
<property name="orientation">
160+
<enum>Qt::Horizontal</enum>
161+
</property>
162+
<property name="sizeHint" stdset="0">
163+
<size>
164+
<width>40</width>
165+
<height>1</height>
166+
</size>
167+
</property>
168+
</spacer>
169+
</item>
170+
</layout>
171+
</item>
172+
</layout>
173+
</item>
174+
<item row="3" column="0">
175+
<widget class="QLabel" name="mWhereLabel">
176+
<property name="text">
177+
<string>Where </string>
178+
</property>
179+
</widget>
180+
</item>
181+
<item row="3" column="1">
182+
<widget class="QTextEdit" name="mWhereEditor"/>
183+
</item>
184+
<item row="4" column="0">
185+
<widget class="QLabel" name="mOrderLabel">
186+
<property name="text">
187+
<string>Order by</string>
188+
</property>
189+
</widget>
190+
</item>
191+
<item row="4" column="1">
192+
<widget class="QTextEdit" name="mOrderEditor">
193+
<property name="sizePolicy">
194+
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
195+
<horstretch>0</horstretch>
196+
<verstretch>0</verstretch>
197+
</sizepolicy>
198+
</property>
199+
<property name="maximumSize">
200+
<size>
201+
<width>16777215</width>
202+
<height>25</height>
203+
</size>
204+
</property>
205+
<property name="verticalScrollBarPolicy">
206+
<enum>Qt::ScrollBarAlwaysOff</enum>
207+
</property>
208+
<property name="horizontalScrollBarPolicy">
209+
<enum>Qt::ScrollBarAlwaysOff</enum>
210+
</property>
211+
</widget>
212+
</item>
213+
</layout>
214+
</item>
215+
<item>
216+
<layout class="QVBoxLayout" name="verticalLayout_4">
217+
<item>
218+
<widget class="QToolBox" name="mToolbox">
219+
<property name="sizePolicy">
220+
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
221+
<horstretch>0</horstretch>
222+
<verstretch>0</verstretch>
223+
</sizepolicy>
224+
</property>
225+
<property name="minimumSize">
226+
<size>
227+
<width>250</width>
228+
<height>0</height>
229+
</size>
230+
</property>
231+
<property name="maximumSize">
232+
<size>
233+
<width>250</width>
234+
<height>16777215</height>
235+
</size>
236+
</property>
237+
<property name="currentIndex">
238+
<number>0</number>
239+
</property>
240+
<widget class="QWidget" name="mPageData">
241+
<property name="geometry">
242+
<rect>
243+
<x>0</x>
244+
<y>0</y>
245+
<width>250</width>
246+
<height>495</height>
247+
</rect>
248+
</property>
249+
<attribute name="label">
250+
<string>Data</string>
251+
</attribute>
252+
<layout class="QVBoxLayout" name="verticalLayout_5">
253+
<item>
254+
<widget class="QComboBox" name="mTablesCombo">
255+
<item>
256+
<property name="text">
257+
<string>Tables</string>
258+
</property>
259+
</item>
260+
</widget>
261+
</item>
262+
<item>
263+
<widget class="QComboBox" name="mColumnsCombo">
264+
<item>
265+
<property name="text">
266+
<string>Columns</string>
267+
</property>
268+
</item>
269+
</widget>
270+
</item>
271+
<item>
272+
<spacer name="verticalSpacer_4">
273+
<property name="orientation">
274+
<enum>Qt::Vertical</enum>
275+
</property>
276+
<property name="sizeHint" stdset="0">
277+
<size>
278+
<width>20</width>
279+
<height>40</height>
280+
</size>
281+
</property>
282+
</spacer>
283+
</item>
284+
<item>
285+
<widget class="Line" name="line_2">
286+
<property name="orientation">
287+
<enum>Qt::Horizontal</enum>
288+
</property>
289+
</widget>
290+
</item>
291+
<item>
292+
<widget class="QComboBox" name="mAggregatesCombo">
293+
<item>
294+
<property name="text">
295+
<string>Aggregates</string>
296+
</property>
297+
</item>
298+
</widget>
299+
</item>
300+
<item>
301+
<widget class="QComboBox" name="mFunctionsCombo">
302+
<item>
303+
<property name="text">
304+
<string>Functions</string>
305+
</property>
306+
</item>
307+
</widget>
308+
</item>
309+
<item>
310+
<widget class="QComboBox" name="mSpatialPredicatesCombo">
311+
<item>
312+
<property name="text">
313+
<string>Spatial predicates</string>
314+
</property>
315+
</item>
316+
</widget>
317+
</item>
318+
<item>
319+
<widget class="QComboBox" name="mStringFunctionsCombo">
320+
<item>
321+
<property name="text">
322+
<string>Strings functions</string>
323+
</property>
324+
</item>
325+
</widget>
326+
</item>
327+
<item>
328+
<widget class="QComboBox" name="mOperatorsCombo">
329+
<item>
330+
<property name="text">
331+
<string>Operators</string>
332+
</property>
333+
</item>
334+
</widget>
335+
</item>
336+
</layout>
337+
</widget>
338+
<widget class="QWidget" name="mPageColumnsValues">
339+
<property name="geometry">
340+
<rect>
341+
<x>0</x>
342+
<y>0</y>
343+
<width>250</width>
344+
<height>495</height>
345+
</rect>
346+
</property>
347+
<attribute name="label">
348+
<string>Columns' values</string>
349+
</attribute>
350+
<layout class="QVBoxLayout" name="verticalLayout_6">
351+
<item>
352+
<widget class="QComboBox" name="columns_2">
353+
<item>
354+
<property name="text">
355+
<string>Columns</string>
356+
</property>
357+
</item>
358+
</widget>
359+
</item>
360+
<item>
361+
<widget class="QCheckBox" name="extract">
362+
<property name="text">
363+
<string>Only 10 first values</string>
364+
</property>
365+
</widget>
366+
</item>
367+
<item>
368+
<widget class="QListView" name="values">
369+
<property name="maximumSize">
370+
<size>
371+
<width>16777215</width>
372+
<height>16777215</height>
373+
</size>
374+
</property>
375+
<property name="editTriggers">
376+
<set>QAbstractItemView::NoEditTriggers</set>
377+
</property>
378+
<property name="showDropIndicator" stdset="0">
379+
<bool>false</bool>
380+
</property>
381+
<property name="dragEnabled">
382+
<bool>false</bool>
383+
</property>
384+
<property name="dragDropMode">
385+
<enum>QAbstractItemView::NoDragDrop</enum>
386+
</property>
387+
</widget>
388+
</item>
389+
</layout>
390+
</widget>
391+
</widget>
392+
</item>
393+
</layout>
394+
</item>
395+
</layout>
396+
</item>
397+
<item>
398+
<layout class="QHBoxLayout" name="horizontalLayout_7">
399+
<item>
400+
<widget class="QDialogButtonBox" name="mButtonBox">
401+
<property name="orientation">
402+
<enum>Qt::Horizontal</enum>
403+
</property>
404+
<property name="standardButtons">
405+
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
406+
</property>
407+
<property name="centerButtons">
408+
<bool>false</bool>
409+
</property>
410+
</widget>
411+
</item>
412+
</layout>
413+
</item>
414+
</layout>
415+
</widget>
416+
<customwidgets>
417+
<customwidget>
418+
<class>QgsCodeEditorSQL</class>
419+
<extends>QWidget</extends>
420+
<header>qgscodeeditorsql.h</header>
421+
</customwidget>
422+
</customwidgets>
423+
<resources>
424+
<include location="../../images/images.qrc"/>
425+
</resources>
426+
<connections>
427+
<connection>
428+
<sender>mButtonBox</sender>
429+
<signal>rejected()</signal>
430+
<receiver>QgsSQLComposerDialogBase</receiver>
431+
<slot>reject()</slot>
432+
<hints>
433+
<hint type="sourcelabel">
434+
<x>633</x>
435+
<y>440</y>
436+
</hint>
437+
<hint type="destinationlabel">
438+
<x>286</x>
439+
<y>274</y>
440+
</hint>
441+
</hints>
442+
</connection>
443+
<connection>
444+
<sender>mButtonBox</sender>
445+
<signal>accepted()</signal>
446+
<receiver>QgsSQLComposerDialogBase</receiver>
447+
<slot>accept()</slot>
448+
<hints>
449+
<hint type="sourcelabel">
450+
<x>516</x>
451+
<y>433</y>
452+
</hint>
453+
<hint type="destinationlabel">
454+
<x>306</x>
455+
<y>423</y>
456+
</hint>
457+
</hints>
458+
</connection>
459+
</connections>
460+
</ui>

‎tests/src/gui/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,5 @@ ADD_QGIS_TEST(qgsguitest testqgsgui.cpp)
137137
ADD_QGIS_TEST(rubberbandtest testqgsrubberband.cpp)
138138
ADD_QGIS_TEST(scalecombobox testqgsscalecombobox.cpp)
139139
ADD_QGIS_TEST(spinbox testqgsspinbox.cpp)
140-
140+
ADD_QGIS_TEST(sqlcomposerdialog testqgssqlcomposerdialog.cpp)
141141

Lines changed: 468 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,468 @@
1+
/***************************************************************************
2+
testqgssqlcomposerdialog.cpp
3+
--------------------------------------
4+
Date : April 2016
5+
Copyright : (C) 2016 Even Rouault
6+
Email : even.rouault at spatialys.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+
#include <QtTest/QtTest>
18+
19+
#include <qgslogger.h>
20+
#include <qgssqlcomposerdialog.h>
21+
22+
class TestQgsSQLComposerDialog: public QObject
23+
{
24+
Q_OBJECT
25+
private slots:
26+
void testReciprocalEditorsUpdate();
27+
void testSelectTable();
28+
void testSelectColumn();
29+
void testSelectFunction();
30+
void testSelectSpatialPredicate();
31+
void testSelectOperator();
32+
void testJoins();
33+
private:
34+
bool runTest();
35+
};
36+
37+
bool TestQgsSQLComposerDialog::runTest()
38+
{
39+
// Those tests are fragile because may depend on focus works without
40+
// widget being displayed. Or shortcuts to go to end of line
41+
//const char* travis = getenv( "TRAVIS_OS_NAME" );
42+
//if ( travis && strcmp( travis, "osx" ) == 0 )
43+
//{
44+
// QgsDebugMsg( "Test disabled" );
45+
// return false;
46+
//}
47+
return true;
48+
}
49+
50+
static QWidget* getQueryEdit( QgsSQLComposerDialog& d )
51+
{
52+
QWidget* widget = d.findChild<QWidget*>( "mQueryEdit" );
53+
Q_ASSERT( widget );
54+
return widget;
55+
}
56+
57+
static QWidget* getColumnsEditor( QgsSQLComposerDialog& d )
58+
{
59+
QWidget* widget = d.findChild<QWidget*>( "mColumnsEditor" );
60+
Q_ASSERT( widget );
61+
return widget;
62+
}
63+
64+
static QWidget* getTablesEditor( QgsSQLComposerDialog& d )
65+
{
66+
QWidget* widget = d.findChild<QWidget*>( "mTablesEditor" );
67+
Q_ASSERT( widget );
68+
return widget;
69+
}
70+
71+
static QWidget* getWhereEditor( QgsSQLComposerDialog& d )
72+
{
73+
QWidget* widget = d.findChild<QWidget*>( "mWhereEditor" );
74+
Q_ASSERT( widget );
75+
return widget;
76+
}
77+
78+
static QWidget* getOrderEditor( QgsSQLComposerDialog& d )
79+
{
80+
QWidget* widget = d.findChild<QWidget*>( "mOrderEditor" );
81+
Q_ASSERT( widget );
82+
return widget;
83+
}
84+
85+
static QComboBox* getTablesCombo( QgsSQLComposerDialog& d )
86+
{
87+
QComboBox* widget = d.findChild<QComboBox*>( "mTablesCombo" );
88+
Q_ASSERT( widget );
89+
return widget;
90+
}
91+
92+
static QComboBox* getColumnsCombo( QgsSQLComposerDialog& d )
93+
{
94+
QComboBox* widget = d.findChild<QComboBox*>( "mColumnsCombo" );
95+
Q_ASSERT( widget );
96+
return widget;
97+
}
98+
99+
static QComboBox* getFunctionsCombo( QgsSQLComposerDialog& d )
100+
{
101+
QComboBox* widget = d.findChild<QComboBox*>( "mFunctionsCombo" );
102+
Q_ASSERT( widget );
103+
return widget;
104+
}
105+
106+
static QComboBox* getSpatialPredicatesCombo( QgsSQLComposerDialog& d )
107+
{
108+
QComboBox* widget = d.findChild<QComboBox*>( "mSpatialPredicatesCombo" );
109+
Q_ASSERT( widget );
110+
return widget;
111+
}
112+
113+
static QComboBox* getOperatorsCombo( QgsSQLComposerDialog& d )
114+
{
115+
QComboBox* widget = d.findChild<QComboBox*>( "mOperatorsCombo" );
116+
Q_ASSERT( widget );
117+
return widget;
118+
}
119+
120+
static QWidget* getResetButton( QgsSQLComposerDialog& d )
121+
{
122+
QDialogButtonBox* mButtonBox = d.findChild<QDialogButtonBox*>( "mButtonBox" );
123+
Q_ASSERT( mButtonBox );
124+
QPushButton* button = mButtonBox->button( QDialogButtonBox::Reset );
125+
Q_ASSERT( button );
126+
return button;
127+
}
128+
129+
static QTableWidget* getTableJoins( QgsSQLComposerDialog& d )
130+
{
131+
QTableWidget* widget = d.findChild<QTableWidget*>( "mTableJoins" );
132+
Q_ASSERT( widget );
133+
return widget;
134+
}
135+
136+
static QWidget* getAddJoinButton( QgsSQLComposerDialog& d )
137+
{
138+
QWidget* widget = d.findChild<QWidget*>( "mAddJoinButton" );
139+
Q_ASSERT( widget );
140+
return widget;
141+
}
142+
143+
static QWidget* getRemoveJoinButton( QgsSQLComposerDialog& d )
144+
{
145+
QWidget* widget = d.findChild<QWidget*>( "mRemoveJoinButton" );
146+
Q_ASSERT( widget );
147+
return widget;
148+
}
149+
150+
static void gotoEndOfLine( QWidget* w )
151+
{
152+
#ifdef Q_OS_MAC
153+
QTest::keyPress( w, Qt::Key_Right, Qt::ControlModifier );
154+
#else
155+
QTest::keyPress( w, Qt::Key_End );
156+
#endif
157+
}
158+
159+
void TestQgsSQLComposerDialog::testReciprocalEditorsUpdate()
160+
{
161+
if ( !runTest() )
162+
return;
163+
QgsSQLComposerDialog d;
164+
QString oriSql( "SELECT a_column FROM my_table JOIN join_table ON cond WHERE where_expr ORDER BY column DESC" );
165+
d.setSql( oriSql );
166+
QCOMPARE( d.sql(), oriSql );
167+
168+
gotoEndOfLine( getColumnsEditor( d ) );
169+
QTest::keyClicks( getColumnsEditor( d ), ", another_column" );
170+
gotoEndOfLine( getTablesEditor( d ) );
171+
QTest::keyClicks( getTablesEditor( d ), ", another_from_table" );
172+
gotoEndOfLine( getWhereEditor( d ) );
173+
QTest::keyClicks( getWhereEditor( d ), " AND another_cond" );
174+
gotoEndOfLine( getOrderEditor( d ) );
175+
QTest::keyClicks( getOrderEditor( d ), ", another_column_asc" );
176+
QCOMPARE( d.sql(), QString( "SELECT a_column, another_column FROM my_table, another_from_table JOIN join_table ON cond WHERE where_expr AND another_cond ORDER BY column DESC, another_column_asc" ) );
177+
178+
QTest::mouseClick( getResetButton( d ), Qt::LeftButton );
179+
QCOMPARE( d.sql(), oriSql );
180+
}
181+
182+
static void setFocusIn( QWidget* widget )
183+
{
184+
QFocusEvent focusInEvent( QEvent::FocusIn );
185+
QApplication::sendEvent( widget, &focusInEvent );
186+
}
187+
188+
void TestQgsSQLComposerDialog::testSelectTable()
189+
{
190+
if ( !runTest() )
191+
return;
192+
QgsSQLComposerDialog d;
193+
d.addTableNames( QStringList() << "my_table" );
194+
d.addTableNames( QList<QgsSQLComposerDialog::PairNameTitle>() << QgsSQLComposerDialog::PairNameTitle( "another_table", "title" ) );
195+
196+
QCOMPARE( getTablesCombo( d )->itemText( 1 ), QString( "my_table" ) );
197+
QCOMPARE( getTablesCombo( d )->itemText( 2 ), QString( "another_table (title)" ) );
198+
199+
d.setSql( "SELECT * FROM " );
200+
gotoEndOfLine( getQueryEdit( d ) );
201+
202+
// Set focus in SQL zone
203+
setFocusIn( getQueryEdit( d ) );
204+
205+
// Select dummy entry in combo
206+
getTablesCombo( d )->setCurrentIndex( 0 );
207+
208+
// Select first entry in combo
209+
getTablesCombo( d )->setCurrentIndex( 1 );
210+
211+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table" ) );
212+
213+
// Set focus in table editor
214+
setFocusIn( getTablesEditor( d ) );
215+
216+
// Select second entry in combo
217+
getTablesCombo( d )->setCurrentIndex( 2 );
218+
219+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table, another_table" ) );
220+
}
221+
222+
void TestQgsSQLComposerDialog::testSelectColumn()
223+
{
224+
if ( !runTest() )
225+
return;
226+
QgsSQLComposerDialog d;
227+
d.addColumnNames( QList<QgsSQLComposerDialog::PairNameType>() <<
228+
QgsSQLComposerDialog::PairNameType( "a", "" ) <<
229+
QgsSQLComposerDialog::PairNameType( "b", "type" ), "my_table" );
230+
231+
QCOMPARE( getColumnsCombo( d )->itemText( 1 ), QString( "a" ) );
232+
QCOMPARE( getColumnsCombo( d )->itemText( 2 ), QString( "b (type)" ) );
233+
234+
d.setSql( "SELECT " );
235+
gotoEndOfLine( getQueryEdit( d ) );
236+
237+
// Set focus in SQL zone
238+
setFocusIn( getQueryEdit( d ) );
239+
240+
// Select dummy entry in combo
241+
getColumnsCombo( d )->setCurrentIndex( 0 );
242+
243+
// Select first entry in combo
244+
getColumnsCombo( d )->setCurrentIndex( 1 );
245+
246+
QCOMPARE( d.sql(), QString( "SELECT a" ) );
247+
248+
gotoEndOfLine( getQueryEdit( d ) );
249+
QTest::keyClicks( getQueryEdit( d ), " FROM my_table" );
250+
251+
QCOMPARE( d.sql(), QString( "SELECT a FROM my_table" ) );
252+
253+
// Set focus in column editor
254+
setFocusIn( getColumnsEditor( d ) );
255+
QTest::keyPress( getColumnsEditor( d ), Qt::Key_End );
256+
257+
// Select second entry in combo
258+
getColumnsCombo( d )->setCurrentIndex( 2 );
259+
260+
QCOMPARE( d.sql(), QString( "SELECT a,\nb FROM my_table" ) );
261+
262+
// Set focus in where editor
263+
setFocusIn( getWhereEditor( d ) );
264+
getColumnsCombo( d )->setCurrentIndex( 1 );
265+
266+
QCOMPARE( d.sql(), QString( "SELECT a,\nb FROM my_table WHERE a" ) );
267+
268+
// Set focus in order editor
269+
setFocusIn( getOrderEditor( d ) );
270+
getColumnsCombo( d )->setCurrentIndex( 2 );
271+
272+
QCOMPARE( d.sql(), QString( "SELECT a,\nb FROM my_table WHERE a ORDER BY b" ) );
273+
}
274+
275+
void TestQgsSQLComposerDialog::testSelectFunction()
276+
{
277+
if ( !runTest() )
278+
return;
279+
QgsSQLComposerDialog d;
280+
281+
QList<QgsSQLComposerDialog::Function> functions;
282+
{
283+
QgsSQLComposerDialog::Function f;
284+
f.name = "first_func";
285+
functions << f;
286+
}
287+
{
288+
QgsSQLComposerDialog::Function f;
289+
f.name = "second_func";
290+
f.returnType = "xs:int";
291+
functions << f;
292+
}
293+
{
294+
QgsSQLComposerDialog::Function f;
295+
f.name = "third_func";
296+
f.minArgs = 1;
297+
f.maxArgs = 1;
298+
functions << f;
299+
}
300+
{
301+
QgsSQLComposerDialog::Function f;
302+
f.name = "fourth_func";
303+
f.minArgs = 1;
304+
f.maxArgs = 2;
305+
functions << f;
306+
}
307+
{
308+
QgsSQLComposerDialog::Function f;
309+
f.name = "fifth_func";
310+
f.minArgs = 1;
311+
functions << f;
312+
}
313+
{
314+
QgsSQLComposerDialog::Function f;
315+
f.name = "sixth_func";
316+
f.argumentList << QgsSQLComposerDialog::Argument( "arg1", "" );
317+
f.argumentList << QgsSQLComposerDialog::Argument( "arg2", "xs:double" );
318+
f.argumentList << QgsSQLComposerDialog::Argument( "arg3", "gml:AbstractGeometryType" );
319+
f.argumentList << QgsSQLComposerDialog::Argument( "number", "xs:int" );
320+
functions << f;
321+
}
322+
{
323+
QgsSQLComposerDialog::Function f;
324+
f.name = "seventh_func";
325+
f.argumentList << QgsSQLComposerDialog::Argument( "arg1", "" );
326+
f.argumentList << QgsSQLComposerDialog::Argument( "arg2", "xs:double" );
327+
f.minArgs = 1;
328+
functions << f;
329+
}
330+
d.addFunctions( functions );
331+
332+
QCOMPARE( getFunctionsCombo( d )->itemText( 1 ), QString( "first_func()" ) );
333+
QCOMPARE( getFunctionsCombo( d )->itemText( 2 ), QString( "second_func(): int" ) );
334+
QCOMPARE( getFunctionsCombo( d )->itemText( 3 ), QString( "third_func(1 argument)" ) );
335+
QCOMPARE( getFunctionsCombo( d )->itemText( 4 ), QString( "fourth_func(1 to 2 arguments)" ) );
336+
QCOMPARE( getFunctionsCombo( d )->itemText( 5 ), QString( "fifth_func(1 argument or more)" ) );
337+
QCOMPARE( getFunctionsCombo( d )->itemText( 6 ), QString( "sixth_func(arg1, arg2: double, arg3: geometry, int)" ) );
338+
QCOMPARE( getFunctionsCombo( d )->itemText( 7 ), QString( "seventh_func(arg1[, arg2: double])" ) );
339+
340+
d.setSql( "SELECT * FROM my_table" );
341+
342+
// Set focus in where editor
343+
setFocusIn( getWhereEditor( d ) );
344+
345+
getFunctionsCombo( d )->setCurrentIndex( 0 );
346+
getFunctionsCombo( d )->setCurrentIndex( 1 );
347+
348+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE first_func(" ) );
349+
350+
// Set focus in SQL zone
351+
d.setSql( "SELECT * FROM my_table WHERE " );
352+
setFocusIn( getQueryEdit( d ) );
353+
gotoEndOfLine( getQueryEdit( d ) );
354+
getFunctionsCombo( d )->setCurrentIndex( 0 );
355+
getFunctionsCombo( d )->setCurrentIndex( 1 );
356+
357+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE first_func(" ) );
358+
}
359+
360+
void TestQgsSQLComposerDialog::testSelectSpatialPredicate()
361+
{
362+
if ( !runTest() )
363+
return;
364+
QgsSQLComposerDialog d;
365+
d.addSpatialPredicates( QList<QgsSQLComposerDialog::Function>() << QgsSQLComposerDialog::Function( "predicate", 2 ) );
366+
367+
d.setSql( "SELECT * FROM my_table" );
368+
369+
// Set focus in where editor
370+
setFocusIn( getWhereEditor( d ) );
371+
getSpatialPredicatesCombo( d )->setCurrentIndex( 0 );
372+
getSpatialPredicatesCombo( d )->setCurrentIndex( 1 );
373+
374+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE predicate(" ) );
375+
376+
// Set focus in SQL zone
377+
d.setSql( "SELECT * FROM my_table WHERE " );
378+
setFocusIn( getQueryEdit( d ) );
379+
gotoEndOfLine( getQueryEdit( d ) );
380+
getSpatialPredicatesCombo( d )->setCurrentIndex( 0 );
381+
getSpatialPredicatesCombo( d )->setCurrentIndex( 1 );
382+
383+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE predicate(" ) );
384+
}
385+
386+
void TestQgsSQLComposerDialog::testSelectOperator()
387+
{
388+
if ( !runTest() )
389+
return;
390+
QgsSQLComposerDialog d;
391+
392+
d.setSql( "SELECT * FROM my_table" );
393+
394+
// Set focus in where editor
395+
setFocusIn( getWhereEditor( d ) );
396+
getOperatorsCombo( d )->setCurrentIndex( 0 );
397+
getOperatorsCombo( d )->setCurrentIndex( 1 );
398+
399+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE AND" ) );
400+
401+
// Set focus in SQL zone
402+
d.setSql( "SELECT * FROM my_table WHERE " );
403+
setFocusIn( getQueryEdit( d ) );
404+
gotoEndOfLine( getQueryEdit( d ) );
405+
getOperatorsCombo( d )->setCurrentIndex( 0 );
406+
getOperatorsCombo( d )->setCurrentIndex( 1 );
407+
408+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table WHERE AND" ) );
409+
}
410+
411+
void TestQgsSQLComposerDialog::testJoins()
412+
{
413+
if ( !runTest() )
414+
return;
415+
QgsSQLComposerDialog d;
416+
d.setSql( "SELECT * FROM my_table" );
417+
d.setSupportMultipleTables( true );
418+
419+
QTableWidget* table = getTableJoins( d );
420+
QCOMPARE( table->rowCount(), 1 );
421+
QCOMPARE( table->item( 0, 0 ) != nullptr, true );
422+
table->item( 0, 0 )->setText( "join_table" );
423+
table->item( 0, 1 )->setText( "join_expr" );
424+
425+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table JOIN join_table ON join_expr" ) );
426+
427+
QTest::mouseClick( getAddJoinButton( d ), Qt::LeftButton );
428+
QCOMPARE( table->rowCount(), 2 );
429+
table->item( 1, 0 )->setText( "join2_table" );
430+
table->item( 1, 1 )->setText( "join2_expr" );
431+
432+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table JOIN join_table ON join_expr JOIN join2_table ON join2_expr" ) );
433+
434+
table->setCurrentCell( 0, 0 );
435+
QTest::mouseClick( getAddJoinButton( d ), Qt::LeftButton );
436+
QCOMPARE( table->rowCount(), 3 );
437+
table->item( 1, 0 )->setText( "join15_table" );
438+
table->item( 1, 1 )->setText( "join15_expr" );
439+
440+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table JOIN join_table ON join_expr JOIN join15_table ON join15_expr JOIN join2_table ON join2_expr" ) );
441+
442+
table->setCurrentCell( 1, 0 );
443+
QTest::mouseClick( getRemoveJoinButton( d ), Qt::LeftButton );
444+
QCOMPARE( table->rowCount(), 2 );
445+
446+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table JOIN join_table ON join_expr JOIN join2_table ON join2_expr" ) );
447+
448+
QTest::mouseClick( getRemoveJoinButton( d ), Qt::LeftButton );
449+
QCOMPARE( table->rowCount(), 1 );
450+
451+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table JOIN join_table ON join_expr" ) );
452+
453+
QTest::mouseClick( getRemoveJoinButton( d ), Qt::LeftButton );
454+
QCOMPARE( table->rowCount(), 1 );
455+
456+
table->setCurrentCell( 0, 0 );
457+
QTest::mouseClick( getRemoveJoinButton( d ), Qt::LeftButton );
458+
QCOMPARE( table->rowCount(), 0 );
459+
460+
QCOMPARE( d.sql(), QString( "SELECT * FROM my_table" ) );
461+
462+
QTest::mouseClick( getAddJoinButton( d ), Qt::LeftButton );
463+
QCOMPARE( table->rowCount(), 1 );
464+
QCOMPARE( table->item( 0, 0 ) != nullptr, true );
465+
}
466+
467+
QTEST_MAIN( TestQgsSQLComposerDialog )
468+
#include "testqgssqlcomposerdialog.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.