QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgssettingstreemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssettingstreemodel.cpp
3 --------------------------------------
4 Date : January 2023
5 Copyright : (C) 2023 by Denis Rouzaud
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#include <QGuiApplication>
17#include <QFont>
18
20#include "qgssettingsentry.h"
21#include "qgssettingstreenode.h"
24#include "qgsgui.h"
25#include "qgslogger.h"
26
28
29
30QgsSettingsTreeModelNodeData *QgsSettingsTreeModelNodeData::createRootNodeData( const QgsSettingsTreeNode *rootNode, QObject *parent = nullptr )
31{
32 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( parent );
33 nodeData->mType = Type::RootNode;
34 nodeData->mName = rootNode->key();
35 nodeData->mTreeNode = rootNode;
36 nodeData->fillChildren();
37 return nodeData;
38}
39
40void QgsSettingsTreeModelNodeData::applyChanges()
41{
42 switch ( type() )
43 {
44 case Type::NamedListTreeNode:
45 case Type::RootNode:
46 case Type::TreeNode:
47 case Type::NamedListItem:
48 {
49 QList<QgsSettingsTreeModelNodeData *>::iterator it = mChildren.begin();
50 for ( ; it != mChildren.end(); ++it )
51 ( *it )->applyChanges();
52 break;
53 }
54 case Type::Setting:
55 {
56 if ( isEdited() )
57 {
58 setting()->setVariantValue( mValue, mNamedParentNodes );
59 }
60 break;
61 }
62 }
63}
64
65bool QgsSettingsTreeModelNodeData::setValue( const QVariant &value )
66{
67 Q_ASSERT( mType == Type::Setting );
68 if ( !value.isValid() && mValue.isValid() )
69 {
70 mExists = false;
71 mIsEdited = true;
72 mValue = QVariant();
73 }
74 else if ( !mValue.isValid() || mValue != value )
75 {
76 mValue = value;
77 mIsEdited = ( value != mOriginalValue );
78 }
79 // TODO: check the value of setting is fulfilling the settings' contsraints ?
80 return true;
81}
82
83
84void QgsSettingsTreeModelNodeData::addChildForTreeNode( const QgsSettingsTreeNode *node )
85{
86 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
87 nodeData->mParent = this;
88 nodeData->mNamedParentNodes = mNamedParentNodes;
89 nodeData->mName = node->key();
90 nodeData->mTreeNode = node;
92 {
93 nodeData->mType = Type::NamedListTreeNode;
94 const QgsSettingsTreeNamedListNode *nln = dynamic_cast<const QgsSettingsTreeNamedListNode *>( node );
95 const QStringList items = nln->items( mNamedParentNodes );
96 for ( const QString &item : items )
97 {
98 nodeData->addChildForNamedListItemNode( item, nln );
99 }
100 }
101 else
102 {
103 nodeData->mType = Type::TreeNode;
104 nodeData->fillChildren();
105 }
106 mChildren.append( nodeData );
107}
108
109void QgsSettingsTreeModelNodeData::addChildForNamedListItemNode( const QString &item, const QgsSettingsTreeNamedListNode *namedListNode )
110{
111 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
112 nodeData->mType = Type::NamedListItem;
113 nodeData->mParent = this;
114 nodeData->mNamedParentNodes = mNamedParentNodes;
115 nodeData->mNamedParentNodes.append( item );
116 nodeData->mName = item;
117 nodeData->mTreeNode = namedListNode;
118 nodeData->fillChildren();
119 mChildren.append( nodeData );
120}
121
122void QgsSettingsTreeModelNodeData::addChildForSetting( const QgsSettingsEntryBase *setting )
123{
124 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
125 nodeData->mType = Type::Setting;
126 nodeData->mParent = this;
127 nodeData->mNamedParentNodes = mNamedParentNodes;
128 nodeData->mSetting = setting;
129 nodeData->mName = setting->name();
130 nodeData->mValue = setting->valueAsVariant( mNamedParentNodes );
131 nodeData->mOriginalValue = nodeData->mValue;
132 nodeData->mExists = setting->exists( mNamedParentNodes );
133
134 switch ( mNamedParentNodes.count() )
135 {
136 case 1:
137 QgsDebugMsgLevel( QString( "getting %1 with %2" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ) ), 3 );
138 break;
139 case 2:
140 QgsDebugMsgLevel( QString( "getting %1 with %2 and %3" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ), mNamedParentNodes.at( 1 ) ), 3 );
141 break;
142 case 0:
143 QgsDebugMsgLevel( QString( "getting %1" ).arg( setting->definitionKey() ), 3 );
144 break;
145 default:
146 Q_ASSERT( false );
147 QgsDebugError( QString( "Not handling that many named parent nodes for %1" ).arg( setting->definitionKey() ) );
148 break;
149 }
150
151 mChildren.append( nodeData );
152}
153
154void QgsSettingsTreeModelNodeData::fillChildren()
155{
156 const QList<QgsSettingsTreeNode *> childrenNodes = mTreeNode->childrenNodes();
157 for ( const QgsSettingsTreeNode *childNode : childrenNodes )
158 {
159 addChildForTreeNode( childNode );
160 }
161 const QList<const QgsSettingsEntryBase *> childrenSettings = mTreeNode->childrenSettings();
162 for ( const QgsSettingsEntryBase *setting : childrenSettings )
163 {
164 addChildForSetting( setting );
165 }
166}
167
169
170
171
173 : QAbstractItemModel( parent )
174{
175 mRootNode = QgsSettingsTreeModelNodeData::createRootNodeData( rootNode, this );
176
177 QPalette pal = qApp->palette();
178 mEditedColorBack = pal.color( QPalette::Active, QPalette::Dark );
179 mEditedColorFore = pal.color( QPalette::Active, QPalette::BrightText );
180 mNotSetColor = pal.color( QPalette::Disabled, QPalette::WindowText );
181}
182
184{
185 //delete mRootNode;
186}
187
189{
190 beginResetModel();
191 mRootNode->applyChanges();
192 endResetModel();
193}
194
195QgsSettingsTreeModelNodeData *QgsSettingsTreeModel::index2node( const QModelIndex &index ) const
196{
197 if ( !index.isValid() )
198 return mRootNode;
199
200 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
201 return qobject_cast<QgsSettingsTreeModelNodeData *>( obj );
202}
203
204QModelIndex QgsSettingsTreeModel::node2index( QgsSettingsTreeModelNodeData *node ) const
205{
206 if ( !node || !node->parent() )
207 return QModelIndex(); // this is the only root item -> invalid index
208
209 QModelIndex parentIndex = node2index( node->parent() );
210
211 int row = node->parent()->children().indexOf( node );
212 Q_ASSERT( row >= 0 );
213 return index( row, static_cast<int>( Column::Name ), parentIndex );
214}
215
216
217QModelIndex QgsSettingsTreeModel::index( int row, int column, const QModelIndex &parent ) const
218{
219 if ( column < 0 || column >= columnCount( parent ) ||
220 row < 0 || row >= rowCount( parent ) )
221 return QModelIndex();
222
223 QgsSettingsTreeModelNodeData *n = index2node( parent );
224 if ( !n )
225 return QModelIndex(); // have no children
226
227
228 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
229}
230
231QModelIndex QgsSettingsTreeModel::parent( const QModelIndex &child ) const
232{
233 if ( !child.isValid() )
234 return QModelIndex();
235
236 if ( QgsSettingsTreeModelNodeData *n = index2node( child ) )
237 {
238 return indexOfParentSettingsTreeNode( n->parent() ); // must not be null
239 }
240 else
241 {
242 Q_ASSERT( false ); // no other node types!
243 return QModelIndex();
244 }
245}
246
247QModelIndex QgsSettingsTreeModel::indexOfParentSettingsTreeNode( QgsSettingsTreeModelNodeData *parentNode ) const
248{
249 Q_ASSERT( parentNode );
250
251 const QgsSettingsTreeModelNodeData *grandParentNode = parentNode->parent();
252 if ( !grandParentNode )
253 return QModelIndex(); // root node -> invalid index
254
255 int row = grandParentNode->children().indexOf( parentNode );
256 Q_ASSERT( row >= 0 );
257
258 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
259}
260
261int QgsSettingsTreeModel::rowCount( const QModelIndex &parent ) const
262{
263 QgsSettingsTreeModelNodeData *n = index2node( parent );
264 if ( !n )
265 return 0;
266
267 return n->children().count();
268}
269
270int QgsSettingsTreeModel::columnCount( const QModelIndex &parent ) const
271{
272 Q_UNUSED( parent )
273 return 3;
274}
275
276QVariant QgsSettingsTreeModel::data( const QModelIndex &index, int role ) const
277{
278 if ( !index.isValid() || index.column() > columnCount( index ) )
279 return QVariant();
280
281 QgsSettingsTreeModelNodeData *node = index2node( index );
282
283 if ( role == Qt::ForegroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
284 {
285 if ( !node->exists() )
286 {
287 // settings not yet set
288 return mNotSetColor;
289 }
290
291 if ( node->isEdited() &&
292 ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
293 {
294 return mEditedColorFore;
295 }
296 }
297
298 if ( role == Qt::BackgroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
299 {
300 // background for edited settings (except colors)
301 if ( node->isEdited() &&
302 ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
303 {
304 return mEditedColorBack;
305 }
306 }
307
308 switch ( static_cast<Column>( index.column() ) )
309 {
310 case Column::Name:
311 {
312 if ( role == Qt::DisplayRole || role == Qt::EditRole )
313 {
314 return node->name();
315 }
316 break;
317 }
318
319 case Column::Value:
320 {
321 if ( role == Qt::CheckStateRole )
322 {
323 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting &&
324 node->setting()->settingsType() == Qgis::SettingsType::Bool )
325 {
326 // special handling of bool setting to show combobox
327 return node->value().toBool() ? Qt::Checked : Qt::Unchecked;
328 }
329 }
330 if ( role == Qt::DisplayRole || role == Qt::EditRole )
331 {
332 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting &&
333 node->setting()->settingsType() == Qgis::SettingsType::Bool )
334 {
335 // special handling of bool setting to show combobox
336 return QString();
337 }
338 else
339 {
340 return node->value();
341 }
342 }
343 else if ( role == Qt::BackgroundRole )
344 {
345 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
346 {
347 switch ( node->setting()->settingsType() )
348 {
358 break;
359
361 return node->value();
362 }
363 }
364 }
365 break;
366 }
367
369 {
370 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
371 {
372 if ( role == Qt::DisplayRole || role == Qt::EditRole )
373 {
374 return node->setting()->description();
375 }
376 }
377 break;
378 }
379
380 default:
381 break;
382 }
383 return QVariant();
384}
385
386
387QVariant QgsSettingsTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
388{
389 if ( orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole )
390 {
391 switch ( static_cast<Column>( section ) )
392 {
393 case Column::Name:
394 return tr( "Name" );
395 case Column::Value:
396 return tr( "Value" );
398 return tr( "Description" );
399 };
400 }
401
402 return QVariant();
403}
404
405Qt::ItemFlags QgsSettingsTreeModel::flags( const QModelIndex &index ) const
406{
407 if ( index.column() == static_cast<int>( Column::Value ) )
408 {
409 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
410 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
411 {
412 if ( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool )
413 {
414 return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
415 }
416 else
417 {
418 return Qt::ItemIsEnabled | Qt::ItemIsEditable;
419 }
420 }
421 }
422 else
423 {
424 return Qt::ItemIsEnabled;
425 }
426 return Qt::NoItemFlags;
427}
428
429bool QgsSettingsTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
430{
431 if ( index.column() == static_cast<int>( Column::Value ) )
432 {
433 if ( role == Qt::EditRole || role == Qt::CheckStateRole )
434 {
435 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
436 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
437 {
438 if ( role == Qt::CheckStateRole )
439 {
440 Q_ASSERT( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool );
441 nodeData->setValue( value == Qt::Checked ? true : false );
442 }
443 else
444 {
445 nodeData->setValue( value );
446 }
447 emit dataChanged( index.siblingAtColumn( 0 ), index.siblingAtColumn( columnCount( index.parent() ) - 1 ) );
448 return true;
449 }
450 }
451 }
452 return false;
453}
454
455
457
458
459QgsSettingsTreeItemDelegate::QgsSettingsTreeItemDelegate( QgsSettingsTreeModel *model, QObject *parent )
460 : QItemDelegate( parent )
461 , mModel( model )
462{
463}
464
465QWidget *QgsSettingsTreeItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
466{
467 Q_UNUSED( option )
468 if ( static_cast<QgsSettingsTreeModel::Column>( index.column() ) == QgsSettingsTreeModel::Column::Value )
469 {
470 QModelIndex sourceIndex = index;
471 const QgsSettingsTreeProxyModel *proxyModel = qobject_cast<const QgsSettingsTreeProxyModel *>( index.model() );
472 if ( proxyModel )
473 {
474 sourceIndex = proxyModel->mapToSource( index );
475 }
476 QgsSettingsTreeModelNodeData *nodeData = mModel->index2node( sourceIndex );
477 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
478 {
479 return QgsGui::settingsEditorWidgetRegistry()->createEditor( nodeData->setting(), nodeData->namedParentNodes(), parent );
480 }
481 }
482 return nullptr;
483}
484
485void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
486{
488 if ( eww )
489 eww->setWidgetFromVariant( index.model()->data( index, Qt::DisplayRole ) );
490}
491
492void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
493{
495 if ( eww )
496 model->setData( index, eww->variantValueFromWidget(), Qt::EditRole );
497}
498
500
501
502
503
504
506 : QSortFilterProxyModel( parent )
507{
508 mSourceModel = new QgsSettingsTreeModel( rootNode, parent );
509 QSortFilterProxyModel::setSourceModel( mSourceModel );
510}
511
512void QgsSettingsTreeProxyModel::setFilterText( const QString &filterText )
513{
514 if ( filterText == mFilterText )
515 return;
516
517 mFilterText = filterText;
518 invalidateFilter();
519}
520
521bool QgsSettingsTreeProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
522{
523 QgsSettingsTreeModelNodeData *node = mSourceModel->index2node( mSourceModel->index( source_row, static_cast<int>( QgsSettingsTreeModel::Column::Name ), source_parent ) );
524 return nodeShown( node );
525}
526
527bool QgsSettingsTreeProxyModel::nodeShown( QgsSettingsTreeModelNodeData *node ) const
528{
529 if ( !node )
530 return false;
531 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
532 {
533 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
534 return true;
535
536 // also returns settings for which the parent nodes have a match
537 QModelIndex index = mSourceModel->node2index( node ).parent();
538 while ( ( index.isValid() ) )
539 {
540 QgsSettingsTreeModelNodeData *parentNode = mSourceModel->index2node( mSourceModel->index( index.row(), static_cast<int>( QgsSettingsTreeModel::Column::Name ), index.parent() ) );
541 if ( parentNode->name().contains( mFilterText, Qt::CaseInsensitive ) )
542 return true;
543
544 index = index.parent();
545 }
546 return false;
547 }
548 else
549 {
550 // show all children if name of node matches
551 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
552 return true;
553
554 const auto constChildren = node->children();
555 for ( QgsSettingsTreeModelNodeData *child : constChildren )
556 {
557 if ( nodeShown( child ) )
558 {
559 return true;
560 }
561 }
562 return false;
563 }
564}
565
@ Variant
Generic variant.
@ Custom
Custom implementation.
@ StringList
List of strings.
@ EnumFlag
Enum or Flag.
@ VariantMap
Map of strings.
@ Double
Double precision number.
static QgsSettingsEditorWidgetRegistry * settingsEditorWidgetRegistry()
Returns the registry of settings editors.
Definition: qgsgui.cpp:189
QWidget * createEditor(const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent=nullptr) const
Creates an editor widget for the given setting using the corresponding registered wrapper.
Base class for settings editor wrappers.
static QgsSettingsEditorWidgetWrapper * fromWidget(const QWidget *widget)
Creates a wrapper from the definition stored in a widget created by createEditor()
virtual void setWidgetFromVariant(const QVariant &value) const =0
Sets the value of the widget The wrapper must be configured before calling this medthod.
virtual QVariant variantValueFromWidget() const =0
Returns the value from the widget as a variant The wrapper must be configured before calling this med...
Represent settings entry and provides methods for reading and writing settings values.
bool exists(const QString &dynamicKeyPart=QString()) const
Returns true if the settings is contained in the underlying QSettings.
QString name() const
Returns the name of the settings.
QString definitionKey() const
Returns settings entry defining key.
QVariant valueAsVariant(const QString &dynamicKeyPart=QString()) const
Returns settings value with.
QgsSettingsTreeModel is a tree model for the settings tree.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QVariant data(const QModelIndex &index, int role) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex node2index(QgsSettingsTreeModelNodeData *node) const
Returns the index from the settings tree node.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void applyChanges()
Apply pending changes in the model to the corresponding settings.
int columnCount(const QModelIndex &parent) const override
QgsSettingsTreeModelNodeData * index2node(const QModelIndex &index) const
Returns settings tree node for given index or the root node if the index is invalid.
QgsSettingsTreeModel(QgsSettingsTreeNode *rootNode=nullptr, QObject *parent=nullptr)
Constructor.
QModelIndex parent(const QModelIndex &child) const override
int rowCount(const QModelIndex &parent) const override
QgsSettingsTreeNamedListNode is a named list tree node for the settings tree to help organizing and i...
QStringList items(const QStringList &parentsNamedItems=QStringList()) const
Returns the list of items.
QgsSettingsTreeNode is a tree node for the settings tree to help organizing and introspecting the tre...
Qgis::SettingsTreeNodeType type() const
Returns the type of node.
Qgis::SettingsTreeNodeType mType
QString key() const
Returns the key of the node (without its parents)
QgsSettingsTreeProxyModel allows filtering the settings tree.
QgsSettingsTreeProxyModel(QgsSettingsTreeNode *rootNode=nullptr, QObject *parent=nullptr)
Constructor.
void setFilterText(const QString &filterText=QString())
Sets the filter text.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38