mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-19 00:02:48 -04:00
populating browser icons in threads moved to QgsDataItem
This commit is contained in:
parent
917cee0510
commit
d84af0d760
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -283,7 +283,7 @@ QgsOracleLayerItem::QgsOracleLayerItem( QgsDataItem* parent, QString name, QStri
|
||||
, mLayerProperty( layerProperty )
|
||||
{
|
||||
mUri = createUri();
|
||||
mPopulated = true;
|
||||
setState( Populated );
|
||||
}
|
||||
|
||||
QgsOracleLayerItem::~QgsOracleLayerItem()
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -141,7 +141,7 @@ QgsWCSLayerItem::QgsWCSLayerItem( QgsDataItem* parent, QString name, QString pat
|
||||
{
|
||||
mIconName = "mIconWcs.svg";
|
||||
}
|
||||
mPopulated = true;
|
||||
setState( Populated );
|
||||
}
|
||||
|
||||
QgsWCSLayerItem::~QgsWCSLayerItem()
|
||||
|
@ -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";
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user