Switch from QtConcurrent::run to QThreadPool::start
QtConcurrent doesn't give us anyway to cancel queued but not
started tasks. QThreadPool does (and also allows us to specify
task priority)
Now, a QgsTask can have subtask QgsTasks set by calling
QgsTask::addSubTask. Sub tasks can have their own set of
dependent tasks.
Subtasks are not visible to users, and users only see the overall
progress and status of the parent task.
This allows creation of tasks which are themselves built off
many smaller component tasks. The task manager will still handle
firing up and scheduling the subtasks, so eg subtasks can run
in parallel (if their dependancies allow this).
Subtasks can themselves have subtasks.
This change is designed to allow the processing concept of
algorithms and modeller algorithms to be translatable
directly to the task manager architecture.
called when the task has completed (successfully or otherwise).
This allows for simpler task design when the signal/slot
based approach is not required. Just implement run() with your
heavy lifting, and finished() to do whatever follow up stuff
should happen after the task is complete. finished is always
called from the main thread, so it's safe to do GUI operations
here.
Python based tasks using the simplified QgsTask.fromFunction
approach can now set a on_finished argument to a function
to call when the task is complete.
eg:
def calculate(task):
# pretend this is some complex maths and stuff we want
# to run in the background
return 5*6
def calculation_finished(result, value=None):
if result == QgsTask.ResultSuccess:
iface.messageBar().pushMessage(
'the magic number is {}'.format(value))
elif result == QgsTask.ResultFail:
iface.messageBar().pushMessage(
'couldn\'t work it out, sorry')
task = QgsTask.fromFunction('my task', calculate,
on_finished=calculation_finished)
QgsTaskManager.instance().addTask(task)
Multiple values can also be returned, eg:
def calculate(task):
return (4, 8, 15)
def calculation_finished(result, count=None, max=None, sum=None):
# here:
# count = 4
# max = 8
# sum = 15
task = QgsTask.fromFunction('my task', calculate,
on_finished=calculation_finished)
QgsTaskManager.instance().addTask(task)
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.
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
Instead of copyign a pre-existing .db shipped with QGIS, create
a new .db and import an .xml file containing our default symbols.
On the UX front, the main benefit is being able to ship with
pre-defined tags and favorite flags for the default symbols.
On the developer side, it means we get rid of the requirement
to maintain both an .xml and a binary .db in the source tree.
We also say aurevoir to the symbol_xml2db.py script and the
need to update the (obsolete) script every time we add a new
feature to QgsStyle.
- create a dedicated set of buttons for addition of tags and smartgroups
to make those actions more visible as well as getting rid of th need to
select a tag/smartgroup to create those (turns out to be quite confusing
for newcomers)
- move UI elements around, regroup {add,remove,edit} symbol buttons to
harmonize with other parts of QGIS; the elements' placement feels much
more natural now
These may represent additional layers such as layers which
are not included in the map layer registry, or paths to
layers which have not yet been loaded into QGIS.