QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgscheckablecombobox.cpp
Go to the documentation of this file.
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 #include "qgsapplication.h"
20 
21 #include <QEvent>
22 #include <QMouseEvent>
23 #include <QLineEdit>
24 #include <QPoint>
25 #include <QAbstractItemView>
26 
27 
29  : QStandardItemModel( 0, 1, parent )
30 {
31 }
32 
33 Qt::ItemFlags QgsCheckableItemModel::flags( const QModelIndex &index ) const
34 {
35  return QStandardItemModel::flags( index ) | Qt::ItemIsUserCheckable;
36 }
37 
38 QVariant QgsCheckableItemModel::data( const QModelIndex &index, int role ) const
39 {
40  QVariant value = QStandardItemModel::data( index, role );
41 
42  if ( index.isValid() && role == Qt::CheckStateRole && !value.isValid() )
43  {
44  value = Qt::Unchecked;
45  }
46 
47  return value;
48 }
49 
50 bool QgsCheckableItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
51 {
52  bool ok = QStandardItemModel::setData( index, value, role );
53 
54  if ( ok && role == Qt::CheckStateRole )
55  {
56  emit itemCheckStateChanged( index );
57  }
58 
59  emit dataChanged( index, index );
60  return ok;
61 }
62 
63 
65  : QStyledItemDelegate( parent )
66 {
67 }
68 
69 void QgsCheckBoxDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
70 {
71  QStyleOptionViewItem &nonConstOpt = const_cast<QStyleOptionViewItem &>( option );
72  nonConstOpt.showDecorationSelected = false;
73  QStyledItemDelegate::paint( painter, nonConstOpt, index );
74 }
75 
76 
78  : QComboBox( parent )
79  , mModel( new QgsCheckableItemModel( this ) )
80  , mSeparator( QStringLiteral( ", " ) )
81 {
82  setModel( mModel );
83  setItemDelegate( new QgsCheckBoxDelegate( this ) );
84 
85  QLineEdit *lineEdit = new QLineEdit( this );
86  lineEdit->setReadOnly( true );
87  QPalette pal = qApp->palette();
88  pal.setBrush( QPalette::Base, pal.button() );
89  lineEdit->setPalette( pal );
90  setLineEdit( lineEdit );
91  lineEdit->installEventFilter( this );
92  lineEdit->setContextMenuPolicy( Qt::CustomContextMenu );
93  connect( lineEdit, &QAbstractItemView::customContextMenuRequested, this, &QgsCheckableComboBox::showContextMenu );
94 
95  mContextMenu = new QMenu( this );
96  mSelectAllAction = mContextMenu->addAction( tr( "Select All" ) );
97  mDeselectAllAction = mContextMenu->addAction( tr( "Deselect All" ) );
98  connect( mSelectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::selectAllOptions );
99  connect( mDeselectAllAction, &QAction::triggered, this, &QgsCheckableComboBox::deselectAllOptions );
100 
101  view()->viewport()->installEventFilter( this );
102  view()->setContextMenuPolicy( Qt::CustomContextMenu );
103  connect( view(), &QAbstractItemView::customContextMenuRequested, this, &QgsCheckableComboBox::showContextMenu );
104 
105  connect( model(), &QStandardItemModel::rowsInserted, this, [ = ]( const QModelIndex &, int, int ) { updateDisplayText(); } );
106  connect( model(), &QStandardItemModel::rowsRemoved, this, [ = ]( const QModelIndex &, int, int ) { updateDisplayText(); } );
107  connect( model(), &QStandardItemModel::dataChanged, this, [ = ]( const QModelIndex &, const QModelIndex &, const QVector< int > & ) { updateDisplayText(); } );
108 }
109 
111 {
112  return mSeparator;
113 }
114 
115 void QgsCheckableComboBox::setSeparator( const QString &separator )
116 {
117  if ( mSeparator != separator )
118  {
119  mSeparator = separator;
120  updateDisplayText();
121  }
122 }
123 
125 {
126  return mDefaultText;
127 }
128 
129 void QgsCheckableComboBox::setDefaultText( const QString &text )
130 {
131  if ( mDefaultText != text )
132  {
133  mDefaultText = text;
134  updateDisplayText();
135  }
136 }
137 
138 void QgsCheckableComboBox::addItemWithCheckState( const QString &text, Qt::CheckState state, const QVariant &userData )
139 {
140  QComboBox::addItem( text, userData );
141  setItemCheckState( count() - 1, state );
142 }
143 
145 {
146  QStringList items;
147 
148  if ( auto *lModel = model() )
149  {
150  QModelIndex index = lModel->index( 0, modelColumn(), rootModelIndex() );
151  QModelIndexList indexes = lModel->match( index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly );
152  const auto constIndexes = indexes;
153  for ( const QModelIndex &index : constIndexes )
154  {
155  items += index.data().toString();
156  }
157  }
158 
159  return items;
160 }
161 
163 {
164  QVariantList data;
165 
166  if ( auto *lModel = model() )
167  {
168  QModelIndex index = lModel->index( 0, modelColumn(), rootModelIndex() );
169  QModelIndexList indexes = lModel->match( index, Qt::CheckStateRole, Qt::Checked, -1, Qt::MatchExactly );
170  const auto constIndexes = indexes;
171  for ( const QModelIndex &index : constIndexes )
172  {
173  data += index.data( Qt::UserRole ).toString();
174  }
175  }
176 
177  return data;
178 }
179 
180 Qt::CheckState QgsCheckableComboBox::itemCheckState( int index ) const
181 {
182  return static_cast<Qt::CheckState>( itemData( index, Qt::CheckStateRole ).toInt() );
183 }
184 
185 void QgsCheckableComboBox::setItemCheckState( int index, Qt::CheckState state )
186 {
187  setItemData( index, state, Qt::CheckStateRole );
188 }
189 
191 {
192  QVariant value = itemData( index, Qt::CheckStateRole );
193  if ( value.isValid() )
194  {
195  Qt::CheckState state = static_cast<Qt::CheckState>( value.toInt() );
196  setItemData( index, ( state == Qt::Unchecked ? Qt::Checked : Qt::Unchecked ), Qt::CheckStateRole );
197  }
198 }
199 
201 {
202  if ( !mSkipHide )
203  {
204  QComboBox::hidePopup();
205  }
206  mSkipHide = false;
207 }
208 
210 {
211  Q_UNUSED( pos )
212 
213  mContextMenu->exec( QCursor::pos() );
214 }
215 
217 {
218  blockSignals( true );
219  for ( int i = 0; i < count(); i++ )
220  {
221  setItemData( i, Qt::Checked, Qt::CheckStateRole );
222  }
223  blockSignals( false );
224  updateCheckedItems();
225 }
226 
228 {
229  blockSignals( true );
230  for ( int i = 0; i < count(); i++ )
231  {
232  setItemData( i, Qt::Unchecked, Qt::CheckStateRole );
233  }
234  blockSignals( false );
235  updateCheckedItems();
236 }
237 
238 bool QgsCheckableComboBox::eventFilter( QObject *object, QEvent *event )
239 {
240  if ( object == lineEdit() )
241  {
242  if ( event->type() == QEvent::MouseButtonPress && static_cast<QMouseEvent *>( event )->button() == Qt::LeftButton && object == lineEdit() )
243  {
244  mSkipHide = true;
245  showPopup();
246  }
247  }
248  else if ( ( event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonRelease )
249  && object == view()->viewport() )
250  {
251  mSkipHide = true;
252 
253  if ( event->type() == QEvent::MouseButtonRelease && static_cast<QMouseEvent *>( event )->button() == Qt::RightButton )
254  {
255  return true;
256  }
257 
258  if ( event->type() == QEvent::MouseButtonRelease )
259  {
260  QModelIndex index = view()->indexAt( static_cast<QMouseEvent *>( event )->pos() );
261  if ( index.isValid() )
262  {
263  QgsCheckableItemModel *myModel = qobject_cast<QgsCheckableItemModel *>( model() );
264  QStandardItem *item = myModel->itemFromIndex( index );
265  item->checkState() == Qt::Checked ? item->setCheckState( Qt::Unchecked ) : item->setCheckState( Qt::Checked );
266  }
267  return true;
268  }
269  }
270 
271  return QComboBox::eventFilter( object, event );
272 }
273 
274 void QgsCheckableComboBox::setCheckedItems( const QStringList &items )
275 {
276  const auto constItems = items;
277  for ( const QString &text : constItems )
278  {
279  const int index = findText( text );
280  setItemCheckState( index, index != -1 ? Qt::Checked : Qt::Unchecked );
281  }
282  updateCheckedItems();
283 }
284 
285 void QgsCheckableComboBox::resizeEvent( QResizeEvent *event )
286 {
287  QComboBox::resizeEvent( event );
288  updateDisplayText();
289 }
290 
291 void QgsCheckableComboBox::updateCheckedItems()
292 {
293  QStringList items = checkedItems();
294  updateDisplayText();
295  emit checkedItemsChanged( items );
296 }
297 
298 void QgsCheckableComboBox::updateDisplayText()
299 {
300  QString text;
301  QStringList items = checkedItems();
302  if ( items.isEmpty() )
303  {
304  text = mDefaultText;
305  }
306  else
307  {
308  text = items.join( mSeparator );
309  }
310 
311  QRect rect = lineEdit()->rect();
312  QFontMetrics fontMetrics( font() );
313  text = fontMetrics.elidedText( text, Qt::ElideRight, rect.width() );
314  setEditText( text );
315 }
316 
QgsCheckableComboBox::checkedItems
QStringList checkedItems
Definition: qgscheckablecombobox.h:130
QgsCheckableItemModel::flags
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns a combination of the item flags: items are enabled (ItemIsEnabled), selectable (ItemIsSelecta...
Definition: qgscheckablecombobox.cpp:33
QgsCheckableComboBox::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Filters events to enable context menu.
Definition: qgscheckablecombobox.cpp:238
QgsCheckableItemModel::QgsCheckableItemModel
QgsCheckableItemModel(QObject *parent=nullptr)
Constructor for QgsCheckableItemModel.
Definition: qgscheckablecombobox.cpp:28
QgsCheckableComboBox::checkedItemsData
QVariantList checkedItemsData() const
Returns userData (stored in the Qt::UserRole) associated with currently checked items.
Definition: qgscheckablecombobox.cpp:162
QgsCheckableComboBox::hidePopup
void hidePopup() override
Hides the list of items in the combobox if it is currently visible and resets the internal state.
Definition: qgscheckablecombobox.cpp:200
QgsCheckBoxDelegate
QStyledItemDelegate subclass for QgsCheckableComboBox.
Definition: qgscheckablecombobox.h:96
pal
Definition: qgsdiagramrenderer.h:49
QgsCheckableComboBox::deselectAllOptions
void deselectAllOptions()
Removes selection from all items.
Definition: qgscheckablecombobox.cpp:227
qgsapplication.h
qgscheckablecombobox.h
QgsCheckableComboBox::separator
QString separator
Definition: qgscheckablecombobox.h:128
QgsCheckableComboBox::addItemWithCheckState
void addItemWithCheckState(const QString &text, Qt::CheckState state, const QVariant &userData=QVariant())
Adds an item to the combobox with the given text, check state (stored in the Qt::CheckStateRole) and ...
Definition: qgscheckablecombobox.cpp:138
QgsCheckBoxDelegate::QgsCheckBoxDelegate
QgsCheckBoxDelegate(QObject *parent=nullptr)
Constructor for QgsCheckBoxDelegate.
Definition: qgscheckablecombobox.cpp:64
QgsCheckableComboBox::resizeEvent
void resizeEvent(QResizeEvent *event) override
Handler for widget resizing.
Definition: qgscheckablecombobox.cpp:285
QgsCheckableComboBox::mModel
QgsCheckableItemModel * mModel
Definition: qgscheckablecombobox.h:273
QgsCheckableComboBox::QgsCheckableComboBox
QgsCheckableComboBox(QWidget *parent=nullptr)
Constructor for QgsCheckableComboBox.
Definition: qgscheckablecombobox.cpp:77
QgsCheckableComboBox::itemCheckState
Qt::CheckState itemCheckState(int index) const
Returns the checked state of the item identified by index.
Definition: qgscheckablecombobox.cpp:180
QgsCheckableItemModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Returns the data stored under the given role for the item referred to by the index.
Definition: qgscheckablecombobox.cpp:38
QgsCheckableComboBox::setItemCheckState
void setItemCheckState(int index, Qt::CheckState state)
Sets the item check state to state.
Definition: qgscheckablecombobox.cpp:185
QgsCheckableComboBox::setSeparator
void setSeparator(const QString &separator)
Set separator used to separate items in the display text.
Definition: qgscheckablecombobox.cpp:115
QgsCheckableComboBox::toggleItemCheckState
void toggleItemCheckState(int index)
Toggles the item check state.
Definition: qgscheckablecombobox.cpp:190
QgsCheckableComboBox::showContextMenu
void showContextMenu(QPoint pos)
Display context menu which allows selecting/deselecting all items at once.
Definition: qgscheckablecombobox.cpp:209
QgsCheckableComboBox::model
QgsCheckableItemModel * model() const
Returns the custom item model which handles checking the items.
Definition: qgscheckablecombobox.h:218
QgsCheckableComboBox::setCheckedItems
void setCheckedItems(const QStringList &items)
Set items which should be checked/selected.
Definition: qgscheckablecombobox.cpp:274
QgsCheckableComboBox::checkedItemsChanged
void checkedItemsChanged(const QStringList &items)
Emitted whenever the checked items list changed.
QgsCheckableComboBox::setDefaultText
void setDefaultText(const QString &text)
Set default text which will be displayed in the widget when no items selected.
Definition: qgscheckablecombobox.cpp:129
QgsCheckableItemModel
QStandardItemModel subclass which makes all items checkable by default.
Definition: qgscheckablecombobox.h:42
QgsCheckableItemModel::setData
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Sets the role data for the item at index to value.
Definition: qgscheckablecombobox.cpp:50
QgsCheckableComboBox::defaultText
QString defaultText
Definition: qgscheckablecombobox.h:129
QgsCheckableComboBox::selectAllOptions
void selectAllOptions()
Selects all items.
Definition: qgscheckablecombobox.cpp:216
QgsCheckableItemModel::itemCheckStateChanged
void itemCheckStateChanged(const QModelIndex &index)
Emitted whenever the item's checkstate has changed.
QgsCheckBoxDelegate::paint
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Renders the delegate using the given painter and style option for the item specified by index.
Definition: qgscheckablecombobox.cpp:69