QGIS API Documentation 3.99.0-Master (d270888f95f)
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 <QString>
45#include <QStyle>
46#include <QTimer>
47#include <QTreeWidget>
48#include <QTreeWidgetItem>
49#include <QVector>
50#include <QtConcurrentMap>
51#include <QtConcurrentRun>
52
53#include "moc_qgsdataitem.cpp"
54
55using namespace Qt::StringLiterals;
56
57// use GDAL VSI mechanism
58#define CPL_SUPRESS_CPLUSPLUS //#spellok
59#include "cpl_vsi.h"
60#include "cpl_string.h"
61
62QgsAnimatedIcon *QgsDataItem::sPopulatingIcon = nullptr;
63
64QgsDataItem::QgsDataItem( Qgis::BrowserItemType type, QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
65// Do not pass parent to QObject, Qt would delete this when parent is deleted
66 : mType( type )
67 , mParent( parent )
68 , mName( name )
70 , mPath( path )
71{
72}
73
75{
76 QgsDebugMsgLevel( u"mName = %1 mPath = %2 mChildren.size() = %3"_s.arg( mName, mPath ).arg( mChildren.size() ), 2 );
77 const auto constMChildren = mChildren;
78 for ( QgsDataItem *child : constMChildren )
79 {
80 if ( !child ) // should not happen
81 continue;
82 child->deleteLater();
83 }
84 mChildren.clear();
85
86 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
87 {
88 // this should not usually happen (until the item was deleted directly when createChildren was running)
89 QgsDebugError( u"mFutureWatcher not finished (should not happen) -> waitForFinished()"_s );
90 mDeferredDelete = true;
91 mFutureWatcher->waitForFinished();
92 }
93
94
95}
96
97QString QgsDataItem::pathComponent( const QString &string )
98{
99 const thread_local QRegularExpression rx( "[\\\\/]" );
100 return QString( string ).replace( rx, u"|"_s );
101}
102
103QVariant QgsDataItem::sortKey() const
104{
105 return mSortKey.isValid() ? mSortKey : name();
106}
107
108void QgsDataItem::setSortKey( const QVariant &key )
109{
110 mSortKey = key;
111}
112
114{
115 QgsDebugMsgLevel( "path = " + path(), 3 );
116 setParent( nullptr ); // also disconnects parent
117 const auto constMChildren = mChildren;
118 for ( QgsDataItem *child : constMChildren )
119 {
120 if ( !child ) // should not happen
121 continue;
122 child->deleteLater();
123 }
124 mChildren.clear();
125
126 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
127 {
128 QgsDebugMsgLevel( u"mFutureWatcher not finished -> schedule to delete later"_s, 2 );
129 mDeferredDelete = true;
130 }
131 else
132 {
133 QObject::deleteLater();
134 }
135}
136
137void QgsDataItem::deleteLater( QVector<QgsDataItem *> &items )
138{
139 const auto constItems = items;
140 for ( QgsDataItem *item : constItems )
141 {
142 if ( !item ) // should not happen
143 continue;
144 item->deleteLater();
145 }
146 items.clear();
147}
148
149void QgsDataItem::moveToThread( QThread *targetThread )
150{
151 // QObject::moveToThread() cannot move objects with parent, but QgsDataItem is not using paren/children from QObject
152 const auto constMChildren = mChildren;
153 for ( QgsDataItem *child : constMChildren )
154 {
155 if ( !child ) // should not happen
156 continue;
157 QgsDebugMsgLevel( "moveToThread child " + child->path(), 3 );
158 child->QObject::setParent( nullptr ); // to be sure
159 child->moveToThread( targetThread );
160 }
161 QObject::moveToThread( targetThread );
162}
163
168
170{
171 if ( state() == Qgis::BrowserItemState::Populating && sPopulatingIcon )
172 return sPopulatingIcon->icon();
173
174 if ( !mIcon.isNull() )
175 return mIcon;
176
177 if ( !mIconMap.contains( mIconName ) )
178 {
179 mIconMap.insert( mIconName, mIconName.startsWith( ':' ) ? QIcon( mIconName ) : QgsApplication::getThemeIcon( mIconName ) );
180 }
181
182 return mIconMap.value( mIconName );
183}
184
185void QgsDataItem::setName( const QString &name )
186{
187 mName = name;
188 emit dataChanged( this );
189}
190
191QVector<QgsDataItem *> QgsDataItem::createChildren()
192{
193 return QVector<QgsDataItem *>();
194}
195
200
201void QgsDataItem::populate( bool foreground )
202{
204 return;
205
206 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
207
208 if ( capabilities2() & Qgis::BrowserItemCapability::Fast || foreground )
209 {
211 }
212 else
213 {
215 // 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.
216 if ( !mFutureWatcher )
217 {
218 mFutureWatcher = std::make_unique<QFutureWatcher<QVector<QgsDataItem *> >>( this );
219 }
220
221 connect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
222 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
223 }
224}
225
226// This is expected to be run in a separate thread
227QVector<QgsDataItem *> QgsDataItem::runCreateChildren( QgsDataItem *item )
228{
229 QgsDebugMsgLevel( "path = " + item->path(), 2 );
230 QElapsedTimer time;
231 time.start();
232 const QVector <QgsDataItem *> children = item->createChildren();
233 QgsDebugMsgLevel( u"%1 children created in %2 ms"_s.arg( children.size() ).arg( time.elapsed() ), 3 );
234 // Children objects must be pushed to main thread.
235 for ( QgsDataItem *child : children )
236 {
237 if ( !child ) // should not happen
238 continue;
239 QgsDebugMsgLevel( "moveToThread child " + child->path(), 2 );
240 if ( qApp )
241 child->moveToThread( qApp->thread() ); // moves also children
242 }
243 QgsDebugMsgLevel( u"finished path %1: %2 children"_s.arg( item->path() ).arg( children.size() ), 3 );
244 return children;
245}
246
248{
249 QgsDebugMsgLevel( u"path = %1 children.size() = %2"_s.arg( path() ).arg( mFutureWatcher->result().size() ), 3 );
250
251 if ( deferredDelete() )
252 {
253 QgsDebugMsgLevel( u"Item was scheduled to be deleted later"_s, 2 );
254 QObject::deleteLater();
255 return;
256 }
257
258 if ( mChildren.isEmpty() ) // usually populating but may also be refresh if originally there were no children
259 {
260 populate( mFutureWatcher->result() );
261 }
262 else // refreshing
263 {
264 refresh( mFutureWatcher->result() );
265 }
266 disconnect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
267 emit dataChanged( this ); // to replace loading icon by normal icon
268}
269
271{
272 emit dataChanged( this );
273}
274
275void QgsDataItem::populate( const QVector<QgsDataItem *> &children )
276{
277 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
278
279 std::function< void( QgsDataItem *, int ) > setChildAncestorDepthRecursive;
280 setChildAncestorDepthRecursive = [&setChildAncestorDepthRecursive]( QgsDataItem * child, int depth )
281 {
282 child->mCreatorAncestorDepth = depth;
283 const QVector< QgsDataItem * > children = child->children();
284 for ( QgsDataItem *nextChild : children )
285 {
286 setChildAncestorDepthRecursive( nextChild, depth + 1 );
287 }
288 };
289
290 for ( QgsDataItem *child : children )
291 {
292 if ( !child ) // should not happen
293 continue;
294 setChildAncestorDepthRecursive( child, 1 );
295 // update after thread finished -> refresh
296 addChildItem( child, true );
297 }
299}
300
302{
303 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
304
305 const auto constMChildren = mChildren;
306 for ( QgsDataItem *child : constMChildren )
307 {
308 QgsDebugMsgLevel( "remove " + child->path(), 3 );
309 child->depopulate(); // recursive
310 deleteChildItem( child );
311 }
313}
314
316{
318 return;
319
320 QgsDebugMsgLevel( "mPath = " + mPath, 3 );
321
323 {
325 }
326 else
327 {
329 if ( !mFutureWatcher )
330 {
331 mFutureWatcher = std::make_unique<QFutureWatcher<QVector<QgsDataItem *> >>( this );
332 }
333 connect( mFutureWatcher.get(), &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
334 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
335 }
336}
337
338void QgsDataItem::refreshConnections( const QString &key )
339{
340 // Walk up until the root node is reached
341 if ( mParent )
342 {
343 mParent->refreshConnections( key );
344 }
345 else
346 {
347 // if a specific key was specified then we use that -- otherwise we assume the connections
348 // changed belong to the same provider as this item
349 emit connectionsChanged( key.isEmpty() ? providerKey() : key );
350 }
351}
352
353void QgsDataItem::refresh( const QVector<QgsDataItem *> &children )
354{
355 QgsDebugMsgLevel( "mPath = " + mPath, 2 );
356
357 // Remove no more present children
358 QVector<QgsDataItem *> remove;
359 const auto constMChildren = mChildren;
360 for ( QgsDataItem *child : constMChildren )
361 {
362 if ( !child ) // should not happen
363 continue;
364 if ( findItem( children, child ) >= 0 )
365 continue;
366 remove.append( child );
367 }
368 const auto constRemove = remove;
369 for ( QgsDataItem *child : constRemove )
370 {
371 QgsDebugMsgLevel( "remove " + child->path(), 3 );
372 deleteChildItem( child );
373 }
374
375 // Add new children
376 for ( QgsDataItem *child : children )
377 {
378 if ( !child ) // should not happen
379 continue;
380
381 const int index = findItem( mChildren, child );
382 if ( index >= 0 )
383 {
384 // Refresh recursively (some providers may create more generations of descendants)
385 if ( !( child->capabilities2() & Qgis::BrowserItemCapability::Fertile ) )
386 {
387 // The child cannot createChildren() itself
388 mChildren.value( index )->refresh( child->children() );
389 }
390 else if ( mChildren.value( index )->state() == Qgis::BrowserItemState::Populated
392 {
393 mChildren.value( index )->refresh();
394 }
395
396 child->deleteLater();
397 continue;
398 }
399 addChildItem( child, true );
400 }
402}
403
405{
406 return mProviderKey;
407}
408
409void QgsDataItem::setProviderKey( const QString &value )
410{
411 mProviderKey = value;
412}
413
415{
416 return mChildren.size();
417}
419{
420 return ( state() == Qgis::BrowserItemState::Populated ? !mChildren.isEmpty() : true );
421}
422
424{
425 return false;
426}
427
429{
430 if ( mParent )
431 {
432 disconnect( this, nullptr, mParent, nullptr );
433 }
434 if ( parent )
435 {
442 }
443 mParent = parent;
444}
445
447{
448 Q_ASSERT( child );
449 QgsDebugMsgLevel( u"path = %1 add child #%2 - %3 - %4"_s.arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( qgsEnumValueToKey< Qgis::BrowserItemType >( child->mType ) ), 3 );
450
451 //calculate position to insert child
452 int i;
454 {
455 for ( i = 0; i < mChildren.size(); i++ )
456 {
457 // sort items by type, so directories are before data items
458 if ( mChildren.at( i )->mType == child->mType &&
459 mChildren.at( i )->mName.localeAwareCompare( child->mName ) > 0 )
460 break;
461 }
462 }
463 else
464 {
465 for ( i = 0; i < mChildren.size(); i++ )
466 {
467 if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
468 break;
469 }
470 }
471
472 if ( refresh )
473 emit beginInsertItems( this, i, i );
474
475 mChildren.insert( i, child );
476 child->setParent( this );
477
478 if ( refresh )
479 emit endInsertItems();
480}
481
483{
484 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
485 const int i = mChildren.indexOf( child );
486 Q_ASSERT( i >= 0 );
487 emit beginRemoveItems( this, i, i );
488 mChildren.remove( i );
489 child->deleteLater();
490 emit endRemoveItems();
491}
492
494{
495 QgsDebugMsgLevel( "mName = " + child->mName, 2 );
496 const int i = mChildren.indexOf( child );
497 Q_ASSERT( i >= 0 );
498 if ( i < 0 )
499 {
500 child->setParent( nullptr );
501 return nullptr;
502 }
503
504 emit beginRemoveItems( this, i, i );
505 mChildren.remove( i );
506 emit endRemoveItems();
507 return child;
508}
509
510int QgsDataItem::findItem( QVector<QgsDataItem *> items, QgsDataItem *item )
511{
512 for ( int i = 0; i < items.size(); i++ )
513 {
514 Q_ASSERT_X( items[i], "findItem", u"item %1 is nullptr"_s.arg( i ).toLatin1() );
515 QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
516 if ( items[i]->equal( item ) )
517 return i;
518 }
519 return -1;
520}
521
523{
524 if ( depth < 0 )
525 return nullptr;
526
527 QgsDataItem *result = const_cast< QgsDataItem * >( this );
528 while ( result && depth > 0 )
529 {
530 depth--;
531 result = result->parent();
532 }
533 return result;
534}
535
536bool QgsDataItem::equal( const QgsDataItem *other )
537{
538 return ( metaObject()->className() == other->metaObject()->className() &&
539 mPath == other->path() );
540}
541
542QList<QAction *> QgsDataItem::actions( QWidget *parent )
543{
544 Q_UNUSED( parent )
545 return QList<QAction *>();
546}
547
549{
550 return false;
551}
552
554{
555 return mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : mimeUris().constFirst();
556}
557
559{
561 {
563 uri.uri = path();
564 uri.filePath = path();
565 return { uri };
566 }
567
568 return {};
569}
570
572{
573 Q_UNUSED( crs )
574 return false;
575}
576
577bool QgsDataItem::rename( const QString & )
578{
579 return false;
580}
581
582void QgsDataItem::setCapabilities( int capabilities )
583{
584 setCapabilities( static_cast< Qgis::BrowserItemCapabilities >( capabilities ) );
585}
586
591
593{
594 QgsDebugMsgLevel( u"item %1 set state %2 -> %3"_s.arg( path() ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( this->state() ) ).arg( qgsEnumValueToKey< Qgis::BrowserItemState >( state ) ), 3 );
595 if ( state == mState )
596 return;
597
598 const Qgis::BrowserItemState oldState = mState;
599
600 if ( state == Qgis::BrowserItemState::Populating ) // start loading
601 {
602 if ( !sPopulatingIcon )
603 {
604 // TODO: ensure that QgsAnimatedIcon is created on UI thread only
605 sPopulatingIcon = new QgsAnimatedIcon( QgsApplication::iconPath( u"/mIconLoading.gif"_s ), QgsApplication::instance() );
606 }
607
608 sPopulatingIcon->connectFrameChanged( this, &QgsDataItem::updateIcon );
609 }
610 else if ( mState == Qgis::BrowserItemState::Populating && sPopulatingIcon ) // stop loading
611 {
612 sPopulatingIcon->disconnectFrameChanged( this, &QgsDataItem::updateIcon );
613 }
614
615
616 mState = state;
617
618 emit stateChanged( this, oldState );
620 updateIcon();
621}
622
623QList<QMenu *> QgsDataItem::menus( QWidget *parent )
624{
625 Q_UNUSED( parent )
626 return QList<QMenu *>();
627}
628
629QgsErrorItem::QgsErrorItem( QgsDataItem *parent, const QString &error, const QString &path )
630 : QgsDataItem( Qgis::BrowserItemType::Error, parent, error, path )
631{
632 mIconName = u"/mIconDelete.svg"_s;
633
634 setState( Qgis::BrowserItemState::Populated ); // no more children
635}
636
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
BrowserItemState
Browser item states.
Definition qgis.h:957
@ NotPopulated
Children not yet created.
Definition qgis.h:958
@ Populating
Creating children in separate thread (populating or refreshing).
Definition qgis.h:959
@ Populated
Children created.
Definition qgis.h:960
@ Fertile
Can create children. Even items without this capability may have children, but cannot create them,...
Definition qgis.h:973
@ RefreshChildrenWhenItemIsRefreshed
When the item is refreshed, all its populated children will also be refreshed in turn.
Definition qgis.h:979
@ ItemRepresentsFile
Item's path() directly represents a file on disk.
Definition qgis.h:978
@ Fast
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms,...
Definition qgis.h:974
BrowserItemType
Browser item types.
Definition qgis.h:938
@ Directory
Represents a file directory.
Definition qgis.h:940
QFlags< BrowserItemCapability > BrowserItemCapabilities
Browser item capabilities.
Definition qgis.h:984
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:50
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
int mCreatorAncestorDepth
Creator ancestor depth.
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.
int creatorAncestorDepth() const
Returns the hierarchical depth of the item's original creator/source.
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.
QgsDataItem * ancestorAtDepth(int depth) const
Returns the ancestor item at the specified depth.
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:7091
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.