// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "taskwidget.h" #include #include #include #include #include #include #include #include #include using namespace Tasking; using namespace std::chrono; static QWidget *hr() { auto frame = new QFrame; frame->setFrameShape(QFrame::HLine); frame->setFrameShadow(QFrame::Sunken); return frame; } static QWidget *taskGroup(QWidget *groupWidget, const QList &widgets) { QWidget *widget = new QWidget; QBoxLayout *layout = new QHBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(groupWidget); QGroupBox *groupBox = new QGroupBox; QBoxLayout *subLayout = new QVBoxLayout(groupBox); for (int i = 0; i < widgets.size(); ++i) { if (i > 0) subLayout->addWidget(hr()); subLayout->addWidget(widgets.at(i)); } layout->addWidget(groupBox); return widget; } static State resultToState(DoneWith result) { switch (result) { case DoneWith::Success : return State::Success; case DoneWith::Error : return State::Error; case DoneWith::Cancel : return State::Canceled; } return State::Initial; } int main(int argc, char *argv[]) { QApplication app(argc, argv); 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; QScrollArea *scrollArea = new QScrollArea; scrollArea->setWidgetResizable(true); QWidget *scrollAreaWidget = new QWidget; // Task GUI QList allGroupWidgets; QList allTaskWidgets; auto createGroupWidget = [&allGroupWidgets] { auto *widget = new GroupWidget; allGroupWidgets.append(widget); return widget; }; auto createTaskWidget = [&allTaskWidgets] { auto *widget = new TaskWidget; allTaskWidgets.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(WorkflowPolicy::ContinueOnSuccess); groupTask_4->setWorkflowPolicy(WorkflowPolicy::FinishAllAndSuccess); groupTask_4_3->setExecuteMode(ExecuteMode::Parallel); groupTask_4_3->setWorkflowPolicy(WorkflowPolicy::StopOnError); // Task layout { QWidget *taskTree = taskGroup(rootGroup, { taskGroup(groupTask_1, { task_1_1, task_1_2, task_1_3 }), task_2, task_3, taskGroup(groupTask_4, { task_4_1, task_4_2, taskGroup(groupTask_4_3, { task_4_3_1, task_4_3_2, task_4_3_3, task_4_3_4, }), task_4_4, task_4_5 }), task_5 }); QBoxLayout *scrollLayout = new QVBoxLayout(scrollAreaWidget); scrollLayout->addWidget(taskTree); scrollLayout->addStretch(); scrollArea->setWidget(scrollAreaWidget); QBoxLayout *mainLayout = new QVBoxLayout(&mainWidget); QBoxLayout *subLayout = new QHBoxLayout; subLayout->addWidget(startButton); subLayout->addWidget(stopButton); subLayout->addWidget(resetButton); subLayout->addWidget(progressBar); mainLayout->addLayout(subLayout); mainLayout->addWidget(hr()); mainLayout->addWidget(scrollArea); mainLayout->addWidget(hr()); QBoxLayout *footerLayout = new QHBoxLayout; footerLayout->addWidget(new StateLabel(State::Initial)); footerLayout->addWidget(new StateLabel(State::Running)); footerLayout->addWidget(new StateLabel(State::Success)); footerLayout->addWidget(new StateLabel(State::Error)); footerLayout->addWidget(new StateLabel(State::Canceled)); mainLayout->addLayout(footerLayout); } // Task tree (takes initial configuation from GUI) std::unique_ptr taskTree; const auto createGroup = [](GroupWidget *widget) { return List { widget->executeMode(), widget->workflowPolicy(), onGroupSetup([widget] { widget->setState(State::Running); }), onGroupDone([widget](DoneWith result) { widget->setState(resultToState(result)); }), }; }; const auto createTask = [](TaskWidget *widget) { const milliseconds timeout{widget->busyTime() * 1000}; const auto setupTask = [widget, timeout](milliseconds &taskObject) { taskObject = timeout; widget->setState(State::Running); }; const Group root { widget->isSuccess() ? stopOnError : finishAllAndError, TimeoutTask(setupTask), onGroupDone([widget](DoneWith result) { widget->setState(resultToState(result)); }) }; return root; }; const auto recipe = [&] { const Group root { createGroup(rootGroup), Group { createGroup(groupTask_1), createTask(task_1_1), createTask(task_1_2), createTask(task_1_3) }, createTask(task_2), createTask(task_3), Group { createGroup(groupTask_4), createTask(task_4_1), createTask(task_4_2), Group { createGroup(groupTask_4_3), createTask(task_4_3_1), createTask(task_4_3_2), createTask(task_4_3_3), createTask(task_4_3_4) }, createTask(task_4_4), createTask(task_4_5) }, createTask(task_5) }; return root; }; // Non-task GUI handling const auto createTaskTree = [&] { TaskTree *taskTree = new TaskTree(recipe()); progressBar->setMaximum(taskTree->progressMaximum()); QObject::connect(taskTree, &TaskTree::progressValueChanged, progressBar, &QProgressBar::setValue); return taskTree; }; const auto stopTaskTree = [&] { if (taskTree) taskTree->stop(); // TODO: unlock GUI controls? }; const auto resetTaskTree = [&] { if (!taskTree) return; stopTaskTree(); taskTree.reset(); for (GroupWidget *widget : allGroupWidgets) widget->setState(State::Initial); for (TaskWidget *widget : allTaskWidgets) 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 = 4; scrollArea->setMinimumSize(scrollAreaWidget->minimumSizeHint().grownBy({0, 0, margin, margin})); QTimer::singleShot(0, scrollArea, [&] { scrollArea->setMinimumSize({0, 0}); }); mainWidget.show(); return app.exec(); }