Revision control
++ // Copyright (C) 2025 Jarek Kobus
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include "taskwidget.h"
++
++ #include <QSingleTaskTreeRunner>
++ #include <QTaskTree>
++
++ #include <QApplication>
++ #include <QBoxLayout>
++ #include <QGroupBox>
++ #include <QLabel>
++ #include <QProgressBar>
++ #include <QScrollArea>
++ #include <QTimer>
++ #include <QToolButton>
++
++ using namespace QtTaskTree;
++
++ using namespace std::chrono;
++
++ static QWidget *hr()
++ {
++ auto frame = new QFrame;
++ frame->setFrameShape(QFrame::HLine);
++ frame->setFrameShadow(QFrame::Sunken);
++ return frame;
++ }
++
++ 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;
++ }
++
++ struct GroupSetup
++ {
++ WorkflowPolicy policy = WorkflowPolicy::StopOnError;
++ ExecuteMode mode = ExecuteMode::Sequential;
++ };
++
++ //! [0]
++ class GlueItem
++ {
++ public:
++ virtual ExecutableItem recipe() const = 0;
++ virtual QWidget *widget() const = 0;
++ virtual void reset() const = 0;
++
++ virtual ~GlueItem() = default;
++ };
++ //! [0]
++
++ class GroupGlueItem final : public GlueItem
++ {
++ public:
++ GroupGlueItem(const GroupSetup &setup, const QList<GlueItem *> &children)
++ : m_children(children)
++ , m_groupWidget(new GroupWidget)
++ , m_widget(new QWidget)
++ {
++ QBoxLayout *layout = new QHBoxLayout(m_widget);
++ layout->setContentsMargins(0, 0, 0, 0);
++ layout->addWidget(m_groupWidget);
++ QGroupBox *groupBox = new QGroupBox;
++ QBoxLayout *subLayout = new QVBoxLayout(groupBox);
++ for (int i = 0; i < children.size(); ++i) {
++ if (i > 0)
++ subLayout->addWidget(hr());
++ subLayout->addWidget(children.at(i)->widget());
++ }
++ layout->addWidget(groupBox);
++ m_groupWidget->setWorkflowPolicy(setup.policy);
++ m_groupWidget->setExecuteMode(setup.mode);
++ }
++
++ ExecutableItem recipe() const final;
++ QWidget *widget() const final { return m_widget; }
++ void reset() const final {
++ m_groupWidget->setState(State::Initial);
++ for (GlueItem *child : m_children)
++ child->reset();
++ }
++
++ ~GroupGlueItem() override { qDeleteAll(m_children); }
++
++ private:
++ QList<GlueItem *> m_children;
++ GroupWidget *m_groupWidget = nullptr;
++ QWidget *m_widget = nullptr;
++ };
++
++ //! [1]
++ ExecutableItem GroupGlueItem::recipe() const
++ {
++ GroupItems childRecipes;
++ for (GlueItem *child : m_children)
++ childRecipes.append(child->recipe());
++
++ return Group {
++ m_groupWidget->executeModeItem(),
++ m_groupWidget->workflowPolicyItem(),
++ onGroupSetup([this] { m_groupWidget->setState(State::Running); }),
++ childRecipes,
++ onGroupDone([this](DoneWith result) { m_groupWidget->setState(resultToState(result)); })
++ };
++ }
++ //! [1]
++
++ class TaskGlueItem final : public GlueItem
++ {
++ public:
++ TaskGlueItem(int busyTime, DoneResult result)
++ : m_taskWidget(new TaskWidget(busyTime, result))
++ { }
++
++ ExecutableItem recipe() const final;
++ QWidget *widget() const final { return m_taskWidget; }
++ void reset() const final { m_taskWidget->setState(State::Initial); }
++
++ private:
++ TaskWidget *m_taskWidget = nullptr;
++ };
++
++ //! [2]
++ ExecutableItem TaskGlueItem::recipe() const
++ {
++ return Group {
++ onGroupSetup([this] { m_taskWidget->setState(State::Running); }),
++ timeoutTask(seconds(m_taskWidget->busyTime()), m_taskWidget->desiredResult()),
++ onGroupDone([this](DoneWith result) { m_taskWidget->setState(resultToState(result)); })
++ };
++ }
++ //! [2]
++
++ //! [3]
++ static GlueItem *group(const GroupSetup &groupSetup, const QList<GlueItem *> children)
++ {
++ return new GroupGlueItem(groupSetup, children);
++ }
++
++ static GlueItem *task(int busyTime = 1, DoneResult result = DoneResult::Success)
++ {
++ return new TaskGlueItem(busyTime, result);
++ }
++ //! [3]
++
++ int main(int argc, char *argv[])
++ {
++ QApplication app(argc, argv);
++
++ QWidget mainWidget;
++ mainWidget.setWindowTitle(QApplication::tr("Task Tree Demo"));
++
++ QToolButton *startButton = new QToolButton;
++ startButton->setText(QApplication::tr("Start"));
++ QToolButton *cancelButton = new QToolButton;
++ cancelButton->setText(QApplication::tr("Cancel"));
++ cancelButton->setEnabled(false);
++ QToolButton *resetButton = new QToolButton;
++ resetButton->setText(QApplication::tr("Reset"));
++ QProgressBar *progressBar = new QProgressBar;
++
++ //! [4]
++ std::unique_ptr<GlueItem> tree {
++ group({WorkflowPolicy::ContinueOnSuccess}, {
++ group({}, {
++ task(),
++ task(2, DoneResult::Error),
++ task(3)
++ }),
++ task(),
++ task(),
++ group({WorkflowPolicy::FinishAllAndSuccess}, {
++ task(),
++ task(),
++ group({WorkflowPolicy::StopOnError, ExecuteMode::Parallel}, {
++ task(4),
++ task(2),
++ task(1),
++ task(3, DoneResult::Error)
++ }),
++ task(2),
++ task(3)
++ }),
++ task()
++ })
++ };
++ //! [4]
++
++ {
++ QScrollArea *scrollArea = new QScrollArea;
++ scrollArea->setWidgetResizable(true);
++ QWidget *scrollAreaWidget = new QWidget;
++ QBoxLayout *scrollLayout = new QVBoxLayout(scrollAreaWidget);
++ scrollLayout->addWidget(tree->widget());
++ scrollLayout->addStretch();
++ scrollArea->setWidget(scrollAreaWidget);
++
++ QBoxLayout *mainLayout = new QVBoxLayout(&mainWidget);
++ QBoxLayout *subLayout = new QHBoxLayout;
++ subLayout->addWidget(startButton);
++ subLayout->addWidget(cancelButton);
++ 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);
++
++ const int margin = 4;
++ scrollArea->setMinimumSize(scrollAreaWidget->minimumSizeHint().grownBy(
++ {0, 0, margin, margin}));
++ QTimer::singleShot(0, scrollArea, [scrollArea] { scrollArea->setMinimumSize({0, 0}); });
++ }
++
++ //! [5]
++ QSingleTaskTreeRunner taskTreeRunner;
++
++ const auto resetTaskTree = [&] {
++ taskTreeRunner.reset();
++ tree->reset();
++ progressBar->setValue(0);
++ cancelButton->setEnabled(false);
++ };
++
++ const auto cancelTaskTree = [&] { taskTreeRunner.cancel(); };
++
++ const auto startTaskTree = [&] {
++ resetTaskTree();
++ cancelButton->setEnabled(true);
++
++ const auto onTaskTreeSetup = [progressBar](QTaskTree &taskTree) {
++ progressBar->setMaximum(taskTree.progressMaximum());
++ QObject::connect(&taskTree, &QTaskTree::progressValueChanged,
++ progressBar, &QProgressBar::setValue);
++ };
++
++ const auto onTaskTreeDone = [cancelButton] { cancelButton->setEnabled(false); };
++
++ taskTreeRunner.start({tree->recipe()}, onTaskTreeSetup, onTaskTreeDone);
++ };
++
++ QObject::connect(startButton, &QAbstractButton::clicked, &app, startTaskTree);
++ QObject::connect(cancelButton, &QAbstractButton::clicked, &app, cancelTaskTree);
++ QObject::connect(resetButton, &QAbstractButton::clicked, &app, resetTaskTree);
++
++ mainWidget.show();
++
++ return app.exec();
++ //! [5]
++ }