QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
76 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
77  double iconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 10;
78 #else
79  double iconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 10;
80 #endif
81  viewSymbols->setIconSize( QSize( static_cast< int >( iconSize ), static_cast< int >( iconSize * 0.9 ) ) ); // ~100, 90 on low dpi
82 
83 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
84  double treeIconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 2;
85 #else
86  double treeIconSize = Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 2;
87 #endif
88  mSymbolTreeView->setIconSize( QSize( static_cast< int >( treeIconSize ), static_cast< int >( treeIconSize ) ) );
89  mSymbolTreeView->setMinimumHeight( mSymbolTreeView->fontMetrics().height() * 6 );
90 
91  viewSymbols->setSelectionBehavior( QAbstractItemView::SelectRows );
92  mSymbolTreeView->setSelectionMode( viewSymbols->selectionMode() );
93 
94  connect( openStyleManagerButton, &QToolButton::clicked, this, &QgsStyleItemsListWidget::openStyleManager );
95 
96  lblSymbolName->clear();
97 
98  connect( mButtonIconView, &QToolButton::toggled, this, [ = ]( bool active )
99  {
100  if ( active )
101  {
102  mSymbolViewStackedWidget->setCurrentIndex( 0 );
103  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
104  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui );
105  }
106  } );
107  connect( mButtonListView, &QToolButton::toggled, this, [ = ]( bool active )
108  {
109  if ( active )
110  {
111  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 1, QgsSettings::Gui );
112  mSymbolViewStackedWidget->setCurrentIndex( 1 );
113  }
114  } );
115 
116  // restore previous view
117  QgsSettings settings;
118  const int currentView = settings.value( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui ).toInt();
119  if ( currentView == 0 )
120  mButtonIconView->setChecked( true );
121  else
122  mButtonListView->setChecked( true );
123 
124  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
125  connect( mSymbolTreeView->header(), &QHeaderView::sectionResized, this, [this]
126  {
127  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
128  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/treeState" ), mSymbolTreeView->header()->saveState(), QgsSettings::Gui );
129  } );
130 
131  QgsFilterLineEdit *groupEdit = new QgsFilterLineEdit();
132  groupEdit->setShowSearchIcon( true );
133  groupEdit->setShowClearButton( true );
134  groupEdit->setPlaceholderText( tr( "Filter symbols…" ) );
135  groupsCombo->setLineEdit( groupEdit );
136 
137  connect( btnSaveSymbol, &QPushButton::clicked, this, &QgsStyleItemsListWidget::saveEntity );
138 }
139 
141 {
142  mStyle = style;
143 
144  mModel = mStyle == QgsStyle::defaultStyle() ? new QgsReadOnlyStyleModel( QgsApplication::defaultStyleModel(), this )
145  : new QgsReadOnlyStyleModel( mStyle, this );
146 
147  mModel->addDesiredIconSize( viewSymbols->iconSize() );
148  mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
149  viewSymbols->setModel( mModel );
150  mSymbolTreeView->setModel( mModel );
151 
152  connect( mStyle, &QgsStyle::groupsModified, this, &QgsStyleItemsListWidget::populateGroups );
153 
154  mSymbolTreeView->setSelectionModel( viewSymbols->selectionModel() );
155  connect( viewSymbols->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleItemsListWidget::onSelectionChanged );
156 
157  populateGroups();
158  connect( groupsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsStyleItemsListWidget::groupsCombo_currentIndexChanged );
159  connect( groupsCombo, &QComboBox::currentTextChanged, this, &QgsStyleItemsListWidget::updateModelFilters );
160 
161  QgsSettings settings;
162  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
163 }
164 
166 {
167  mModel->setEntityFilterEnabled( true );
168  mModel->setEntityFilter( type );
169  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
170  switch ( type )
171  {
173  btnSaveSymbol->setText( tr( "Save Symbol…" ) );
174  btnSaveSymbol->setToolTip( tr( "Save symbol to styles" ) );
175  if ( allGroup >= 0 )
176  groupsCombo->setItemText( allGroup, tr( "All Symbols" ) );
177  break;
178 
180  btnSaveSymbol->setText( tr( "Save Color Ramp…" ) );
181  btnSaveSymbol->setToolTip( tr( "Save color ramp to styles" ) );
182  if ( allGroup >= 0 )
183  groupsCombo->setItemText( allGroup, tr( "All Color Ramps" ) );
184  break;
185 
187  btnSaveSymbol->setText( tr( "Save Format…" ) );
188  btnSaveSymbol->setToolTip( tr( "Save text format to styles" ) );
189  if ( allGroup >= 0 )
190  groupsCombo->setItemText( allGroup, tr( "All Text Formats" ) );
191  break;
192 
194  btnSaveSymbol->setText( tr( "Save Label Settings…" ) );
195  btnSaveSymbol->setToolTip( tr( "Save label settings to styles" ) );
196  if ( allGroup >= 0 )
197  groupsCombo->setItemText( allGroup, tr( "All Label Settings" ) );
198  break;
199 
201  btnSaveSymbol->setText( tr( "Save Legend Patch Shape…" ) );
202  btnSaveSymbol->setToolTip( tr( "Save legend patch shape to styles" ) );
203  if ( allGroup >= 0 )
204  groupsCombo->setItemText( allGroup, tr( "All Legend Patch Shapes" ) );
205  break;
206 
208  btnSaveSymbol->setText( tr( "Save 3D Symbol…" ) );
209  btnSaveSymbol->setToolTip( tr( "Save 3D symbol to styles" ) );
210  if ( allGroup >= 0 )
211  groupsCombo->setItemText( allGroup, tr( "All 3D Symbols" ) );
212  break;
213 
214  case QgsStyle::TagEntity:
216  break;
217  }
218 }
219 
220 void QgsStyleItemsListWidget::setEntityTypes( const QList<QgsStyle::StyleEntity> &filters )
221 {
222  mModel->setEntityFilterEnabled( true );
223  mModel->setEntityFilters( filters );
224 
225  // bit of a gross hack -- run now! this will need revisiting when other parent widgets use different filter combinations!
226  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
227  if ( filters.length() == 2 && filters.contains( QgsStyle::LabelSettingsEntity ) && filters.contains( QgsStyle::TextFormatEntity ) )
228  {
229  btnSaveSymbol->setText( tr( "Save Settings…" ) );
230  btnSaveSymbol->setToolTip( tr( "Save label settings or text format to styles" ) );
231  if ( allGroup >= 0 )
232  groupsCombo->setItemText( allGroup, tr( "All Settings" ) );
233  }
234 }
235 
237 {
238  mModel->setSymbolTypeFilterEnabled( true );
239  mModel->setSymbolType( type );
240 }
241 
243 {
244  mModel->setLayerType( type );
245 }
246 
248 {
249  return groupsCombo->currentData().toString() == QLatin1String( "tag" ) ? groupsCombo->currentText() : QString();
250 }
251 
253 {
254  return btnAdvanced->menu();
255 }
256 
258 {
259  if ( menu ) // show it if there is a menu pointer
260  {
261  btnAdvanced->show();
262  btnAdvanced->setMenu( menu );
263  }
264 }
265 
267 {
268  btnAdvanced->setVisible( enabled );
269 }
270 
272 {
273  QItemSelection selection = viewSymbols->selectionModel()->selection();
274  if ( selection.isEmpty() )
275  return QString();
276 
277  const QModelIndex index = selection.at( 0 ).topLeft();
278 
279  return mModel->data( index, QgsStyleModel::Name ).toString();
280 }
281 
283 {
284  QItemSelection selection = viewSymbols->selectionModel()->selection();
285  if ( selection.isEmpty() )
286  return QgsStyle::SymbolEntity;
287 
288  const QModelIndex index = selection.at( 0 ).topLeft();
289 
290  return static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
291 }
292 
293 void QgsStyleItemsListWidget::showEvent( QShowEvent *event )
294 {
295  // restore header sizes on show event -- because this widget is used in multiple places simultaneously
296  // (e.g. layer styling dock, it's shown in both the symbology and labeling sections), then we want
297  // to ensure that a header resize for any of the widgets applies the next time any other item list widgets
298  // are shown.
299  QWidget::showEvent( event );
300  QgsSettings settings;
301  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
302 }
303 
304 void QgsStyleItemsListWidget::populateGroups()
305 {
306  if ( !mStyle )
307  return;
308 
309  mUpdatingGroups = true;
310  groupsCombo->blockSignals( true );
311  groupsCombo->clear();
312 
313  groupsCombo->addItem( tr( "Favorites" ), QVariant( "favorite" ) );
314 
315  QString allText = tr( "All Symbols" );
316  if ( mModel->entityFilterEnabled() )
317  {
318  switch ( mModel->entityFilter() )
319  {
321  allText = tr( "All Symbols" );
322  break;
323 
325  allText = tr( "All Color Ramps" );
326  break;
327 
329  allText = tr( "All Text Formats" );
330  break;
331 
333  allText = tr( "All Label Settings" );
334  break;
335 
337  allText = tr( "All Legend Patch Shapes" );
338  break;
339 
341  allText = tr( "All 3D Symbols" );
342  break;
343 
344  case QgsStyle::TagEntity:
346  break;
347  }
348  }
349 
350  groupsCombo->addItem( allText, QVariant( "all" ) );
351 
352  int index = 2;
353  QStringList tags = mStyle->tags();
354  if ( tags.count() > 0 )
355  {
356  tags.sort();
357  groupsCombo->insertSeparator( index );
358  const auto constTags = tags;
359  for ( const QString &tag : constTags )
360  {
361  groupsCombo->addItem( tag, QVariant( "tag" ) );
362  index++;
363  }
364  }
365 
366  QStringList groups = mStyle->smartgroupNames();
367  if ( groups.count() > 0 )
368  {
369  groups.sort();
370  groupsCombo->insertSeparator( index + 1 );
371  const auto constGroups = groups;
372  for ( const QString &group : constGroups )
373  {
374  groupsCombo->addItem( group, QVariant( "smartgroup" ) );
375  }
376  }
377  groupsCombo->blockSignals( false );
378 
379  QgsSettings settings;
380  index = settings.value( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 ).toInt();
381  groupsCombo->setCurrentIndex( index );
382 
383  mUpdatingGroups = false;
384 
385  updateModelFilters();
386 }
387 
388 void QgsStyleItemsListWidget::updateModelFilters()
389 {
390  if ( mUpdatingGroups || !mModel )
391  return;
392 
393  const QString text = groupsCombo->currentText();
394  const bool isFreeText = text != groupsCombo->itemText( groupsCombo->currentIndex() );
395 
396  if ( isFreeText )
397  {
398  mModel->setFavoritesOnly( false );
399  mModel->setTagId( -1 );
400  mModel->setSmartGroupId( -1 );
401  mModel->setFilterString( groupsCombo->currentText() );
402  }
403  else if ( groupsCombo->currentData().toString() == QLatin1String( "favorite" ) )
404  {
405  mModel->setFavoritesOnly( true );
406  mModel->setTagId( -1 );
407  mModel->setSmartGroupId( -1 );
408  mModel->setFilterString( QString() );
409  }
410  else if ( groupsCombo->currentData().toString() == QLatin1String( "all" ) )
411  {
412  mModel->setFavoritesOnly( false );
413  mModel->setTagId( -1 );
414  mModel->setSmartGroupId( -1 );
415  mModel->setFilterString( QString() );
416  }
417  else if ( groupsCombo->currentData().toString() == QLatin1String( "smartgroup" ) )
418  {
419  mModel->setFavoritesOnly( false );
420  mModel->setTagId( -1 );
421  mModel->setSmartGroupId( mStyle->smartgroupId( text ) );
422  mModel->setFilterString( QString() );
423  }
424  else
425  {
426  mModel->setFavoritesOnly( false );
427  mModel->setTagId( mStyle->tagId( text ) );
428  mModel->setSmartGroupId( -1 );
429  mModel->setFilterString( QString() );
430  }
431 }
432 
433 void QgsStyleItemsListWidget::openStyleManager()
434 {
435  // prefer to use global window manager to open the style manager, if possible!
436  // this allows reuse of an existing non-modal window instead of opening a new modal window.
437  // Note that we only use the non-modal dialog if we're open in the panel -- if we're already
438  // open as part of a modal dialog, then we MUST use another modal dialog or the result will
439  // not be focusable!
441  if ( !panel || !panel->dockMode()
443  || !QgsGui::windowManager()->openStandardDialog( QgsWindowManagerInterface::DialogStyleManager ) )
444  {
445  // fallback to modal dialog
446  QgsStyleManagerDialog dlg( mStyle, this );
447  dlg.exec();
448 
449  updateModelFilters(); // probably not needed -- the model should automatically update if any changes were made
450  }
451 }
452 
453 void QgsStyleItemsListWidget::onSelectionChanged( const QModelIndex &index )
454 {
455  if ( !mModel )
456  return;
457 
458  QString symbolName = mModel->data( mModel->index( index.row(), QgsStyleModel::Name ) ).toString();
459  lblSymbolName->setText( symbolName );
460 
461  emit selectionChanged( symbolName, static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() ) );
462 }
463 
464 void QgsStyleItemsListWidget::groupsCombo_currentIndexChanged( int index )
465 {
466  QgsSettings settings;
467  settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), index );
468 }
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:183
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.
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.
void setSymbolType(QgsSymbol::SymbolType type)
Sets the type of symbols to show 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 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.
void setSymbolType(QgsSymbol::SymbolType type)
Sets the symbol type filter.
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.
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:1383
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:2215
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:128
int tagId(const QString &tag)
Returns the database id for the given tag name.
Definition: qgsstyle.cpp:2210
QStringList smartgroupNames() const
Returns the smart groups list.
Definition: qgsstyle.cpp:2328
SymbolType
Type of the symbol.
Definition: qgssymbol.h:87
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.