forked from qt-creator/qt-creator
		
	Introduce the GroupData structure. In this way it's easily possible to add extra properties of already used types, e.g. int. It's also possible to easily create elements with multiple properties. Simplify internal TaskItem::Type enum. Get rid of special ParallelLimit and Workflow elements. Provide global parallelLimit() and workflowPolicy() functions. Make global items (e.g. parallel, stopOnDone, etc...) const. Change-Id: Ic5628255b542fd6c5a5565b055ff11804c8d7b68 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
		
			
				
	
	
		
			269 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright (C) 2022 The Qt Company Ltd.
 | 
						|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
 | 
						|
 | 
						|
#include "taskwidget.h"
 | 
						|
 | 
						|
#include <utils/async.h>
 | 
						|
#include <utils/futuresynchronizer.h>
 | 
						|
#include <utils/layoutbuilder.h>
 | 
						|
#include <utils/theme/theme_p.h>
 | 
						|
 | 
						|
#include <QApplication>
 | 
						|
#include <QCheckBox>
 | 
						|
#include <QProgressBar>
 | 
						|
#include <QScrollArea>
 | 
						|
#include <QTimer>
 | 
						|
#include <QToolButton>
 | 
						|
 | 
						|
using namespace Utils;
 | 
						|
 | 
						|
// TODO: make tasks cancellable
 | 
						|
static void sleepInThread(QPromise<void> &promise, int seconds, bool reportSuccess)
 | 
						|
{
 | 
						|
    QThread::sleep(seconds);
 | 
						|
    if (!reportSuccess)
 | 
						|
        promise.future().cancel();
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char *argv[])
 | 
						|
{
 | 
						|
    QApplication app(argc, argv);
 | 
						|
 | 
						|
    setCreatorTheme(new Theme("default", &app));
 | 
						|
 | 
						|
    QWidget mainWidget;
 | 
						|
    mainWidget.setWindowTitle("Task Tree Demo");
 | 
						|
 | 
						|
    // Non-task GUI
 | 
						|
 | 
						|
    QToolButton *startButton = new QToolButton();
 | 
						|
    startButton->setText("Start");
 | 
						|
    QToolButton *stopButton = new QToolButton();
 | 
						|
    stopButton->setText("Stop");
 | 
						|
    QToolButton *resetButton = new QToolButton();
 | 
						|
    resetButton->setText("Reset");
 | 
						|
    QProgressBar *progressBar = new QProgressBar();
 | 
						|
    QCheckBox *synchronizerCheckBox = new QCheckBox("Use Future Synchronizer");
 | 
						|
    synchronizerCheckBox->setChecked(true);
 | 
						|
    QScrollArea *scrollArea = new QScrollArea();
 | 
						|
    scrollArea->setWidgetResizable(true);
 | 
						|
    QWidget *scrollAreaWidget = new QWidget();
 | 
						|
 | 
						|
    // Task GUI
 | 
						|
 | 
						|
    QList<StateWidget *> allStateWidgets;
 | 
						|
 | 
						|
    auto createGroupWidget = [&allStateWidgets] {
 | 
						|
        auto *widget = new GroupWidget();
 | 
						|
        allStateWidgets.append(widget);
 | 
						|
        return widget;
 | 
						|
    };
 | 
						|
    auto createTaskWidget = [&allStateWidgets] {
 | 
						|
        auto *widget = new TaskWidget();
 | 
						|
        allStateWidgets.append(widget);
 | 
						|
        return widget;
 | 
						|
    };
 | 
						|
 | 
						|
    GroupWidget *rootGroup = createGroupWidget();
 | 
						|
 | 
						|
    GroupWidget *groupTask_1 = createGroupWidget();
 | 
						|
    TaskWidget *task_1_1 = createTaskWidget();
 | 
						|
    TaskWidget *task_1_2 = createTaskWidget();
 | 
						|
    TaskWidget *task_1_3 = createTaskWidget();
 | 
						|
 | 
						|
    TaskWidget *task_2 = createTaskWidget();
 | 
						|
    TaskWidget *task_3 = createTaskWidget();
 | 
						|
 | 
						|
    GroupWidget *groupTask_4 = createGroupWidget();
 | 
						|
    TaskWidget *task_4_1 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_2 = createTaskWidget();
 | 
						|
    GroupWidget *groupTask_4_3 = createGroupWidget();
 | 
						|
    TaskWidget *task_4_3_1 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_3_2 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_3_3 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_3_4 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_4 = createTaskWidget();
 | 
						|
    TaskWidget *task_4_5 = createTaskWidget();
 | 
						|
 | 
						|
    TaskWidget *task_5 = createTaskWidget();
 | 
						|
 | 
						|
    // Task initial configuration
 | 
						|
 | 
						|
    task_1_2->setBusyTime(2);
 | 
						|
    task_1_2->setSuccess(false);
 | 
						|
    task_1_3->setBusyTime(3);
 | 
						|
    task_4_3_1->setBusyTime(4);
 | 
						|
    task_4_3_2->setBusyTime(2);
 | 
						|
    task_4_3_3->setBusyTime(1);
 | 
						|
    task_4_3_4->setBusyTime(3);
 | 
						|
    task_4_3_4->setSuccess(false);
 | 
						|
    task_4_4->setBusyTime(6);
 | 
						|
    task_4_4->setBusyTime(3);
 | 
						|
 | 
						|
    groupTask_1->setWorkflowPolicy(Tasking::WorkflowPolicy::ContinueOnDone);
 | 
						|
    groupTask_4->setWorkflowPolicy(Tasking::WorkflowPolicy::Optional);
 | 
						|
    groupTask_4_3->setExecuteMode(ExecuteMode::Parallel);
 | 
						|
    groupTask_4_3->setWorkflowPolicy(Tasking::WorkflowPolicy::StopOnError);
 | 
						|
 | 
						|
    // Task layout
 | 
						|
 | 
						|
    {
 | 
						|
        using namespace Layouting;
 | 
						|
 | 
						|
        Column {
 | 
						|
            TaskGroup { rootGroup, {
 | 
						|
                TaskGroup { groupTask_1, {
 | 
						|
                    task_1_1, hr,
 | 
						|
                    task_1_2, hr,
 | 
						|
                    task_1_3,
 | 
						|
                }}, hr,
 | 
						|
                task_2, hr,
 | 
						|
                task_3, hr,
 | 
						|
                TaskGroup { groupTask_4, {
 | 
						|
                    task_4_1, hr,
 | 
						|
                    task_4_2, hr,
 | 
						|
                    TaskGroup { groupTask_4_3, {
 | 
						|
                        task_4_3_1, hr,
 | 
						|
                        task_4_3_2, hr,
 | 
						|
                        task_4_3_3, hr,
 | 
						|
                        task_4_3_4,
 | 
						|
                    }}, hr,
 | 
						|
                    task_4_4, hr,
 | 
						|
                    task_4_5,
 | 
						|
                }}, hr,
 | 
						|
                task_5
 | 
						|
            }}, st
 | 
						|
        }.attachTo(scrollAreaWidget);
 | 
						|
 | 
						|
        scrollArea->setWidget(scrollAreaWidget);
 | 
						|
 | 
						|
        Column {
 | 
						|
            Row { startButton, stopButton, resetButton, synchronizerCheckBox, progressBar },
 | 
						|
            hr,
 | 
						|
            scrollArea
 | 
						|
        }.attachTo(&mainWidget);
 | 
						|
    }
 | 
						|
 | 
						|
    // Task tree creator (takes configuation from GUI)
 | 
						|
 | 
						|
    using namespace Tasking;
 | 
						|
 | 
						|
    std::unique_ptr<TaskTree> taskTree;
 | 
						|
    FutureSynchronizer synchronizer;
 | 
						|
 | 
						|
    auto treeRoot = [&] {
 | 
						|
 | 
						|
        auto taskItem = [sync = &synchronizer, synchronizerCheckBox](TaskWidget *widget) {
 | 
						|
            const auto setupHandler = [=](Async<void> &task) {
 | 
						|
                task.setConcurrentCallData(sleepInThread, widget->busyTime(), widget->isSuccess());
 | 
						|
                if (synchronizerCheckBox->isChecked())
 | 
						|
                    task.setFutureSynchronizer(sync);
 | 
						|
                widget->setState(State::Running);
 | 
						|
            };
 | 
						|
            const auto doneHandler = [widget](const Async<void> &) {
 | 
						|
                widget->setState(State::Done);
 | 
						|
            };
 | 
						|
            const auto errorHandler = [widget](const Async<void> &) {
 | 
						|
                widget->setState(State::Error);
 | 
						|
            };
 | 
						|
            return AsyncTask<void>(setupHandler, doneHandler, errorHandler);
 | 
						|
        };
 | 
						|
 | 
						|
        const Group root {
 | 
						|
            rootGroup->executeMode(),
 | 
						|
            workflowPolicy(rootGroup->workflowPolicy()),
 | 
						|
            onGroupSetup([rootGroup] { rootGroup->setState(State::Running); }),
 | 
						|
            onGroupDone([rootGroup] { rootGroup->setState(State::Done); }),
 | 
						|
            onGroupError([rootGroup] { rootGroup->setState(State::Error); }),
 | 
						|
 | 
						|
            Group {
 | 
						|
                groupTask_1->executeMode(),
 | 
						|
                workflowPolicy(groupTask_1->workflowPolicy()),
 | 
						|
                onGroupSetup([groupTask_1] { groupTask_1->setState(State::Running); }),
 | 
						|
                onGroupDone([groupTask_1] { groupTask_1->setState(State::Done); }),
 | 
						|
                onGroupError([groupTask_1] { groupTask_1->setState(State::Error); }),
 | 
						|
 | 
						|
                taskItem(task_1_1),
 | 
						|
                taskItem(task_1_2),
 | 
						|
                taskItem(task_1_3)
 | 
						|
            },
 | 
						|
            taskItem(task_2),
 | 
						|
            taskItem(task_3),
 | 
						|
            Group {
 | 
						|
                groupTask_4->executeMode(),
 | 
						|
                workflowPolicy(groupTask_4->workflowPolicy()),
 | 
						|
                onGroupSetup([groupTask_4] { groupTask_4->setState(State::Running); }),
 | 
						|
                onGroupDone([groupTask_4] { groupTask_4->setState(State::Done); }),
 | 
						|
                onGroupError([groupTask_4] { groupTask_4->setState(State::Error); }),
 | 
						|
 | 
						|
                taskItem(task_4_1),
 | 
						|
                taskItem(task_4_2),
 | 
						|
                Group {
 | 
						|
                    groupTask_4_3->executeMode(),
 | 
						|
                    workflowPolicy(groupTask_4_3->workflowPolicy()),
 | 
						|
                    onGroupSetup([groupTask_4_3] { groupTask_4_3->setState(State::Running); }),
 | 
						|
                    onGroupDone([groupTask_4_3] { groupTask_4_3->setState(State::Done); }),
 | 
						|
                    onGroupError([groupTask_4_3] { groupTask_4_3->setState(State::Error); }),
 | 
						|
 | 
						|
                    taskItem(task_4_3_1),
 | 
						|
                    taskItem(task_4_3_2),
 | 
						|
                    taskItem(task_4_3_3),
 | 
						|
                    taskItem(task_4_3_4)
 | 
						|
                },
 | 
						|
                taskItem(task_4_4),
 | 
						|
                taskItem(task_4_5)
 | 
						|
            },
 | 
						|
            taskItem(task_5)
 | 
						|
        };
 | 
						|
        return root;
 | 
						|
    };
 | 
						|
 | 
						|
    // Non-task GUI handling
 | 
						|
 | 
						|
    auto createTaskTree = [&] {
 | 
						|
        TaskTree *taskTree = new TaskTree(treeRoot());
 | 
						|
        progressBar->setMaximum(taskTree->progressMaximum());
 | 
						|
        QObject::connect(taskTree, &TaskTree::progressValueChanged,
 | 
						|
                         progressBar, &QProgressBar::setValue);
 | 
						|
        return taskTree;
 | 
						|
    };
 | 
						|
 | 
						|
    auto stopTaskTree = [&] {
 | 
						|
        if (taskTree)
 | 
						|
            taskTree->stop();
 | 
						|
        // TODO: unlock GUI controls?
 | 
						|
    };
 | 
						|
 | 
						|
    auto resetTaskTree = [&] {
 | 
						|
        if (!taskTree)
 | 
						|
            return;
 | 
						|
 | 
						|
        stopTaskTree();
 | 
						|
        taskTree.reset();
 | 
						|
        for (StateWidget *widget : allStateWidgets)
 | 
						|
            widget->setState(State::Initial);
 | 
						|
        progressBar->setValue(0);
 | 
						|
    };
 | 
						|
 | 
						|
    auto startTaskTree = [&] {
 | 
						|
        resetTaskTree();
 | 
						|
        taskTree.reset(createTaskTree());
 | 
						|
        taskTree->start();
 | 
						|
        // TODO: lock GUI controls?
 | 
						|
    };
 | 
						|
 | 
						|
    QObject::connect(startButton, &QAbstractButton::clicked, startTaskTree);
 | 
						|
    QObject::connect(stopButton, &QAbstractButton::clicked, stopTaskTree);
 | 
						|
    QObject::connect(resetButton, &QAbstractButton::clicked, resetTaskTree);
 | 
						|
 | 
						|
    // Hack in order to show initial size minimal, but without scrollbars.
 | 
						|
    // Apparently setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow) doesn't work.
 | 
						|
    const int margin = 2;
 | 
						|
    scrollArea->setMinimumSize(scrollAreaWidget->minimumSizeHint().grownBy({0, 0, margin, margin}));
 | 
						|
    QTimer::singleShot(0, scrollArea, [&] { scrollArea->setMinimumSize({0, 0}); });
 | 
						|
 | 
						|
    mainWidget.show();
 | 
						|
 | 
						|
    return app.exec();
 | 
						|
}
 |