Skip to content

Commit e01c306

Browse files
committedDec 5, 2016
Simplify reporting completion of tasks
QgsTask subclasses can now return a result (success or fail) directly from QgsTask::run. If they do so then there's no need for them to manually call completed() or stopped() to report their completion. Alternatively, tasks can also return the ResultPending value to indicate that the task is still operating and will manually report its completion by calling completed() or stopped(). This may be useful for tasks which rely on external events for completion, eg downloading a file. In this case Qt slots could be created which are connected to the download completion or termination and which call completed() or stopped() to indicate the task has finished operations.
1 parent 2590f68 commit e01c306

File tree

5 files changed

+253
-75
lines changed

5 files changed

+253
-75
lines changed
 

‎python/core/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,9 @@ def run(self):
201201
except Exception as ex:
202202
# report error
203203
self.exception = ex
204-
self.stopped()
205-
return
204+
return QgsTask.ResultFail
206205

207-
self.completed()
206+
return QgsTask.ResultSuccess
208207

209208

210209
def fromFunction(cls, description, function, *args, **kwargs):

‎python/core/qgstaskmanager.sip

Lines changed: 150 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
/** \ingroup core
1+
/**
2+
* \ingroup core
23
* \class QgsTask
3-
* \brief Interface class for long running tasks which will be handled by a QgsTaskManager
4-
* \note Added in version 2.16
4+
* \brief Abstract base class for long running background tasks. Tasks can be controlled directly,
5+
* or added to a QgsTaskManager for automatic management.
6+
*
7+
* Derived classes should implement the process they want to execute in the background
8+
* within the run() method. This method will be called when the
9+
* task commences (ie via calling start() ).
10+
*
11+
* Long running tasks should periodically check the isCancelled() flag to detect if the task
12+
* has been cancelled via some external event. If this flag is true then the task should
13+
* clean up and terminate at the earliest possible convenience.
14+
*
15+
* \note Added in version 3.0
516
*/
617
class QgsTask : QObject
718
{
@@ -21,6 +32,14 @@ class QgsTask : QObject
2132
Terminated, /*!< Task was terminated or errored */
2233
};
2334

35+
//! Result of running the task
36+
enum TaskResult
37+
{
38+
ResultSuccess, //!< Task completed successfully
39+
ResultFail, //!< Task was terminated within completion
40+
ResultPending, //!< Task is still running
41+
};
42+
2443
//! Task flags
2544
enum Flag
2645
{
@@ -30,106 +49,176 @@ class QgsTask : QObject
3049
};
3150
typedef QFlags<QgsTask::Flag> Flags;
3251

33-
/** Constructor for QgsTask.
52+
/**
53+
* Constructor for QgsTask.
3454
* @param description text description of task
3555
* @param flags task flags
3656
*/
3757
QgsTask( const QString& description = QString(), const Flags& flags = AllFlags );
3858

39-
//! Returns the flags associated with the task.
59+
/**
60+
* Returns the flags associated with the task.
61+
*/
4062
Flags flags() const;
4163

42-
//! Returns true if the task can be cancelled.
64+
/**
65+
* Returns true if the task can be cancelled.
66+
*/
4367
bool canCancel() const;
4468

45-
//! Returns true if the task is active, ie it is not complete and has
46-
//! not been cancelled.
69+
/**
70+
* Returns true if the task is active, ie it is not complete and has
71+
* not been cancelled.
72+
*/
4773
bool isActive() const;
4874

49-
//! Returns the current task status.
75+
/**
76+
* Returns the current task status.
77+
*/
5078
TaskStatus status() const;
5179

52-
//! Returns the task's description.
80+
/**
81+
* Returns the task's description.
82+
*/
5383
QString description() const;
5484

55-
//! Returns the task's progress (between 0.0 and 100.0)
85+
/**
86+
* Returns the task's progress (between 0.0 and 100.0)
87+
*/
5688
double progress() const;
5789

58-
public slots:
59-
60-
//! Starts the task.
90+
/**
91+
* Starts the task. Should only be called for tasks which are not being
92+
* handled by a QgsTaskManager. If the task is managed by a QgsTaskManager
93+
* then this method should not be called directly, instead it is left to the
94+
* task manager to start the task when appropriate.
95+
*/
6196
void start();
6297

63-
//! Notifies the task that it should terminate.
64-
//! @see isCancelled()
98+
/**
99+
* Notifies the task that it should terminate. Calling this is not gauranteed
100+
* to immediately end the task, rather it sets the isCancelled() flag which
101+
* task subclasses can check and terminate their operations at an appropriate
102+
* time.
103+
* @see isCancelled()
104+
*/
65105
void cancel();
66106

67-
//! Called when the task is placed on hold. If the task in not queued
68-
//! (ie it is running or has finished) then calling this has no effect.
69-
//! @see unhold()
107+
/**
108+
* Places the task on hold. If the task in not queued
109+
* (ie it is already running or has finished) then calling this has no effect.
110+
* Calling this method only has an effect for tasks which are managed
111+
* by a QgsTaskManager.
112+
* @see unhold()
113+
*/
70114
void hold();
71115

72-
//! Called when the task should be unheld and re-added to the queue. If the
73-
//! task in not currently being held then calling this has no effect.
74-
//! @see unhold()
116+
/**
117+
* Releases the task from being held. For tasks managed by a QgsTaskManager
118+
* calling this will re-add them to the queue. If the
119+
* task in not currently being held then calling this has no effect.
120+
* @see hold()
121+
*/
75122
void unhold();
76123

77-
//! Sets the task's current progress. If task reports the CanReportProgress flag then
78-
//! the derived class should call this method whenever the task wants to update its
79-
//! progress. Calling will automatically emit the progressChanged signal.
80-
//! @param progress percent of progress, from 0.0 - 100.0
81-
void setProgress( double progress );
82-
83-
//! Sets the task as completed. Should be called when the task is complete.
84-
//! Calling will automatically emit the statusChanged and taskCompleted signals.
85-
void completed();
86-
87-
//! Sets the task as stopped. Should be called whenever the task ends for any
88-
//! reason other than successful completion.
89-
//! Calling will automatically emit the statusChanged and taskStopped signals.
90-
void stopped();
91-
92124
signals:
93125

94-
//! Will be emitted by task when its progress changes
95-
//! @param progress percent of progress, from 0.0 - 100.0
96-
//! @note derived classes should not emit this signal directly, instead they should call
97-
//! setProgress()
126+
/**
127+
* Will be emitted by task when its progress changes.
128+
* @param progress percent of progress, from 0.0 - 100.0
129+
* @note derived classes should not emit this signal directly, instead they should call
130+
* setProgress()
131+
*/
98132
void progressChanged( double progress );
99133

100-
//! Will be emitted by task when its status changes
101-
//! @param status new task status
102-
//! @note derived classes should not emit this signal directly, instead they should call
103-
//! completed() or stopped()
134+
/**
135+
* Will be emitted by task when its status changes.
136+
* @param status new task status
137+
* @note derived classes should not emit this signal directly, instead they should call
138+
* completed() or stopped()
139+
*/
104140
void statusChanged( int status );
105141

106-
//! Will be emitted by task to indicate its commencement.
107-
//! @note derived classes should not emit this signal directly, it will automatically
108-
//! be emitted when the task begins
142+
/**
143+
* Will be emitted by task to indicate its commencement.
144+
* @note derived classes should not emit this signal directly, it will automatically
145+
* be emitted when the task begins
146+
*/
109147
void begun();
110148

111-
//! Will be emitted by task to indicate its completion.
112-
//! @note derived classes should not emit this signal directly, instead they should call
113-
//! completed()
149+
/**
150+
* Will be emitted by task to indicate its successful completion.
151+
* @note derived classes should not emit this signal directly, instead they should call
152+
* completed()
153+
*/
114154
void taskCompleted();
115155

116-
//! Will be emitted by task if it has terminated for any reason
117-
//! other then completion.
118-
//! @note derived classes should not emit this signal directly, instead they should call
119-
//! stopped()//!
156+
/**
157+
* Will be emitted by task if it has terminated for any reason
158+
* other then completion (eg when a task has been cancelled or encountered
159+
* an internal error).
160+
* @note derived classes should not emit this signal directly, instead they should call
161+
* stopped()
162+
*/
120163
void taskStopped();
121164

122165
protected:
123166

124-
//! Derived tasks must implement a run() method. This method will be called when the
125-
//! task commences (ie via calling start() ).
126-
virtual void run() = 0;
167+
/**
168+
* Performs the task's operation. This method will be called when the task commences
169+
* (ie via calling start() ), and subclasses should implement the operation they
170+
* wish to perform in the background within this method.
171+
*
172+
* A task can return a ResultSuccess and ResultFail value to indicate that the
173+
* task has finished and was either completed successfully or terminated before
174+
* completion.
175+
*
176+
* Alternatively, tasks can also return the ResultPending value
177+
* to indicate that the task is still operating and will manually report its
178+
* completion by calling completed() or stopped(). This may be useful for
179+
* tasks which rely on external events for completion, eg downloading a
180+
* file. In this case Qt slots could be created which are connected to the
181+
* download completion or termination and which call completed() or stopped()
182+
* to indicate the task has finished operations.
183+
* @see completed()
184+
* @see stopped()
185+
*/
186+
virtual TaskResult run() = 0;
127187

128-
//! Will return true if task should terminate ASAP. If the task reports the CanCancel
129-
//! flag, then derived classes' run() methods should periodically check this and
130-
//! terminate in a safe manner.
188+
/**
189+
* Will return true if task should terminate ASAP. If the task reports the CanCancel
190+
* flag, then derived classes' run() methods should periodically check this and
191+
* terminate in a safe manner.
192+
*/
131193
bool isCancelled() const;
132194

195+
/**
196+
* Sets the task as completed. Calling this is only required for tasks which
197+
* returned the ResultPending value as a result of run(). This should be called
198+
* when the task is complete. Calling will automatically emit the statusChanged
199+
* and taskCompleted signals.
200+
*/
201+
void completed();
202+
203+
/**
204+
* Sets the task as stopped. Calling this is only required for tasks which
205+
* returned the ResultPending value as a result of run().
206+
* Should be called whenever the task ends for any reason other than successful
207+
* completion. Calling will automatically emit the statusChanged and taskStopped
208+
* signals.
209+
*/
210+
void stopped();
211+
212+
protected slots:
213+
214+
/**
215+
* Sets the task's current progress. If task reports the CanReportProgress flag then
216+
* the derived class should call this method whenever the task wants to update its
217+
* progress. Calling will automatically emit the progressChanged signal.
218+
* @param progress percent of progress, from 0.0 - 100.0
219+
*/
220+
void setProgress( double progress );
221+
133222
};
134223

135224
QFlags<QgsTask::Flag> operator|(QgsTask::Flag f1, QFlags<QgsTask::Flag> f2);

‎src/core/qgstaskmanager.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,23 @@ void QgsTask::start()
3838
mStatus = Running;
3939
emit statusChanged( Running );
4040
emit begun();
41-
run();
41+
TaskResult result = run();
42+
switch ( result )
43+
{
44+
case ResultSuccess:
45+
completed();
46+
break;
47+
48+
case ResultFail:
49+
stopped();
50+
break;
51+
52+
case ResultPending:
53+
// nothing to do - task will call completed() or stopped()
54+
// in it's own time
55+
break;
56+
57+
}
4258
}
4359

4460
void QgsTask::cancel()

‎src/core/qgstaskmanager.h

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ class CORE_EXPORT QgsTask : public QObject
5454
Terminated, //!< Task was terminated or errored
5555
};
5656

57+
//! Result of running the task
58+
enum TaskResult
59+
{
60+
ResultSuccess = 0, //!< Task completed successfully
61+
ResultFail, //!< Task was terminated within completion
62+
ResultPending, //!< Task is still running
63+
};
64+
5765
//! Task flags
5866
enum Flag
5967
{
@@ -182,8 +190,22 @@ class CORE_EXPORT QgsTask : public QObject
182190
* Performs the task's operation. This method will be called when the task commences
183191
* (ie via calling start() ), and subclasses should implement the operation they
184192
* wish to perform in the background within this method.
185-
*/
186-
virtual void run() = 0;
193+
*
194+
* A task can return a ResultSuccess and ResultFail value to indicate that the
195+
* task has finished and was either completed successfully or terminated before
196+
* completion.
197+
*
198+
* Alternatively, tasks can also return the ResultPending value
199+
* to indicate that the task is still operating and will manually report its
200+
* completion by calling completed() or stopped(). This may be useful for
201+
* tasks which rely on external events for completion, eg downloading a
202+
* file. In this case Qt slots could be created which are connected to the
203+
* download completion or termination and which call completed() or stopped()
204+
* to indicate the task has finished operations.
205+
* @see completed()
206+
* @see stopped()
207+
*/
208+
virtual TaskResult run() = 0;
187209

188210
/**
189211
* Will return true if task should terminate ASAP. If the task reports the CanCancel
@@ -193,15 +215,19 @@ class CORE_EXPORT QgsTask : public QObject
193215
bool isCancelled() const { return mShouldTerminate; }
194216

195217
/**
196-
* Sets the task as completed. Should be called when the task is complete.
197-
* Calling will automatically emit the statusChanged and taskCompleted signals.
218+
* Sets the task as completed. Calling this is only required for tasks which
219+
* returned the ResultPending value as a result of run(). This should be called
220+
* when the task is complete. Calling will automatically emit the statusChanged
221+
* and taskCompleted signals.
198222
*/
199223
void completed();
200224

201225
/**
202-
* Sets the task as stopped. Should be called whenever the task ends for any
203-
* reason other than successful completion.
204-
* Calling will automatically emit the statusChanged and taskStopped signals.
226+
* Sets the task as stopped. Calling this is only required for tasks which
227+
* returned the ResultPending value as a result of run().
228+
* Should be called whenever the task ends for any reason other than successful
229+
* completion. Calling will automatically emit the statusChanged and taskStopped
230+
* signals.
205231
*/
206232
void stopped();
207233

‎tests/src/core/testqgstaskmanager.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ class TestTask : public QgsTask
4040

4141
protected:
4242

43-
void run() override
43+
TaskResult run() override
4444
{
4545
runCalled = true;
46+
return ResultPending;
4647
}
4748

4849
};
@@ -61,11 +62,35 @@ class TestTerminationTask : public TestTask
6162

6263
protected:
6364

64-
void run() override
65+
TaskResult run() override
6566
{
6667
while ( !isCancelled() )
6768
{}
68-
stopped();
69+
return ResultFail;
70+
}
71+
};
72+
73+
class SuccessTask : public TestTask
74+
{
75+
Q_OBJECT
76+
77+
protected:
78+
79+
TaskResult run() override
80+
{
81+
return ResultSuccess;
82+
}
83+
};
84+
85+
class FailTask : public TestTask
86+
{
87+
Q_OBJECT
88+
89+
protected:
90+
91+
TaskResult run() override
92+
{
93+
return ResultFail;
6994
}
7095
};
7196

@@ -80,6 +105,7 @@ class TestQgsTaskManager : public QObject
80105
void init();// will be called before each testfunction is executed.
81106
void cleanup();// will be called after every testfunction.
82107
void task();
108+
void taskResult();
83109
void createInstance();
84110
void addTask();
85111
void deleteTask();
@@ -178,6 +204,28 @@ void TestQgsTaskManager::task()
178204
QVERIFY( task->flags() & QgsTask::CanCancel );
179205
}
180206

207+
void TestQgsTaskManager::taskResult()
208+
{
209+
QScopedPointer< TestTask > task( new SuccessTask() );
210+
QCOMPARE( task->status(), QgsTask::Queued );
211+
QSignalSpy statusSpy( task.data(), SIGNAL( statusChanged( int ) ) );
212+
213+
task->start();
214+
QCOMPARE( statusSpy.count(), 2 );
215+
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy.at( 0 ).at( 0 ).toInt() ), QgsTask::Running );
216+
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy.at( 1 ).at( 0 ).toInt() ), QgsTask::Complete );
217+
QCOMPARE( task->status(), QgsTask::Complete );
218+
219+
task.reset( new FailTask() );
220+
QCOMPARE( task->status(), QgsTask::Queued );
221+
QSignalSpy statusSpy2( task.data(), SIGNAL( statusChanged( int ) ) );
222+
223+
task->start();
224+
QCOMPARE( statusSpy2.count(), 2 );
225+
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy2.at( 0 ).at( 0 ).toInt() ), QgsTask::Running );
226+
QCOMPARE( static_cast< QgsTask::TaskStatus >( statusSpy2.at( 1 ).at( 0 ).toInt() ), QgsTask::Terminated );
227+
QCOMPARE( task->status(), QgsTask::Terminated );
228+
}
181229

182230
void TestQgsTaskManager::createInstance()
183231
{

0 commit comments

Comments
 (0)
Please sign in to comment.