Skip to content

Commit 78d02b7

Browse files
authoredApr 10, 2017
Merge pull request #4299 from alexbruy/checkable-combobox
[FEATURE] New custom widget checkable combobox
2 parents fc2581c + 0803b56 commit 78d02b7

11 files changed

+875
-1
lines changed
 

‎python/gui/gui.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
%Include qgscodeeditorcss.sip
3838
%End
3939
%Include qgscharacterselectdialog.sip
40+
%Include qgscheckablecombobox.sip
4041
%Include qgscolorbrewercolorrampdialog.sip
4142
%Include qgscolorbutton.sip
4243
%Include qgscolorrampbutton.sip

‎python/gui/qgscheckablecombobox.sip

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgscheckablecombobox.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
13+
14+
15+
16+
class QgsCheckableComboBox : QComboBox
17+
{
18+
19+
%TypeHeaderCode
20+
#include "qgscheckablecombobox.h"
21+
%End
22+
public:
23+
24+
QgsCheckableComboBox( QWidget *parent /TransferThis/ = 0 );
25+
%Docstring
26+
Constructor for QgsCheckableComboBox.
27+
%End
28+
29+
QString separator() const;
30+
%Docstring
31+
Returns separator used to separate items in the display text.
32+
\see setSeparator()
33+
:rtype: str
34+
%End
35+
36+
void setSeparator( const QString &separator );
37+
%Docstring
38+
Set separator used to separate items in the display text.
39+
\param separator separator to use
40+
\see separator()
41+
%End
42+
43+
QString defaultText() const;
44+
%Docstring
45+
Returns default text which will be displayed in the widget
46+
when no items selected.
47+
\see setDefaultText()
48+
:rtype: str
49+
%End
50+
51+
void setDefaultText( const QString &text );
52+
%Docstring
53+
Set default text which will be displayed in the widget when
54+
no items selected.
55+
\param text default text
56+
\see defaultText()
57+
%End
58+
59+
QStringList checkedItems() const;
60+
%Docstring
61+
Returns currently checked items.
62+
\see setCheckedItems()
63+
:rtype: list of str
64+
%End
65+
66+
Qt::CheckState itemCheckState( int index ) const;
67+
%Docstring
68+
Returns the checked state of the item identified by index
69+
\param index item index
70+
\see setItemCheckState()
71+
\see toggleItemCheckState()
72+
:rtype: Qt.CheckState
73+
%End
74+
75+
void setItemCheckState( int index, Qt::CheckState state );
76+
%Docstring
77+
Sets the item check state to state
78+
\param index item index
79+
\param state check state
80+
\see itemCheckState()
81+
\see toggleItemCheckState()
82+
%End
83+
84+
void toggleItemCheckState( int index );
85+
%Docstring
86+
Toggles the item check state
87+
\param index item index
88+
\see itemCheckState()
89+
\see setItemCheckState()
90+
%End
91+
92+
virtual void hidePopup();
93+
%Docstring
94+
Hides the list of items in the combobox if it is currently
95+
visible and resets the internal state.
96+
%End
97+
98+
virtual bool eventFilter( QObject *object, QEvent *event );
99+
%Docstring
100+
Filters events to enable context menu
101+
:rtype: bool
102+
%End
103+
104+
signals:
105+
106+
void checkedItemsChanged( const QStringList &items );
107+
%Docstring
108+
This signal is emitted whenever the checked items list changed.
109+
%End
110+
111+
public slots:
112+
113+
void setCheckedItems( const QStringList &items );
114+
%Docstring
115+
Set items which should be checked/selected.
116+
\param items items to select
117+
\see checkedItems()
118+
%End
119+
120+
protected:
121+
122+
virtual void resizeEvent( QResizeEvent *event );
123+
%Docstring
124+
Handler for widget resizing
125+
%End
126+
127+
protected slots:
128+
129+
void showContextMenu( const QPoint &pos );
130+
%Docstring
131+
Display context menu which allows to select/deselect
132+
all items at once.
133+
%End
134+
135+
void selectAllOptions();
136+
%Docstring
137+
Selects all items.
138+
%End
139+
140+
void deselectAllOptions();
141+
%Docstring
142+
Removes selection from all items.
143+
%End
144+
145+
};
146+
147+
/************************************************************************
148+
* This file has been generated automatically from *
149+
* *
150+
* src/gui/qgscheckablecombobox.h *
151+
* *
152+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
153+
************************************************************************/

‎src/customwidgets/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SET (QGIS_CUSTOMWIDGETS_SRCS
1414
qgiscustomwidgets.cpp
1515
qgscollapsiblegroupboxplugin.cpp
1616
qgscolorbuttonplugin.cpp
17+
qgscheckablecomboboxplugin.cpp
1718
qgsdatetimeeditplugin.cpp
1819
qgsdockwidgetplugin.cpp
1920
qgsdoublespinboxplugin.cpp
@@ -40,6 +41,7 @@ SET (QGIS_CUSTOMWIDGETS_MOC_HDRS
4041
qgiscustomwidgets.h
4142
qgscollapsiblegroupboxplugin.h
4243
qgscolorbuttonplugin.h
44+
qgscheckablecomboboxplugin.h
4345
qgsdatetimeeditplugin.h
4446
qgsdockwidgetplugin.h
4547
qgsdoublespinboxplugin.h
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/***************************************************************************
2+
qgscheckablecomboboxplugin.cpp
3+
--------------------------------------
4+
Date : March 22, 2017
5+
Copyright : (C) 2017 Alexander Bruy
6+
Email : alexander dot bruy 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+
#include "qgiscustomwidgets.h"
17+
#include "qgscheckablecombobox.h"
18+
#include "qgscheckablecomboboxplugin.h"
19+
20+
21+
QgsCheckableComboBoxPlugin::QgsCheckableComboBoxPlugin( QObject *parent )
22+
: QObject( parent )
23+
, mInitialized( false )
24+
{
25+
}
26+
27+
28+
QString QgsCheckableComboBoxPlugin::name() const
29+
{
30+
return "QgsCheckableComboBox";
31+
}
32+
33+
QString QgsCheckableComboBoxPlugin::group() const
34+
{
35+
return QgisCustomWidgets::groupName();
36+
}
37+
38+
QString QgsCheckableComboBoxPlugin::includeFile() const
39+
{
40+
return "qgscheckablecombobox.h";
41+
}
42+
43+
QIcon QgsCheckableComboBoxPlugin::icon() const
44+
{
45+
return QIcon( ":/images/icons/qgis-icon-60x60.png" );
46+
}
47+
48+
bool QgsCheckableComboBoxPlugin::isContainer() const
49+
{
50+
return false;
51+
}
52+
53+
QWidget *QgsCheckableComboBoxPlugin::createWidget( QWidget *parent )
54+
{
55+
return new QgsCheckableComboBox( parent );
56+
}
57+
58+
bool QgsCheckableComboBoxPlugin::isInitialized() const
59+
{
60+
return mInitialized;
61+
}
62+
63+
void QgsCheckableComboBoxPlugin::initialize( QDesignerFormEditorInterface *core )
64+
{
65+
Q_UNUSED( core );
66+
if ( mInitialized )
67+
return;
68+
mInitialized = true;
69+
}
70+
71+
72+
QString QgsCheckableComboBoxPlugin::toolTip() const
73+
{
74+
return "";
75+
}
76+
77+
QString QgsCheckableComboBoxPlugin::whatsThis() const
78+
{
79+
return "";
80+
}
81+
82+
QString QgsCheckableComboBoxPlugin::domXml() const
83+
{
84+
return QString( "<ui language=\"c++\">\n"
85+
" <widget class=\"%1\" name=\"mComboBox\">\n"
86+
" <property name=\"geometry\">\n"
87+
" <rect>\n"
88+
" <x>0</x>\n"
89+
" <y>0</y>\n"
90+
" <width>160</width>\n"
91+
" <height>27</height>\n"
92+
" </rect>\n"
93+
" </property>\n"
94+
" </widget>\n"
95+
"</ui>\n" )
96+
.arg( name() );
97+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/***************************************************************************
2+
qgscheckablecomboboxplugin.h
3+
--------------------------------------
4+
Date : March 22, 2017
5+
Copyright : (C) 2017 Alexander Bruy
6+
Email : alexander dot bruy 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+
#ifndef QGSCHECKABLECOMBOBOXPLUGIN_H
17+
#define QGSCHECKABLECOMBOBOXPLUGIN_H
18+
19+
20+
#include <QtGlobal>
21+
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
22+
#include <QtUiPlugin/QDesignerExportWidget>
23+
#include "qgis_customwidgets.h"
24+
25+
26+
class CUSTOMWIDGETS_EXPORT QgsCheckableComboBoxPlugin : public QObject, public QDesignerCustomWidgetInterface
27+
{
28+
Q_OBJECT
29+
Q_INTERFACES( QDesignerCustomWidgetInterface )
30+
31+
public:
32+
explicit QgsCheckableComboBoxPlugin( QObject *parent = 0 );
33+
34+
private:
35+
bool mInitialized = false;
36+
37+
// QDesignerCustomWidgetInterface interface
38+
public:
39+
QString name() const override;
40+
QString group() const override;
41+
QString includeFile() const override;
42+
QIcon icon() const override;
43+
bool isContainer() const override;
44+
QWidget *createWidget( QWidget *parent ) override;
45+
bool isInitialized() const override;
46+
void initialize( QDesignerFormEditorInterface *core ) override;
47+
QString toolTip() const override;
48+
QString whatsThis() const override;
49+
QString domXml() const override;
50+
};
51+
#endif // QGSCHECKABLECOMBOBOXPLUGIN_H

‎src/customwidgets/qgspasswordlineeditplugin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/***************************************************************************
2-
qgsfilterlineeditplugin.cpp
2+
qgspasswordlineeditplugin.cpp
33
--------------------------------------
44
Date : March 13, 2017
55
Copyright : (C) 2017 Alexander Bruy

‎src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ SET(QGIS_GUI_SRCS
172172
qgsbrowsertreeview.cpp
173173
qgsbusyindicatordialog.cpp
174174
qgscharacterselectdialog.cpp
175+
qgscheckablecombobox.cpp
175176
qgscodeeditor.cpp
176177
qgscodeeditorcss.cpp
177178
qgscodeeditorhtml.cpp
@@ -329,6 +330,7 @@ SET(QGIS_GUI_MOC_HDRS
329330
qgsbrowsertreeview.h
330331
qgsbusyindicatordialog.h
331332
qgscharacterselectdialog.h
333+
qgscheckablecombobox.h
332334
qgscodeeditor.h
333335
qgscodeeditorcss.h
334336
qgscodeeditorhtml.h

‎src/gui/qgscheckablecombobox.cpp

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/***************************************************************************
2+
qgscheckablecombobox.cpp
3+
------------------------
4+
begin : March 21, 2017
5+
copyright : (C) 2017 by Alexander Bruy
6+
email : alexander dot bruy at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgscheckablecombobox.h"
19+
20+
#include <QEvent>
21+
#include <QMouseEvent>
22+
#include <QLineEdit>
23+
#include <QPoint>
24+
#include <QAbstractItemView>
25+
26+
27+
QgsCheckableItemModel::QgsCheckableItemModel( QObject *parent )
28+
: QStandardItemModel( 0, 1, parent )
29+
{
30+
}
31+
32+
Qt::ItemFlags QgsCheckableItemModel::flags( const QModelIndex &index ) const
33+
{
34+
return QStandardItemModel::flags( index ) | Qt::ItemIsUserCheckable;
35+
}
36+
37+
QVariant QgsCheckableItemModel::data( const QModelIndex &index, int role ) const
38+
{
39+
QVariant value = QStandardItemModel::data( index, role );
40+
41+
if ( index.isValid() && role == Qt::CheckStateRole && !value.isValid() )
42+
{
43+
value = Qt::Unchecked;
44+
}
45+
46+
return value;
47+
}
48+
49+
bool QgsCheckableItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
50+
{
51+
bool ok = QStandardItemModel::setData( index, value, role );
52+
53+
if ( ok && role == Qt::CheckStateRole )
54+
{
55+
emit dataChanged( index, index );
56+
emit itemCheckStateChanged();
57+
}
58+
59+
return ok;
60+
}
61+
62+
63+
QgsCheckBoxDelegate::QgsCheckBoxDelegate( QObject *parent )
64+
: QStyledItemDelegate( parent )
65+
{
66+
}
67+
68+
void QgsCheckBoxDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
69+
{
70+
QStyleOptionViewItem &nonConstOpt = const_cast<QStyleOptionViewItem &>( option );
71+
nonConstOpt.showDecorationSelected = false;
72+
QStyledItemDelegate::paint( painter, nonConstOpt, index );
73+
}
74+
75+
76+
QgsCheckableComboBox::QgsCheckableComboBox( QWidget *parent )
77+
: QComboBox( parent )
78+
, mSeparator( ", " )
79+
{
80+
setModel( new QgsCheckableItemModel( this ) );
81+
setItemDelegate( new QgsCheckBoxDelegate( this ) );
82+
83+
QLineEdit *lineEdit = new QLineEdit( this );
84+
lineEdit->setReadOnly( true );
85+
setLineEdit( lineEdit );
86+
87+
mContextMenu = new QMenu( this );
88+
mSelectAllAction = mContextMenu->addAction( tr( "Select all" ) );
89+
mDeselectAllAction = mContextMenu->addAction( tr( "Deselect all" ) );
90+
connect( mSelectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::selectAllOptions );
91+
connect( mDeselectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::deselectAllOptions );
92+
93+
view()->viewport()->installEventFilter( this );
94+
view()->setContextMenuPolicy( Qt::CustomContextMenu );
95+
connect( view(), &QAbstractItemView::customContextMenuRequested, this, &QgsCheckableComboBox::showContextMenu );
96+
97+
QgsCheckableItemModel *myModel = qobject_cast<QgsCheckableItemModel *>( model() );
98+
connect( myModel, &QgsCheckableItemModel::itemCheckStateChanged, this, &QgsCheckableComboBox::updateCheckedItems );
99+
connect( model(), &QStandardItemModel::rowsInserted, this, [ = ]( const QModelIndex &, int, int ) { updateCheckedItems(); } );
100+
connect( model(), &QStandardItemModel::rowsRemoved, this, [ = ]( const QModelIndex &, int, int ) { updateCheckedItems(); } );
101+
102+
connect( this, static_cast< void ( QComboBox::* )( int ) >( &QComboBox::activated ), this, &QgsCheckableComboBox::toggleItemCheckState );
103+
}
104+
105+
QString QgsCheckableComboBox::separator() const
106+
{
107+
return mSeparator;
108+
}
109+
110+
void QgsCheckableComboBox::setSeparator( const QString &separator )
111+
{
112+
if ( mSeparator != separator )
113+
{
114+
mSeparator = separator;
115+
updateDisplayText();
116+
}
117+
}
118+
119+
QString QgsCheckableComboBox::defaultText() const
120+
{
121+
return mDefaultText;
122+
}
123+
124+
void QgsCheckableComboBox::setDefaultText( const QString &text )
125+
{
126+
if ( mDefaultText != text )
127+
{
128+
mDefaultText = text;
129+
updateDisplayText();
130+
}
131+
}
132+
133+
QStringList QgsCheckableComboBox::checkedItems() const
134+
{
135+
QStringList items;
136+
137+
if ( model() )
138+
{
139+
QModelIndex index = model()->index( 0, modelColumn(), rootModelIndex() );
140+
QModelIndexList indexes = model()->match( index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly );
141+
Q_FOREACH ( const QModelIndex &index, indexes )
142+
{
143+
items += index.data().toString();
144+
}
145+
}
146+
147+
return items;
148+
}
149+
150+
Qt::CheckState QgsCheckableComboBox::itemCheckState( int index ) const
151+
{
152+
return static_cast<Qt::CheckState>( itemData( index, Qt::CheckStateRole ).toInt() );
153+
}
154+
155+
void QgsCheckableComboBox::setItemCheckState( int index, Qt::CheckState state )
156+
{
157+
setItemData( index, state, Qt::CheckStateRole );
158+
}
159+
160+
void QgsCheckableComboBox::toggleItemCheckState( int index )
161+
{
162+
QVariant value = itemData( index, Qt::CheckStateRole );
163+
if ( value.isValid() )
164+
{
165+
Qt::CheckState state = static_cast<Qt::CheckState>( value.toInt() );
166+
setItemData( index, ( state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked ), Qt::CheckStateRole );
167+
}
168+
}
169+
170+
void QgsCheckableComboBox::hidePopup()
171+
{
172+
if ( !mSkipHide )
173+
{
174+
QComboBox::hidePopup();
175+
}
176+
mSkipHide = false;
177+
}
178+
179+
void QgsCheckableComboBox::showContextMenu( const QPoint &pos )
180+
{
181+
Q_UNUSED( pos );
182+
183+
mContextMenu->exec( QCursor::pos() );
184+
}
185+
186+
void QgsCheckableComboBox::selectAllOptions()
187+
{
188+
blockSignals( true );
189+
for ( int i = 0; i < count(); i++ )
190+
{
191+
setItemData( i, Qt::Checked, Qt::CheckStateRole );
192+
}
193+
blockSignals( false );
194+
updateCheckedItems();
195+
}
196+
197+
void QgsCheckableComboBox::deselectAllOptions()
198+
{
199+
blockSignals( true );
200+
for ( int i = 0; i < count(); i++ )
201+
{
202+
setItemData( i, Qt::Unchecked, Qt::CheckStateRole );
203+
}
204+
blockSignals( false );
205+
updateCheckedItems();
206+
}
207+
208+
bool QgsCheckableComboBox::eventFilter( QObject *object, QEvent *event )
209+
{
210+
if ( ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
211+
&& object == view()->viewport() )
212+
{
213+
mSkipHide = true;
214+
}
215+
216+
if ( event->type() == QEvent::MouseButtonRelease )
217+
{
218+
if ( static_cast<QMouseEvent *>( event )->button() == Qt::RightButton )
219+
{
220+
return true;
221+
}
222+
}
223+
return false;
224+
}
225+
226+
void QgsCheckableComboBox::setCheckedItems( const QStringList &items )
227+
{
228+
Q_FOREACH ( const QString &text, items )
229+
{
230+
const int index = findText( text );
231+
setItemCheckState( index, index != -1 ? Qt::Checked : Qt::Unchecked );
232+
}
233+
}
234+
235+
void QgsCheckableComboBox::resizeEvent( QResizeEvent *event )
236+
{
237+
QComboBox::resizeEvent( event );
238+
updateDisplayText();
239+
}
240+
241+
void QgsCheckableComboBox::updateCheckedItems()
242+
{
243+
QStringList items = checkedItems();
244+
updateDisplayText();
245+
emit checkedItemsChanged( items );
246+
}
247+
248+
void QgsCheckableComboBox::updateDisplayText()
249+
{
250+
QString text;
251+
QStringList items = checkedItems();
252+
if ( items.isEmpty() )
253+
{
254+
text = mDefaultText;
255+
}
256+
else
257+
{
258+
text = items.join( mSeparator );
259+
}
260+
261+
QRect rect = lineEdit()->rect();
262+
QFontMetrics fontMetrics( font() );
263+
text = fontMetrics.elidedText( text, Qt::ElideRight, rect.width() );
264+
setEditText( text );
265+
}
266+

‎src/gui/qgscheckablecombobox.h

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
/***************************************************************************
2+
qgscheckablecombobox.h
3+
------------------------
4+
begin : March 21, 2017
5+
copyright : (C) 2017 by Alexander Bruy
6+
email : alexander dot bruy at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSCHECKABLECOMBOBOX_H
19+
#define QGSCHECKABLECOMBOBOX_H
20+
21+
#include <QComboBox>
22+
#include <QMenu>
23+
#include <QStandardItemModel>
24+
#include <QStyledItemDelegate>
25+
26+
#include "qgis.h"
27+
#include "qgis_gui.h"
28+
29+
class QEvent;
30+
31+
/** \class QgsCheckableItemModel
32+
* \ingroup gui
33+
* QStandardItemModel subclass which makes all items checkable
34+
* by default.
35+
* \since QGIS 3.0
36+
* \note not available in Python bindings
37+
**/
38+
#ifndef SIP_RUN
39+
class QgsCheckableItemModel : public QStandardItemModel
40+
{
41+
Q_OBJECT
42+
43+
public:
44+
45+
/** Constructor for QgsCheckableItemModel.
46+
* \param parent parent object
47+
*/
48+
QgsCheckableItemModel( QObject *parent = nullptr );
49+
50+
/** Returns a combination of the item flags: items are enabled
51+
* (ItemIsEnabled), selectable (ItemIsSelectable) and checkable
52+
* (ItemIsUserCheckable).
53+
* \param index item index
54+
*/
55+
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
56+
57+
/** Returns the data stored under the given role for the item
58+
* referred to by the index.
59+
* \param index item index
60+
* \param role data role
61+
*/
62+
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
63+
64+
/** Sets the role data for the item at index to value.
65+
* \param index item index
66+
* \param value data value
67+
* \param role data role
68+
* \returns true on success, false otherwise
69+
*/
70+
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
71+
72+
signals:
73+
74+
/** This signal is emitted whenever the items checkstate has changed.
75+
*/
76+
void itemCheckStateChanged();
77+
};
78+
79+
80+
/** \class QgsCheckBoxDelegate
81+
* \ingroup gui
82+
* QStyledItemDelegate subclass for QgsCheckableComboBox. Needed for
83+
* correct drawing of the checkable items on Mac and GTK.
84+
* \since QGIS 3.0
85+
* \note not available in Python bindings
86+
**/
87+
88+
class QgsCheckBoxDelegate : public QStyledItemDelegate
89+
{
90+
Q_OBJECT
91+
92+
public:
93+
94+
/** Constructor for QgsCheckBoxDelegate.
95+
* \param parent parent object
96+
*/
97+
QgsCheckBoxDelegate( QObject *parent = nullptr );
98+
99+
/** Renders the delegate using the given painter and style option
100+
* for the item specified by index.
101+
* \param painter painter to use
102+
* \param option style option
103+
* \param index item index
104+
*/
105+
virtual void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
106+
};
107+
#endif
108+
109+
/** \class QgsCheckableComboBox
110+
* \ingroup gui
111+
* QComboBox subclass which allows selecting multiple items.
112+
* \since QGIS 3.0
113+
**/
114+
115+
class GUI_EXPORT QgsCheckableComboBox : public QComboBox
116+
{
117+
Q_OBJECT
118+
119+
Q_PROPERTY( QString separator READ separator WRITE setSeparator )
120+
Q_PROPERTY( QString defaultText READ defaultText WRITE setDefaultText )
121+
Q_PROPERTY( QStringList checkedItems READ checkedItems WRITE setCheckedItems )
122+
123+
public:
124+
125+
/** Constructor for QgsCheckableComboBox.
126+
*/
127+
QgsCheckableComboBox( QWidget *parent SIP_TRANSFERTHIS = nullptr );
128+
129+
/** Returns separator used to separate items in the display text.
130+
* \see setSeparator()
131+
*/
132+
QString separator() const;
133+
134+
/** Set separator used to separate items in the display text.
135+
* \param separator separator to use
136+
* \see separator()
137+
*/
138+
void setSeparator( const QString &separator );
139+
140+
/** Returns default text which will be displayed in the widget
141+
* when no items selected.
142+
* \see setDefaultText()
143+
*/
144+
QString defaultText() const;
145+
146+
/** Set default text which will be displayed in the widget when
147+
* no items selected.
148+
* \param text default text
149+
* \see defaultText()
150+
*/
151+
void setDefaultText( const QString &text );
152+
153+
/** Returns currently checked items.
154+
* \see setCheckedItems()
155+
*/
156+
QStringList checkedItems() const;
157+
158+
/** Returns the checked state of the item identified by index
159+
* \param index item index
160+
* \see setItemCheckState()
161+
* \see toggleItemCheckState()
162+
*/
163+
Qt::CheckState itemCheckState( int index ) const;
164+
165+
/** Sets the item check state to state
166+
* \param index item index
167+
* \param state check state
168+
* \see itemCheckState()
169+
* \see toggleItemCheckState()
170+
*/
171+
void setItemCheckState( int index, Qt::CheckState state );
172+
173+
/** Toggles the item check state
174+
* \param index item index
175+
* \see itemCheckState()
176+
* \see setItemCheckState()
177+
*/
178+
void toggleItemCheckState( int index );
179+
180+
/** Hides the list of items in the combobox if it is currently
181+
* visible and resets the internal state.
182+
*/
183+
virtual void hidePopup() override;
184+
185+
/** Filters events to enable context menu
186+
*/
187+
virtual bool eventFilter( QObject *object, QEvent *event ) override;
188+
189+
signals:
190+
191+
/** This signal is emitted whenever the checked items list changed.
192+
*/
193+
void checkedItemsChanged( const QStringList &items );
194+
195+
public slots:
196+
197+
/** Set items which should be checked/selected.
198+
* \param items items to select
199+
* \see checkedItems()
200+
*/
201+
void setCheckedItems( const QStringList &items );
202+
203+
protected:
204+
205+
/** Handler for widget resizing
206+
*/
207+
virtual void resizeEvent( QResizeEvent *event ) override;
208+
209+
protected slots:
210+
211+
/** Display context menu which allows to select/deselect
212+
* all items at once.
213+
*/
214+
void showContextMenu( const QPoint &pos );
215+
216+
/** Selects all items.
217+
*/
218+
void selectAllOptions();
219+
220+
/** Removes selection from all items.
221+
*/
222+
void deselectAllOptions();
223+
224+
private:
225+
void updateCheckedItems();
226+
void updateDisplayText();
227+
228+
QString mSeparator;
229+
QString mDefaultText;
230+
231+
bool mSkipHide = false;
232+
233+
QMenu *mContextMenu = nullptr;
234+
QAction *mSelectAllAction = nullptr;
235+
QAction *mDeselectAllAction = nullptr;
236+
};
237+
238+
#endif // QGSCHECKABLECOMBOBOX_H

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ ADD_PYTHON_TEST(PyQgsAttributeTableModel test_qgsattributetablemodel.py)
2020
ADD_PYTHON_TEST(PyQgsBearingUtils test_qgsbearingutils.py)
2121
ADD_PYTHON_TEST(PyQgsBlendModes test_qgsblendmodes.py)
2222
ADD_PYTHON_TEST(PyQgsCategorizedSymbolRenderer test_qgscategorizedsymbolrenderer.py)
23+
ADD_PYTHON_TEST(PyQgsCheckableComboBox test_qgscheckablecombobox.py)
2324
ADD_PYTHON_TEST(PyQgsColorButton test_qgscolorbutton.py)
2425
ADD_PYTHON_TEST(PyQgsColorScheme test_qgscolorscheme.py)
2526
ADD_PYTHON_TEST(PyQgsColorSchemeRegistry test_qgscolorschemeregistry.py)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsCheckableComboBox
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Alexander Bruy'
10+
__date__ = '22/03/2017'
11+
__copyright__ = 'Copyright 2017, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.PyQt.QtCore import Qt
18+
from qgis.PyQt.QtTest import QSignalSpy
19+
20+
from qgis.gui import QgsCheckableComboBox
21+
from qgis.testing import start_app, unittest
22+
23+
start_app()
24+
25+
26+
class TestQgsCheckableComboBox(unittest.TestCase):
27+
28+
def testGettersSetters(self):
29+
""" test widget getters/setters """
30+
w = qgis.gui.QgsCheckableComboBox()
31+
32+
w.setSeparator('|')
33+
self.assertEqual(w.separator(), '|')
34+
w.setDefaultText('Select items...')
35+
self.assertEqual(w.defaultText(), 'Select items...')
36+
37+
w.addItems(['One', 'Two', 'Three'])
38+
39+
w.setCheckedItems(['Two'])
40+
self.assertEqual(len(w.checkedItems()), 1)
41+
self.assertEqual(w.checkedItems(), ['Two'])
42+
w.setCheckedItems(['Three'])
43+
self.assertEqual(len(w.checkedItems()), 2)
44+
self.assertEqual(w.checkedItems(), ['Two', 'Three'])
45+
46+
w.setItemCheckState(2, Qt.Unchecked)
47+
self.assertEqual(w.itemCheckState(2), Qt.Unchecked)
48+
49+
def test_ChangedSignals(self):
50+
""" test that signals are correctly emitted when clearing"""
51+
52+
w = qgis.gui.QgsCheckableComboBox()
53+
54+
w.addItems(['One', 'Two', 'Three'])
55+
56+
checkedItemsChanged_spy = QSignalSpy(w.checkedItemsChanged)
57+
w.setCheckedItems(['Two'])
58+
59+
self.assertEqual(len(checkedItemsChanged_spy), 1)
60+
61+
62+
if __name__ == '__main__':
63+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.