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