From d84af0d76025c911f95b5d4bb95ce79c622072ee Mon Sep 17 00:00:00 2001 From: Radim Blazek Date: Fri, 12 Dec 2014 12:27:43 +0100 Subject: [PATCH] populating browser icons in threads moved to QgsDataItem --- python/core/qgsdataitem.sip | 1 - src/core/qgsbrowsermodel.cpp | 130 ++------- src/core/qgsbrowsermodel.h | 12 +- src/core/qgsdataitem.cpp | 265 +++++++++++++----- src/core/qgsdataitem.h | 41 ++- src/providers/gdal/qgsgdaldataitems.cpp | 4 +- .../grass/qgsgrassprovidermodule.cpp | 6 +- src/providers/mssql/qgsmssqldataitems.cpp | 4 +- src/providers/ogr/qgsogrdataitems.cpp | 2 +- src/providers/oracle/qgsoracledataitems.cpp | 2 +- .../postgres/qgspostgresdataitems.cpp | 2 +- .../spatialite/qgsspatialitedataitems.cpp | 2 +- src/providers/wcs/qgswcsdataitems.cpp | 2 +- src/providers/wfs/qgswfsdataitems.cpp | 2 +- src/providers/wms/qgswmsdataitems.cpp | 4 +- 15 files changed, 262 insertions(+), 217 deletions(-) diff --git a/python/core/qgsdataitem.sip b/python/core/qgsdataitem.sip index a31a9d544588..a0bb5621af79 100644 --- a/python/core/qgsdataitem.sip +++ b/python/core/qgsdataitem.sip @@ -28,7 +28,6 @@ class QgsDataItem : QObject // Populate children using children vector created by createChildren() virtual void populate(); bool isPopulated(); - void setPopulated(); // Insert new child using alphabetical order based on mName, emits necessary signal to model before and after, sets parent and connects signals // refresh - refresh populated item, emit signals to model diff --git a/src/core/qgsbrowsermodel.cpp b/src/core/qgsbrowsermodel.cpp index 4374a85bc323..aefcb5e41b0d 100644 --- a/src/core/qgsbrowsermodel.cpp +++ b/src/core/qgsbrowsermodel.cpp @@ -51,9 +51,6 @@ QgsBrowserModel::QgsBrowserModel( QObject *parent ) { connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( updateProjectHome() ) ); connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ), this, SLOT( updateProjectHome() ) ); - mLoadingMovie.setFileName( QgsApplication::iconPath( "/mIconLoading.gif" ) ); - mLoadingMovie.setCacheMode( QMovie::CacheAll ); - connect( &mLoadingMovie, SIGNAL( frameChanged( int ) ), SLOT( loadingFrameChanged() ) ); addRootItems(); } @@ -232,10 +229,6 @@ QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const } else if ( role == Qt::DecorationRole && index.column() == 0 ) { - if ( fetching( item ) ) - { - return mLoadingIcon; - } return item->icon(); } else @@ -330,6 +323,7 @@ QModelIndex QgsBrowserModel::findPath( QString path, Qt::MatchFlag matchFlag ) void QgsBrowserModel::reload() { + // TODO: put items creating currently children in threads to deleteLater (does not seem urget because reload() is not used in QGIS) beginResetModel(); removeRootItems(); addRootItems(); @@ -398,6 +392,14 @@ void QgsBrowserModel::endRemoveItems() QgsDebugMsgLevel( "Entered", 3 ); endRemoveRows(); } +void QgsBrowserModel::dataItemChanged( QgsDataItem * item ) +{ + QgsDebugMsgLevel( "Entered", 3 ); + QModelIndex idx = findItem( item ); + if ( !idx.isValid() ) + return; + emit dataChanged( idx, idx ); +} void QgsBrowserModel::connectItem( QgsDataItem* item ) { connect( item, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ), @@ -408,6 +410,8 @@ void QgsBrowserModel::connectItem( QgsDataItem* item ) this, SLOT( beginRemoveItems( QgsDataItem*, int, int ) ) ); connect( item, SIGNAL( endRemoveItems() ), this, SLOT( endRemoveItems() ) ); + connect( item, SIGNAL( dataChanged( QgsDataItem* ) ), + this, SLOT( dataItemChanged( QgsDataItem* ) ) ); } QStringList QgsBrowserModel::mimeTypes() const @@ -463,7 +467,7 @@ bool QgsBrowserModel::canFetchMore( const QModelIndex & parent ) const QgsDataItem* item = dataItem( parent ); // if ( item ) // QgsDebugMsg( QString( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) ); - return ( item && ! item->isPopulated() ); + return ( item && item->state() == QgsDataItem::NotPopulated ); } void QgsBrowserModel::fetchMore( const QModelIndex & parent ) @@ -471,22 +475,12 @@ void QgsBrowserModel::fetchMore( const QModelIndex & parent ) QgsDebugMsg( "Entered" ); QgsDataItem* item = dataItem( parent ); - if ( !item || fetching( item ) ) + if ( !item || item->state() == QgsDataItem::Populating || item->state() == QgsDataItem::Populated ) return; QgsDebugMsg( "path = " + item->path() ); - if ( item->isPopulated() ) - return; - - QList itemList; - itemList << item; - QgsBrowserWatcher * watcher = new QgsBrowserWatcher( item ); - connect( watcher, SIGNAL( finished() ), SLOT( childrenCreated() ) ); - watcher->setFuture( QtConcurrent::mapped( itemList, QgsBrowserModel::createChildren ) ); - mWatchers.append( watcher ); - mLoadingMovie.setPaused( false ); - emit dataChanged( parent, parent ); + item->populate(); } /* Refresh dir path */ @@ -500,104 +494,12 @@ void QgsBrowserModel::refresh( QString path ) void QgsBrowserModel::refresh( const QModelIndex& theIndex ) { QgsDataItem *item = dataItem( theIndex ); - if ( !item ) + if ( !item || item->state() == QgsDataItem::Populating ) return; QgsDebugMsg( "Refresh " + item->path() ); - QList itemList; - itemList << item; - QgsBrowserWatcher * watcher = new QgsBrowserWatcher( item ); - connect( watcher, SIGNAL( finished() ), SLOT( refreshChildrenCreated() ) ); - watcher->setFuture( QtConcurrent::mapped( itemList, QgsBrowserModel::createChildren ) ); - mWatchers.append( watcher ); - mLoadingMovie.setPaused( false ); - emit dataChanged( theIndex, theIndex ); -} - -// This is expected to be run in a separate thread -QVector QgsBrowserModel::createChildren( QgsDataItem* item ) -{ - QgsDebugMsg( "Entered" ); - QTime time; - time.start(); - QVector children = item->createChildren(); - QgsDebugMsg( QString( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ) ); - // Children objects must be pushed to main thread. - foreach ( QgsDataItem* child, children ) - { - if ( !child ) // should not happen - continue; - // However it seems to work without resetting parent, the Qt doc says that - // "The object cannot be moved if it has a parent." - QgsDebugMsg( "moveToThread child" + child->path() ); - child->setParent( 0 ); - child->moveToThread( QApplication::instance()->thread() ); - child->setParent( item ); - } - return children; -} - -void QgsBrowserModel::childrenCreated() -{ - QgsBrowserWatcher *watcher = dynamic_cast( sender() ); - if ( !watcher ) - return; - QgsDataItem* item = watcher->item(); - QVector children = watcher->result(); - QgsDebugMsg( QString( "path = %1 children.size() = %2" ).arg( item->path() ).arg( children.size() ) ); - QModelIndex index = findItem( item ); - if ( !index.isValid() ) // check if item still exists - return; - item->populate( children ); - emit dataChanged( index, index ); - emit fetchFinished( index ); -} - -void QgsBrowserModel::refreshChildrenCreated() -{ - QgsBrowserWatcher *watcher = dynamic_cast( sender() ); - if ( !watcher ) - return; - QgsDataItem* item = watcher->item(); - QVector children = watcher->result(); - QgsDebugMsg( QString( "path = %1 children.size() = %2" ).arg( item->path() ).arg( children.size() ) ); - QModelIndex index = findItem( item ); - if ( !index.isValid() ) // check if item still exists - return; - item->refresh( children ); - emit dataChanged( index, index ); -} - -bool QgsBrowserModel::fetching( QgsDataItem* item ) const -{ - foreach ( QgsBrowserWatcher * watcher, mWatchers ) - { - if ( !watcher->isFinished() && watcher->item() == item ) - return true; - } - return false; -} - -void QgsBrowserModel::loadingFrameChanged() -{ - mLoadingIcon = QIcon( mLoadingMovie.currentPixmap() ); - int notFinished = 0; - foreach ( QgsBrowserWatcher * watcher, mWatchers ) - { - if ( watcher->isFinished() ) - { - delete watcher; - mWatchers.removeOne( watcher ); - continue; - } - QModelIndex index = findItem( watcher->item() ); - QgsDebugMsg( QString( "path = %1 not finished" ).arg( watcher->item()->path() ) ); - emit dataChanged( index, index ); - notFinished++; - } - if ( notFinished == 0 ) - mLoadingMovie.setPaused( true ); + item->refresh(); } void QgsBrowserModel::addFavouriteDirectory( QString favDir ) diff --git a/src/core/qgsbrowsermodel.h b/src/core/qgsbrowsermodel.h index 91e6f30da845..0657a991d477 100644 --- a/src/core/qgsbrowsermodel.h +++ b/src/core/qgsbrowsermodel.h @@ -113,8 +113,6 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel bool canFetchMore( const QModelIndex & parent ) const; void fetchMore( const QModelIndex & parent ); - static QVector createChildren( QgsDataItem *item ); - bool fetching( QgsDataItem *item ) const; signals: /** Emitted when item children fetch was finished */ @@ -127,14 +125,11 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel void endInsertItems(); void beginRemoveItems( QgsDataItem *parent, int first, int last ); void endRemoveItems(); + void dataItemChanged( QgsDataItem * item ); void addFavouriteDirectory( QString favDir ); void removeFavourite( const QModelIndex &index ); - void updateProjectHome(); - void childrenCreated(); - void refreshChildrenCreated(); - void loadingFrameChanged(); protected: // populates the model @@ -144,11 +139,6 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel QVector mRootItems; QgsFavouritesItem *mFavourites; QgsDirectoryItem *mProjectHome; - - private: - QList mWatchers; - QMovie mLoadingMovie; - QIcon mLoadingIcon; }; #endif // QGSBROWSERMODEL_H diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 06fffebc44b2..17eed3017b76 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -16,6 +16,8 @@ ***************************************************************************/ #include +#include +#include #include #include #include @@ -149,15 +151,21 @@ const QIcon &QgsZipItem::iconZip() QMap QgsDataItem::mIconMap = QMap(); +int QgsDataItem::mLoadingCount = 0; +QMovie * QgsDataItem::mLoadingMovie = 0; +QIcon QgsDataItem::mLoadingIcon = QIcon(); + QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem* parent, QString name, QString path ) // Do not pass parent to QObject, Qt would delete this when parent is deleted : QObject() , mType( type ) , mCapabilities( NoCapabilities ) , mParent( parent ) + , mState( NotPopulated ) , mPopulated( false ) , mName( name ) , mPath( path ) + , mWatcher( 0 ) { } @@ -168,6 +176,9 @@ QgsDataItem::~QgsDataItem() QIcon QgsDataItem::icon() { + if ( state() == Populating ) + return mLoadingIcon; + if ( !mIcon.isNull() ) return mIcon; @@ -194,6 +205,16 @@ void QgsDataItem::emitEndRemoveItems() emit endRemoveItems(); } +void QgsDataItem::emitDataChanged( QgsDataItem* item ) +{ + emit dataChanged( item ); +} + +void QgsDataItem::emitDataChanged() +{ + emit dataChanged( this ); +} + QVector QgsDataItem::createChildren() { return QVector(); @@ -201,25 +222,68 @@ QVector QgsDataItem::createChildren() void QgsDataItem::populate() { - if ( mPopulated ) + if ( state() == Populated || state() == Populating ) return; QgsDebugMsg( "mPath = " + mPath ); - QVector children = createChildren(); - foreach ( QgsDataItem *child, children ) + if ( capabilities2() & QgsDataItem::Fast ) { - // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate) - addChildItem( child ); + populate( createChildren() ); + } + else + { + setState( Populating ); + // 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. + if ( !mWatcher ) + { + mWatcher = new QFutureWatcher< QVector >( this ); + } + connect( mWatcher, SIGNAL( finished() ), SLOT( childrenCreated() ) ); + mWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) ); } - mPopulated = true; } -void QgsDataItem::populate( QVector children ) +// This is expected to be run in a separate thread +QVector QgsDataItem::runCreateChildren( QgsDataItem* item ) { - if ( mPopulated ) - return; + QgsDebugMsg( "path = " + item->path() ); + //QTime time; + //time.start(); + QVector children = item->createChildren(); + //QgsDebugMsg( QString( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ) ); + // Children objects must be pushed to main thread. + foreach ( QgsDataItem* child, children ) + { + if ( !child ) // should not happen + continue; + // The object cannot be moved if it has a parent. + QgsDebugMsg( "moveToThread child " + child->path() ); + child->setParent( 0 ); + child->moveToThread( QApplication::instance()->thread() ); // moves also children + child->setParent( item ); + } + QgsDebugMsg( "finished path = " + item->path() ); + return children; +} + +void QgsDataItem::childrenCreated() +{ + QgsDebugMsg( QString( "path = %1 children.size() = %2" ).arg( path() ).arg( mWatcher->result().size() ) ); + if ( mChildren.size() == 0 ) // usually populating but may also be refresh if originaly there were no children + { + populate( mWatcher->result() ); + } + else // refreshing + { + refresh( mWatcher->result() ); + } + disconnect( mWatcher, SIGNAL( finished() ), this, SLOT( childrenCreated() ) ); + emit dataChanged( this ); // to replace loading icon by normal icon +} +void QgsDataItem::populate( QVector children ) +{ QgsDebugMsg( "mPath = " + mPath ); foreach ( QgsDataItem *child, children ) @@ -229,23 +293,87 @@ void QgsDataItem::populate( QVector children ) // update after thread finished -> refresh addChildItem( child, true ); } - mPopulated = true; + setState( Populated ); } void QgsDataItem::depopulate() { - if ( !mPopulated ) + QgsDebugMsg( "mPath = " + mPath ); + + foreach ( QgsDataItem *child, mChildren ) + { + QgsDebugMsg( "remove " + child->path() ); + child->depopulate(); // recursive + deleteChildItem( child ); + } + setState( NotPopulated ); +} + +void QgsDataItem::refresh() +{ + if ( state() == Populating ) return; QgsDebugMsg( "mPath = " + mPath ); + if ( capabilities2() & QgsDataItem::Fast ) + { + refresh( createChildren() ); + } + else + { + setState( Populating ); + if ( !mWatcher ) + { + mWatcher = new QFutureWatcher< QVector >( this ); + } + connect( mWatcher, SIGNAL( finished() ), SLOT( childrenCreated() ) ); + mWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) ); + } +} + +void QgsDataItem::refresh( QVector children ) +{ + QgsDebugMsgLevel( "mPath = " + mPath, 2 ); + + // Remove no more present children + QVector remove; foreach ( QgsDataItem *child, mChildren ) + { + if ( !child ) // should not happen + continue; + if ( findItem( children, child ) >= 0 ) + continue; + remove.append( child ); + } + foreach ( QgsDataItem *child, remove ) { QgsDebugMsg( "remove " + child->path() ); - child->depopulate(); // recursive deleteChildItem( child ); } - mPopulated = false; + + // Add new children + foreach ( QgsDataItem *child, children ) + { + if ( !child ) // should not happen + continue; + + int index = findItem( mChildren, child ); + if ( index >= 0 ) + { + // Refresh recursively (some providers may create more generations of descendants) + if ( !( child->capabilities2() & QgsDataItem::Fertile ) ) + { + // The child cannot createChildren() itself + mChildren.value( index )->refresh( child->children() ); + } + + delete child; + continue; + } + addChildItem( child, true ); + } + setState( Populated ); } int QgsDataItem::rowCount() @@ -254,7 +382,7 @@ int QgsDataItem::rowCount() } bool QgsDataItem::hasChildren() { - return ( mPopulated ? mChildren.count() > 0 : true ); + return ( state() == Populated ? mChildren.count() > 0 : true ); } void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh ) @@ -295,6 +423,8 @@ void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh ) this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) ); connect( child, SIGNAL( endRemoveItems() ), this, SLOT( emitEndRemoveItems() ) ); + connect( child, SIGNAL( dataChanged( QgsDataItem* ) ), + this, SLOT( emitDataChanged( QgsDataItem* ) ) ); if ( refresh ) emit endInsertItems(); @@ -326,6 +456,8 @@ QgsDataItem * QgsDataItem::removeChildItem( QgsDataItem * child ) this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) ); disconnect( child, SIGNAL( endRemoveItems() ), this, SLOT( emitEndRemoveItems() ) ); + disconnect( child, SIGNAL( dataChanged( QgsDataItem* ) ), + this, SLOT( emitDataChanged( QgsDataItem* ) ) ); child->setParent( 0 ); return child; } @@ -341,70 +473,63 @@ int QgsDataItem::findItem( QVector items, QgsDataItem * item ) return -1; } -void QgsDataItem::refresh( QVector children ) +bool QgsDataItem::equal( const QgsDataItem *other ) { - QgsDebugMsgLevel( "mPath = " + mPath, 2 ); - - // Remove no more present children - QVector remove; - foreach ( QgsDataItem *child, mChildren ) - { - if ( !child ) // should not happen - continue; - if ( findItem( children, child ) >= 0 ) - continue; - remove.append( child ); - } - foreach ( QgsDataItem *child, remove ) - { - QgsDebugMsg( "remove " + child->path() ); - deleteChildItem( child ); - } - - // Add new children - foreach ( QgsDataItem *child, children ) + if ( metaObject()->className() == other->metaObject()->className() && + mPath == other->path() ) { - if ( !child ) // should not happen - continue; - - int index = findItem( mChildren, child ); - if ( index >= 0 ) - { - // Refresh recursively (some providers may create more generations of descendants) - if ( !( child->capabilities2() & QgsDataItem::Fertile ) ) - { - // The child cannot createChildren() itself - mChildren.value( index )->refresh( child->children() ); - } - - delete child; - continue; - } - addChildItem( child, true ); + return true; } + return false; } -void QgsDataItem::refresh() +void QgsDataItem::setLoadingIcon() { - QgsDebugMsgLevel( "mPath = " + mPath, 2 ); - - QApplication::setOverrideCursor( Qt::WaitCursor ); - - QVector children = createChildren(); - - refresh( children ); + mLoadingIcon = QIcon( mLoadingMovie->currentPixmap() ); +} - QApplication::restoreOverrideCursor(); +QgsDataItem::State QgsDataItem::state() const +{ + // for backward compatibility (if subclass set mPopulated directly) + // TODO: remove in 3.0 + if ( mPopulated ) + return Populated; + return mState; } -bool QgsDataItem::equal( const QgsDataItem *other ) +void QgsDataItem::setState( State state ) { - if ( metaObject()->className() == other->metaObject()->className() && - mPath == other->path() ) + if ( state == mState ) + return; + + if ( state == Populating ) // start loading { - return true; + if ( !mLoadingMovie ) + { + // QApplication as parent to ensure that it is deleted before QApplication + mLoadingMovie = new QMovie( QApplication::instance() ); + mLoadingMovie->setFileName( QgsApplication::iconPath( "/mIconLoading.gif" ) ); + mLoadingMovie->setCacheMode( QMovie::CacheAll ); + connect( mLoadingMovie, SIGNAL( frameChanged( int ) ), SLOT( setLoadingIcon() ) ); + } + connect( mLoadingMovie, SIGNAL( frameChanged( int ) ), SLOT( emitDataChanged() ) ); + mLoadingCount++; + mLoadingMovie->setPaused( false ); } - return false; + else if ( mState == Populating && mLoadingMovie ) // stop loading + { + disconnect( mLoadingMovie, SIGNAL( frameChanged( int ) ), this, SLOT( emitDataChanged() ) ); + mLoadingCount--; + if ( mLoadingCount == 0 ) + { + mLoadingMovie->setPaused( true ); + } + } + + mState = state; + // for backward compatibility (if subclass access mPopulated directly) + // TODO: remove in 3.0 + mPopulated = state == Populated; } // --------------------------------------------------------------------- @@ -762,7 +887,7 @@ QgsErrorItem::QgsErrorItem( QgsDataItem* parent, QString error, QString path ) { mIconName = "/mIconDelete.png"; - mPopulated = true; // no more children + setState( Populated ); // no more children } QgsErrorItem::~QgsErrorItem() @@ -811,7 +936,7 @@ void QgsFavouritesItem::addDirectory( QString favDir ) favDirs.append( favDir ); settings.setValue( "/browser/favourites", favDirs ); - if ( mPopulated ) + if ( state() == Populated ) addChildItem( new QgsDirectoryItem( this, favDir, favDir ), true ); } @@ -832,7 +957,7 @@ void QgsFavouritesItem::removeDirectory( QgsDirectoryItem *item ) return; } - if ( mPopulated ) + if ( state() == Populated ) deleteChildItem( mChildren[idx] ); } @@ -1108,7 +1233,7 @@ QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString dirPath, QSt // force populate if less than 10 items if ( zipFileList.count() > 0 && zipFileList.count() <= 10 ) { - zipItem->populate(); + zipItem->populate( zipItem->createChildren() ); populated = true; // there is no QgsDataItem::isPopulated() function QgsDebugMsgLevel( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ), 3 ); } diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index e2fc2933fca2..65a8e92ed4b8 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -17,13 +17,15 @@ #ifndef QGSDATAITEM_H #define QGSDATAITEM_H +#include #include #include +#include #include #include #include -#include #include +#include #include "qgsapplication.h" #include "qgsmaplayer.h" @@ -57,22 +59,31 @@ class CORE_EXPORT QgsDataItem : public QObject int rowCount(); virtual void refresh(); - virtual void refresh( QVector children ); // Create vector of children virtual QVector createChildren(); // Populate children using children vector created by createChildren() virtual void populate(); - virtual void populate( QVector children ); /** Remove children recursively and set as not populated. This is used when refreshing collapsed items. */ virtual void depopulate(); - bool isPopulated() { return mPopulated; } + enum State + { + NotPopulated, //!< Children not yet created + Populating, //!< Creating children in separate thread (populating or refreshing) + Populated //!< children created + }; + + State state() const; - /** Set as populated without populating. */ - void setPopulated() { mPopulated = true; } + /** Set item state. It also take care about starting/stopping loading icon animation. + * @param state */ + void setState( State state ); + + //! @deprecated in 2.8, use state() + bool isPopulated() { return state() == Populated; } // Insert new child using alphabetical order based on mName, emits necessary signal to model before and after, sets parent and connects signals // refresh - refresh populated item, emit signals to model @@ -144,11 +155,15 @@ class CORE_EXPORT QgsDataItem : public QObject QString toolTip() const { return mToolTip; } protected: + virtual void populate( QVector children ); + virtual void refresh( QVector children ); Type mType; Capabilities mCapabilities; QgsDataItem* mParent; QVector mChildren; // easier to have it always + State mState; + //! @deprecated since 2.8, use mState bool mPopulated; QString mName; // Path is slash ('/') separated chain of item identifiers which are usually item names, but may be differen if it is @@ -166,12 +181,26 @@ class CORE_EXPORT QgsDataItem : public QObject void emitEndInsertItems(); void emitBeginRemoveItems( QgsDataItem* parent, int first, int last ); void emitEndRemoveItems(); + void emitDataChanged( QgsDataItem* item ); + void emitDataChanged( ); + void childrenCreated(); + void setLoadingIcon(); signals: void beginInsertItems( QgsDataItem* parent, int first, int last ); void endInsertItems(); void beginRemoveItems( QgsDataItem* parent, int first, int last ); void endRemoveItems(); + void dataChanged( QgsDataItem * item ); + + private: + static QVector runCreateChildren( QgsDataItem* item ); + + QFutureWatcher< QVector > *mWatcher; + // number of items currently in loading (populating) state + static int mLoadingCount; + static QMovie * mLoadingMovie; + static QIcon mLoadingIcon; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsDataItem::Capabilities ) diff --git a/src/providers/gdal/qgsgdaldataitems.cpp b/src/providers/gdal/qgsgdaldataitems.cpp index 838afc3ce6ea..ba3cbec34dee 100644 --- a/src/providers/gdal/qgsgdaldataitems.cpp +++ b/src/providers/gdal/qgsgdaldataitems.cpp @@ -34,10 +34,10 @@ QgsGdalLayerItem::QgsGdalLayerItem( QgsDataItem* parent, if ( theSublayers && theSublayers->size() > 0 ) { sublayers = *theSublayers; - mPopulated = false; + setState( NotPopulated ); } else - mPopulated = true; + setState( Populated ); } QgsGdalLayerItem::~QgsGdalLayerItem() diff --git a/src/providers/grass/qgsgrassprovidermodule.cpp b/src/providers/grass/qgsgrassprovidermodule.cpp index eddb20d57aff..0f92e2016c39 100644 --- a/src/providers/grass/qgsgrassprovidermodule.cpp +++ b/src/providers/grass/qgsgrassprovidermodule.cpp @@ -121,7 +121,7 @@ QVector QgsGrassMapsetItem::createChildren() { /* This may happen (one layer only) in GRASS 7 with points (no topo layers) */ QgsLayerItem *layer = new QgsLayerItem( this, name + " " + baseLayerName, layerPath, uri, layerType, "grass" ); - layer->setPopulated(); + layer->setState( Populated ); items.append( layer ); } else @@ -143,7 +143,7 @@ QVector QgsGrassMapsetItem::createChildren() QgsDebugMsg( "uri = " + uri ); QgsLayerItem *layer = new QgsLayerItem( this, name, path, uri, QgsLayerItem::Raster, "grassraster" ); - layer->setPopulated(); + layer->setState( Populated ); items.append( layer ); } @@ -155,7 +155,7 @@ QgsGrassVectorLayerItem::QgsGrassVectorLayerItem( QgsDataItem* parent, QString m : QgsLayerItem( parent, layerName, path, uri, layerType, providerKey ) , mMapName( mapName ) { - mPopulated = true; // no children, to show non expandable in browser + setState( Populated ); // no children, to show non expandable in browser } QString QgsGrassVectorLayerItem::layerName() const diff --git a/src/providers/mssql/qgsmssqldataitems.cpp b/src/providers/mssql/qgsmssqldataitems.cpp index 4269c5317c5b..c9866dd99fbb 100644 --- a/src/providers/mssql/qgsmssqldataitems.cpp +++ b/src/providers/mssql/qgsmssqldataitems.cpp @@ -392,7 +392,7 @@ bool QgsMssqlConnectionItem::handleDrop( const QMimeData * data, Qt::DropAction QMessageBox::information( 0, tr( "Import to MSSQL database" ), tr( "Import was successful." ) ); } - if ( mPopulated ) + if ( state() == Populated ) refresh(); else populate(); @@ -407,7 +407,7 @@ QgsMssqlLayerItem::QgsMssqlLayerItem( QgsDataItem* parent, QString name, QString , mLayerProperty( layerProperty ) { mUri = createUri(); - mPopulated = true; + setState( Populated ); } QgsMssqlLayerItem::~QgsMssqlLayerItem() diff --git a/src/providers/ogr/qgsogrdataitems.cpp b/src/providers/ogr/qgsogrdataitems.cpp index 806e9a75de8d..c9ea83610088 100644 --- a/src/providers/ogr/qgsogrdataitems.cpp +++ b/src/providers/ogr/qgsogrdataitems.cpp @@ -36,7 +36,7 @@ QgsOgrLayerItem::QgsOgrLayerItem( QgsDataItem* parent, : QgsLayerItem( parent, name, path, uri, layerType, "ogr" ) { mToolTip = uri; - mPopulated = true; // children are not expected + setState( Populated ); // children are not expected } QgsOgrLayerItem::~QgsOgrLayerItem() diff --git a/src/providers/oracle/qgsoracledataitems.cpp b/src/providers/oracle/qgsoracledataitems.cpp index b83a260b83f8..8ce1307d0130 100644 --- a/src/providers/oracle/qgsoracledataitems.cpp +++ b/src/providers/oracle/qgsoracledataitems.cpp @@ -283,7 +283,7 @@ QgsOracleLayerItem::QgsOracleLayerItem( QgsDataItem* parent, QString name, QStri , mLayerProperty( layerProperty ) { mUri = createUri(); - mPopulated = true; + setState( Populated ); } QgsOracleLayerItem::~QgsOracleLayerItem() diff --git a/src/providers/postgres/qgspostgresdataitems.cpp b/src/providers/postgres/qgspostgresdataitems.cpp index dd78272d5d88..bcb32a0ff0d5 100644 --- a/src/providers/postgres/qgspostgresdataitems.cpp +++ b/src/providers/postgres/qgspostgresdataitems.cpp @@ -217,7 +217,7 @@ QgsPGLayerItem::QgsPGLayerItem( QgsDataItem* parent, QString name, QString path, , mLayerProperty( layerProperty ) { mUri = createUri(); - mPopulated = true; + setState( Populated ); Q_ASSERT( mLayerProperty.size() == 1 ); } diff --git a/src/providers/spatialite/qgsspatialitedataitems.cpp b/src/providers/spatialite/qgsspatialitedataitems.cpp index 4f041f8f9682..b31c291827a7 100644 --- a/src/providers/spatialite/qgsspatialitedataitems.cpp +++ b/src/providers/spatialite/qgsspatialitedataitems.cpp @@ -32,7 +32,7 @@ QGISEXTERN bool deleteLayer( const QString& dbPath, const QString& tableName, QS QgsSLLayerItem::QgsSLLayerItem( QgsDataItem* parent, QString name, QString path, QString uri, LayerType layerType ) : QgsLayerItem( parent, name, path, uri, layerType, "spatialite" ) { - mPopulated = true; // no children are expected + setState( Populated ); // no children are expected } QList QgsSLLayerItem::actions() diff --git a/src/providers/wcs/qgswcsdataitems.cpp b/src/providers/wcs/qgswcsdataitems.cpp index ed108bb03856..864da456d2db 100644 --- a/src/providers/wcs/qgswcsdataitems.cpp +++ b/src/providers/wcs/qgswcsdataitems.cpp @@ -141,7 +141,7 @@ QgsWCSLayerItem::QgsWCSLayerItem( QgsDataItem* parent, QString name, QString pat { mIconName = "mIconWcs.svg"; } - mPopulated = true; + setState( Populated ); } QgsWCSLayerItem::~QgsWCSLayerItem() diff --git a/src/providers/wfs/qgswfsdataitems.cpp b/src/providers/wfs/qgswfsdataitems.cpp index a91885d6cc69..47ebe895f608 100644 --- a/src/providers/wfs/qgswfsdataitems.cpp +++ b/src/providers/wfs/qgswfsdataitems.cpp @@ -28,7 +28,7 @@ QgsWFSLayerItem::QgsWFSLayerItem( QgsDataItem* parent, QString name, QgsDataSour : QgsLayerItem( parent, title, parent->path() + "/" + name, QString(), QgsLayerItem::Vector, "WFS" ) { mUri = QgsWFSCapabilities( uri.encodedUri() ).uriGetFeature( featureType, crsString ); - mPopulated = true; + setState( Populated ); mIconName = "mIconConnect.png"; } diff --git a/src/providers/wms/qgswmsdataitems.cpp b/src/providers/wms/qgswmsdataitems.cpp index 9b9bfc771f09..be2f8bfa4502 100644 --- a/src/providers/wms/qgswmsdataitems.cpp +++ b/src/providers/wms/qgswmsdataitems.cpp @@ -261,7 +261,7 @@ QgsWMSLayerItem::QgsWMSLayerItem( QgsDataItem* parent, QString name, QString pat mIconName = "mIconWms.svg"; - mPopulated = true; + setState( Populated ); } QgsWMSLayerItem::~QgsWMSLayerItem() @@ -335,7 +335,7 @@ QgsWMTSLayerItem::QgsWMTSLayerItem( QgsDataItem *parent, , mTitle( title ) { mUri = createUri(); - mPopulated = true; + setState( Populated ); } QgsWMTSLayerItem::~QgsWMTSLayerItem()