QGIS API Documentation  3.25.0-Master (dec16ba68b)
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  const 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  const 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  const 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 
141  // set a grid size which allows sufficient vertical spacing to fit reasonably sized entity names
142  viewSymbols->setGridSize( QSize( static_cast< int >( viewSymbols->iconSize().width() * 1.4 ), static_cast< int >( viewSymbols->iconSize().height() * 1.7 ) ) );
143  viewSymbols->setTextElideMode( Qt::TextElideMode::ElideRight );
144 
145  viewSymbols->setModel( mModel );
146  mSymbolTreeView->setModel( mModel );
147 
148  connect( mStyle, &QgsStyle::groupsModified, this, &QgsStyleItemsListWidget::populateGroups );
149 
150  mSymbolTreeView->setSelectionModel( viewSymbols->selectionModel() );
151  connect( viewSymbols->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleItemsListWidget::onSelectionChanged );
152 
153  populateGroups();
154  connect( groupsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsStyleItemsListWidget::groupsCombo_currentIndexChanged );
155  connect( groupsCombo, &QComboBox::currentTextChanged, this, &QgsStyleItemsListWidget::updateModelFilters );
156 
157  const QgsSettings settings;
158  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
159 }
160 
162 {
163  mModel->setEntityFilterEnabled( true );
164  mModel->setEntityFilter( type );
165  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
166  switch ( type )
167  {
169  btnSaveSymbol->setText( tr( "Save Symbol…" ) );
170  btnSaveSymbol->setToolTip( tr( "Save symbol to styles" ) );
171  if ( allGroup >= 0 )
172  groupsCombo->setItemText( allGroup, tr( "All Symbols" ) );
173  break;
174 
176  btnSaveSymbol->setText( tr( "Save Color Ramp…" ) );
177  btnSaveSymbol->setToolTip( tr( "Save color ramp to styles" ) );
178  if ( allGroup >= 0 )
179  groupsCombo->setItemText( allGroup, tr( "All Color Ramps" ) );
180  break;
181 
183  btnSaveSymbol->setText( tr( "Save Format…" ) );
184  btnSaveSymbol->setToolTip( tr( "Save text format to styles" ) );
185  if ( allGroup >= 0 )
186  groupsCombo->setItemText( allGroup, tr( "All Text Formats" ) );
187  break;
188 
190  btnSaveSymbol->setText( tr( "Save Label Settings…" ) );
191  btnSaveSymbol->setToolTip( tr( "Save label settings to styles" ) );
192  if ( allGroup >= 0 )
193  groupsCombo->setItemText( allGroup, tr( "All Label Settings" ) );
194  break;
195 
197  btnSaveSymbol->setText( tr( "Save Legend Patch Shape…" ) );
198  btnSaveSymbol->setToolTip( tr( "Save legend patch shape to styles" ) );
199  if ( allGroup >= 0 )
200  groupsCombo->setItemText( allGroup, tr( "All Legend Patch Shapes" ) );
201  break;
202 
204  btnSaveSymbol->setText( tr( "Save 3D Symbol…" ) );
205  btnSaveSymbol->setToolTip( tr( "Save 3D symbol to styles" ) );
206  if ( allGroup >= 0 )
207  groupsCombo->setItemText( allGroup, tr( "All 3D Symbols" ) );
208  break;
209 
210  case QgsStyle::TagEntity:
212  break;
213  }
214 }
215 
216 void QgsStyleItemsListWidget::setEntityTypes( const QList<QgsStyle::StyleEntity> &filters )
217 {
218  mModel->setEntityFilterEnabled( true );
219  mModel->setEntityFilters( filters );
220 
221  // bit of a gross hack -- run now! this will need revisiting when other parent widgets use different filter combinations!
222  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
223  if ( filters.length() == 2 && filters.contains( QgsStyle::LabelSettingsEntity ) && filters.contains( QgsStyle::TextFormatEntity ) )
224  {
225  btnSaveSymbol->setText( tr( "Save Settings…" ) );
226  btnSaveSymbol->setToolTip( tr( "Save label settings or text format to styles" ) );
227  if ( allGroup >= 0 )
228  groupsCombo->setItemText( allGroup, tr( "All Settings" ) );
229  }
230 }
231 
233 {
234  mModel->setSymbolTypeFilterEnabled( true );
235  mModel->setSymbolType( type );
236 }
237 
239 {
240  mModel->setLayerType( type );
241 }
242 
244 {
245  return groupsCombo->currentData().toString() == QLatin1String( "tag" ) ? groupsCombo->currentText() : QString();
246 }
247 
249 {
250  return btnAdvanced->menu();
251 }
252 
254 {
255  if ( menu ) // show it if there is a menu pointer
256  {
257  btnAdvanced->show();
258  btnAdvanced->setMenu( menu );
259  }
260 }
261 
263 {
264  btnAdvanced->setVisible( enabled );
265 }
266 
268 {
269  const QItemSelection selection = viewSymbols->selectionModel()->selection();
270  if ( selection.isEmpty() )
271  return QString();
272 
273  const QModelIndex index = selection.at( 0 ).topLeft();
274 
275  return mModel->data( index, QgsStyleModel::Name ).toString();
276 }
277 
279 {
280  const QItemSelection selection = viewSymbols->selectionModel()->selection();
281  if ( selection.isEmpty() )
282  return QgsStyle::SymbolEntity;
283 
284  const QModelIndex index = selection.at( 0 ).topLeft();
285 
286  return static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
287 }
288 
289 void QgsStyleItemsListWidget::showEvent( QShowEvent *event )
290 {
291  // restore header sizes on show event -- because this widget is used in multiple places simultaneously
292  // (e.g. layer styling dock, it's shown in both the symbology and labeling sections), then we want
293  // to ensure that a header resize for any of the widgets applies the next time any other item list widgets
294  // are shown.
295  QWidget::showEvent( event );
296  const QgsSettings settings;
297  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
298 }
299 
300 void QgsStyleItemsListWidget::populateGroups()
301 {
302  if ( !mStyle )
303  return;
304 
305  mUpdatingGroups = true;
306  groupsCombo->blockSignals( true );
307  groupsCombo->clear();
308 
309  groupsCombo->addItem( tr( "Favorites" ), QVariant( "favorite" ) );
310 
311  QString allText = tr( "All Symbols" );
312  if ( mModel->entityFilterEnabled() )
313  {
314  switch ( mModel->entityFilter() )
315  {
317  allText = tr( "All Symbols" );
318  break;
319 
321  allText = tr( "All Color Ramps" );
322  break;
323 
325  allText = tr( "All Text Formats" );
326  break;
327 
329  allText = tr( "All Label Settings" );
330  break;
331 
333  allText = tr( "All Legend Patch Shapes" );
334  break;
335 
337  allText = tr( "All 3D Symbols" );
338  break;
339 
340  case QgsStyle::TagEntity:
342  break;
343  }
344  }
345 
346  groupsCombo->addItem( allText, QVariant( "all" ) );
347 
348  int index = 2;
349  QStringList tags = mStyle->tags();
350  if ( tags.count() > 0 )
351  {
352  tags.sort();
353  groupsCombo->insertSeparator( index );
354  const auto constTags = tags;
355  for ( const QString &tag : constTags )
356  {
357  groupsCombo->addItem( tag, QVariant( "tag" ) );
358  index++;
359  }
360  }
361 
362  QStringList groups = mStyle->smartgroupNames();
363  if ( groups.count() > 0 )
364  {
365  groups.sort();
366  groupsCombo->insertSeparator( index + 1 );
367  const auto constGroups = groups;
368  for ( const QString &group : constGroups )
369  {
370  groupsCombo->addItem( group, QVariant( "smartgroup" ) );
371  }
372  }
373  groupsCombo->blockSignals( false );
374 
375  const QgsSettings settings;
376  index = settings.value( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 ).toInt();
377  groupsCombo->setCurrentIndex( index );
378 
379  mUpdatingGroups = false;
380 
381  updateModelFilters();
382 }
383 
384 void QgsStyleItemsListWidget::updateModelFilters()
385 {
386  if ( mUpdatingGroups || !mModel )
387  return;
388 
389  const QString text = groupsCombo->currentText();
390  const bool isFreeText = text != groupsCombo->itemText( groupsCombo->currentIndex() );
391 
392  if ( isFreeText )
393  {
394  mModel->setFavoritesOnly( false );
395  mModel->setTagId( -1 );
396  mModel->setSmartGroupId( -1 );
397  mModel->setFilterString( groupsCombo->currentText() );
398  }
399  else if ( groupsCombo->currentData().toString() == QLatin1String( "favorite" ) )
400  {
401  mModel->setFavoritesOnly( true );
402  mModel->setTagId( -1 );
403  mModel->setSmartGroupId( -1 );
404  mModel->setFilterString( QString() );
405  }
406  else if ( groupsCombo->currentData().toString() == QLatin1String( "all" ) )
407  {
408  mModel->setFavoritesOnly( false );
409  mModel->setTagId( -1 );
410  mModel->setSmartGroupId( -1 );
411  mModel->setFilterString( QString() );
412  }
413  else if ( groupsCombo->currentData().toString() == QLatin1String( "smartgroup" ) )
414  {
415  mModel->setFavoritesOnly( false );
416  mModel->setTagId( -1 );
417  mModel->setSmartGroupId( mStyle->smartgroupId( text ) );
418  mModel->setFilterString( QString() );
419  }
420  else
421  {
422  mModel->setFavoritesOnly( false );
423  mModel->setTagId( mStyle->tagId( text ) );
424  mModel->setSmartGroupId( -1 );
425  mModel->setFilterString( QString() );
426  }
427 }
428 
429 void QgsStyleItemsListWidget::openStyleManager()
430 {
431  // prefer to use global window manager to open the style manager, if possible!
432  // this allows reuse of an existing non-modal window instead of opening a new modal window.
433  // Note that we only use the non-modal dialog if we're open in the panel -- if we're already
434  // open as part of a modal dialog, then we MUST use another modal dialog or the result will
435  // not be focusable!
437  if ( !panel || !panel->dockMode()
439  || !QgsGui::windowManager()->openStandardDialog( QgsWindowManagerInterface::DialogStyleManager ) )
440  {
441  // fallback to modal dialog
442  QgsStyleManagerDialog dlg( mStyle, this );
443  dlg.exec();
444 
445  updateModelFilters(); // probably not needed -- the model should automatically update if any changes were made
446  }
447 }
448 
449 void QgsStyleItemsListWidget::onSelectionChanged( const QModelIndex &index )
450 {
451  if ( !mModel )
452  return;
453 
454  const QString symbolName = mModel->data( mModel->index( index.row(), QgsStyleModel::Name ) ).toString();
455  lblSymbolName->setText( symbolName );
456 
457  emit selectionChanged( symbolName, static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() ) );
458 }
459 
460 void QgsStyleItemsListWidget::groupsCombo_currentIndexChanged( int index )
461 {
462  QgsSettings settings;
463  settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), index );
464 }
SymbolType
Symbol types.
Definition: qgis.h:205
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1785
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:189
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.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
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:1425
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:2257
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:143
int tagId(const QString &tag)
Returns the database id for the given tag name.
Definition: qgsstyle.cpp:2252
QStringList smartgroupNames() const
Returns the smart groups list.
Definition: qgsstyle.cpp:2372
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.