1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtCore module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qfuturewatcher.h" |
41 | #include "qfuturewatcher_p.h" |
42 | |
43 | #include <QtCore/qcoreevent.h> |
44 | #include <QtCore/qcoreapplication.h> |
45 | #include <QtCore/qmetaobject.h> |
46 | #include <QtCore/qthread.h> |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | /*! \class QFutureWatcher |
51 | \reentrant |
52 | \since 4.4 |
53 | |
54 | \inmodule QtCore |
55 | \ingroup thread |
56 | |
57 | \brief The QFutureWatcher class allows monitoring a QFuture using signals |
58 | and slots. |
59 | |
60 | QFutureWatcher provides information and notifications about a QFuture. Use |
61 | the setFuture() function to start watching a particular QFuture. The |
62 | future() function returns the future set with setFuture(). |
63 | |
64 | For convenience, several of QFuture's functions are also available in |
65 | QFutureWatcher: progressValue(), progressMinimum(), progressMaximum(), |
66 | progressText(), isStarted(), isFinished(), isRunning(), isCanceled(), |
67 | isPaused(), waitForFinished(), result(), and resultAt(). The cancel(), |
68 | setPaused(), pause(), resume(), and togglePaused() functions are slots in |
69 | QFutureWatcher. |
70 | |
71 | Status changes are reported via the started(), finished(), canceled(), |
72 | paused(), resumed(), resultReadyAt(), and resultsReadyAt() signals. |
73 | Progress information is provided from the progressRangeChanged(), |
74 | void progressValueChanged(), and progressTextChanged() signals. |
75 | |
76 | Throttling control is provided by the setPendingResultsLimit() function. |
77 | When the number of pending resultReadyAt() or resultsReadyAt() signals |
78 | exceeds the limit, the computation represented by the future will be |
79 | throttled automatically. The computation will resume once the number of |
80 | pending signals drops below the limit. |
81 | |
82 | Example: Starting a computation and getting a slot callback when it's |
83 | finished: |
84 | |
85 | \snippet code/src_corelib_thread_qfuturewatcher.cpp 0 |
86 | |
87 | Be aware that not all running asynchronous computations can be canceled or |
88 | paused. For example, the future returned by QtConcurrent::run() cannot be |
89 | canceled; but the future returned by QtConcurrent::mappedReduced() can. |
90 | |
91 | QFutureWatcher<void> is specialized to not contain any of the result |
92 | fetching functions. Any QFuture<T> can be watched by a |
93 | QFutureWatcher<void> as well. This is useful if only status or progress |
94 | information is needed; not the actual result data. |
95 | |
96 | \sa QFuture, {Qt Concurrent} |
97 | */ |
98 | |
99 | /*! \fn template <typename T> QFutureWatcher<T>::QFutureWatcher(QObject *parent) |
100 | |
101 | Constructs a new QFutureWatcher with the given \a parent. Until a future is |
102 | set with setFuture(), the functions isStarted(), isCanceled(), and |
103 | isFinished() return \c true. |
104 | */ |
105 | QFutureWatcherBase::QFutureWatcherBase(QObject *parent) |
106 | :QObject(*new QFutureWatcherBasePrivate, parent) |
107 | { } |
108 | |
109 | /*! \fn template <typename T> QFutureWatcher<T>::~QFutureWatcher() |
110 | |
111 | Destroys the QFutureWatcher. |
112 | */ |
113 | |
114 | /*! \fn template <typename T> void QFutureWatcher<T>::cancel() |
115 | |
116 | Cancels the asynchronous computation represented by the future(). Note that |
117 | the cancelation is asynchronous. Use waitForFinished() after calling |
118 | cancel() when you need synchronous cancelation. |
119 | |
120 | Currently available results may still be accessed on a canceled QFuture, |
121 | but new results will \e not become available after calling this function. |
122 | Also, this QFutureWatcher will not deliver progress and result ready |
123 | signals once canceled. This includes the progressValueChanged(), |
124 | progressRangeChanged(), progressTextChanged(), resultReadyAt(), and |
125 | resultsReadyAt() signals. |
126 | |
127 | Be aware that not all running asynchronous computations can be canceled. |
128 | For example, the QFuture returned by QtConcurrent::run() cannot be |
129 | canceled; but the QFuture returned by QtConcurrent::mappedReduced() can. |
130 | */ |
131 | void QFutureWatcherBase::cancel() |
132 | { |
133 | futureInterface().cancel(); |
134 | } |
135 | |
136 | /*! \fn template <typename T> void QFutureWatcher<T>::setPaused(bool paused) |
137 | |
138 | If \a paused is true, this function pauses the asynchronous computation |
139 | represented by the future(). If the computation is already paused, this |
140 | function does nothing. This QFutureWatcher will stop delivering progress |
141 | and result ready signals while the future is paused. Signal delivery will |
142 | continue once the computation is resumed. |
143 | |
144 | If \a paused is false, this function resumes the asynchronous computation. |
145 | If the computation was not previously paused, this function does nothing. |
146 | |
147 | Be aware that not all computations can be paused. For example, the |
148 | QFuture returned by QtConcurrent::run() cannot be paused; but the QFuture |
149 | returned by QtConcurrent::mappedReduced() can. |
150 | |
151 | \sa pause(), resume(), togglePaused() |
152 | */ |
153 | void QFutureWatcherBase::setPaused(bool paused) |
154 | { |
155 | futureInterface().setPaused(paused); |
156 | } |
157 | |
158 | /*! \fn template <typename T> void QFutureWatcher<T>::pause() |
159 | |
160 | Pauses the asynchronous computation represented by the future(). This is a |
161 | convenience method that simply calls setPaused(true). |
162 | |
163 | \sa resume() |
164 | */ |
165 | void QFutureWatcherBase::pause() |
166 | { |
167 | futureInterface().setPaused(true); |
168 | } |
169 | |
170 | /*! \fn template <typename T> void QFutureWatcher<T>::resume() |
171 | |
172 | Resumes the asynchronous computation represented by the future(). This is |
173 | a convenience method that simply calls setPaused(false). |
174 | |
175 | \sa pause() |
176 | */ |
177 | void QFutureWatcherBase::resume() |
178 | { |
179 | futureInterface().setPaused(false); |
180 | } |
181 | |
182 | /*! \fn template <typename T> void QFutureWatcher<T>::togglePaused() |
183 | |
184 | Toggles the paused state of the asynchronous computation. In other words, |
185 | if the computation is currently paused, calling this function resumes it; |
186 | if the computation is running, it becomes paused. This is a convenience |
187 | method for calling setPaused(!isPaused()). |
188 | |
189 | \sa setPaused(), pause(), resume() |
190 | */ |
191 | void QFutureWatcherBase::togglePaused() |
192 | { |
193 | futureInterface().togglePaused(); |
194 | } |
195 | |
196 | /*! \fn template <typename T> int QFutureWatcher<T>::progressValue() const |
197 | |
198 | Returns the current progress value, which is between the progressMinimum() |
199 | and progressMaximum(). |
200 | |
201 | \sa progressMinimum(), progressMaximum() |
202 | */ |
203 | int QFutureWatcherBase::progressValue() const |
204 | { |
205 | return futureInterface().progressValue(); |
206 | } |
207 | |
208 | /*! \fn template <typename T> int QFutureWatcher<T>::progressMinimum() const |
209 | |
210 | Returns the minimum progressValue(). |
211 | |
212 | \sa progressValue(), progressMaximum() |
213 | */ |
214 | int QFutureWatcherBase::progressMinimum() const |
215 | { |
216 | return futureInterface().progressMinimum(); |
217 | } |
218 | |
219 | /*! \fn template <typename T> int QFutureWatcher<T>::progressMaximum() const |
220 | |
221 | Returns the maximum progressValue(). |
222 | |
223 | \sa progressValue(), progressMinimum() |
224 | */ |
225 | int QFutureWatcherBase::progressMaximum() const |
226 | { |
227 | return futureInterface().progressMaximum(); |
228 | } |
229 | |
230 | /*! \fn template <typename T> QString QFutureWatcher<T>::progressText() const |
231 | |
232 | Returns the (optional) textual representation of the progress as reported |
233 | by the asynchronous computation. |
234 | |
235 | Be aware that not all computations provide a textual representation of the |
236 | progress, and as such, this function may return an empty string. |
237 | */ |
238 | QString QFutureWatcherBase::progressText() const |
239 | { |
240 | return futureInterface().progressText(); |
241 | } |
242 | |
243 | /*! \fn template <typename T> bool QFutureWatcher<T>::isStarted() const |
244 | |
245 | Returns \c true if the asynchronous computation represented by the future() |
246 | has been started, or if no future has been set; otherwise returns \c false. |
247 | */ |
248 | bool QFutureWatcherBase::isStarted() const |
249 | { |
250 | return futureInterface().queryState(state: QFutureInterfaceBase::Started); |
251 | } |
252 | |
253 | /*! \fn template <typename T> bool QFutureWatcher<T>::isFinished() const |
254 | |
255 | Returns \c true if the asynchronous computation represented by the future() |
256 | has finished, or if no future has been set; otherwise returns \c false. |
257 | */ |
258 | bool QFutureWatcherBase::isFinished() const |
259 | { |
260 | Q_D(const QFutureWatcherBase); |
261 | return d->finished; |
262 | } |
263 | |
264 | /*! \fn template <typename T> bool QFutureWatcher<T>::isRunning() const |
265 | |
266 | Returns \c true if the asynchronous computation represented by the future() |
267 | is currently running; otherwise returns \c false. |
268 | */ |
269 | bool QFutureWatcherBase::isRunning() const |
270 | { |
271 | return futureInterface().queryState(state: QFutureInterfaceBase::Running); |
272 | } |
273 | |
274 | /*! \fn template <typename T> bool QFutureWatcher<T>::isCanceled() const |
275 | |
276 | Returns \c true if the asynchronous computation has been canceled with the |
277 | cancel() function, or if no future has been set; otherwise returns \c false. |
278 | |
279 | Be aware that the computation may still be running even though this |
280 | function returns \c true. See cancel() for more details. |
281 | */ |
282 | bool QFutureWatcherBase::isCanceled() const |
283 | { |
284 | return futureInterface().queryState(state: QFutureInterfaceBase::Canceled); |
285 | } |
286 | |
287 | /*! \fn template <typename T> bool QFutureWatcher<T>::isPaused() const |
288 | |
289 | Returns \c true if the asynchronous computation has been paused with the |
290 | pause() function; otherwise returns \c false. |
291 | |
292 | Be aware that the computation may still be running even though this |
293 | function returns \c true. See setPaused() for more details. |
294 | |
295 | \sa setPaused(), togglePaused() |
296 | */ |
297 | bool QFutureWatcherBase::isPaused() const |
298 | { |
299 | return futureInterface().queryState(state: QFutureInterfaceBase::Paused); |
300 | } |
301 | |
302 | /*! \fn template <typename T> void QFutureWatcher<T>::waitForFinished() |
303 | |
304 | Waits for the asynchronous computation to finish (including cancel()ed |
305 | computations). |
306 | */ |
307 | void QFutureWatcherBase::waitForFinished() |
308 | { |
309 | futureInterface().waitForFinished(); |
310 | } |
311 | |
312 | bool QFutureWatcherBase::event(QEvent *event) |
313 | { |
314 | Q_D(QFutureWatcherBase); |
315 | if (event->type() == QEvent::FutureCallOut) { |
316 | QFutureCallOutEvent *callOutEvent = static_cast<QFutureCallOutEvent *>(event); |
317 | |
318 | if (futureInterface().isPaused()) { |
319 | d->pendingCallOutEvents.append(t: callOutEvent->clone()); |
320 | return true; |
321 | } |
322 | |
323 | if (callOutEvent->callOutType == QFutureCallOutEvent::Resumed |
324 | && !d->pendingCallOutEvents.isEmpty()) { |
325 | // send the resume |
326 | d->sendCallOutEvent(event: callOutEvent); |
327 | |
328 | // next send all pending call outs |
329 | for (int i = 0; i < d->pendingCallOutEvents.count(); ++i) |
330 | d->sendCallOutEvent(event: d->pendingCallOutEvents.at(i)); |
331 | qDeleteAll(c: d->pendingCallOutEvents); |
332 | d->pendingCallOutEvents.clear(); |
333 | } else { |
334 | d->sendCallOutEvent(event: callOutEvent); |
335 | } |
336 | return true; |
337 | } |
338 | return QObject::event(event); |
339 | } |
340 | |
341 | /*! \fn template <typename T> void QFutureWatcher<T>::setPendingResultsLimit(int limit) |
342 | |
343 | The setPendingResultsLimit() provides throttling control. When the number |
344 | of pending resultReadyAt() or resultsReadyAt() signals exceeds the |
345 | \a limit, the computation represented by the future will be throttled |
346 | automatically. The computation will resume once the number of pending |
347 | signals drops below the \a limit. |
348 | */ |
349 | void QFutureWatcherBase::setPendingResultsLimit(int limit) |
350 | { |
351 | Q_D(QFutureWatcherBase); |
352 | d->maximumPendingResultsReady = limit; |
353 | } |
354 | |
355 | void QFutureWatcherBase::connectNotify(const QMetaMethod &signal) |
356 | { |
357 | Q_D(QFutureWatcherBase); |
358 | static const QMetaMethod resultReadyAtSignal = QMetaMethod::fromSignal(signal: &QFutureWatcherBase::resultReadyAt); |
359 | if (signal == resultReadyAtSignal) |
360 | d->resultAtConnected.ref(); |
361 | #ifndef QT_NO_DEBUG |
362 | static const QMetaMethod finishedSignal = QMetaMethod::fromSignal(signal: &QFutureWatcherBase::finished); |
363 | if (signal == finishedSignal) { |
364 | if (futureInterface().isRunning()) { |
365 | //connections should be established before calling stFuture to avoid race. |
366 | // (The future could finish before the connection is made.) |
367 | qWarning(msg: "QFutureWatcher::connect: connecting after calling setFuture() is likely to produce race" ); |
368 | } |
369 | } |
370 | #endif |
371 | } |
372 | |
373 | void QFutureWatcherBase::disconnectNotify(const QMetaMethod &signal) |
374 | { |
375 | Q_D(QFutureWatcherBase); |
376 | static const QMetaMethod resultReadyAtSignal = QMetaMethod::fromSignal(signal: &QFutureWatcherBase::resultReadyAt); |
377 | if (signal == resultReadyAtSignal) |
378 | d->resultAtConnected.deref(); |
379 | } |
380 | |
381 | /*! |
382 | \internal |
383 | */ |
384 | QFutureWatcherBasePrivate::QFutureWatcherBasePrivate() |
385 | : maximumPendingResultsReady(QThread::idealThreadCount() * 2), |
386 | resultAtConnected(0), |
387 | finished(true) /* the initial m_future is a canceledResult(), with Finished set */ |
388 | { } |
389 | |
390 | /*! |
391 | \internal |
392 | */ |
393 | void QFutureWatcherBase::connectOutputInterface() |
394 | { |
395 | futureInterface().d->connectOutputInterface(iface: d_func()); |
396 | } |
397 | |
398 | /*! |
399 | \internal |
400 | */ |
401 | void QFutureWatcherBase::disconnectOutputInterface(bool pendingAssignment) |
402 | { |
403 | if (pendingAssignment) { |
404 | Q_D(QFutureWatcherBase); |
405 | d->pendingResultsReady.storeRelaxed(newValue: 0); |
406 | qDeleteAll(c: d->pendingCallOutEvents); |
407 | d->pendingCallOutEvents.clear(); |
408 | d->finished = false; /* May soon be amended, during connectOutputInterface() */ |
409 | } |
410 | |
411 | futureInterface().d->disconnectOutputInterface(iface: d_func()); |
412 | } |
413 | |
414 | void QFutureWatcherBasePrivate::postCallOutEvent(const QFutureCallOutEvent &callOutEvent) |
415 | { |
416 | Q_Q(QFutureWatcherBase); |
417 | |
418 | if (callOutEvent.callOutType == QFutureCallOutEvent::ResultsReady) { |
419 | if (pendingResultsReady.fetchAndAddRelaxed(valueToAdd: 1) >= maximumPendingResultsReady) |
420 | q->futureInterface().d->internal_setThrottled(enable: true); |
421 | } |
422 | |
423 | QCoreApplication::postEvent(receiver: q, event: callOutEvent.clone()); |
424 | } |
425 | |
426 | void QFutureWatcherBasePrivate::callOutInterfaceDisconnected() |
427 | { |
428 | QCoreApplication::removePostedEvents(receiver: q_func(), eventType: QEvent::FutureCallOut); |
429 | } |
430 | |
431 | void QFutureWatcherBasePrivate::sendCallOutEvent(QFutureCallOutEvent *event) |
432 | { |
433 | Q_Q(QFutureWatcherBase); |
434 | |
435 | switch (event->callOutType) { |
436 | case QFutureCallOutEvent::Started: |
437 | emit q->started(); |
438 | break; |
439 | case QFutureCallOutEvent::Finished: |
440 | finished = true; |
441 | emit q->finished(); |
442 | break; |
443 | case QFutureCallOutEvent::Canceled: |
444 | pendingResultsReady.storeRelaxed(newValue: 0); |
445 | emit q->canceled(); |
446 | break; |
447 | case QFutureCallOutEvent::Paused: |
448 | if (q->futureInterface().isCanceled()) |
449 | break; |
450 | emit q->paused(); |
451 | break; |
452 | case QFutureCallOutEvent::Resumed: |
453 | if (q->futureInterface().isCanceled()) |
454 | break; |
455 | emit q->resumed(); |
456 | break; |
457 | case QFutureCallOutEvent::ResultsReady: { |
458 | if (q->futureInterface().isCanceled()) |
459 | break; |
460 | |
461 | if (pendingResultsReady.fetchAndAddRelaxed(valueToAdd: -1) <= maximumPendingResultsReady) |
462 | q->futureInterface().setThrottled(false); |
463 | |
464 | const int beginIndex = event->index1; |
465 | const int endIndex = event->index2; |
466 | |
467 | emit q->resultsReadyAt(beginIndex, endIndex); |
468 | |
469 | if (resultAtConnected.loadRelaxed() <= 0) |
470 | break; |
471 | |
472 | for (int i = beginIndex; i < endIndex; ++i) |
473 | emit q->resultReadyAt(resultIndex: i); |
474 | |
475 | } break; |
476 | case QFutureCallOutEvent::Progress: |
477 | if (q->futureInterface().isCanceled()) |
478 | break; |
479 | |
480 | emit q->progressValueChanged(progressValue: event->index1); |
481 | if (!event->text.isNull()) // ### |
482 | emit q->progressTextChanged(progressText: event->text); |
483 | break; |
484 | case QFutureCallOutEvent::ProgressRange: |
485 | emit q->progressRangeChanged(minimum: event->index1, maximum: event->index2); |
486 | break; |
487 | default: break; |
488 | } |
489 | } |
490 | |
491 | |
492 | /*! \fn template <typename T> const T &QFutureWatcher<T>::result() const |
493 | |
494 | Returns the first result in the future(). If the result is not immediately |
495 | available, this function will block and wait for the result to become |
496 | available. This is a convenience method for calling resultAt(0). |
497 | |
498 | \sa resultAt() |
499 | */ |
500 | |
501 | /*! \fn template <typename T> const T &QFutureWatcher<T>::resultAt(int index) const |
502 | |
503 | Returns the result at \a index in the future(). If the result is not |
504 | immediately available, this function will block and wait for the result to |
505 | become available. |
506 | |
507 | \sa result() |
508 | */ |
509 | |
510 | /*! \fn template <typename T> void QFutureWatcher<T>::setFuture(const QFuture<T> &future) |
511 | |
512 | Starts watching the given \a future. |
513 | |
514 | If \a future has already started, the watcher will initially emit signals |
515 | that bring their listeners up to date about the future's state. The |
516 | following signals will, if applicable, be emitted in the given order: |
517 | started(), progressRangeChanged(), progressValueChanged(), |
518 | progressTextChanged(), resultsReadyAt(), resultReadyAt(), paused(), |
519 | canceled(), and finished(). Of these, resultsReadyAt() and |
520 | resultReadyAt() may be emitted several times to cover all available |
521 | results. progressValueChanged() and progressTextChanged() will only be |
522 | emitted once for the latest available progress value and text. |
523 | |
524 | To avoid a race condition, it is important to call this function |
525 | \e after doing the connections. |
526 | */ |
527 | |
528 | /*! \fn template <typename T> QFuture<T> QFutureWatcher<T>::future() const |
529 | |
530 | Returns the watched future. |
531 | */ |
532 | |
533 | /*! \fn template <typename T> void QFutureWatcher<T>::started() |
534 | |
535 | This signal is emitted when this QFutureWatcher starts watching the future |
536 | set with setFuture(). |
537 | */ |
538 | |
539 | /*! |
540 | \fn template <typename T> void QFutureWatcher<T>::finished() |
541 | This signal is emitted when the watched future finishes. |
542 | */ |
543 | |
544 | /*! |
545 | \fn template <typename T> void QFutureWatcher<T>::canceled() |
546 | This signal is emitted if the watched future is canceled. |
547 | */ |
548 | |
549 | /*! \fn template <typename T> void QFutureWatcher<T>::paused() |
550 | This signal is emitted when the watched future is paused. |
551 | */ |
552 | |
553 | /*! \fn template <typename T> void QFutureWatcher<T>::resumed() |
554 | This signal is emitted when the watched future is resumed. |
555 | */ |
556 | |
557 | /*! |
558 | \fn template <typename T> void QFutureWatcher<T>::progressRangeChanged(int minimum, int maximum) |
559 | |
560 | The progress range for the watched future has changed to \a minimum and |
561 | \a maximum |
562 | */ |
563 | |
564 | /*! |
565 | \fn template <typename T> void QFutureWatcher<T>::progressValueChanged(int progressValue) |
566 | |
567 | This signal is emitted when the watched future reports progress, |
568 | \a progressValue gives the current progress. In order to avoid overloading |
569 | the GUI event loop, QFutureWatcher limits the progress signal emission |
570 | rate. This means that listeners connected to this slot might not get all |
571 | progress reports the future makes. The last progress update (where |
572 | \a progressValue equals the maximum value) will always be delivered. |
573 | */ |
574 | |
575 | /*! \fn template <typename T> void QFutureWatcher<T>::progressTextChanged(const QString &progressText) |
576 | |
577 | This signal is emitted when the watched future reports textual progress |
578 | information, \a progressText. |
579 | */ |
580 | |
581 | /*! |
582 | \fn template <typename T> void QFutureWatcher<T>::resultReadyAt(int index) |
583 | |
584 | This signal is emitted when the watched future reports a ready result at |
585 | \a index. If the future reports multiple results, the index will indicate |
586 | which one it is. Results can be reported out-of-order. To get the result, |
587 | call resultAt(index); |
588 | */ |
589 | |
590 | /*! |
591 | \fn template <typename T> void QFutureWatcher<T>::resultsReadyAt(int beginIndex, int endIndex); |
592 | |
593 | This signal is emitted when the watched future reports ready results. |
594 | The results are indexed from \a beginIndex to \a endIndex. |
595 | |
596 | */ |
597 | |
598 | QT_END_NAMESPACE |
599 | |
600 | #include "moc_qfuturewatcher.cpp" |
601 | |