mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
Framework for task manager
Adds new classes: - QgsTask. An interface for long-running background tasks - QgsTaskManager. Handles groups of tasks - also available as a global instance for tracking application wide tasks - QgsTaskManagerWidget. A list view for showing active tasks and their progress, and for cancelling them A new dock widget has been added with a task manager widget showing global tasks
This commit is contained in:
parent
dc697d0590
commit
ebae15f23a
@ -138,6 +138,7 @@
|
||||
%Include qgsstatisticalsummary.sip
|
||||
%Include qgsstringstatisticalsummary.sip
|
||||
%Include qgsstringutils.sip
|
||||
%Include qgstaskmanager.sip
|
||||
%Include qgstextrenderer.sip
|
||||
%Include qgstolerance.sip
|
||||
%Include qgstracer.sip
|
||||
|
174
python/core/qgstaskmanager.sip
Normal file
174
python/core/qgstaskmanager.sip
Normal file
@ -0,0 +1,174 @@
|
||||
/** \ingroup core
|
||||
* \class QgsTask
|
||||
* \brief Interface class for long running tasks which will be handled by a QgsTaskManager
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class QgsTask : QObject
|
||||
{
|
||||
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanager.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
//! Status of tasks
|
||||
enum TaskStatus
|
||||
{
|
||||
Running, /*!< Task is currently running */
|
||||
Complete, /*!< Task successfully completed */
|
||||
Terminated, /*!< Task was terminated or errored */
|
||||
// Paused,
|
||||
// Queued,
|
||||
};
|
||||
|
||||
/** Constructor for QgsTask.
|
||||
* @param description text description of task
|
||||
*/
|
||||
QgsTask( const QString& description = QString() );
|
||||
|
||||
//! Will be called when task has been terminated, either through
|
||||
//! user interaction or other reason (eg application exit)
|
||||
//! @note derived classes must ensure they call the base method
|
||||
virtual void terminate();
|
||||
|
||||
//! Returns true if the task is active, ie it is not complete and has
|
||||
//! not been terminated.
|
||||
bool isActive() const;
|
||||
|
||||
//! Returns the current task status.
|
||||
TaskStatus status() const;
|
||||
|
||||
//! Returns the task's description.
|
||||
QString description() const;
|
||||
|
||||
//! Returns the task's progress (between 0.0 and 100.0)
|
||||
double progress() const;
|
||||
|
||||
signals:
|
||||
|
||||
//! Will be emitted by task when its progress changes
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! setProgress()
|
||||
void progressChanged( double progress );
|
||||
|
||||
//! Will be emitted by task when its status changes
|
||||
//! @param status new task status
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! completed() or stopped()
|
||||
void statusChanged( int status );
|
||||
|
||||
//! Will be emitted by task to indicate its completion.
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! completed()
|
||||
void taskCompleted();
|
||||
|
||||
//! Will be emitted by task if it has terminated for any reason
|
||||
//! other then completion.
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! stopped()//!
|
||||
void taskStopped();
|
||||
|
||||
protected:
|
||||
|
||||
//! Sets the task's current progress. Should be called whenever the
|
||||
//! task wants to update it's progress. Calling will automatically emit the progressChanged
|
||||
//! signal.
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
void setProgress( double progress );
|
||||
|
||||
//! Sets the task as completed. Should be called when the task is complete.
|
||||
//! Calling will automatically emit the statusChanged and taskCompleted signals.
|
||||
void completed();
|
||||
|
||||
//! Sets the task as stopped. Should be called whenever the task ends for any
|
||||
//! reason other than successful completion.
|
||||
//! Calling will automatically emit the statusChanged and taskStopped signals.
|
||||
void stopped();
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsTaskManager
|
||||
* \brief Task manager for managing a set of long-running QgsTask tasks. This class can be created directly,
|
||||
* or accessed via a global instance.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class QgsTaskManager : QObject
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanager.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Returns the global task manager instance pointer, creating the object on the first call.
|
||||
*/
|
||||
static QgsTaskManager * instance();
|
||||
|
||||
/** Constructor for QgsTaskManager.
|
||||
* @param parent parent QObject
|
||||
*/
|
||||
QgsTaskManager( QObject* parent /TransferThis/ = nullptr );
|
||||
|
||||
virtual ~QgsTaskManager();
|
||||
|
||||
/** Adds a task to the manager. Ownership of the task is transferred
|
||||
* to the manager.
|
||||
* @param task task to add
|
||||
* @returns unique task ID
|
||||
*/
|
||||
long addTask( QgsTask* task /Transfer/ );
|
||||
|
||||
/** Deletes the specified task, first terminating it if it is currently
|
||||
* running.
|
||||
* @param id task ID
|
||||
* @returns true if task was found and deleted
|
||||
*/
|
||||
bool deleteTask( long id );
|
||||
|
||||
/** Deletes the specified task, first terminating it if it is currently
|
||||
* running.
|
||||
* @param task task to delete
|
||||
* @returns true if task was contained in manager and deleted
|
||||
*/
|
||||
bool deleteTask( QgsTask* task );
|
||||
|
||||
/** Returns the task with matching ID.
|
||||
* @param id task ID
|
||||
* @returns task if found, or nullptr
|
||||
*/
|
||||
QgsTask* task( long id ) const;
|
||||
|
||||
/** Returns all tasks tracked by the manager.
|
||||
*/
|
||||
QList<QgsTask*> tasks() const;
|
||||
|
||||
//! Returns the number of tasks tracked by the manager.
|
||||
int count() const;
|
||||
|
||||
/** Returns the unique task ID corresponding to a task managed by the class.
|
||||
* @param task task to find
|
||||
* @returns task ID, or -1 if task not found
|
||||
*/
|
||||
long taskId( QgsTask* task ) const;
|
||||
|
||||
signals:
|
||||
|
||||
//! Will be emitted when a task reports a progress change
|
||||
//! @param taskId ID of task
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
void progressChanged( long taskId, double progress );
|
||||
|
||||
//! Will be emitted when a task reports a status change
|
||||
//! @param taskId ID of task
|
||||
//! @param status new task status
|
||||
void statusChanged( long taskId, int status );
|
||||
|
||||
//! Emitted when a new task has been added to the manager
|
||||
//! @param taskId ID of task
|
||||
void taskAdded( long taskId );
|
||||
|
||||
//! Emitted when a task is about to be deleted
|
||||
//! @param taskId ID of task
|
||||
void taskAboutToBeDeleted( long taskId );
|
||||
|
||||
};
|
@ -161,6 +161,7 @@
|
||||
%Include qgstablewidgetbase.sip
|
||||
%Include qgstabwidget.sip
|
||||
%Include qgstablewidgetitem.sip
|
||||
%Include qgstaskmanagerwidget.sip
|
||||
%Include qgstextannotationitem.sip
|
||||
%Include qgstextformatwidget.sip
|
||||
%Include qgstextpreview.sip
|
||||
|
95
python/gui/qgstaskmanagerwidget.sip
Normal file
95
python/gui/qgstaskmanagerwidget.sip
Normal file
@ -0,0 +1,95 @@
|
||||
/** \ingroup gui
|
||||
* \class QgsTaskManagerWidget
|
||||
* A widget which displays tasks from a QgsTaskManager and allows for interaction with the manager
|
||||
* @see QgsTaskManager
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class QgsTaskManagerWidget : QTreeView
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanagerwidget.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskManagerWidget
|
||||
* @param manager task manager associated with widget
|
||||
* @param parent parent widget
|
||||
*/
|
||||
QgsTaskManagerWidget( QgsTaskManager* manager, QWidget* parent /TransferThis/ = nullptr );
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsTaskManagerModel
|
||||
* A model representing a QgsTaskManager
|
||||
* @see QgsTaskManager
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class QgsTaskManagerModel: QAbstractItemModel
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanagerwidget.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskManagerModel
|
||||
* @param manager task manager for model
|
||||
* @param parent parent object
|
||||
*/
|
||||
explicit QgsTaskManagerModel( QgsTaskManager* manager, QObject* parent /TransferThis/ = nullptr );
|
||||
|
||||
//reimplemented QAbstractItemModel methods
|
||||
QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const;
|
||||
QModelIndex parent( const QModelIndex &index ) const;
|
||||
int rowCount( const QModelIndex &parent = QModelIndex() ) const;
|
||||
int columnCount( const QModelIndex &parent = QModelIndex() ) const;
|
||||
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;
|
||||
Qt::ItemFlags flags( const QModelIndex & index ) const;
|
||||
bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsProgressBarDelegate
|
||||
* A delegate for showing a progress bar within a view
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class QgsProgressBarDelegate : QStyledItemDelegate
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanagerwidget.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsProgressBarDelegate
|
||||
* @param parent parent object
|
||||
*/
|
||||
QgsProgressBarDelegate( QObject* parent /TransferThis/ = nullptr );
|
||||
|
||||
void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
|
||||
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
|
||||
};
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsProgressBarDelegate
|
||||
* A delegate for showing task status within a view. Clicks on the delegate will cause the task to be cancelled (via the model).
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class QgsTaskStatusDelegate : QStyledItemDelegate
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include <qgstaskmanagerwidget.h>
|
||||
%End
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskStatusDelegate
|
||||
* @param parent parent object
|
||||
*/
|
||||
QgsTaskStatusDelegate( QObject* parent /TransferThis/ = nullptr );
|
||||
|
||||
void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
|
||||
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const;
|
||||
bool editorEvent( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index );
|
||||
};
|
@ -221,6 +221,8 @@
|
||||
#include "qgsstatusbarscalewidget.h"
|
||||
#include "qgsstyle.h"
|
||||
#include "qgssvgannotationitem.h"
|
||||
#include "qgstaskmanager.h"
|
||||
#include "qgstaskmanagerwidget.h"
|
||||
#include "qgssymbolselectordialog.h"
|
||||
#include "qgstextannotationitem.h"
|
||||
#include "qgstipgui.h"
|
||||
@ -851,6 +853,13 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
|
||||
QMainWindow::addDockWidget( Qt::BottomDockWidgetArea, mUserInputDockWidget );
|
||||
mUserInputDockWidget->setFloating( true );
|
||||
|
||||
// create the task manager dock on starting QGIS - this is like the browser
|
||||
mTaskManagerDock = new QDockWidget( tr( "Task Manager" ), this );
|
||||
mTaskManagerDock->setObjectName( "TaskManager" );
|
||||
addDockWidget( Qt::RightDockWidgetArea, mTaskManagerDock );
|
||||
mTaskManagerDock->setWidget( new QgsTaskManagerWidget( QgsTaskManager::instance(), mTaskManagerDock ) );
|
||||
mTaskManagerDock->hide();
|
||||
|
||||
// create the GPS tool on starting QGIS - this is like the browser
|
||||
mpGpsWidget = new QgsGPSInformationWidget( mMapCanvas );
|
||||
//create the dock widget
|
||||
@ -1139,6 +1148,7 @@ QgisApp::QgisApp()
|
||||
, mOverviewDock( nullptr )
|
||||
, mpGpsDock( nullptr )
|
||||
, mLogDock( nullptr )
|
||||
, mTaskManagerDock( nullptr )
|
||||
, mNonEditMapTool( nullptr )
|
||||
, mScaleWidget( nullptr )
|
||||
, mMagnifierWidget( nullptr )
|
||||
|
@ -1560,6 +1560,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
|
||||
QgsDockWidget *mOverviewDock;
|
||||
QgsDockWidget *mpGpsDock;
|
||||
QgsDockWidget *mLogDock;
|
||||
QDockWidget *mTaskManagerDock;
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
//! Window menu action to select this window
|
||||
|
@ -211,6 +211,7 @@ SET(QGIS_CORE_SRCS
|
||||
qgsstatisticalsummary.cpp
|
||||
qgsstringstatisticalsummary.cpp
|
||||
qgsstringutils.cpp
|
||||
qgstaskmanager.cpp
|
||||
qgstextlabelfeature.cpp
|
||||
qgstextrenderer.cpp
|
||||
qgstolerance.cpp
|
||||
@ -495,6 +496,7 @@ SET(QGIS_CORE_MOC_HDRS
|
||||
qgsrelationmanager.h
|
||||
qgsrunprocess.h
|
||||
qgssnappingutils.h
|
||||
qgstaskmanager.h
|
||||
qgstracer.h
|
||||
qgstrackedvectorlayertools.h
|
||||
qgstransaction.h
|
||||
|
181
src/core/qgstaskmanager.cpp
Normal file
181
src/core/qgstaskmanager.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
/***************************************************************************
|
||||
qgstaskmanager.cpp
|
||||
------------------
|
||||
begin : April 2016
|
||||
copyright : (C) 2016 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgstaskmanager.h"
|
||||
#include <QMutex>
|
||||
|
||||
|
||||
//
|
||||
// QgsTask
|
||||
//
|
||||
|
||||
QgsTask::QgsTask( const QString &name )
|
||||
: QObject()
|
||||
, mDescription( name )
|
||||
, mStatus( Running )
|
||||
, mProgress( 0.0 )
|
||||
{}
|
||||
|
||||
void QgsTask::setProgress( double progress )
|
||||
{
|
||||
mProgress = progress;
|
||||
emit progressChanged( progress );
|
||||
}
|
||||
|
||||
void QgsTask::completed()
|
||||
{
|
||||
mStatus = Complete;
|
||||
emit statusChanged( Complete );
|
||||
emit taskCompleted();
|
||||
}
|
||||
|
||||
void QgsTask::stopped()
|
||||
{
|
||||
mStatus = Terminated;
|
||||
emit statusChanged( Terminated );
|
||||
emit taskStopped();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsTaskManager
|
||||
//
|
||||
|
||||
// Static calls to enforce singleton behaviour
|
||||
QgsTaskManager *QgsTaskManager::sInstance = nullptr;
|
||||
QgsTaskManager *QgsTaskManager::instance()
|
||||
{
|
||||
if ( !sInstance )
|
||||
{
|
||||
sInstance = new QgsTaskManager();
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
QgsTaskManager::QgsTaskManager( QObject* parent )
|
||||
: QObject( parent )
|
||||
, mNextTaskId( 0 )
|
||||
{}
|
||||
|
||||
QgsTaskManager::~QgsTaskManager()
|
||||
{
|
||||
QMap< long, QgsTask* >::const_iterator it = mTasks.constBegin();
|
||||
for ( ; it != mTasks.constEnd(); ++it )
|
||||
{
|
||||
cleanupAndDeleteTask( it.value() );
|
||||
}
|
||||
}
|
||||
|
||||
long QgsTaskManager::addTask( QgsTask* task )
|
||||
{
|
||||
static QMutex sAddMutex( QMutex::Recursive );
|
||||
QMutexLocker locker( &sAddMutex );
|
||||
|
||||
mTasks.insert( mNextTaskId, task );
|
||||
connect( task, SIGNAL( progressChanged( double ) ), this, SLOT( taskProgressChanged( double ) ) );
|
||||
connect( task, SIGNAL( statusChanged( int ) ), this, SLOT( taskStatusChanged( int ) ) );
|
||||
emit taskAdded( mNextTaskId );
|
||||
return mNextTaskId++;
|
||||
}
|
||||
|
||||
bool QgsTaskManager::deleteTask( long id )
|
||||
{
|
||||
QgsTask* task = mTasks.value( id );
|
||||
return deleteTask( task );
|
||||
}
|
||||
|
||||
bool QgsTaskManager::deleteTask( QgsTask *task )
|
||||
{
|
||||
if ( !task )
|
||||
return false;
|
||||
|
||||
bool result = cleanupAndDeleteTask( task );
|
||||
|
||||
// remove from internal task list
|
||||
for ( QMap< long, QgsTask* >::iterator it = mTasks.begin(); it != mTasks.end(); )
|
||||
{
|
||||
if ( it.value() == task )
|
||||
it = mTasks.erase( it );
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QgsTask*QgsTaskManager::task( long id ) const
|
||||
{
|
||||
return mTasks.value( id );
|
||||
}
|
||||
|
||||
QList<QgsTask*> QgsTaskManager::tasks() const
|
||||
{
|
||||
return mTasks.values();
|
||||
}
|
||||
|
||||
long QgsTaskManager::taskId( QgsTask *task ) const
|
||||
{
|
||||
if ( !task )
|
||||
return -1;
|
||||
|
||||
QMap< long, QgsTask* >::const_iterator it = mTasks.constBegin();
|
||||
for ( ; it != mTasks.constEnd(); ++it )
|
||||
{
|
||||
if ( it.value() == task )
|
||||
return it.key();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void QgsTaskManager::taskProgressChanged( double progress )
|
||||
{
|
||||
QgsTask* task = qobject_cast< QgsTask* >( sender() );
|
||||
|
||||
//find ID of task
|
||||
long id = taskId( task );
|
||||
if ( id < 0 )
|
||||
return;
|
||||
|
||||
emit progressChanged( id, progress );
|
||||
}
|
||||
|
||||
void QgsTaskManager::taskStatusChanged( int status )
|
||||
{
|
||||
QgsTask* task = qobject_cast< QgsTask* >( sender() );
|
||||
|
||||
//find ID of task
|
||||
long id = taskId( task );
|
||||
if ( id < 0 )
|
||||
return;
|
||||
|
||||
emit statusChanged( id, status );
|
||||
}
|
||||
|
||||
bool QgsTaskManager::cleanupAndDeleteTask( QgsTask *task )
|
||||
{
|
||||
if ( !task )
|
||||
return false;
|
||||
|
||||
if ( task->isActive() )
|
||||
task->terminate();
|
||||
|
||||
emit taskAboutToBeDeleted( taskId( task ) );
|
||||
|
||||
task->deleteLater();
|
||||
return true;
|
||||
}
|
222
src/core/qgstaskmanager.h
Normal file
222
src/core/qgstaskmanager.h
Normal file
@ -0,0 +1,222 @@
|
||||
/***************************************************************************
|
||||
qgstaskmanager.h
|
||||
----------------
|
||||
begin : April 2016
|
||||
copyright : (C) 2016 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSTASKMANAGER_H
|
||||
#define QGSTASKMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsTask
|
||||
* \brief Interface class for long running background tasks which will be handled by a QgsTaskManager
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class CORE_EXPORT QgsTask : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
//! Status of tasks
|
||||
enum TaskStatus
|
||||
{
|
||||
Running, /*!< Task is currently running */
|
||||
Complete, /*!< Task successfully completed */
|
||||
Terminated, /*!< Task was terminated or errored */
|
||||
// Paused,
|
||||
// Queued,
|
||||
};
|
||||
|
||||
/** Constructor for QgsTask.
|
||||
* @param description text description of task
|
||||
*/
|
||||
QgsTask( const QString& description = QString() );
|
||||
|
||||
//! Will be called when task has been terminated, either through
|
||||
//! user interaction or other reason (eg application exit)
|
||||
//! @note derived classes must ensure they call the base method
|
||||
virtual void terminate()
|
||||
{
|
||||
stopped();
|
||||
}
|
||||
|
||||
//! Returns true if the task is active, ie it is not complete and has
|
||||
//! not been terminated.
|
||||
bool isActive() const { return mStatus == Running; }
|
||||
|
||||
//! Returns the current task status.
|
||||
TaskStatus status() const { return mStatus; }
|
||||
|
||||
//! Returns the task's description.
|
||||
QString description() const { return mDescription; }
|
||||
|
||||
//! Returns the task's progress (between 0.0 and 100.0)
|
||||
double progress() const { return mProgress; }
|
||||
|
||||
signals:
|
||||
|
||||
//! Will be emitted by task when its progress changes
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! setProgress()
|
||||
void progressChanged( double progress );
|
||||
|
||||
//! Will be emitted by task when its status changes
|
||||
//! @param status new task status
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! completed() or stopped()
|
||||
void statusChanged( int status );
|
||||
|
||||
//! Will be emitted by task to indicate its completion.
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! completed()
|
||||
void taskCompleted();
|
||||
|
||||
//! Will be emitted by task if it has terminated for any reason
|
||||
//! other then completion.
|
||||
//! @note derived classes should not emit this signal directly, instead they should call
|
||||
//! stopped()//!
|
||||
void taskStopped();
|
||||
|
||||
protected:
|
||||
|
||||
//! Sets the task's current progress. Should be called whenever the
|
||||
//! task wants to update it's progress. Calling will automatically emit the progressChanged
|
||||
//! signal.
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
void setProgress( double progress );
|
||||
|
||||
//! Sets the task as completed. Should be called when the task is complete.
|
||||
//! Calling will automatically emit the statusChanged and taskCompleted signals.
|
||||
void completed();
|
||||
|
||||
//! Sets the task as stopped. Should be called whenever the task ends for any
|
||||
//! reason other than successful completion.
|
||||
//! Calling will automatically emit the statusChanged and taskStopped signals.
|
||||
void stopped();
|
||||
|
||||
private:
|
||||
|
||||
QString mDescription;
|
||||
TaskStatus mStatus;
|
||||
double mProgress;
|
||||
|
||||
};
|
||||
|
||||
/** \ingroup core
|
||||
* \class QgsTaskManager
|
||||
* \brief Task manager for managing a set of long-running QgsTask tasks. This class can be created directly,
|
||||
* or accessed via a global instance.
|
||||
* \note Added in version 2.16
|
||||
*/
|
||||
class CORE_EXPORT QgsTaskManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Returns the global task manager instance pointer, creating the object on the first call.
|
||||
*/
|
||||
static QgsTaskManager * instance();
|
||||
|
||||
/** Constructor for QgsTaskManager.
|
||||
* @param parent parent QObject
|
||||
*/
|
||||
QgsTaskManager( QObject* parent = nullptr );
|
||||
|
||||
virtual ~QgsTaskManager();
|
||||
|
||||
/** Adds a task to the manager. Ownership of the task is transferred
|
||||
* to the manager.
|
||||
* @param task task to add
|
||||
* @returns unique task ID
|
||||
*/
|
||||
long addTask( QgsTask* task );
|
||||
|
||||
/** Deletes the specified task, first terminating it if it is currently
|
||||
* running.
|
||||
* @param id task ID
|
||||
* @returns true if task was found and deleted
|
||||
*/
|
||||
bool deleteTask( long id );
|
||||
|
||||
/** Deletes the specified task, first terminating it if it is currently
|
||||
* running.
|
||||
* @param task task to delete
|
||||
* @returns true if task was contained in manager and deleted
|
||||
*/
|
||||
bool deleteTask( QgsTask* task );
|
||||
|
||||
/** Returns the task with matching ID.
|
||||
* @param id task ID
|
||||
* @returns task if found, or nullptr
|
||||
*/
|
||||
QgsTask* task( long id ) const;
|
||||
|
||||
/** Returns all tasks tracked by the manager.
|
||||
*/
|
||||
QList<QgsTask*> tasks() const;
|
||||
|
||||
//! Returns the number of tasks tracked by the manager.
|
||||
int count() const { return mTasks.count(); }
|
||||
|
||||
/** Returns the unique task ID corresponding to a task managed by the class.
|
||||
* @param task task to find
|
||||
* @returns task ID, or -1 if task not found
|
||||
*/
|
||||
long taskId( QgsTask* task ) const;
|
||||
|
||||
signals:
|
||||
|
||||
//! Will be emitted when a task reports a progress change
|
||||
//! @param taskId ID of task
|
||||
//! @param progress percent of progress, from 0.0 - 100.0
|
||||
void progressChanged( long taskId, double progress );
|
||||
|
||||
//! Will be emitted when a task reports a status change
|
||||
//! @param taskId ID of task
|
||||
//! @param status new task status
|
||||
void statusChanged( long taskId, int status );
|
||||
|
||||
//! Emitted when a new task has been added to the manager
|
||||
//! @param taskId ID of task
|
||||
void taskAdded( long taskId );
|
||||
|
||||
//! Emitted when a task is about to be deleted
|
||||
//! @param taskId ID of task
|
||||
void taskAboutToBeDeleted( long taskId );
|
||||
|
||||
private slots:
|
||||
|
||||
void taskProgressChanged( double progress );
|
||||
void taskStatusChanged( int status );
|
||||
|
||||
private:
|
||||
|
||||
static QgsTaskManager *sInstance;
|
||||
QMap< long, QgsTask* > mTasks;
|
||||
|
||||
//! Tracks the next unique task ID
|
||||
long mNextTaskId;
|
||||
|
||||
bool cleanupAndDeleteTask( QgsTask* task );
|
||||
|
||||
};
|
||||
|
||||
#endif //QGSTASKMANAGER_H
|
@ -308,6 +308,7 @@ SET(QGIS_GUI_SRCS
|
||||
qgstablewidgetbase.cpp
|
||||
qgstabwidget.cpp
|
||||
qgstablewidgetitem.cpp
|
||||
qgstaskmanagerwidget.cpp
|
||||
qgstextannotationitem.cpp
|
||||
qgstextformatwidget.cpp
|
||||
qgstextpreview.cpp
|
||||
@ -465,6 +466,7 @@ SET(QGIS_GUI_MOC_HDRS
|
||||
qgssubstitutionlistwidget.h
|
||||
qgstablewidgetbase.h
|
||||
qgstabwidget.h
|
||||
qgstaskmanagerwidget.h
|
||||
qgstextformatwidget.h
|
||||
qgstextpreview.h
|
||||
qgstreewidgetitem.h
|
||||
|
363
src/gui/qgstaskmanagerwidget.cpp
Normal file
363
src/gui/qgstaskmanagerwidget.cpp
Normal file
@ -0,0 +1,363 @@
|
||||
/***************************************************************************
|
||||
qgstaskmanagerwidget.cpp
|
||||
------------------------
|
||||
begin : April 2016
|
||||
copyright : (C) 2016 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgstaskmanagerwidget.h"
|
||||
#include "qgstaskmanager.h"
|
||||
#include "qgsapplication.h"
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
|
||||
//
|
||||
// QgsTaskManagerWidget
|
||||
//
|
||||
|
||||
QgsTaskManagerWidget::QgsTaskManagerWidget( QgsTaskManager *manager, QWidget *parent )
|
||||
: QTreeView( parent )
|
||||
{
|
||||
Q_ASSERT( manager );
|
||||
|
||||
setModel( new QgsTaskManagerModel( manager, this ) );
|
||||
|
||||
setItemDelegateForColumn( 1, new QgsProgressBarDelegate( this ) );
|
||||
setItemDelegateForColumn( 2, new QgsTaskStatusDelegate( this ) );
|
||||
|
||||
setHeaderHidden( true );
|
||||
setRootIsDecorated( false );
|
||||
setSelectionBehavior( QAbstractItemView::SelectRows );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsTaskManagerModel
|
||||
//
|
||||
|
||||
QgsTaskManagerModel::QgsTaskManagerModel( QgsTaskManager *manager, QObject *parent )
|
||||
: QAbstractItemModel( parent )
|
||||
, mManager( manager )
|
||||
{
|
||||
Q_ASSERT( mManager );
|
||||
|
||||
//populate row to id map
|
||||
int i = 0;
|
||||
Q_FOREACH ( QgsTask* task, mManager->tasks() )
|
||||
{
|
||||
mRowToTaskIdMap.insert( i, mManager->taskId( task ) );
|
||||
}
|
||||
|
||||
connect( mManager, SIGNAL( taskAdded( long ) ), this, SLOT( taskAdded( long ) ) );
|
||||
connect( mManager, SIGNAL( taskAboutToBeDeleted( long ) ), this, SLOT( taskDeleted( long ) ) );
|
||||
connect( mManager, SIGNAL( progressChanged( long, double ) ), this, SLOT( progressChanged( long, double ) ) );
|
||||
connect( mManager, SIGNAL( statusChanged( long, int ) ), this, SLOT( statusChanged( long, int ) ) );
|
||||
}
|
||||
|
||||
QModelIndex QgsTaskManagerModel::index( int row, int column, const QModelIndex &parent ) const
|
||||
{
|
||||
if ( column < 0 || column >= columnCount() )
|
||||
{
|
||||
//column out of bounds
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if ( !parent.isValid() && row >= 0 && row < mManager->count() )
|
||||
{
|
||||
//return an index for the task at this position
|
||||
return createIndex( row, column );
|
||||
}
|
||||
|
||||
//only top level supported
|
||||
return QModelIndex();
|
||||
|
||||
}
|
||||
|
||||
QModelIndex QgsTaskManagerModel::parent( const QModelIndex &index ) const
|
||||
{
|
||||
Q_UNUSED( index );
|
||||
|
||||
//all items are top level
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int QgsTaskManagerModel::rowCount( const QModelIndex &parent ) const
|
||||
{
|
||||
if ( !parent.isValid() )
|
||||
{
|
||||
return mManager->count();
|
||||
}
|
||||
else
|
||||
{
|
||||
//no children
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int QgsTaskManagerModel::columnCount( const QModelIndex &parent ) const
|
||||
{
|
||||
Q_UNUSED( parent );
|
||||
return 3;
|
||||
}
|
||||
|
||||
QVariant QgsTaskManagerModel::data( const QModelIndex &index, int role ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
return QVariant();
|
||||
|
||||
QgsTask* task = indexToTask( index );
|
||||
if ( !task )
|
||||
return QVariant();
|
||||
|
||||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch ( index.column() )
|
||||
{
|
||||
case Description:
|
||||
return task->description();
|
||||
case Progress:
|
||||
return task->progress();
|
||||
case Status:
|
||||
return static_cast<int>( task->status() );
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case StatusRole:
|
||||
return static_cast<int>( task->status() );
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
Qt::ItemFlags QgsTaskManagerModel::flags( const QModelIndex &index ) const
|
||||
{
|
||||
Qt::ItemFlags flags = QAbstractItemModel::flags( index );
|
||||
|
||||
if ( ! index.isValid() )
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
if ( index.column() == Status )
|
||||
{
|
||||
if ( static_cast< QgsTask::TaskStatus >( data( index, StatusRole ).toInt() ) == QgsTask::Running )
|
||||
flags = flags | Qt::ItemIsEditable;
|
||||
}
|
||||
return flags | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
bool QgsTaskManagerModel::setData( const QModelIndex &index, const QVariant &value, int role )
|
||||
{
|
||||
Q_UNUSED( role );
|
||||
|
||||
if ( !index.isValid() )
|
||||
return false;
|
||||
|
||||
QgsTask* task = indexToTask( index );
|
||||
if ( !task )
|
||||
return false;
|
||||
|
||||
switch ( index.column() )
|
||||
{
|
||||
case Status:
|
||||
{
|
||||
if ( value.toBool() )
|
||||
task->terminate();
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsTaskManagerModel::taskAdded( long id )
|
||||
{
|
||||
beginInsertRows( QModelIndex(), mRowToTaskIdMap.count(), mRowToTaskIdMap.count() );
|
||||
mRowToTaskIdMap.insert( mRowToTaskIdMap.count(), id );
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void QgsTaskManagerModel::taskDeleted( long id )
|
||||
{
|
||||
for ( QMap< int, long >::iterator it = mRowToTaskIdMap.begin(); it != mRowToTaskIdMap.end(); )
|
||||
{
|
||||
if ( it.value() == id )
|
||||
{
|
||||
beginRemoveRows( QModelIndex(), it.key(), it.key() );
|
||||
it = mRowToTaskIdMap.erase( it );
|
||||
endRemoveRows();
|
||||
return;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsTaskManagerModel::progressChanged( long id, double progress )
|
||||
{
|
||||
Q_UNUSED( progress );
|
||||
|
||||
QModelIndex index = idToIndex( id, Progress );
|
||||
if ( !index.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataChanged( index, index );
|
||||
}
|
||||
|
||||
void QgsTaskManagerModel::statusChanged( long id, int status )
|
||||
{
|
||||
Q_UNUSED( status );
|
||||
|
||||
QModelIndex index = idToIndex( id, Status );
|
||||
if ( !index.isValid() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit dataChanged( index, index );
|
||||
}
|
||||
|
||||
QgsTask *QgsTaskManagerModel::indexToTask( const QModelIndex &index ) const
|
||||
{
|
||||
if ( !index.isValid() || index.parent().isValid() )
|
||||
return nullptr;
|
||||
|
||||
long id = mRowToTaskIdMap.value( index.row() );
|
||||
return mManager->task( id );
|
||||
}
|
||||
|
||||
int QgsTaskManagerModel::idToRow( long id ) const
|
||||
{
|
||||
for ( QMap< int, long >::const_iterator it = mRowToTaskIdMap.constBegin(); it != mRowToTaskIdMap.constEnd(); ++it )
|
||||
{
|
||||
if ( it.value() == id )
|
||||
{
|
||||
return it.key();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
QModelIndex QgsTaskManagerModel::idToIndex( long id, int column ) const
|
||||
{
|
||||
int row = idToRow( id );
|
||||
if ( row < 0 )
|
||||
return QModelIndex();
|
||||
|
||||
return index( row, column );
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsProgressBarDelegate
|
||||
//
|
||||
|
||||
QgsProgressBarDelegate::QgsProgressBarDelegate( QObject *parent )
|
||||
: QStyledItemDelegate( parent )
|
||||
{}
|
||||
|
||||
void QgsProgressBarDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
||||
{
|
||||
QStyledItemDelegate::paint( painter, option, index );
|
||||
|
||||
int progress = index.data().toInt();
|
||||
|
||||
QStyleOptionProgressBarV2 progressBarOption;
|
||||
progressBarOption.state = option.state;
|
||||
progressBarOption.rect = option.rect;
|
||||
progressBarOption.rect.setTop( option.rect.top() + 1 );
|
||||
progressBarOption.rect.setHeight( option.rect.height() - 2 );
|
||||
progressBarOption.minimum = 0;
|
||||
progressBarOption.maximum = 100;
|
||||
progressBarOption.progress = progress;
|
||||
progressBarOption.text = QString::number( progress ) + "%";
|
||||
progressBarOption.textVisible = true;
|
||||
progressBarOption.textAlignment = Qt::AlignCenter;
|
||||
|
||||
QgsApplication::style()->drawControl( QStyle::CE_ProgressBar, &progressBarOption, painter );
|
||||
}
|
||||
|
||||
QSize QgsProgressBarDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
||||
{
|
||||
Q_UNUSED( index );
|
||||
return QSize( option.rect.width(), option.fontMetrics.height() + 10 );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsTaskStatusDelegate
|
||||
//
|
||||
|
||||
QgsTaskStatusDelegate::QgsTaskStatusDelegate( QObject *parent )
|
||||
: QStyledItemDelegate( parent )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QgsTaskStatusDelegate::paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
||||
{
|
||||
QStyledItemDelegate::paint( painter, option, index );
|
||||
|
||||
QIcon icon;
|
||||
switch ( static_cast< QgsTask::TaskStatus >( index.data().toInt() ) )
|
||||
{
|
||||
case QgsTask::Running:
|
||||
icon = QgsApplication::getThemeIcon( "/mActionRefresh.png" );
|
||||
break;
|
||||
case QgsTask::Complete:
|
||||
icon = QgsApplication::getThemeIcon( "/mActionCheckQgisVersion.png" );
|
||||
break;
|
||||
case QgsTask::Terminated:
|
||||
icon = QgsApplication::getThemeIcon( "/mActionRemove.svg" );
|
||||
break;
|
||||
}
|
||||
icon.paint( painter, option.rect.left() + 1, ( option.rect.top() + option.rect.bottom() ) / 2 - 12, 24, 24 );
|
||||
}
|
||||
|
||||
QSize QgsTaskStatusDelegate::sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const
|
||||
{
|
||||
Q_UNUSED( option );
|
||||
Q_UNUSED( index );
|
||||
return QSize( 32, 32 );
|
||||
}
|
||||
|
||||
bool QgsTaskStatusDelegate::editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index )
|
||||
{
|
||||
Q_UNUSED( option );
|
||||
if ( event->type() == QEvent::MouseButtonPress )
|
||||
{
|
||||
QMouseEvent *e = static_cast<QMouseEvent*>( event );
|
||||
if ( e->button() == Qt::LeftButton )
|
||||
{
|
||||
if ( !index.model()->flags( index ).testFlag( Qt::ItemIsEditable ) )
|
||||
{
|
||||
//item not editable
|
||||
return false;
|
||||
}
|
||||
|
||||
return model->setData( index, true, Qt::EditRole );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
146
src/gui/qgstaskmanagerwidget.h
Normal file
146
src/gui/qgstaskmanagerwidget.h
Normal file
@ -0,0 +1,146 @@
|
||||
/***************************************************************************
|
||||
qgstaskmanagerwidget.h
|
||||
----------------------
|
||||
begin : April 2016
|
||||
copyright : (C) 2016 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSTASKMANAGERWIDGET_H
|
||||
#define QGSTASKMANAGERWIDGET_H
|
||||
|
||||
#include <QTreeView>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class QgsTaskManager;
|
||||
class QgsTask;
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsTaskManagerWidget
|
||||
* A widget which displays tasks from a QgsTaskManager and allows for interaction with the manager
|
||||
* @see QgsTaskManager
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class GUI_EXPORT QgsTaskManagerWidget : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskManagerWidget
|
||||
* @param manager task manager associated with widget
|
||||
* @param parent parent widget
|
||||
*/
|
||||
QgsTaskManagerWidget( QgsTaskManager* manager, QWidget* parent = nullptr );
|
||||
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsTaskManagerModel
|
||||
* A model representing a QgsTaskManager
|
||||
* @see QgsTaskManager
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class GUI_EXPORT QgsTaskManagerModel: public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskManagerModel
|
||||
* @param manager task manager for model
|
||||
* @param parent parent object
|
||||
*/
|
||||
explicit QgsTaskManagerModel( QgsTaskManager* manager, QObject* parent = nullptr );
|
||||
|
||||
//reimplemented QAbstractItemModel methods
|
||||
QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex() ) const override;
|
||||
QModelIndex parent( const QModelIndex &index ) const override;
|
||||
int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
|
||||
int columnCount( const QModelIndex &parent = QModelIndex() ) const override;
|
||||
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
|
||||
Qt::ItemFlags flags( const QModelIndex & index ) const override;
|
||||
bool setData( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ) override;
|
||||
|
||||
private slots:
|
||||
|
||||
void taskAdded( long id );
|
||||
void taskDeleted( long id );
|
||||
void progressChanged( long id, double progress );
|
||||
void statusChanged( long id, int status );
|
||||
|
||||
private:
|
||||
|
||||
enum Columns
|
||||
{
|
||||
Description = 0,
|
||||
Progress = 1,
|
||||
Status = 2,
|
||||
};
|
||||
|
||||
enum Roles
|
||||
{
|
||||
StatusRole = Qt::UserRole,
|
||||
};
|
||||
|
||||
QgsTaskManager* mManager;
|
||||
|
||||
QMap< int, long > mRowToTaskIdMap;
|
||||
|
||||
QgsTask* indexToTask( const QModelIndex& index ) const;
|
||||
int idToRow( long id ) const;
|
||||
QModelIndex idToIndex( long id, int column ) const;
|
||||
};
|
||||
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsProgressBarDelegate
|
||||
* A delegate for showing a progress bar within a view
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class GUI_EXPORT QgsProgressBarDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsProgressBarDelegate
|
||||
* @param parent parent object
|
||||
*/
|
||||
QgsProgressBarDelegate( QObject* parent = nullptr );
|
||||
|
||||
void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
|
||||
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
|
||||
};
|
||||
|
||||
/** \ingroup gui
|
||||
* \class QgsProgressBarDelegate
|
||||
* A delegate for showing task status within a view. Clicks on the delegate will cause the task to be cancelled (via the model).
|
||||
* @note introduced in QGIS 2.16
|
||||
*/
|
||||
class GUI_EXPORT QgsTaskStatusDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor for QgsTaskStatusDelegate
|
||||
* @param parent parent object
|
||||
*/
|
||||
QgsTaskStatusDelegate( QObject* parent = nullptr );
|
||||
|
||||
void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
|
||||
QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const override;
|
||||
bool editorEvent( QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index ) override;
|
||||
};
|
||||
|
||||
#endif //QGSTASKMANAGERWIDGET_H
|
@ -184,6 +184,7 @@ ADD_QGIS_TEST(stringutilstest testqgsstringutils.cpp)
|
||||
ADD_QGIS_TEST(styletest testqgsstyle.cpp)
|
||||
ADD_QGIS_TEST(svgmarkertest testqgssvgmarker.cpp)
|
||||
ADD_QGIS_TEST(symboltest testqgssymbol.cpp)
|
||||
ADD_QGIS_TEST(taskmanagertest testqgstaskmanager.cpp)
|
||||
ADD_QGIS_TEST(tracertest testqgstracer.cpp)
|
||||
#for some obscure reason calling this test "fontutils" kills the build on Ubuntu 15.10
|
||||
ADD_QGIS_TEST(typographicstylingutils testqgsfontutils.cpp)
|
||||
|
289
tests/src/core/testqgstaskmanager.cpp
Normal file
289
tests/src/core/testqgstaskmanager.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/***************************************************************************
|
||||
testqgstaskmanager.cpp
|
||||
----------------------
|
||||
begin : April 2016
|
||||
copyright : (C) 2016 by Nyall Dawson
|
||||
email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgstaskmanager.h"
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
class TestTask : public QgsTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
TestTask( const QString& desc = QString() ) : QgsTask( desc ) {}
|
||||
|
||||
void emitProgressChanged( double progress ) { setProgress( progress ); }
|
||||
void emitTaskStopped() { stopped(); }
|
||||
void emitTaskCompleted() { completed(); }
|
||||
|
||||
};
|
||||
|
||||
class TestTerminationTask : public TestTask
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
~TestTerminationTask()
|
||||
{
|
||||
//make sure task has been terminated by manager prior to deletion
|
||||
Q_ASSERT( status() == QgsTask::Terminated );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class TestQgsTaskManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init();// will be called before each testfunction is executed.
|
||||
void cleanup();// will be called after every testfunction.
|
||||
void task();
|
||||
void createInstance();
|
||||
void addTask();
|
||||
void deleteTask();
|
||||
void taskTerminationBeforeDelete();
|
||||
void taskId();
|
||||
void progressChanged();
|
||||
void statusChanged();
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
void TestQgsTaskManager::initTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::cleanupTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::task()
|
||||
{
|
||||
QScopedPointer< TestTask > task( new TestTask( "desc" ) );
|
||||
QCOMPARE( task->status(), QgsTask::Running );
|
||||
QCOMPARE( task->description(), QString( "desc" ) );
|
||||
QVERIFY( task->isActive() );
|
||||
|
||||
//test that calling stopped sets correct state
|
||||
QSignalSpy stoppedSpy( task.data(), SIGNAL( taskStopped() ) );
|
||||
QSignalSpy statusSpy( task.data(), SIGNAL( statusChanged( int ) ) );
|
||||
task->emitTaskStopped();
|
||||
QCOMPARE( task->status(), QgsTask::Terminated );
|
||||
QVERIFY( !task->isActive() );
|
||||
QCOMPARE( stoppedSpy.count(), 1 );
|
||||
QCOMPARE( statusSpy.count(), 1 );
|
||||
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy.last().at( 0 ).toInt() ), QgsTask::Terminated );
|
||||
|
||||
//test that calling completed sets correct state
|
||||
task.reset( new TestTask() );
|
||||
QSignalSpy completeSpy( task.data(), SIGNAL( taskCompleted() ) );
|
||||
QSignalSpy statusSpy2( task.data(), SIGNAL( statusChanged( int ) ) );
|
||||
task->emitTaskCompleted();
|
||||
QCOMPARE( task->status(), QgsTask::Complete );
|
||||
QVERIFY( !task->isActive() );
|
||||
QCOMPARE( completeSpy.count(), 1 );
|
||||
QCOMPARE( statusSpy2.count(), 1 );
|
||||
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy2.last().at( 0 ).toInt() ), QgsTask::Complete );
|
||||
}
|
||||
|
||||
|
||||
void TestQgsTaskManager::createInstance()
|
||||
{
|
||||
QgsTaskManager* manager = QgsTaskManager::instance();
|
||||
QVERIFY( manager );
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::addTask()
|
||||
{
|
||||
//create an empty manager
|
||||
QgsTaskManager manager;
|
||||
|
||||
//should be empty
|
||||
QVERIFY( manager.tasks().isEmpty() );
|
||||
QCOMPARE( manager.count(), 0 );
|
||||
QVERIFY( !manager.task( 0L ) );
|
||||
|
||||
QSignalSpy spy( &manager, SIGNAL( taskAdded( long ) ) );
|
||||
|
||||
//add a task
|
||||
TestTask* task = new TestTask();
|
||||
long id = manager.addTask( task );
|
||||
QCOMPARE( id, 0L );
|
||||
QCOMPARE( manager.tasks().count(), 1 );
|
||||
QCOMPARE( manager.count(), 1 );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 0LL );
|
||||
|
||||
//retrieve task
|
||||
QCOMPARE( manager.task( 0L ), task );
|
||||
QCOMPARE( manager.tasks().at( 0 ), task );
|
||||
|
||||
//add a second task
|
||||
TestTask* task2 = new TestTask();
|
||||
id = manager.addTask( task2 );
|
||||
QCOMPARE( id, 1L );
|
||||
QCOMPARE( manager.tasks().count(), 2 );
|
||||
QCOMPARE( manager.count(), 2 );
|
||||
QCOMPARE( manager.task( 0L ), task );
|
||||
QCOMPARE( manager.tasks().at( 0 ), task );
|
||||
QCOMPARE( manager.task( 1L ), task2 );
|
||||
QCOMPARE( manager.tasks().at( 1 ), task2 );
|
||||
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 1LL );
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::deleteTask()
|
||||
{
|
||||
//create manager with some tasks
|
||||
QgsTaskManager manager;
|
||||
TestTask* task = new TestTask();
|
||||
TestTask* task2 = new TestTask();
|
||||
TestTask* task3 = new TestTask();
|
||||
manager.addTask( task );
|
||||
manager.addTask( task2 );
|
||||
manager.addTask( task3 );
|
||||
|
||||
QSignalSpy spy( &manager, SIGNAL( taskAboutToBeDeleted( long ) ) );
|
||||
|
||||
//try deleting a non-existant task
|
||||
QVERIFY( !manager.deleteTask( 56 ) );
|
||||
QCOMPARE( spy.count(), 0 );
|
||||
|
||||
//try deleting a task by ID
|
||||
QVERIFY( manager.deleteTask( 1 ) );
|
||||
QCOMPARE( manager.tasks().count(), 2 );
|
||||
QVERIFY( !manager.task( 1 ) );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 1LL );
|
||||
|
||||
//can't delete twice
|
||||
QVERIFY( !manager.deleteTask( 1 ) );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
|
||||
//delete task by reference
|
||||
QVERIFY( manager.deleteTask( task ) );
|
||||
QCOMPARE( manager.tasks().count(), 1 );
|
||||
QVERIFY( !manager.task( 0 ) );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 0LL );
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::taskTerminationBeforeDelete()
|
||||
{
|
||||
//test that task is terminated by manager prior to delete
|
||||
QgsTaskManager* manager = new QgsTaskManager();
|
||||
|
||||
//TestTerminationTask will assert that it's been terminated prior to deletion
|
||||
TestTask* task = new TestTerminationTask();
|
||||
manager->addTask( task );
|
||||
|
||||
// if task is not terminated assert will trip
|
||||
delete manager;
|
||||
QApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::taskId()
|
||||
{
|
||||
//test finding task IDs
|
||||
|
||||
//create manager with some tasks
|
||||
QgsTaskManager manager;
|
||||
TestTask* task = new TestTask();
|
||||
TestTask* task2 = new TestTask();
|
||||
manager.addTask( task );
|
||||
manager.addTask( task2 );
|
||||
|
||||
//also a task not in the manager
|
||||
TestTask* task3 = new TestTask();
|
||||
|
||||
QCOMPARE( manager.taskId( nullptr ), -1L );
|
||||
QCOMPARE( manager.taskId( task ), 0L );
|
||||
QCOMPARE( manager.taskId( task2 ), 1L );
|
||||
QCOMPARE( manager.taskId( task3 ), -1L );
|
||||
|
||||
delete task3;
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::progressChanged()
|
||||
{
|
||||
// check that progressChanged signals emitted by tasks result in progressChanged signal from manager
|
||||
QgsTaskManager manager;
|
||||
TestTask* task = new TestTask();
|
||||
TestTask* task2 = new TestTask();
|
||||
manager.addTask( task );
|
||||
manager.addTask( task2 );
|
||||
|
||||
QSignalSpy spy( &manager, SIGNAL( progressChanged( long, double ) ) );
|
||||
|
||||
task->emitProgressChanged( 50.0 );
|
||||
QCOMPARE( task->progress(), 50.0 );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 0LL );
|
||||
QCOMPARE( spy.last().at( 1 ).toDouble(), 50.0 );
|
||||
|
||||
task2->emitProgressChanged( 75.0 );
|
||||
QCOMPARE( task2->progress(), 75.0 );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 1LL );
|
||||
QCOMPARE( spy.last().at( 1 ).toDouble(), 75.0 );
|
||||
}
|
||||
|
||||
void TestQgsTaskManager::statusChanged()
|
||||
{
|
||||
// check that statusChanged signals emitted by tasks result in statusChanged signal from manager
|
||||
QgsTaskManager manager;
|
||||
TestTask* task = new TestTask();
|
||||
TestTask* task2 = new TestTask();
|
||||
manager.addTask( task );
|
||||
manager.addTask( task2 );
|
||||
|
||||
QSignalSpy spy( &manager, SIGNAL( statusChanged( long, int ) ) );
|
||||
|
||||
task->emitTaskStopped();
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 0LL );
|
||||
QCOMPARE( static_cast< QgsTask::TaskStatus >( spy.last().at( 1 ).toInt() ), QgsTask::Terminated );
|
||||
|
||||
task2->emitTaskCompleted();
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QCOMPARE( spy.last().at( 0 ).toLongLong(), 1LL );
|
||||
QCOMPARE( static_cast< QgsTask::TaskStatus >( spy.last().at( 1 ).toInt() ), QgsTask::Complete );
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN( TestQgsTaskManager )
|
||||
#include "testqgstaskmanager.moc"
|
Loading…
x
Reference in New Issue
Block a user