populating browser icons in threads moved to QgsDataItem

This commit is contained in:
Radim Blazek 2014-12-12 12:27:43 +01:00
parent 917cee0510
commit d84af0d760
15 changed files with 297 additions and 252 deletions

View File

@ -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

View File

@ -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<QgsDataItem*> 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<QgsDataItem*> 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<QgsDataItem*> QgsBrowserModel::createChildren( QgsDataItem* item )
{
QgsDebugMsg( "Entered" );
QTime time;
time.start();
QVector <QgsDataItem*> 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<QgsBrowserWatcher *>( sender() );
if ( !watcher )
return;
QgsDataItem* item = watcher->item();
QVector <QgsDataItem*> 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<QgsBrowserWatcher *>( sender() );
if ( !watcher )
return;
QgsDataItem* item = watcher->item();
QVector <QgsDataItem*> 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 )

View File

@ -113,8 +113,6 @@ class CORE_EXPORT QgsBrowserModel : public QAbstractItemModel
bool canFetchMore( const QModelIndex & parent ) const;
void fetchMore( const QModelIndex & parent );
static QVector<QgsDataItem*> 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<QgsDataItem*> mRootItems;
QgsFavouritesItem *mFavourites;
QgsDirectoryItem *mProjectHome;
private:
QList<QgsBrowserWatcher *> mWatchers;
QMovie mLoadingMovie;
QIcon mLoadingIcon;
};
#endif // QGSBROWSERMODEL_H

View File

@ -16,6 +16,8 @@
***************************************************************************/
#include <QApplication>
#include <QtConcurrentMap>
#include <QtConcurrentRun>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
@ -149,15 +151,21 @@ const QIcon &QgsZipItem::iconZip()
QMap<QString, QIcon> QgsDataItem::mIconMap = QMap<QString, QIcon>();
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*> QgsDataItem::createChildren()
{
return QVector<QgsDataItem*>();
@ -201,25 +222,68 @@ QVector<QgsDataItem*> QgsDataItem::createChildren()
void QgsDataItem::populate()
{
if ( mPopulated )
if ( state() == Populated || state() == Populating )
return;
QgsDebugMsg( "mPath = " + mPath );
QVector<QgsDataItem*> 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() );
}
mPopulated = true;
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 <QgsDataItem*> >( this );
}
connect( mWatcher, SIGNAL( finished() ), SLOT( childrenCreated() ) );
mWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
}
}
// This is expected to be run in a separate thread
QVector<QgsDataItem*> QgsDataItem::runCreateChildren( QgsDataItem* item )
{
QgsDebugMsg( "path = " + item->path() );
//QTime time;
//time.start();
QVector <QgsDataItem*> 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<QgsDataItem*> children )
{
if ( mPopulated )
return;
QgsDebugMsg( "mPath = " + mPath );
foreach ( QgsDataItem *child, children )
@ -229,14 +293,11 @@ void QgsDataItem::populate( QVector<QgsDataItem*> children )
// update after thread finished -> refresh
addChildItem( child, true );
}
mPopulated = true;
setState( Populated );
}
void QgsDataItem::depopulate()
{
if ( !mPopulated )
return;
QgsDebugMsg( "mPath = " + mPath );
foreach ( QgsDataItem *child, mChildren )
@ -245,100 +306,30 @@ void QgsDataItem::depopulate()
child->depopulate(); // recursive
deleteChildItem( child );
}
mPopulated = false;
setState( NotPopulated );
}
int QgsDataItem::rowCount()
void QgsDataItem::refresh()
{
return mChildren.size();
}
bool QgsDataItem::hasChildren()
{
return ( mPopulated ? mChildren.count() > 0 : true );
}
if ( state() == Populating )
return;
void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh )
{
QgsDebugMsg( QString( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
QgsDebugMsg( "mPath = " + mPath );
int i;
if ( type() == Directory )
if ( capabilities2() & QgsDataItem::Fast )
{
for ( i = 0; i < mChildren.size(); i++ )
{
// sort items by type, so directories are before data items
if ( mChildren[i]->mType == child->mType &&
mChildren[i]->mName.localeAwareCompare( child->mName ) > 0 )
break;
}
refresh( createChildren() );
}
else
{
for ( i = 0; i < mChildren.size(); i++ )
setState( Populating );
if ( !mWatcher )
{
if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
break;
mWatcher = new QFutureWatcher< QVector <QgsDataItem*> >( this );
}
connect( mWatcher, SIGNAL( finished() ), SLOT( childrenCreated() ) );
mWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
}
if ( refresh )
emit beginInsertItems( this, i, i );
mChildren.insert( i, child );
child->setParent( this );
connect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
connect( child, SIGNAL( endInsertItems() ),
this, SLOT( emitEndInsertItems() ) );
connect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
connect( child, SIGNAL( endRemoveItems() ),
this, SLOT( emitEndRemoveItems() ) );
if ( refresh )
emit endInsertItems();
}
void QgsDataItem::deleteChildItem( QgsDataItem * child )
{
QgsDebugMsgLevel( "mName = " + child->mName, 2 );
int i = mChildren.indexOf( child );
Q_ASSERT( i >= 0 );
emit beginRemoveItems( this, i, i );
mChildren.remove( i );
delete child; // deleting QObject child removes it from QObject parent
emit endRemoveItems();
}
QgsDataItem * QgsDataItem::removeChildItem( QgsDataItem * child )
{
QgsDebugMsgLevel( "mName = " + child->mName, 2 );
int i = mChildren.indexOf( child );
Q_ASSERT( i >= 0 );
emit beginRemoveItems( this, i, i );
mChildren.remove( i );
emit endRemoveItems();
disconnect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
disconnect( child, SIGNAL( endInsertItems() ),
this, SLOT( emitEndInsertItems() ) );
disconnect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
disconnect( child, SIGNAL( endRemoveItems() ),
this, SLOT( emitEndRemoveItems() ) );
child->setParent( 0 );
return child;
}
int QgsDataItem::findItem( QVector<QgsDataItem*> items, QgsDataItem * item )
{
for ( int i = 0; i < items.size(); i++ )
{
QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
if ( items[i]->equal( item ) )
return i;
}
return -1;
}
void QgsDataItem::refresh( QVector<QgsDataItem*> children )
@ -382,19 +373,104 @@ void QgsDataItem::refresh( QVector<QgsDataItem*> children )
}
addChildItem( child, true );
}
setState( Populated );
}
void QgsDataItem::refresh()
int QgsDataItem::rowCount()
{
QgsDebugMsgLevel( "mPath = " + mPath, 2 );
return mChildren.size();
}
bool QgsDataItem::hasChildren()
{
return ( state() == Populated ? mChildren.count() > 0 : true );
}
QApplication::setOverrideCursor( Qt::WaitCursor );
void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh )
{
QgsDebugMsg( QString( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
QVector<QgsDataItem*> children = createChildren();
int i;
if ( type() == Directory )
{
for ( i = 0; i < mChildren.size(); i++ )
{
// sort items by type, so directories are before data items
if ( mChildren[i]->mType == child->mType &&
mChildren[i]->mName.localeAwareCompare( child->mName ) > 0 )
break;
}
}
else
{
for ( i = 0; i < mChildren.size(); i++ )
{
if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
break;
}
}
refresh( children );
if ( refresh )
emit beginInsertItems( this, i, i );
QApplication::restoreOverrideCursor();
mChildren.insert( i, child );
child->setParent( this );
connect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
connect( child, SIGNAL( endInsertItems() ),
this, SLOT( emitEndInsertItems() ) );
connect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
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();
}
void QgsDataItem::deleteChildItem( QgsDataItem * child )
{
QgsDebugMsgLevel( "mName = " + child->mName, 2 );
int i = mChildren.indexOf( child );
Q_ASSERT( i >= 0 );
emit beginRemoveItems( this, i, i );
mChildren.remove( i );
delete child; // deleting QObject child removes it from QObject parent
emit endRemoveItems();
}
QgsDataItem * QgsDataItem::removeChildItem( QgsDataItem * child )
{
QgsDebugMsgLevel( "mName = " + child->mName, 2 );
int i = mChildren.indexOf( child );
Q_ASSERT( i >= 0 );
emit beginRemoveItems( this, i, i );
mChildren.remove( i );
emit endRemoveItems();
disconnect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
disconnect( child, SIGNAL( endInsertItems() ),
this, SLOT( emitEndInsertItems() ) );
disconnect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
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;
}
int QgsDataItem::findItem( QVector<QgsDataItem*> items, QgsDataItem * item )
{
for ( int i = 0; i < items.size(); i++ )
{
QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
if ( items[i]->equal( item ) )
return i;
}
return -1;
}
bool QgsDataItem::equal( const QgsDataItem *other )
@ -407,6 +483,55 @@ bool QgsDataItem::equal( const QgsDataItem *other )
return false;
}
void QgsDataItem::setLoadingIcon()
{
mLoadingIcon = QIcon( mLoadingMovie->currentPixmap() );
}
QgsDataItem::State QgsDataItem::state() const
{
// for backward compatibility (if subclass set mPopulated directly)
// TODO: remove in 3.0
if ( mPopulated )
return Populated;
return mState;
}
void QgsDataItem::setState( State state )
{
if ( state == mState )
return;
if ( state == Populating ) // start loading
{
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 );
}
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;
}
// ---------------------------------------------------------------------
QgsLayerItem::QgsLayerItem( QgsDataItem* parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey )
@ -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 );
}

View File

@ -17,13 +17,15 @@
#ifndef QGSDATAITEM_H
#define QGSDATAITEM_H
#include <QFutureWatcher>
#include <QIcon>
#include <QLibrary>
#include <QMovie>
#include <QObject>
#include <QPixmap>
#include <QString>
#include <QVector>
#include <QTreeWidget>
#include <QVector>
#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<QgsDataItem*> children );
// Create vector of children
virtual QVector<QgsDataItem*> createChildren();
// Populate children using children vector created by createChildren()
virtual void populate();
virtual void populate( QVector<QgsDataItem*> 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
};
/** Set as populated without populating. */
void setPopulated() { mPopulated = true; }
State state() const;
/** 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<QgsDataItem*> children );
virtual void refresh( QVector<QgsDataItem*> children );
Type mType;
Capabilities mCapabilities;
QgsDataItem* mParent;
QVector<QgsDataItem*> 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<QgsDataItem*> runCreateChildren( QgsDataItem* item );
QFutureWatcher< QVector <QgsDataItem*> > *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 )

View File

@ -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()

View File

@ -121,7 +121,7 @@ QVector<QgsDataItem*> 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<QgsDataItem*> 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

View File

@ -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()

View File

@ -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()

View File

@ -283,7 +283,7 @@ QgsOracleLayerItem::QgsOracleLayerItem( QgsDataItem* parent, QString name, QStri
, mLayerProperty( layerProperty )
{
mUri = createUri();
mPopulated = true;
setState( Populated );
}
QgsOracleLayerItem::~QgsOracleLayerItem()

View File

@ -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 );
}

View File

@ -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<QAction*> QgsSLLayerItem::actions()

View File

@ -141,7 +141,7 @@ QgsWCSLayerItem::QgsWCSLayerItem( QgsDataItem* parent, QString name, QString pat
{
mIconName = "mIconWcs.svg";
}
mPopulated = true;
setState( Populated );
}
QgsWCSLayerItem::~QgsWCSLayerItem()

View File

@ -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";
}

View File

@ -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()