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