QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsstyleitemslistwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsstyleitemslistwidget.cpp
3  ---------------------------
4  begin : June 2019
5  copyright : (C) 2019 by Nyall Dawson
6  email : nyall dot dawson at gmail.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 
18 #include "qgsstylemanagerdialog.h"
19 #include "qgsstylesavedialog.h"
20 #include "qgspanelwidget.h"
21 #include "qgssettings.h"
22 #include "qgsgui.h"
24 #include "qgsapplication.h"
25 
26 //
27 // QgsReadOnlyStyleModel
28 //
29 
31 QgsReadOnlyStyleModel::QgsReadOnlyStyleModel( QgsStyleModel *sourceModel, QObject *parent )
32  : QgsStyleProxyModel( sourceModel, parent )
33 {
34 
35 }
36 
37 QgsReadOnlyStyleModel::QgsReadOnlyStyleModel( QgsStyle *style, QObject *parent )
38  : QgsStyleProxyModel( style, parent )
39 {
40 
41 }
42 
43 Qt::ItemFlags QgsReadOnlyStyleModel::flags( const QModelIndex &index ) const
44 {
45  return QgsStyleProxyModel::flags( index ) & ~Qt::ItemIsEditable;
46 }
47 
48 QVariant QgsReadOnlyStyleModel::data( const QModelIndex &index, int role ) const
49 {
50  if ( role == Qt::FontRole )
51  {
52  // drop font size to get reasonable amount of item name shown
53  QFont f = QgsStyleProxyModel::data( index, role ).value< QFont >();
54  f.setPointSize( 9 );
55  return f;
56  }
57  return QgsStyleProxyModel::data( index, role );
58 }
59 
61 
62 
63 //
64 // QgsStyleItemsListWidget
65 //
66 
68  : QWidget( parent )
69 {
70  setupUi( this );
71 
72  btnAdvanced->hide(); // advanced button is hidden by default
73  btnAdvanced->setMenu( new QMenu( this ) );
74 
75  double iconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 10;
76  viewSymbols->setIconSize( QSize( static_cast< int >( iconSize ), static_cast< int >( iconSize * 0.9 ) ) ); // ~100, 90 on low dpi
77 
78  double treeIconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 2;
79  mSymbolTreeView->setIconSize( QSize( static_cast< int >( treeIconSize ), static_cast< int >( treeIconSize ) ) );
80  mSymbolTreeView->setMinimumHeight( mSymbolTreeView->fontMetrics().height() * 6 );
81 
82  viewSymbols->setSelectionBehavior( QAbstractItemView::SelectRows );
83  mSymbolTreeView->setSelectionMode( viewSymbols->selectionMode() );
84 
85  connect( openStyleManagerButton, &QToolButton::clicked, this, &QgsStyleItemsListWidget::openStyleManager );
86 
87  lblSymbolName->clear();
88 
89  connect( mButtonIconView, &QToolButton::toggled, this, [ = ]( bool active )
90  {
91  if ( active )
92  {
93  mSymbolViewStackedWidget->setCurrentIndex( 0 );
94  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
95  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui );
96  }
97  } );
98  connect( mButtonListView, &QToolButton::toggled, this, [ = ]( bool active )
99  {
100  if ( active )
101  {
102  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 1, QgsSettings::Gui );
103  mSymbolViewStackedWidget->setCurrentIndex( 1 );
104  }
105  } );
106 
107  // restore previous view
108  QgsSettings settings;
109  const int currentView = settings.value( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui ).toInt();
110  if ( currentView == 0 )
111  mButtonIconView->setChecked( true );
112  else
113  mButtonListView->setChecked( true );
114 
115  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
116  connect( mSymbolTreeView->header(), &QHeaderView::sectionResized, this, [this]
117  {
118  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
119  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/treeState" ), mSymbolTreeView->header()->saveState(), QgsSettings::Gui );
120  } );
121 
122  QgsFilterLineEdit *groupEdit = new QgsFilterLineEdit();
123  groupEdit->setShowSearchIcon( true );
124  groupEdit->setShowClearButton( true );
125  groupEdit->setPlaceholderText( tr( "Filter symbols…" ) );
126  groupsCombo->setLineEdit( groupEdit );
127 
128  connect( btnSaveSymbol, &QPushButton::clicked, this, &QgsStyleItemsListWidget::saveEntity );
129 }
130 
132 {
133  mStyle = style;
134 
135  mModel = mStyle == QgsStyle::defaultStyle() ? new QgsReadOnlyStyleModel( QgsApplication::defaultStyleModel(), this )
136  : new QgsReadOnlyStyleModel( mStyle, this );
137 
138  mModel->addDesiredIconSize( viewSymbols->iconSize() );
139  mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
140  viewSymbols->setModel( mModel );
141  mSymbolTreeView->setModel( mModel );
142 
143  connect( mStyle, &QgsStyle::groupsModified, this, &QgsStyleItemsListWidget::populateGroups );
144 
145  mSymbolTreeView->setSelectionModel( viewSymbols->selectionModel() );
146  connect( viewSymbols->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleItemsListWidget::onSelectionChanged );
147 
148  populateGroups();
149  connect( groupsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsStyleItemsListWidget::groupsCombo_currentIndexChanged );
150  connect( groupsCombo, &QComboBox::currentTextChanged, this, &QgsStyleItemsListWidget::updateModelFilters );
151 
152  QgsSettings settings;
153  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
154 }
155 
157 {
158  mModel->setEntityFilterEnabled( true );
159  mModel->setEntityFilter( type );
160  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
161  switch ( type )
162  {
164  btnSaveSymbol->setText( tr( "Save Symbol…" ) );
165  btnSaveSymbol->setToolTip( tr( "Save symbol to styles" ) );
166  if ( allGroup >= 0 )
167  groupsCombo->setItemText( allGroup, tr( "All Symbols" ) );
168  break;
169 
171  btnSaveSymbol->setText( tr( "Save Color Ramp…" ) );
172  btnSaveSymbol->setToolTip( tr( "Save color ramp to styles" ) );
173  if ( allGroup >= 0 )
174  groupsCombo->setItemText( allGroup, tr( "All Color Ramps" ) );
175  break;
176 
178  btnSaveSymbol->setText( tr( "Save Format…" ) );
179  btnSaveSymbol->setToolTip( tr( "Save text format to styles" ) );
180  if ( allGroup >= 0 )
181  groupsCombo->setItemText( allGroup, tr( "All Text Formats" ) );
182  break;
183 
185  btnSaveSymbol->setText( tr( "Save Label Settings…" ) );
186  btnSaveSymbol->setToolTip( tr( "Save label settings to styles" ) );
187  if ( allGroup >= 0 )
188  groupsCombo->setItemText( allGroup, tr( "All Label Settings" ) );
189  break;
190 
192  btnSaveSymbol->setText( tr( "Save Legend Patch Shape…" ) );
193  btnSaveSymbol->setToolTip( tr( "Save legend patch shape to styles" ) );
194  if ( allGroup >= 0 )
195  groupsCombo->setItemText( allGroup, tr( "All Legend Patch Shapes" ) );
196  break;
197 
199  btnSaveSymbol->setText( tr( "Save 3D Symbol…" ) );
200  btnSaveSymbol->setToolTip( tr( "Save 3D symbol to styles" ) );
201  if ( allGroup >= 0 )
202  groupsCombo->setItemText( allGroup, tr( "All 3D Symbols" ) );
203  break;
204 
205  case QgsStyle::TagEntity:
207  break;
208  }
209 }
210 
211 void QgsStyleItemsListWidget::setEntityTypes( const QList<QgsStyle::StyleEntity> &filters )
212 {
213  mModel->setEntityFilterEnabled( true );
214  mModel->setEntityFilters( filters );
215 
216  // bit of a gross hack -- run now! this will need revisiting when other parent widgets use different filter combinations!
217  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
218  if ( filters.length() == 2 && filters.contains( QgsStyle::LabelSettingsEntity ) && filters.contains( QgsStyle::TextFormatEntity ) )
219  {
220  btnSaveSymbol->setText( tr( "Save Settings…" ) );
221  btnSaveSymbol->setToolTip( tr( "Save label settings or text format to styles" ) );
222  if ( allGroup >= 0 )
223  groupsCombo->setItemText( allGroup, tr( "All Settings" ) );
224  }
225 }
226 
228 {
229  mModel->setSymbolTypeFilterEnabled( true );
230  mModel->setSymbolType( type );
231 }
232 
234 {
235  mModel->setLayerType( type );
236 }
237 
239 {
240  return groupsCombo->currentData().toString() == QLatin1String( "tag" ) ? groupsCombo->currentText() : QString();
241 }
242 
244 {
245  return btnAdvanced->menu();
246 }
247 
249 {
250  if ( menu ) // show it if there is a menu pointer
251  {
252  btnAdvanced->show();
253  btnAdvanced->setMenu( menu );
254  }
255 }
256 
258 {
259  btnAdvanced->setVisible( enabled );
260 }
261 
263 {
264  QItemSelection selection = viewSymbols->selectionModel()->selection();
265  if ( selection.isEmpty() )
266  return QString();
267 
268  const QModelIndex index = selection.at( 0 ).topLeft();
269 
270  return mModel->data( index, QgsStyleModel::Name ).toString();
271 }
272 
274 {
275  QItemSelection selection = viewSymbols->selectionModel()->selection();
276  if ( selection.isEmpty() )
277  return QgsStyle::SymbolEntity;
278 
279  const QModelIndex index = selection.at( 0 ).topLeft();
280 
281  return static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
282 }
283 
284 void QgsStyleItemsListWidget::showEvent( QShowEvent *event )
285 {
286  // restore header sizes on show event -- because this widget is used in multiple places simultaneously
287  // (e.g. layer styling dock, it's shown in both the symbology and labeling sections), then we want
288  // to ensure that a header resize for any of the widgets applies the next time any other item list widgets
289  // are shown.
290  QWidget::showEvent( event );
291  QgsSettings settings;
292  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
293 }
294 
295 void QgsStyleItemsListWidget::populateGroups()
296 {
297  if ( !mStyle )
298  return;
299 
300  mUpdatingGroups = true;
301  groupsCombo->blockSignals( true );
302  groupsCombo->clear();
303 
304  groupsCombo->addItem( tr( "Favorites" ), QVariant( "favorite" ) );
305 
306  QString allText = tr( "All Symbols" );
307  if ( mModel->entityFilterEnabled() )
308  {
309  switch ( mModel->entityFilter() )
310  {
312  allText = tr( "All Symbols" );
313  break;
314 
316  allText = tr( "All Color Ramps" );
317  break;
318 
320  allText = tr( "All Text Formats" );
321  break;
322 
324  allText = tr( "All Label Settings" );
325  break;
326 
328  allText = tr( "All Legend Patch Shapes" );
329  break;
330 
332  allText = tr( "All 3D Symbols" );
333  break;
334 
335  case QgsStyle::TagEntity:
337  break;
338  }
339  }
340 
341  groupsCombo->addItem( allText, QVariant( "all" ) );
342 
343  int index = 2;
344  QStringList tags = mStyle->tags();
345  if ( tags.count() > 0 )
346  {
347  tags.sort();
348  groupsCombo->insertSeparator( index );
349  const auto constTags = tags;
350  for ( const QString &tag : constTags )
351  {
352  groupsCombo->addItem( tag, QVariant( "tag" ) );
353  index++;
354  }
355  }
356 
357  QStringList groups = mStyle->smartgroupNames();
358  if ( groups.count() > 0 )
359  {
360  groups.sort();
361  groupsCombo->insertSeparator( index + 1 );
362  const auto constGroups = groups;
363  for ( const QString &group : constGroups )
364  {
365  groupsCombo->addItem( group, QVariant( "smartgroup" ) );
366  }
367  }
368  groupsCombo->blockSignals( false );
369 
370  QgsSettings settings;
371  index = settings.value( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 ).toInt();
372  groupsCombo->setCurrentIndex( index );
373 
374  mUpdatingGroups = false;
375 
376  updateModelFilters();
377 }
378 
379 void QgsStyleItemsListWidget::updateModelFilters()
380 {
381  if ( mUpdatingGroups || !mModel )
382  return;
383 
384  const QString text = groupsCombo->currentText();
385  const bool isFreeText = text != groupsCombo->itemText( groupsCombo->currentIndex() );
386 
387  if ( isFreeText )
388  {
389  mModel->setFavoritesOnly( false );
390  mModel->setTagId( -1 );
391  mModel->setSmartGroupId( -1 );
392  mModel->setFilterString( groupsCombo->currentText() );
393  }
394  else if ( groupsCombo->currentData().toString() == QLatin1String( "favorite" ) )
395  {
396  mModel->setFavoritesOnly( true );
397  mModel->setTagId( -1 );
398  mModel->setSmartGroupId( -1 );
399  mModel->setFilterString( QString() );
400  }
401  else if ( groupsCombo->currentData().toString() == QLatin1String( "all" ) )
402  {
403  mModel->setFavoritesOnly( false );
404  mModel->setTagId( -1 );
405  mModel->setSmartGroupId( -1 );
406  mModel->setFilterString( QString() );
407  }
408  else if ( groupsCombo->currentData().toString() == QLatin1String( "smartgroup" ) )
409  {
410  mModel->setFavoritesOnly( false );
411  mModel->setTagId( -1 );
412  mModel->setSmartGroupId( mStyle->smartgroupId( text ) );
413  mModel->setFilterString( QString() );
414  }
415  else
416  {
417  mModel->setFavoritesOnly( false );
418  mModel->setTagId( mStyle->tagId( text ) );
419  mModel->setSmartGroupId( -1 );
420  mModel->setFilterString( QString() );
421  }
422 }
423 
424 void QgsStyleItemsListWidget::openStyleManager()
425 {
426  // prefer to use global window manager to open the style manager, if possible!
427  // this allows reuse of an existing non-modal window instead of opening a new modal window.
428  // Note that we only use the non-modal dialog if we're open in the panel -- if we're already
429  // open as part of a modal dialog, then we MUST use another modal dialog or the result will
430  // not be focusable!
432  if ( !panel || !panel->dockMode()
434  || !QgsGui::windowManager()->openStandardDialog( QgsWindowManagerInterface::DialogStyleManager ) )
435  {
436  // fallback to modal dialog
437  QgsStyleManagerDialog dlg( mStyle, this );
438  dlg.exec();
439 
440  updateModelFilters(); // probably not needed -- the model should automatically update if any changes were made
441  }
442 }
443 
444 void QgsStyleItemsListWidget::onSelectionChanged( const QModelIndex &index )
445 {
446  if ( !mModel )
447  return;
448 
449  QString symbolName = mModel->data( mModel->index( index.row(), QgsStyleModel::Name ) ).toString();
450  lblSymbolName->setText( symbolName );
451 
452  emit selectionChanged( symbolName, static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() ) );
453 }
454 
455 void QgsStyleItemsListWidget::groupsCombo_currentIndexChanged( int index )
456 {
457  QgsSettings settings;
458  settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), index );
459 }
SymbolType
Symbol types.
Definition: qgis.h:168
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:416
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
QLineEdit subclass with built in support for clearing the widget's value and handling custom null val...
void setShowSearchIcon(bool visible)
Define if a search icon shall be shown on the left of the image when no text is entered.
void setShowClearButton(bool visible)
Sets whether the widget's clear button is visible.
static QgsWindowManagerInterface * windowManager()
Returns the global window manager, if set.
Definition: qgsgui.cpp:165
Base class for any widget that can be shown as a inline panel.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
bool dockMode()
Returns the dock mode state.
void setStyle(QgsStyle *style)
Sets the style database associated with the widget.
void selectionChanged(const QString &name, QgsStyle::StyleEntity type)
Emitted when the selected item is changed in the widget.
QString currentItemName() const
Returns the name of the item currently selected in the widget.
void setAdvancedMenu(QMenu *menu)
Sets the widget's advanced menu, which is shown when the user clicks the "Advanced" button in the wid...
void saveEntity()
Emitted when the user has opted to save a new entity to the style database, by clicking the "Save" bu...
void showAdvancedButton(bool enabled)
Sets whether the advanced button should be shown in the widget.
QString currentTagFilter() const
Returns the current tag filter set for the widget, if any is set.
void setSymbolType(Qgis::SymbolType type)
Sets the type of symbols to show in the widget.
void setLayerType(QgsWkbTypes::GeometryType type)
Sets the layer type to show in the widget.
QgsStyle::StyleEntity currentEntityType() const
Returns the type of the item currently selected in the widget.
QgsStyleItemsListWidget(QWidget *parent SIP_TRANSFERTHIS)
Constructor for QgsStyleItemsListWidget, with the specified parent widget.
void setEntityType(QgsStyle::StyleEntity type)
Sets the type of style entity to show in the widget.
void setEntityTypes(const QList< QgsStyle::StyleEntity > &filters) SIP_SKIP
Sets the types of style entity to show in the widget.
void showEvent(QShowEvent *event) override
QMenu * advancedMenu()
Returns a pointer to the widget's current advanced menu.
A dialog allowing users to customize and populate a QgsStyle.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
@ TypeRole
Style entity type, see QgsStyle::StyleEntity.
@ Name
Name column.
A QSortFilterProxyModel subclass for showing filtered symbol and color ramps entries from a QgsStyle ...
void setEntityFilter(QgsStyle::StyleEntity filter)
Sets the style entity type filter.
void setSymbolTypeFilterEnabled(bool enabled)
Sets whether filtering by symbol type is enabled.
void setTagId(int id)
Sets a tag id to filter style entities by.
QgsStyle::StyleEntity entityFilter() const
Returns the style entity type filter.
void setEntityFilters(const QList< QgsStyle::StyleEntity > &filters)
Sets the style entity type filters.
void setFavoritesOnly(bool favoritesOnly)
Sets whether the model should show only favorited entities.
void setLayerType(QgsWkbTypes::GeometryType type)
Sets the layer type filter.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol type filter.
bool entityFilterEnabled() const
Returns true if filtering by entity type is enabled.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void setEntityFilterEnabled(bool enabled)
Sets whether filtering by entity type is enabled.
void setSmartGroupId(int id)
Sets a smart group id to filter style entities by.
void setFilterString(const QString &filter)
Sets a filter string, such that only symbol entities with names matching the specified string will be...
QStringList tags() const
Returns a list of all tags in the style database.
Definition: qgsstyle.cpp:1403
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstyle.h:179
@ LabelSettingsEntity
Label settings.
Definition: qgsstyle.h:185
@ TextFormatEntity
Text formats.
Definition: qgsstyle.h:184
@ SmartgroupEntity
Smart groups.
Definition: qgsstyle.h:183
@ Symbol3DEntity
3D symbol entity (since QGIS 3.14)
Definition: qgsstyle.h:187
@ SymbolEntity
Symbols.
Definition: qgsstyle.h:180
@ TagEntity
Tags.
Definition: qgsstyle.h:181
@ ColorrampEntity
Color ramps.
Definition: qgsstyle.h:182
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition: qgsstyle.h:186
void groupsModified()
Emitted every time a tag or smartgroup has been added, removed, or renamed.
int smartgroupId(const QString &smartgroup)
Returns the database id for the given smartgroup name.
Definition: qgsstyle.cpp:2235
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:131
int tagId(const QString &tag)
Returns the database id for the given tag name.
Definition: qgsstyle.cpp:2230
QStringList smartgroupNames() const
Returns the smart groups list.
Definition: qgsstyle.cpp:2350
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.