QGIS API Documentation  3.13.0-Master (5a3b1ced84)
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 
90  viewSymbols->setSelectionBehavior( QAbstractItemView::SelectRows );
91  mSymbolTreeView->setSelectionMode( viewSymbols->selectionMode() );
92 
93  connect( openStyleManagerButton, &QToolButton::clicked, this, &QgsStyleItemsListWidget::openStyleManager );
94 
95  lblSymbolName->clear();
96 
97  connect( mButtonIconView, &QToolButton::toggled, this, [ = ]( bool active )
98  {
99  if ( active )
100  {
101  mSymbolViewStackedWidget->setCurrentIndex( 0 );
102  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
103  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui );
104  }
105  } );
106  connect( mButtonListView, &QToolButton::toggled, this, [ = ]( bool active )
107  {
108  if ( active )
109  {
110  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/lastIconView" ), 1, QgsSettings::Gui );
111  mSymbolViewStackedWidget->setCurrentIndex( 1 );
112  }
113  } );
114 
115  // restore previous view
116  QgsSettings settings;
117  const int currentView = settings.value( QStringLiteral( "UI/symbolsList/lastIconView" ), 0, QgsSettings::Gui ).toInt();
118  if ( currentView == 0 )
119  mButtonIconView->setChecked( true );
120  else
121  mButtonListView->setChecked( true );
122 
123  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
124  connect( mSymbolTreeView->header(), &QHeaderView::sectionResized, this, [this]
125  {
126  // note -- we have to save state here and not in destructor, as new symbol list widgets are created before the previous ones are destroyed
127  QgsSettings().setValue( QStringLiteral( "UI/symbolsList/treeState" ), mSymbolTreeView->header()->saveState(), QgsSettings::Gui );
128  } );
129 
130  QgsFilterLineEdit *groupEdit = new QgsFilterLineEdit();
131  groupEdit->setShowSearchIcon( true );
132  groupEdit->setShowClearButton( true );
133  groupEdit->setPlaceholderText( tr( "Filter symbols…" ) );
134  groupsCombo->setLineEdit( groupEdit );
135 
136  connect( btnSaveSymbol, &QPushButton::clicked, this, &QgsStyleItemsListWidget::saveEntity );
137 }
138 
140 {
141  mStyle = style;
142 
143  mModel = mStyle == QgsStyle::defaultStyle() ? new QgsReadOnlyStyleModel( QgsApplication::defaultStyleModel(), this )
144  : new QgsReadOnlyStyleModel( mStyle, this );
145 
146  mModel->addDesiredIconSize( viewSymbols->iconSize() );
147  mModel->addDesiredIconSize( mSymbolTreeView->iconSize() );
148  viewSymbols->setModel( mModel );
149  mSymbolTreeView->setModel( mModel );
150 
151  connect( mStyle, &QgsStyle::groupsModified, this, &QgsStyleItemsListWidget::populateGroups );
152 
153  mSymbolTreeView->setSelectionModel( viewSymbols->selectionModel() );
154  connect( viewSymbols->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsStyleItemsListWidget::onSelectionChanged );
155 
156  populateGroups();
157  connect( groupsCombo, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsStyleItemsListWidget::groupsCombo_currentIndexChanged );
158  connect( groupsCombo, &QComboBox::currentTextChanged, this, &QgsStyleItemsListWidget::updateModelFilters );
159 
160  QgsSettings settings;
161  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
162 }
163 
165 {
166  mModel->setEntityFilterEnabled( true );
167  mModel->setEntityFilter( type );
168  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
169  switch ( type )
170  {
172  btnSaveSymbol->setText( tr( "Save Symbol…" ) );
173  btnSaveSymbol->setToolTip( tr( "Save symbol to styles" ) );
174  if ( allGroup >= 0 )
175  groupsCombo->setItemText( allGroup, tr( "All Symbols" ) );
176  break;
177 
179  btnSaveSymbol->setText( tr( "Save Color Ramp…" ) );
180  btnSaveSymbol->setToolTip( tr( "Save color ramp to styles" ) );
181  if ( allGroup >= 0 )
182  groupsCombo->setItemText( allGroup, tr( "All Color Ramps" ) );
183  break;
184 
186  btnSaveSymbol->setText( tr( "Save Format…" ) );
187  btnSaveSymbol->setToolTip( tr( "Save text format to styles" ) );
188  if ( allGroup >= 0 )
189  groupsCombo->setItemText( allGroup, tr( "All Text Formats" ) );
190  break;
191 
193  btnSaveSymbol->setText( tr( "Save Label Settings…" ) );
194  btnSaveSymbol->setToolTip( tr( "Save label settings to styles" ) );
195  if ( allGroup >= 0 )
196  groupsCombo->setItemText( allGroup, tr( "All Label Settings" ) );
197  break;
198 
199  case QgsStyle::TagEntity:
201  break;
202  }
203 }
204 
205 void QgsStyleItemsListWidget::setEntityTypes( const QList<QgsStyle::StyleEntity> &filters )
206 {
207  mModel->setEntityFilterEnabled( true );
208  mModel->setEntityFilters( filters );
209 
210  // bit of a gross hack -- run now! this will need revisiting when other parent widgets use different filter combinations!
211  const int allGroup = groupsCombo->findData( QVariant( "all" ) );
212  if ( filters.length() == 2 && filters.contains( QgsStyle::LabelSettingsEntity ) && filters.contains( QgsStyle::TextFormatEntity ) )
213  {
214  btnSaveSymbol->setText( tr( "Save Settings…" ) );
215  btnSaveSymbol->setToolTip( tr( "Save label settings or text format to styles" ) );
216  if ( allGroup >= 0 )
217  groupsCombo->setItemText( allGroup, tr( "All Settings" ) );
218  }
219 }
220 
222 {
223  mModel->setSymbolTypeFilterEnabled( true );
224  mModel->setSymbolType( type );
225 }
226 
228 {
229  mModel->setLayerType( type );
230 }
231 
233 {
234  return groupsCombo->currentData().toString() == QLatin1String( "tag" ) ? groupsCombo->currentText() : QString();
235 }
236 
238 {
239  return btnAdvanced->menu();
240 }
241 
243 {
244  if ( menu ) // show it if there is a menu pointer
245  {
246  btnAdvanced->show();
247  btnAdvanced->setMenu( menu );
248  }
249 }
250 
252 {
253  btnAdvanced->setVisible( enabled );
254 }
255 
257 {
258  QItemSelection selection = viewSymbols->selectionModel()->selection();
259  if ( selection.isEmpty() )
260  return QString();
261 
262  const QModelIndex index = selection.at( 0 ).topLeft();
263 
264  return mModel->data( index, QgsStyleModel::Name ).toString();
265 }
266 
268 {
269  QItemSelection selection = viewSymbols->selectionModel()->selection();
270  if ( selection.isEmpty() )
271  return QgsStyle::SymbolEntity;
272 
273  const QModelIndex index = selection.at( 0 ).topLeft();
274 
275  return static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() );
276 }
277 
278 void QgsStyleItemsListWidget::showEvent( QShowEvent *event )
279 {
280  // restore header sizes on show event -- because this widget is used in multiple places simultaneously
281  // (e.g. layer styling dock, it's shown in both the symbology and labeling sections), then we want
282  // to ensure that a header resize for any of the widgets applies the next time any other item list widgets
283  // are shown.
284  QWidget::showEvent( event );
285  QgsSettings settings;
286  mSymbolTreeView->header()->restoreState( settings.value( QStringLiteral( "UI/symbolsList/treeState" ), QByteArray(), QgsSettings::Gui ).toByteArray() );
287 }
288 
289 void QgsStyleItemsListWidget::populateGroups()
290 {
291  if ( !mStyle )
292  return;
293 
294  mUpdatingGroups = true;
295  groupsCombo->blockSignals( true );
296  groupsCombo->clear();
297 
298  groupsCombo->addItem( tr( "Favorites" ), QVariant( "favorite" ) );
299 
300  QString allText = tr( "All Symbols" );
301  if ( mModel->entityFilterEnabled() )
302  {
303  switch ( mModel->entityFilter() )
304  {
306  allText = tr( "All Symbols" );
307  break;
308 
310  allText = tr( "All Color Ramps" );
311  break;
312 
314  allText = tr( "All Text Formats" );
315  break;
316 
318  allText = tr( "All Label Settings" );
319  break;
320 
321  case QgsStyle::TagEntity:
323  break;
324  }
325  }
326 
327  groupsCombo->addItem( allText, QVariant( "all" ) );
328 
329  int index = 2;
330  QStringList tags = mStyle->tags();
331  if ( tags.count() > 0 )
332  {
333  tags.sort();
334  groupsCombo->insertSeparator( index );
335  const auto constTags = tags;
336  for ( const QString &tag : constTags )
337  {
338  groupsCombo->addItem( tag, QVariant( "tag" ) );
339  index++;
340  }
341  }
342 
343  QStringList groups = mStyle->smartgroupNames();
344  if ( groups.count() > 0 )
345  {
346  groups.sort();
347  groupsCombo->insertSeparator( index + 1 );
348  const auto constGroups = groups;
349  for ( const QString &group : constGroups )
350  {
351  groupsCombo->addItem( group, QVariant( "smartgroup" ) );
352  }
353  }
354  groupsCombo->blockSignals( false );
355 
356  QgsSettings settings;
357  index = settings.value( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 ).toInt();
358  groupsCombo->setCurrentIndex( index );
359 
360  mUpdatingGroups = false;
361 
362  updateModelFilters();
363 }
364 
365 void QgsStyleItemsListWidget::updateModelFilters()
366 {
367  if ( mUpdatingGroups || !mModel )
368  return;
369 
370  const QString text = groupsCombo->currentText();
371  const bool isFreeText = text != groupsCombo->itemText( groupsCombo->currentIndex() );
372 
373  if ( isFreeText )
374  {
375  mModel->setFavoritesOnly( false );
376  mModel->setTagId( -1 );
377  mModel->setSmartGroupId( -1 );
378  mModel->setFilterString( groupsCombo->currentText() );
379  }
380  else if ( groupsCombo->currentData().toString() == QLatin1String( "favorite" ) )
381  {
382  mModel->setFavoritesOnly( true );
383  mModel->setTagId( -1 );
384  mModel->setSmartGroupId( -1 );
385  mModel->setFilterString( QString() );
386  }
387  else if ( groupsCombo->currentData().toString() == QLatin1String( "all" ) )
388  {
389  mModel->setFavoritesOnly( false );
390  mModel->setTagId( -1 );
391  mModel->setSmartGroupId( -1 );
392  mModel->setFilterString( QString() );
393  }
394  else if ( groupsCombo->currentData().toString() == QLatin1String( "smartgroup" ) )
395  {
396  mModel->setFavoritesOnly( false );
397  mModel->setTagId( -1 );
398  mModel->setSmartGroupId( mStyle->smartgroupId( text ) );
399  mModel->setFilterString( QString() );
400  }
401  else
402  {
403  mModel->setFavoritesOnly( false );
404  mModel->setTagId( mStyle->tagId( text ) );
405  mModel->setSmartGroupId( -1 );
406  mModel->setFilterString( QString() );
407  }
408 }
409 
410 void QgsStyleItemsListWidget::openStyleManager()
411 {
412  // prefer to use global window manager to open the style manager, if possible!
413  // this allows reuse of an existing non-modal window instead of opening a new modal window.
414  // Note that we only use the non-modal dialog if we're open in the panel -- if we're already
415  // open as part of a modal dialog, then we MUST use another modal dialog or the result will
416  // not be focusable!
418  if ( !panel || !panel->dockMode()
420  || !QgsGui::windowManager()->openStandardDialog( QgsWindowManagerInterface::DialogStyleManager ) )
421  {
422  // fallback to modal dialog
423  QgsStyleManagerDialog dlg( mStyle, this );
424  dlg.exec();
425 
426  updateModelFilters(); // probably not needed -- the model should automatically update if any changes were made
427  }
428 }
429 
430 void QgsStyleItemsListWidget::onSelectionChanged( const QModelIndex &index )
431 {
432  if ( !mModel )
433  return;
434 
435  QString symbolName = mModel->data( mModel->index( index.row(), QgsStyleModel::Name ) ).toString();
436  lblSymbolName->setText( symbolName );
437 
438  emit selectionChanged( symbolName, static_cast< QgsStyle::StyleEntity >( mModel->data( index, QgsStyleModel::TypeRole ).toInt() ) );
439 }
440 
441 void QgsStyleItemsListWidget::groupsCombo_currentIndexChanged( int index )
442 {
443  QgsSettings settings;
444  settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), index );
445 }
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
Definition: qgsstylemodel.h:45
QgsStyle::StyleEntity currentEntityType() const
Returns the type of the item currently selected in the widget.
void setFilterString(const QString &filter)
Sets a filter string, such that only symbol entities with names matching the specified string will be...
static QgsWindowManagerInterface * windowManager()
Returns the global window manager, if set.
Definition: qgsgui.cpp:142
bool dockMode()
Returns the dock mode state.
A dialog allowing users to customize and populate a QgsStyle.
void setSmartGroupId(int id)
Sets a smart group id to filter style entities by.
QString currentItemName() const
Returns the name of the item currently selected in the widget.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:182
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setLayerType(QgsWkbTypes::GeometryType type)
Sets the layer type to show in the widget.
void setTagId(int id)
Sets a tag id to filter style entities by.
void saveEntity()
Emitted when the user has opted to save a new entity to the style database, by clicking the "Save" bu...
void setSymbolType(QgsSymbol::SymbolType type)
Sets the symbol type filter.
Base class for any widget that can be shown as a inline panel.
void setFavoritesOnly(bool favoritesOnly)
Sets whether the model should show only favorited entities.
void selectionChanged(const QString &name, QgsStyle::StyleEntity type)
Emitted when the selected item is changed in the widget.
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:74
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstyle.h:177
void setSymbolTypeFilterEnabled(bool enabled)
Sets whether filtering by symbol type is enabled.
SymbolType
Type of the symbol.
Definition: qgssymbol.h:84
void setAdvancedMenu(QMenu *menu)
Sets the widget&#39;s advanced menu, which is shown when the user clicks the "Advanced" button in the wid...
void setEntityFilterEnabled(bool enabled)
Sets whether filtering by entity type is enabled.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget...
void groupsModified()
Emitted every time a tag or smartgroup has been added, removed, or renamed.
void setEntityTypes(const QList< QgsStyle::StyleEntity > &filters) SIP_SKIP
Sets the types of style entity to show in the widget.
QMenu * advancedMenu()
Returns a pointer to the widget&#39;s current advanced menu.
QLineEdit subclass with built in support for clearing the widget&#39;s value and handling custom null val...
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
A QSortFilterProxyModel subclass for showing filtered symbol and color ramps entries from a QgsStyle ...
void setLayerType(QgsWkbTypes::GeometryType type)
Sets the layer type filter.
int smartgroupId(const QString &smartgroup)
Returns the DB id for the given smartgroup name.
Definition: qgsstyle.cpp:2081
int tagId(const QString &tag)
Returns the DB id for the given tag name.
Definition: qgsstyle.cpp:2076
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:139
QStringList tags() const
Returns a list of all tags in the style database.
Definition: qgsstyle.cpp:1032
void setSymbolType(QgsSymbol::SymbolType type)
Sets the type of symbols to show in the widget.
Name column.
Definition: qgsstylemodel.h:54
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool entityFilterEnabled() const
Returns true if filtering by entity type is enabled.
void showAdvancedButton(bool enabled)
Sets whether the advanced button should be shown in the widget.
void setStyle(QgsStyle *style)
Sets the style database associated with the widget.
void showEvent(QShowEvent *event) override
void setEntityFilter(QgsStyle::StyleEntity filter)
Sets the style entity type filter.
void setEntityType(QgsStyle::StyleEntity type)
Sets the type of style entity to show in the widget.
Style entity type, see QgsStyle::StyleEntity.
Definition: qgsstylemodel.h:61
QgsStyle::StyleEntity entityFilter() const
Returns the style entity type filter.
QString currentTagFilter() const
Returns the current tag filter set for the widget, if any is set.
QgsStyleItemsListWidget(QWidget *parent SIP_TRANSFERTHIS)
Constructor for QgsStyleItemsListWidget, with the specified parent widget.
void setEntityFilters(const QList< QgsStyle::StyleEntity > &filters)
Sets the style entity type filters.
QStringList smartgroupNames() const
Returns the smart groups list.
Definition: qgsstyle.cpp:2188