1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "progressmanager_p.h"
34 #include "progressview.h"
35 #include "coreconstants.h"
38 #include <utils/qtcassert.h>
41 using namespace Core::Internal;
45 \class Core::ProgressManager
46 \brief The ProgressManager class is used to show a user interface
47 for running tasks in Qt Creator.
49 It tracks the progress of a task that it is told
50 about, and shows a progress indicator in the left hand tool bar
51 of Qt Creator's main window to the user.
52 The progress indicator also allows the user to cancel the task.
54 You get the single instance of this class via the
55 Core::ICore::progressManager() method.
57 \section1 Registering a task
58 The ProgressManager API uses QtConcurrent as the basis for defining
59 tasks. A task consists of the following properties:
69 \o A \c QFuture object that represents the task which is
70 responsible for reporting the state of the task. See below
71 for coding patterns how to create this object for your
76 \o A very short title describing your task. This is shown
77 as a title over the progress bar.
81 \o A string identifier that is used to group different tasks that
83 For example, all the search operations use the same type
87 \o \l ProgressManager::ProgressFlags
88 \o Additional flags that specify how the progress bar should
89 be presented to the user.
92 To register a task you create your \c QFuture<void> object, and call
93 addTask(). This method returns a
94 \l{Core::FutureProgress}{FutureProgress}
95 object that you can use to further customize the progress bar's appearance.
96 See the \l{Core::FutureProgress}{FutureProgress} documentation for
99 In the following you will learn about two common patterns how to
100 create the \c QFuture<void> object for your task.
102 \section2 Create a threaded task with QtConcurrent
103 The first option is to directly use QtConcurrent to actually
104 start a task concurrently in a different thread.
105 QtConcurrent has several different methods to run e.g.
106 a class method in a different thread. Qt Creator itself
107 adds a few more in \c{src/libs/qtconcurrent/runextensions.h}.
108 The QtConcurrent methods to run a concurrent task return a
109 \c QFuture object. This is what you want to give the
110 ProgressManager in the addTask() method.
112 Have a look at e.g Locator::ILocatorFilter. Locator filters implement
113 a method \c refresh which takes a \c QFutureInterface object
114 as a parameter. These methods look something like:
116 void Filter::refresh(QFutureInterface<void> &future) {
117 future.setProgressRange(0, MAX);
119 while (!future.isCanceled()) {
120 // Do a part of the long stuff
122 future.setProgressValue(currentProgress);
128 The actual refresh, which calls all the filters' refresh methods
129 in a different thread, looks like this:
131 QFuture<void> task = QtConcurrent::run(&ILocatorFilter::refresh, filters);
132 Core::FutureProgress *progress = Core::ICore::instance()
133 ->progressManager()->addTask(task, tr("Indexing"),
134 Locator::Constants::TASK_INDEX);
136 First, we tell QtConcurrent to start a thread which calls all the filters'
137 refresh method. After that we register the returned QFuture object
138 with the ProgressManager.
140 \section2 Manually create QtConcurrent objects for your thread
141 If your task has its own means to create and run a thread,
142 you need to create the necessary objects yourselves, and
143 report the start/stop state.
146 // We are already running in a different thread here
147 QFutureInterface<void> *progressObject = new QFutureInterface<void>;
148 progressObject->setProgressRange(0, MAX);
149 Core::ICore::instance()->progressManager()->addTask(
150 progressObject->future(),
151 tr("DoIt"), MYTASKTYPE);
152 progressObject->reportStarted();
155 progressObject->setProgressValue(currentProgress);
157 // We have done what we needed to do
158 progressObject->reportFinished();
159 delete progressObject;
161 In the first line we create the QFutureInterface object that will be
162 our way for reporting the task's state.
163 The first thing we report is the expected range of the progress values.
164 We register the task with the ProgressManager, using the internal
165 QFuture object that has been created for our QFutureInterface object.
166 Next we report that the task has begun and start doing our actual
167 work, regularly reporting the progress via the methods
168 in QFutureInterface. After the long taking operation has finished,
169 we report so through the QFutureInterface object, and delete it
172 \section1 Customizing progress appearance
174 You can set a custom widget to show below the progress bar itself,
175 using the FutureProgress object returned by the addTask() method.
176 Also use this object to get notified when the user clicks on the
181 \enum Core::ProgressManager::ProgressFlag
182 Additional flags that specify details in behavior. The
183 default for a task is to not have any of these flags set.
185 The progress indicator stays visible after the task has finished.
186 \value ShowInApplicationIcon
187 The progress indicator for this task is additionally
188 shown in the application icon in the system's task bar or dock, on
189 platforms that support that (at the moment Windows 7 and Mac OS X).
193 \fn Core::ProgressManager::ProgressManager(QObject *parent = 0)
198 \fn Core::ProgressManager::~ProgressManager()
203 \fn FutureProgress *Core::ProgressManager::addTask(const QFuture<void> &future, const QString &title, const QString &type, ProgressFlags flags = 0)
205 Shows a progress indicator for task given by the QFuture object \a future.
206 The progress indicator shows the specified \a title along with the progress bar.
207 The \a type of a task will specify a logical grouping with other
208 running tasks. Via the \a flags parameter you can e.g. let the
209 progress indicator stay visible after the task has finished.
210 Returns an object that represents the created progress indicator,
211 which can be used to further customize. The FutureProgress object's
212 life is managed by the ProgressManager and is guaranteed to live
216 \fn void Core::ProgressManager::setApplicationLabel(const QString &text)
218 Shows the given \a text in a platform dependent way in the application
219 icon in the system's task bar or dock. This is used
220 to show the number of build errors on Windows 7 and Mac OS X.
224 \fn void Core::ProgressManager::cancelTasks(const QString &type)
226 Schedules a cancel for all running tasks of the given \a type.
227 Please note that the cancel functionality depends on the
228 running task to actually check the \c QFutureInterface::isCanceled
233 \fn void Core::ProgressManager::taskStarted(const QString &type)
235 Sent whenever a task of a given \a type is started.
239 \fn void Core::ProgressManager::allTasksFinished(const QString &type)
241 Sent when all tasks of a \a type have finished.
244 ProgressManagerPrivate::ProgressManagerPrivate(QObject *parent)
245 : ProgressManager(parent),
248 m_progressView = new ProgressView;
249 ICore *core = ICore::instance();
250 connect(core, SIGNAL(coreAboutToClose()), this, SLOT(cancelAllRunningTasks()));
253 ProgressManagerPrivate::~ProgressManagerPrivate()
258 void ProgressManagerPrivate::cancelTasks(const QString &type)
261 QMap<QFutureWatcher<void> *, QString>::iterator task = m_runningTasks.begin();
262 while (task != m_runningTasks.end()) {
263 if (task.value() != type) {
268 disconnect(task.key(), SIGNAL(finished()), this, SLOT(taskFinished()));
269 if (m_applicationTask == task.key())
270 disconnectApplicationTask();
271 task.key()->cancel();
273 task = m_runningTasks.erase(task);
276 emit allTasksFinished(type);
280 void ProgressManagerPrivate::cancelAllRunningTasks()
282 QMap<QFutureWatcher<void> *, QString>::const_iterator task = m_runningTasks.constBegin();
283 while (task != m_runningTasks.constEnd()) {
284 disconnect(task.key(), SIGNAL(finished()), this, SLOT(taskFinished()));
285 if (m_applicationTask == task.key())
286 disconnectApplicationTask();
287 task.key()->cancel();
291 m_runningTasks.clear();
294 FutureProgress *ProgressManagerPrivate::addTask(const QFuture<void> &future, const QString &title,
295 const QString &type, ProgressFlags flags)
297 QFutureWatcher<void> *watcher = new QFutureWatcher<void>();
298 m_runningTasks.insert(watcher, type);
299 connect(watcher, SIGNAL(finished()), this, SLOT(taskFinished()));
300 if (flags & ShowInApplicationIcon) {
301 if (m_applicationTask)
302 disconnectApplicationTask();
303 m_applicationTask = watcher;
304 setApplicationProgressRange(future.progressMinimum(), future.progressMaximum());
305 setApplicationProgressValue(future.progressValue());
306 connect(m_applicationTask, SIGNAL(progressRangeChanged(int,int)),
307 this, SLOT(setApplicationProgressRange(int,int)));
308 connect(m_applicationTask, SIGNAL(progressValueChanged(int)),
309 this, SLOT(setApplicationProgressValue(int)));
310 setApplicationProgressVisible(true);
312 watcher->setFuture(future);
313 emit taskStarted(type);
314 return m_progressView->addTask(future, title, type, flags);
317 QWidget *ProgressManagerPrivate::progressView()
319 return m_progressView;
322 void ProgressManagerPrivate::taskFinished()
324 QObject *taskObject = sender();
325 QTC_ASSERT(taskObject, return);
326 QFutureWatcher<void> *task = static_cast<QFutureWatcher<void> *>(taskObject);
327 if (m_applicationTask == task)
328 disconnectApplicationTask();
329 QString type = m_runningTasks.value(task);
330 m_runningTasks.remove(task);
333 if (!m_runningTasks.key(type, 0)) {
334 emit allTasksFinished(type);
338 void ProgressManagerPrivate::disconnectApplicationTask()
340 disconnect(m_applicationTask, SIGNAL(progressRangeChanged(int,int)),
341 this, SLOT(setApplicationProgressRange(int,int)));
342 disconnect(m_applicationTask, SIGNAL(progressValueChanged(int)),
343 this, SLOT(setApplicationProgressValue(int)));
344 setApplicationProgressVisible(false);
345 m_applicationTask = 0;