QGIS API Documentation 3.41.0-Master (af5edcb665c)
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 "qgis.h"
19#include "qgsdataitem.h"
20#include "moc_qgsdataitem.cpp"
21#include "qgsapplication.h"
22#include "qgsdataitemprovider.h"
24#include "qgsdataprovider.h"
25#include "qgslogger.h"
26#include "qgsproviderregistry.h"
27#include "qgsconfig.h"
28#include "qgssettings.h"
29#include "qgsanimatedicon.h"
30#include "qgsproject.h"
31#include "qgsvectorlayer.h"
32#include "qgsprovidermetadata.h"
33
34#include <QApplication>
35#include <QtConcurrentMap>
36#include <QtConcurrentRun>
37#include <QDateTime>
38#include <QElapsedTimer>
39#include <QDir>
40#include <QFileInfo>
41#include <QMenu>
42#include <QMouseEvent>
43#include <QTreeWidget>
44#include <QTreeWidgetItem>
45#include <QVector>
46#include <QStyle>
47#include <QTimer>
48#include <mutex>
49#include <QRegularExpression>
50
51// use GDAL VSI mechanism
52#define CPL_SUPRESS_CPLUSPLUS //#spellok
53#include "cpl_vsi.h"
54#include "cpl_string.h"
55
56QgsAnimatedIcon *QgsDataItem::sPopulatingIcon = nullptr;
57
58QgsDataItem::QgsDataItem( Qgis::BrowserItemType type, QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
59// Do not pass parent to QObject, Qt would delete this when parent is deleted
60 : mType( type )
61 , mParent( parent )
62 , mName( name )
63 , mProviderKey( providerKey )
64 , mPath( path )
65{
66}
67
69{
70 QgsDebugMsgLevel( QStringLiteral( "mName = %1 mPath = %2 mChildren.size() = %3" ).arg( mName, mPath ).arg( mChildren.size() ), 2 );
71 const auto constMChildren = mChildren;
72 for ( QgsDataItem *child : constMChildren )
73 {
74 if ( !child ) // should not happen
75 continue;
76 child->deleteLater();
77 }
78 mChildren.clear();
79
80 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
81 {
82 // this should not usually happen (until the item was deleted directly when createChildren was running)
83 QgsDebugError( QStringLiteral( "mFutureWatcher not finished (should not happen) -> waitForFinished()" ) );
84 mDeferredDelete = true;
85 mFutureWatcher->waitForFinished();
86 }
87
88 delete mFutureWatcher;
89}
90
91QString QgsDataItem::pathComponent( const QString &string )
92{
93 const thread_local QRegularExpression rx( "[\\\\/]" );
94 return QString( string ).replace( rx, QStringLiteral( "|" ) );
95}
96
97QVariant QgsDataItem::sortKey() const
98{
99 return mSortKey.isValid() ? mSortKey : name();
100}
101
102void QgsDataItem::setSortKey( const QVariant &key )
103{
104 mSortKey = key;
105}
106
108{
109 QgsDebugMsgLevel( "path = " + path(), 3 );
110 setParent( nullptr ); // also disconnects parent
111 const auto constMChildren = mChildren;
112 for ( QgsDataItem *child : constMChildren )
113 {
114 if ( !child ) // should not happen
115 continue;
116 child->deleteLater();
117 }
118 mChildren.clear();
119
120 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
121 {
122 QgsDebugMsgLevel( QStringLiteral( "mFutureWatcher not finished -> schedule to delete later" ), 2 );
123 mDeferredDelete = true;
124 }
125 else
126 {
127 QObject::deleteLater();
128 }
129}
130
131void QgsDataItem::deleteLater( QVector<QgsDataItem *> &items )
132{
133 const auto constItems = items;
134 for ( QgsDataItem *item : constItems )
135 {
136 if ( !item ) // should not happen
137 continue;
138 item->deleteLater();
139 }
140 items.clear();
141}
142
143void QgsDataItem::moveToThread( QThread *targetThread )
144{
145 // QObject::moveToThread() cannot move objects with parent, but QgsDataItem is not using paren/children from QObject
146 const auto constMChildren = mChildren;
147 for ( QgsDataItem *child : constMChildren )
148 {
149 if ( !child ) // should not happen
150 continue;
151 QgsDebugMsgLevel( "moveToThread child " + child->path(), 3 );
152 child->QObject::setParent( nullptr ); // to be sure
153 child->moveToThread( targetThread );
154 }
155 QObject::moveToThread( targetThread );
156}
157
162
164{
165 if ( state() == Qgis::BrowserItemState::Populating && sPopulatingIcon )
166 return sPopulatingIcon->icon();
167
168 if ( !mIcon.isNull() )
169 return mIcon;
170
171 if ( !mIconMap.contains( mIconName ) )
172 {
173 mIconMap.insert( mIconName, mIconName.startsWith( ':' ) ? QIcon( mIconName ) : QgsApplication::getThemeIcon( mIconName ) );
174 }
175
176 return mIconMap.value( mIconName );
177}
178
179void QgsDataItem::setName( const QString &name )
180{
181 mName = name;
182 emit dataChanged( this );
183}
184
185QVector<QgsDataItem *> QgsDataItem::createChildren()
186{
187 return QVector<QgsDataItem *>();
188}
189
190void QgsDataItem::populate( bool foreground )
191{
193 return;
194
195 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
196
197 if ( capabilities2() & Qgis::BrowserItemCapability::Fast || foreground )
198 {
200 }
201 else
202 {
204 // 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.
205 if ( !mFutureWatcher )
206 {
207 mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
208 }
209
210 connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
211 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
212 }
213}
214
215// This is expected to be run in a separate thread
216QVector<QgsDataItem *> QgsDataItem::runCreateChildren( QgsDataItem *item )
217{
218 QgsDebugMsgLevel( "path = " + item->path(), 2 );
219 QElapsedTimer time;
220 time.start();
221 const QVector <QgsDataItem *> children = item->createChildren();
222 QgsDebugMsgLevel( QStringLiteral( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ), 3 );
223 // Children objects must be pushed to main thread.
224 for ( QgsDataItem *child : children )
225 {
226 if ( !child ) // should not happen
227 continue;
228 QgsDebugMsgLevel( "moveToThread child " + child->path(), 2 );
229 if ( qApp )
230 child->moveToThread( qApp->thread() ); // moves also children
231 }
232 QgsDebugMsgLevel( QStringLiteral( "finished path %1: %2 children" ).arg( item->path() ).arg( children.size() ), 3 );
233 return children;
234}
235
237{
238 QgsDebugMsgLevel( QStringLiteral( "path = %1 children.size() = %2" ).arg( path() ).arg( mFutureWatcher->result().size() ), 3 );
239
240 if ( deferredDelete() )
241 {
242 QgsDebugMsgLevel( QStringLiteral( "Item was scheduled to be deleted later" ), 2 );
243 QObject::deleteLater();
244 return;
245 }
246
247 if ( mChildren.isEmpty() ) // usually populating but may also be refresh if originally there were no children
248 {
249 populate( mFutureWatcher->result() );
250 }
251 else // refreshing
252 {
253 refresh( mFutureWatcher->result() );
254 }
255 disconnect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
256 emit dataChanged( this ); // to replace loading icon by normal icon
257}
258
260{
261 emit dataChanged( this );
262}
263
264void QgsDataItem::populate( const QVector<QgsDataItem *> &children )
265{
266 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
267
268 for ( QgsDataItem *child : children )
269 {
270 if ( !child ) // should not happen
271 continue;
272 // update after thread finished -> refresh
273 addChildItem( child, true );
274 }
276}
277
279{
280 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
281
282 const auto constMChildren = mChildren;
283 for ( QgsDataItem *child : constMChildren )
284 {
285 QgsDebugMsgLevel( "remove " + child->path(), 3 );
286 child->depopulate(); // recursive
287 deleteChildItem( child );
288 }
290}
291
293{
295 return;
296
297 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
298
300 {
302 }
303 else
304 {
306 if ( !mFutureWatcher )
307 {
308 mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
309 }
310 connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
311 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
312 }
313}
314
315void QgsDataItem::refreshConnections( const QString &key )
316{
317 // Walk up until the root node is reached
318 if ( mParent )
319 {
321 }
322 else
323 {
324 // if a specific key was specified then we use that -- otherwise we assume the connections
325 // changed belong to the same provider as this item
326 emit connectionsChanged( key.isEmpty() ? providerKey() : key );
327 }
328}
329
330void QgsDataItem::refresh( const QVector<QgsDataItem *> &children )
331{
332 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
333
334 // Remove no more present children
335 QVector<QgsDataItem *> remove;
336 const auto constMChildren = mChildren;
337 for ( QgsDataItem *child : constMChildren )
338 {
339 if ( !child ) // should not happen
340 continue;
341 if ( findItem( children, child ) >= 0 )
342 continue;
343 remove.append( child );
344 }
345 const auto constRemove = remove;
346 for ( QgsDataItem *child : constRemove )
347 {
348 QgsDebugMsgLevel( "remove " + child->path(), 3 );
349 deleteChildItem( child );
350 }
351
352 // Add new children
353 for ( QgsDataItem *child : children )
354 {
355 if ( !child ) // should not happen
356 continue;
357
358 const int index = findItem( mChildren, child );
359 if ( index >= 0 )
360 {
361 // Refresh recursively (some providers may create more generations of descendants)
362 if ( !( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
363 {
364 // The child cannot createChildren() itself
365 mChildren.value( index )->refresh( child->children() );
366 }
367 else if ( mChildren.value( index )->state() == Qgis::BrowserItemState::Populated
369 {
370 mChildren.value( index )->refresh();
371 }
372
373 child->deleteLater();
374 continue;
375 }
376 addChildItem( child, true );
377 }
379}
380
382{
383 return mProviderKey;
384}
385
386void QgsDataItem::setProviderKey( const QString &value )
387{
388 mProviderKey = value;
389}
390
392{
393 return mChildren.size();
394}
396{
397 return ( state() == Qgis::BrowserItemState::Populated ? !mChildren.isEmpty() : true );
398}
399
401{
402 return false;
403}
404
406{
407 if ( mParent )
408 {
409 disconnect( this, nullptr, mParent, nullptr );
410 }
411 if ( parent )
412 {
419 }
420 mParent = parent;
421}
422
423void QgsDataItem::addChildItem( QgsDataItem *child, bool refresh )
424{
425 Q_ASSERT( child );
426 QgsDebugMsgLevel( QStringLiteral( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( qgsEnumValueToKey< Qgis::BrowserItemType >( child->mType ) ), 3 );
427
428 //calculate position to insert child
429 int i;
431 {
432 for ( i = 0; i < mChildren.size(); i++ )
433 {
434 // sort items by type, so directories are before data items
435 if ( mChildren.at( i )->mType == child->mType &&
436 mChildren.at( i )->mName.localeAwareCompare( child->mName ) > 0 )
437 break;
438 }
439 }
440 else
441 {
442 for ( i = 0; i < mChildren.size(); i++ )
443 {
444 if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
445 break;
446 }
447 }
448
449 if ( refresh )
450 emit beginInsertItems( this, i, i );
451
452 mChildren.insert( i, child );
453 child->setParent( this );
454
455 if ( refresh )
456 emit endInsertItems();
457}
458
460{
461 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
462 const int i = mChildren.indexOf( child );
463 Q_ASSERT( i >= 0 );
464 emit beginRemoveItems( this, i, i );
465 mChildren.remove( i );
466 child->deleteLater();
467 emit endRemoveItems();
468}
469
471{
472 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
473 const int i = mChildren.indexOf( child );
474 Q_ASSERT( i >= 0 );
475 if ( i < 0 )
476 {
477 child->setParent( nullptr );
478 return nullptr;
479 }
480
481 emit beginRemoveItems( this, i, i );
482 mChildren.remove( i );
483 emit endRemoveItems();
484 return child;
485}
486
487int QgsDataItem::findItem( QVector<QgsDataItem *> items, QgsDataItem *item )
488{
489 for ( int i = 0; i < items.size(); i++ )
490 {
491 Q_ASSERT_X( items[i], "findItem", QStringLiteral( "item %1 is nullptr" ).arg( i ).toLatin1() );
492 QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
493 if ( items[i]->equal( item ) )
494 return i;
495 }
496 return -1;
497}
498
499bool QgsDataItem::equal( const QgsDataItem *other )
500{
501 return ( metaObject()->className() == other->metaObject()->className() &&
502 mPath == other->path() );
503}
504
505QList<QAction *> QgsDataItem::actions( QWidget *parent )
506{
507 Q_UNUSED( parent )
508 return QList<QAction *>();
509}
510
512{
513 return false;
514}
515
517{
518 return mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : mimeUris().constFirst();
519}
520
522{
524 {
526 uri.uri = path();
527 uri.filePath = path();
528 return { uri };
529 }
530
531 return {};
532}
533
535{
536 Q_UNUSED( crs )
537 return false;
538}
539
540bool QgsDataItem::rename( const QString & )
541{
542 return false;
543}
544
545void QgsDataItem::setCapabilities( int capabilities )
546{
547 setCapabilities( static_cast< Qgis::BrowserItemCapabilities >( capabilities ) );
548}
549
554
556{
557 QgsDebugMsgLevel( QStringLiteral( "item %1 set state %2 -> %3" ).arg( path() ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( this->state() ) ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( state ) ), 3 );
558 if ( state == mState )
559 return;
560
561 const Qgis::BrowserItemState oldState = mState;
562
563 if ( state == Qgis::BrowserItemState::Populating ) // start loading
564 {
565 if ( !sPopulatingIcon )
566 {
567 // TODO: ensure that QgsAnimatedIcon is created on UI thread only
568 sPopulatingIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), QgsApplication::instance() );
569 }
570
571 sPopulatingIcon->connectFrameChanged( this, &QgsDataItem::updateIcon );
572 }
573 else if ( mState == Qgis::BrowserItemState::Populating && sPopulatingIcon ) // stop loading
574 {
575 sPopulatingIcon->disconnectFrameChanged( this, &QgsDataItem::updateIcon );
576 }
577
578
579 mState = state;
580
581 emit stateChanged( this, oldState );
583 updateIcon();
584}
585
586QList<QMenu *> QgsDataItem::menus( QWidget *parent )
587{
588 Q_UNUSED( parent )
589 return QList<QMenu *>();
590}
591
592QgsErrorItem::QgsErrorItem( QgsDataItem *parent, const QString &error, const QString &path )
593 : QgsDataItem( Qgis::BrowserItemType::Error, parent, error, path )
594{
595 mIconName = QStringLiteral( "/mIconDelete.svg" );
596
597 setState( Qgis::BrowserItemState::Populated ); // no more children
598}
599
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
BrowserItemState
Browser item states.
Definition qgis.h:872
@ NotPopulated
Children not yet created.
@ Populating
Creating children in separate thread (populating or refreshing)
@ Populated
Children created.
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
@ RefreshChildrenWhenItemIsRefreshed
When the item is refreshed, all its populated children will also be refreshed in turn.
@ ItemRepresentsFile
Item's path() directly represents a file on disk.
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
BrowserItemType
Browser item types.
Definition qgis.h:853
@ Directory
Represents a file directory.
QFlags< BrowserItemCapability > BrowserItemCapabilities
Browser item capabilities.
Definition qgis.h:899
The QgsAbstractDatabaseProviderConnection class provides common functionality for DB based connection...
Animated icon is keeping an animation running if there are listeners connected to frameChanged.
bool disconnectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Convenience function to disconnect the same style that the frame change connection was established.
bool connectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Connect a slot that will be notified repeatedly whenever a frame changes and which should request the...
QIcon icon() const
Gets the icons representation in the current frame.
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.
This class represents a coordinate reference system (CRS).
Base class for all items in the model.
Definition qgsdataitem.h:46
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)
QString mName
Qgis::BrowserItemType mType
bool hasChildren()
virtual QList< QMenu * > menus(QWidget *parent)
Returns the list of menus available for 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.
void dataChanged(QgsDataItem *item)
Emitted when data changes for an item.
bool deferredDelete()
The item is scheduled to be deleted.
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.
QgsErrorItem(QgsDataItem *parent, const QString &error, const QString &path)
QList< QgsMimeDataUtils::Uri > UriList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
const QgsCoordinateReferenceSystem & crs
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.