QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsdataitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdataitem.cpp - Data items
3 -------------------
4 begin : 2011-04-01
5 copyright : (C) 2011 Radim Blazek
6 email : radim dot blazek at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsconfig.h"
19#include "qgsdataitem.h"
20
21#include <mutex>
22
23#include "qgis.h"
24#include "qgsanimatedicon.h"
25#include "qgsapplication.h"
26#include "qgsdataitemprovider.h"
28#include "qgsdataprovider.h"
29#include "qgslogger.h"
30#include "qgsproject.h"
31#include "qgsprovidermetadata.h"
32#include "qgsproviderregistry.h"
33#include "qgssettings.h"
34#include "qgsvectorlayer.h"
35
36#include <QApplication>
37#include <QDateTime>
38#include <QDir>
39#include <QElapsedTimer>
40#include <QFileInfo>
41#include <QMenu>
42#include <QMouseEvent>
43#include <QRegularExpression>
44#include <QStyle>
45#include <QTimer>
46#include <QTreeWidget>
47#include <QTreeWidgetItem>
48#include <QVector>
49#include <QtConcurrentMap>
50#include <QtConcurrentRun>
51
52#include "moc_qgsdataitem.cpp"
53
54// use GDAL VSI mechanism
55#define CPL_SUPRESS_CPLUSPLUS //#spellok
56#include "cpl_vsi.h"
57#include "cpl_string.h"
58
59QgsAnimatedIcon *QgsDataItem::sPopulatingIcon = nullptr;
60
61QgsDataItem::QgsDataItem( Qgis::BrowserItemType type, QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
62// Do not pass parent to QObject, Qt would delete this when parent is deleted
63 : mType( type )
64 , mParent( parent )
65 , mName( name )
67 , mPath( path )
68{
69}
70
72{
73 QgsDebugMsgLevel( QStringLiteral( "mName = %1 mPath = %2 mChildren.size() = %3" ).arg( mName, mPath ).arg( mChildren.size() ), 2 );
74 const auto constMChildren = mChildren;
75 for ( QgsDataItem *child : constMChildren )
76 {
77 if ( !child ) // should not happen
78 continue;
79 child->deleteLater();
80 }
81 mChildren.clear();
82
83 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
84 {
85 // this should not usually happen (until the item was deleted directly when createChildren was running)
86 QgsDebugError( QStringLiteral( "mFutureWatcher not finished (should not happen) -> waitForFinished()" ) );
87 mDeferredDelete = true;
88 mFutureWatcher->waitForFinished();
89 }
90
91
92}
93
94QString QgsDataItem::pathComponent( const QString &string )
95{
96 const thread_local QRegularExpression rx( "[\\\\/]" );
97 return QString( string ).replace( rx, QStringLiteral( "|" ) );
98}
99
100QVariant QgsDataItem::sortKey() const
101{
102 return mSortKey.isValid() ? mSortKey : name();
103}
104
105void QgsDataItem::setSortKey( const QVariant &key )
106{
107 mSortKey = key;
108}
109
111{
112 QgsDebugMsgLevel( "path = " + path(), 3 );
113 setParent( nullptr ); // also disconnects parent
114 const auto constMChildren = mChildren;
115 for ( QgsDataItem *child : constMChildren )
116 {
117 if ( !child ) // should not happen
118 continue;
119 child->deleteLater();
120 }
121 mChildren.clear();
122
123 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
124 {
125 QgsDebugMsgLevel( QStringLiteral( "mFutureWatcher not finished -> schedule to delete later" ), 2 );
126 mDeferredDelete = true;
127 }
128 else
129 {
130 QObject::deleteLater();
131 }
132}
133
134void QgsDataItem::deleteLater( QVector<QgsDataItem *> &items )
135{
136 const auto constItems = items;
137 for ( QgsDataItem *item : constItems )
138 {
139 if ( !item ) // should not happen
140 continue;
141 item->deleteLater();
142 }
143 items.clear();
144}
145
146void QgsDataItem::moveToThread( QThread *targetThread )
147{
148 // QObject::moveToThread() cannot move objects with parent, but QgsDataItem is not using paren/children from QObject
149 const auto constMChildren = mChildren;
150 for ( QgsDataItem *child : constMChildren )
151 {
152 if ( !child ) // should not happen
153 continue;
154 QgsDebugMsgLevel( "moveToThread child " + child->path(), 3 );
155 child->QObject::setParent( nullptr ); // to be sure
156 child->moveToThread( targetThread );
157 }
158 QObject::moveToThread( targetThread );
159}
160
165
167{
168 if ( state() == Qgis::BrowserItemState::Populating && sPopulatingIcon )
169 return sPopulatingIcon->icon();
170
171 if ( !mIcon.isNull() )
172 return mIcon;
173
174 if ( !mIconMap.contains( mIconName ) )
175 {
176 mIconMap.insert( mIconName, mIconName.startsWith( ':' ) ? QIcon( mIconName ) : QgsApplication::getThemeIcon( mIconName ) );
177 }
178
179 return mIconMap.value( mIconName );
180}
181
182void QgsDataItem::setName( const QString &name )
183{
184 mName = name;
185 emit dataChanged( this );
186}
187
188QVector<QgsDataItem *> QgsDataItem::createChildren()
189{
190 return QVector<QgsDataItem *>();
191}
192
193void QgsDataItem::populate( bool foreground )
194{
196 return;
197
198 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
199
200 if ( capabilities2() & Qgis::BrowserItemCapability::Fast || foreground )
201 {
203 }
204 else
205 {
207 // The watcher must not be created with item (in constructor) because the item may be created in thread and the watcher created in thread does not work correctly.
208 if ( !mFutureWatcher )
209 {
210 mFutureWatcher = std::make_unique<QFutureWatcher<QVector<QgsDataItem *> >>( this );
211 }
212
213 connect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
214 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
215 }
216}
217
218// This is expected to be run in a separate thread
219QVector<QgsDataItem *> QgsDataItem::runCreateChildren( QgsDataItem *item )
220{
221 QgsDebugMsgLevel( "path = " + item->path(), 2 );
222 QElapsedTimer time;
223 time.start();
224 const QVector <QgsDataItem *> children = item->createChildren();
225 QgsDebugMsgLevel( QStringLiteral( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ), 3 );
226 // Children objects must be pushed to main thread.
227 for ( QgsDataItem *child : children )
228 {
229 if ( !child ) // should not happen
230 continue;
231 QgsDebugMsgLevel( "moveToThread child " + child->path(), 2 );
232 if ( qApp )
233 child->moveToThread( qApp->thread() ); // moves also children
234 }
235 QgsDebugMsgLevel( QStringLiteral( "finished path %1: %2 children" ).arg( item->path() ).arg( children.size() ), 3 );
236 return children;
237}
238
240{
241 QgsDebugMsgLevel( QStringLiteral( "path = %1 children.size() = %2" ).arg( path() ).arg( mFutureWatcher->result().size() ), 3 );
242
243 if ( deferredDelete() )
244 {
245 QgsDebugMsgLevel( QStringLiteral( "Item was scheduled to be deleted later" ), 2 );
246 QObject::deleteLater();
247 return;
248 }
249
250 if ( mChildren.isEmpty() ) // usually populating but may also be refresh if originally there were no children
251 {
252 populate( mFutureWatcher->result() );
253 }
254 else // refreshing
255 {
256 refresh( mFutureWatcher->result() );
257 }
258 disconnect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
259 emit dataChanged( this ); // to replace loading icon by normal icon
260}
261
263{
264 emit dataChanged( this );
265}
266
267void QgsDataItem::populate( const QVector<QgsDataItem *> &children )
268{
269 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
270
271 for ( QgsDataItem *child : children )
272 {
273 if ( !child ) // should not happen
274 continue;
275 // update after thread finished -> refresh
276 addChildItem( child, true );
277 }
279}
280
282{
283 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
284
285 const auto constMChildren = mChildren;
286 for ( QgsDataItem *child : constMChildren )
287 {
288 QgsDebugMsgLevel( "remove " + child->path(), 3 );
289 child->depopulate(); // recursive
290 deleteChildItem( child );
291 }
293}
294
296{
298 return;
299
300 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
301
303 {
305 }
306 else
307 {
309 if ( !mFutureWatcher )
310 {
311 mFutureWatcher = std::make_unique<QFutureWatcher<QVector<QgsDataItem *> >>( this );
312 }
313 connect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
314 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
315 }
316}
317
318void QgsDataItem::refreshConnections( const QString &key )
319{
320 // Walk up until the root node is reached
321 if ( mParent )
322 {
323 mParent->refreshConnections( key );
324 }
325 else
326 {
327 // if a specific key was specified then we use that -- otherwise we assume the connections
328 // changed belong to the same provider as this item
329 emit connectionsChanged( key.isEmpty() ? providerKey() : key );
330 }
331}
332
333void QgsDataItem::refresh( const QVector<QgsDataItem *> &children )
334{
335 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
336
337 // Remove no more present children
338 QVector<QgsDataItem *> remove;
339 const auto constMChildren = mChildren;
340 for ( QgsDataItem *child : constMChildren )
341 {
342 if ( !child ) // should not happen
343 continue;
344 if ( findItem( children, child ) >= 0 )
345 continue;
346 remove.append( child );
347 }
348 const auto constRemove = remove;
349 for ( QgsDataItem *child : constRemove )
350 {
351 QgsDebugMsgLevel( "remove " + child->path(), 3 );
352 deleteChildItem( child );
353 }
354
355 // Add new children
356 for ( QgsDataItem *child : children )
357 {
358 if ( !child ) // should not happen
359 continue;
360
361 const int index = findItem( mChildren, child );
362 if ( index >= 0 )
363 {
364 // Refresh recursively (some providers may create more generations of descendants)
365 if ( !( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
366 {
367 // The child cannot createChildren() itself
368 mChildren.value( index )->refresh( child->children() );
369 }
370 else if ( mChildren.value( index )->state() == Qgis::BrowserItemState::Populated
372 {
373 mChildren.value( index )->refresh();
374 }
375
376 child->deleteLater();
377 continue;
378 }
379 addChildItem( child, true );
380 }
382}
383
385{
386 return mProviderKey;
387}
388
389void QgsDataItem::setProviderKey( const QString &value )
390{
391 mProviderKey = value;
392}
393
395{
396 return mChildren.size();
397}
399{
400 return ( state() == Qgis::BrowserItemState::Populated ? !mChildren.isEmpty() : true );
401}
402
404{
405 return false;
406}
407
409{
410 if ( mParent )
411 {
412 disconnect( this, nullptr, mParent, nullptr );
413 }
414 if ( parent )
415 {
422 }
423 mParent = parent;
424}
425
427{
428 Q_ASSERT( child );
429 QgsDebugMsgLevel( QStringLiteral( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( qgsEnumValueToKey< Qgis::BrowserItemType >( child->mType ) ), 3 );
430
431 //calculate position to insert child
432 int i;
434 {
435 for ( i = 0; i < mChildren.size(); i++ )
436 {
437 // sort items by type, so directories are before data items
438 if ( mChildren.at( i )->mType == child->mType &&
439 mChildren.at( i )->mName.localeAwareCompare( child->mName ) > 0 )
440 break;
441 }
442 }
443 else
444 {
445 for ( i = 0; i < mChildren.size(); i++ )
446 {
447 if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
448 break;
449 }
450 }
451
452 if ( refresh )
453 emit beginInsertItems( this, i, i );
454
455 mChildren.insert( i, child );
456 child->setParent( this );
457
458 if ( refresh )
459 emit endInsertItems();
460}
461
463{
464 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
465 const int i = mChildren.indexOf( child );
466 Q_ASSERT( i >= 0 );
467 emit beginRemoveItems( this, i, i );
468 mChildren.remove( i );
469 child->deleteLater();
470 emit endRemoveItems();
471}
472
474{
475 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
476 const int i = mChildren.indexOf( child );
477 Q_ASSERT( i >= 0 );
478 if ( i < 0 )
479 {
480 child->setParent( nullptr );
481 return nullptr;
482 }
483
484 emit beginRemoveItems( this, i, i );
485 mChildren.remove( i );
486 emit endRemoveItems();
487 return child;
488}
489
490int QgsDataItem::findItem( QVector<QgsDataItem *> items, QgsDataItem *item )
491{
492 for ( int i = 0; i < items.size(); i++ )
493 {
494 Q_ASSERT_X( items[i], "findItem", QStringLiteral( "item %1 is nullptr" ).arg( i ).toLatin1() );
495 QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
496 if ( items[i]->equal( item ) )
497 return i;
498 }
499 return -1;
500}
501
502bool QgsDataItem::equal( const QgsDataItem *other )
503{
504 return ( metaObject()->className() == other->metaObject()->className() &&
505 mPath == other->path() );
506}
507
508QList<QAction *> QgsDataItem::actions( QWidget *parent )
509{
510 Q_UNUSED( parent )
511 return QList<QAction *>();
512}
513
515{
516 return false;
517}
518
520{
521 return mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : mimeUris().constFirst();
522}
523
525{
527 {
529 uri.uri = path();
530 uri.filePath = path();
531 return { uri };
532 }
533
534 return {};
535}
536
538{
539 Q_UNUSED( crs )
540 return false;
541}
542
543bool QgsDataItem::rename( const QString & )
544{
545 return false;
546}
547
548void QgsDataItem::setCapabilities( int capabilities )
549{
550 setCapabilities( static_cast< Qgis::BrowserItemCapabilities >( capabilities ) );
551}
552
557
559{
560 QgsDebugMsgLevel( QStringLiteral( "item %1 set state %2 -> %3" ).arg( path() ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( this->state() ) ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( state ) ), 3 );
561 if ( state == mState )
562 return;
563
564 const Qgis::BrowserItemState oldState = mState;
565
566 if ( state == Qgis::BrowserItemState::Populating ) // start loading
567 {
568 if ( !sPopulatingIcon )
569 {
570 // TODO: ensure that QgsAnimatedIcon is created on UI thread only
571 sPopulatingIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), QgsApplication::instance() );
572 }
573
574 sPopulatingIcon->connectFrameChanged( this, &QgsDataItem::updateIcon );
575 }
576 else if ( mState == Qgis::BrowserItemState::Populating && sPopulatingIcon ) // stop loading
577 {
578 sPopulatingIcon->disconnectFrameChanged( this, &QgsDataItem::updateIcon );
579 }
580
581
582 mState = state;
583
584 emit stateChanged( this, oldState );
586 updateIcon();
587}
588
589QList<QMenu *> QgsDataItem::menus( QWidget *parent )
590{
591 Q_UNUSED( parent )
592 return QList<QMenu *>();
593}
594
595QgsErrorItem::QgsErrorItem( QgsDataItem *parent, const QString &error, const QString &path )
596 : QgsDataItem( Qgis::BrowserItemType::Error, parent, error, path )
597{
598 mIconName = QStringLiteral( "/mIconDelete.svg" );
599
600 setState( Qgis::BrowserItemState::Populated ); // no more children
601}
602
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:56
BrowserItemState
Browser item states.
Definition qgis.h:938
@ NotPopulated
Children not yet created.
Definition qgis.h:939
@ Populating
Creating children in separate thread (populating or refreshing).
Definition qgis.h:940
@ Populated
Children created.
Definition qgis.h:941
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:954
@ RefreshChildrenWhenItemIsRefreshed
When the item is refreshed, all its populated children will also be refreshed in turn.
Definition qgis.h:960
@ ItemRepresentsFile
Item's path() directly represents a file on disk.
Definition qgis.h:959
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:955
BrowserItemType
Browser item types.
Definition qgis.h:919
@ Directory
Represents a file directory.
Definition qgis.h:921
QFlags< BrowserItemCapability > BrowserItemCapabilities
Browser item capabilities.
Definition qgis.h:965
Provides common functionality for database based connections.
virtual QIcon icon() const
Returns an icon representing the connection.
Animated icon is keeping an animation running if there are listeners connected to frameChanged.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Represents a coordinate reference system (CRS).
Base class for all items in the model.
Definition qgsdataitem.h:47
void stateChanged(QgsDataItem *item, Qgis::BrowserItemState oldState)
Emitted when an item's state is changed.
void setSortKey(const QVariant &key)
Sets a custom sorting key for the item.
static int findItem(QVector< QgsDataItem * > items, QgsDataItem *item)
bool hasChildren() const
Returns whether this item has children.
QString mName
Qgis::BrowserItemType mType
virtual QList< QMenu * > menus(QWidget *parent)
Returns the list of menus available for this item.
int rowCount() const
Returns the number of rows of this item.
virtual void deleteChildItem(QgsDataItem *child)
Removes and deletes a child item, emitting relevant signals to the model.
QVector< QgsDataItem * > mChildren
virtual QVariant sortKey() const
Returns the sorting key for the item.
Qgis::BrowserItemState mState
virtual bool handleDoubleClick()
Called when a user double clicks on the item.
virtual void refresh(const QVector< QgsDataItem * > &children)
Refresh the items from a specified list of child items.
void dataChanged(QgsDataItem *item)
Emitted when data changes for an item.
void setParent(QgsDataItem *parent)
Set item parent and connect / disconnect parent to / from item signals.
static void deleteLater(QVector< QgsDataItem * > &items)
void endRemoveItems()
Emitted after child items have been removed from this data item.
virtual bool layerCollection() const
Returns true if the data item is a collection of layers The default implementation returns false,...
QString mPath
QVector< QgsDataItem * > children() const
void beginRemoveItems(QgsDataItem *parent, int first, int last)
Emitted before child items are removed from this data item.
virtual void deleteLater()
Safely delete the item:
virtual QgsAbstractDatabaseProviderConnection * databaseConnection() const
For data items that represent a DB connection or one of its children, this method returns a connectio...
Qgis::BrowserItemType type() const
QgsDataItem(Qgis::BrowserItemType type, QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey=QString())
Constructor for QgsDataItem, with the specified parent item.
QString mIconName
virtual QVector< QgsDataItem * > createChildren()
Create children.
QMap< QString, QIcon > mIconMap
Qgis::BrowserItemState state() const
virtual void childrenCreated()
virtual QgsDataItem * removeChildItem(QgsDataItem *child)
Removes a child item and returns it without deleting it.
static QString pathComponent(const QString &component)
Create path component replacing path separators.
virtual QList< QAction * > actions(QWidget *parent)
Returns the list of actions available for this item.
QVariant mSortKey
Custom sort key. If invalid, name() will be used for sorting instead.
void updateIcon()
Will request a repaint of this icon.
virtual Q_DECL_DEPRECATED bool rename(const QString &name)
Sets a new name for the item, and returns true if the item was successfully renamed.
void connectionsChanged(const QString &providerKey=QString())
Emitted when the connections of the provider with the specified providerKey have changed.
virtual Q_DECL_DEPRECATED QgsMimeDataUtils::Uri mimeUri() const
Returns mime URI for the data item.
QgsDataItem * mParent
QString name() const
Returns the name of the item (the displayed text for the item).
QString path() const
virtual QIcon icon()
virtual void setState(Qgis::BrowserItemState state)
Set item state.
void beginInsertItems(QgsDataItem *parent, int first, int last)
Emitted before child items are added to this data item.
virtual void refreshConnections(const QString &providerKey=QString())
Causes a data item provider to refresh all registered connections.
virtual void setCapabilities(Qgis::BrowserItemCapabilities capabilities)
Sets the capabilities for the data item.
virtual void addChildItem(QgsDataItem *child, bool refresh=false)
Inserts a new child item.
void setName(const QString &name)
Sets the name of the item (the displayed text for the item).
virtual void refresh()
QgsDataItem * parent() const
Gets item parent.
void moveToThread(QThread *targetThread)
Move object and all its descendants to thread.
virtual QgsMimeDataUtils::UriList mimeUris() const
Returns mime URIs for the data item, most data providers will only return a single URI but some data ...
QString providerKey() const
Returns the provider key that created this item (e.g.
void endInsertItems()
Emitted after child items have been added to this data item.
virtual Q_DECL_DEPRECATED bool setCrs(const QgsCoordinateReferenceSystem &crs)
Writes the selected crs into data source.
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
~QgsDataItem() override
void setProviderKey(const QString &value)
Sets the provider key that created this item (e.g.
QString mProviderKey
virtual void populate(const QVector< QgsDataItem * > &children)
virtual bool equal(const QgsDataItem *other)
Returns true if this item is equal to another item (by testing item type and path).
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items.
bool deferredDelete() const
Returns true if the item is scheduled to be deleted.
QgsErrorItem(QgsDataItem *parent, const QString &error, const QString &path)
QList< QgsMimeDataUtils::Uri > UriList
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.