Navigation in Qt 6.9 documentation

In this blog post, I would like to talk about the improvements in documentation navigation you can see in the Qt Framework's documentation for the Qt 6.9 release. 

Previously, our documentation portal relied mostly on searching: you type a request and get the results. This works well if you know how the things are named in the Qt world. But if you switch to Qt from a different platform, how do you know what to enter in the search box? What if you want to explore the features of a particular module? In this case, you need to have better navigation in Qt docs.

Navigation in the Qt Creator help viewer 

In Qt 6.9, we reorganized the documentation structure for each module in Qt Creator's help viewer. 

 undefined-Apr-04-2025-12-12-24-0000-PM

In the Qt Creator's help viewer, you can find all the topics we have for each module organized in a tree structure. 

Navigation in online docs for Qt 6.9 

We added a common navigation tree for the entire Qt Framework to our online docs. 

 image_docs69_onlinetree-png

This tree includes module docs as subtrees. The essential modules, like Qt Core, Qt GUI, or Qt Quick, are placed on the first level of the tree. The module subtree may include docs for other relevant modules. For example, the Qt Quick subtree contains other Qt Quick related modules: 

image_docs69_onlinetree_quick-png 

In the Modules subtree, you can find all the add-ons shipped with Qt Installer. 

 image_docs69_onlinetree_modules-png

We'd love to hear your feedback 

To be honest, the job is not done yet. Now, we are in a situation where we added a tree to a huge documentation set that never had this kind of navigation. The new navigation tree brought us new capabilities and new challenges, too. Now, we should write the docs with the new navigation in mind. 

If you notice that the topic tree can be improved or the content relative to the tree can be optimized, please create a ticket at bugreports.qt.io for the Documentation component and describe in detail how we can improve our navigation or content. 


Blog Topics:

Comments

Ivan Komissarov
2 points
56 months ago

These are great news!

Vadim Peretokin
2 points
56 months ago

This is great indeed! QtConcurrent in Qt5 is a good improvement on what's available in C++ and this improves it even further.

Г
Государство 2.0 - Принципы успеха
2 points
56 months ago

Seems very similar https://qtpromise.netlify.app/qtpromise/getting-started.html Making a promise from signal is very useful

P
Philip Schuchardt
2 points
56 months ago

Another feature that AsyncFuture has that this implementation is lacking is a way to combine multiple QFutures into one future. For example: QFuture<int> futureFunc1 = QtConcurrent::run(func1); QFuture<int> futureFunc2 = QtConcurrent::run(func2); QFuture<void> futureSignal = QtFuture::connect(button, &QButton::onClicked);

//Combine multiple futures and sync when all of them are finished QFuture<void> finalFuture = QtFuture::combine({futureFunc1, futureFunc2, futureSignal}); finalFuture.then([futureFunc1, futureFunce2](){ qDebug() << "Results func1:" << futureFunc1.result() << "Result func2:" << futureFunc2.result();});

The current implementation doesn't allow for parallel pipelines to be merged into one QFuture.

Or is this supported?

S
Sona Kurazyan
2 points
56 months ago

You're right, it's not supported at the moment. Support for combining multiple futures is also one of the things we can consider adding in future.

P
Philip Schuchardt
1 point
56 months ago
Daniel Nicoletti
1 point
56 months ago

Any plans for a QCoroutine? :)

S
Sona Kurazyan
0 points
56 months ago

No plans for it yet :)

P
Philip Schuchardt
1 point
56 months ago

This looks really nice and is well thought out. I've been using AsyncFuture library for a while now and it filled in missing QFuture functionality for Qt5. Couple questions since the Qt6 snapshot documentation doesn't appear to have this new API published:

  1. Can you dynamically chain QFuture? QFuture future = QtConcurrent::task(doSomething) QFuture chainFuture; if(...) { chainFuture = future.then(func1); } else { chainFuture = future.then(func2); } chainFuture.then(finalFunc);

  2. It looks like QFuture isn't templated anymore. Does it still support a list of results? I always thought this was a bit confusing. I guess the new QFuture api isn't compatible with the Qt5 line?

  3. Can you set QFuture (or QPromise) with a completed value. Surprisingly this is very useful. Sometimes you know the value and don't need to reprocess.

  4. Can the .then() statements return QFuture? For example if you have QtConncurrent::mapped() run inside of a .then(QtFuture::Launch::Async). This is something I do a lot in AsyncFuture.

S
Sona Kurazyan
1 point
56 months ago

Thank you for the feedback! I fixed the link to documentation snapshots, it was pointing to a wrong version before. And QFuture docs can be found here.

Regarding to your questions:

  1. Yes, your example is completely valid.

  2. QFuture is actually still templated and is entirely compatible with QFuture in Qt 5. And it still supports multiple results, because that's useful with QtConcurrent and in other scenarios, for example, progress reporting, etc.

  3. Unfortunately there is no way of creating a completed QFuture/QPromise yet, but I agree with you, that could be very useful. Will consider implementing it in the upcoming releases.

  4. If the callable passed to .then() returns a QFuture, then the return type of .then() will be QFuture<QFuture<T>>, i.e. no unwrapping is implemented yet. But again, that's also something we'll consider adding :)

P
Philip Schuchardt
0 points
56 months ago

Awesome! Thanks for the quick feedback.

F
Flavio
0 points
56 months ago

I didn't really study all these old and new APIs, BUT as a Qt user I honestly don't get all the complexities. Can't we just a have a Promise object like in Javascript? I don't understand why QFuture exists in the first place...These APIs look more complicated than signals connected to lambdas so they defeat their own purpose.

I understand this is about multithreading. The confusione arises from talking about "asynchronous" instead of "multithreaded" . Multithreading is 1% of asynchronous use cases. It's needed only for heavy number crunching, not for network requests as in this post examples. What is really missing is a very simple QPromise class, very much like Javascript's. Not a multithreaded one. A function returning a QPromise could be used like this:

myPromiseFunction(myArgs) .then([](auto &result) { // do something with result }).error([](auto &e) { // handler error });

S
Sona Kurazyan
1 point
56 months ago

I'm not familiar with Javascript's Promise, but don't see how the separation of producer (i.e. QPromise) and consumer (i.e. QFuture) interfaces makes things complex. The idea is that you can have one producer, and multiple consumers (possibly in different threads) independent from it. Many libraries have this separation (including STL, boost, etc.)

I understand this is about multithreading. The confusione arises from talking about "asynchronous" instead of "multithreaded" .

This is not only about multithreading, the new functionality is useful for any asynchronous computation. I think, the example with network request demonstrates that.

A function returning a QPromise could be used like this:

Well, you can write exactly the same example with a function returning a QFuture, I don't see what's the problem.

Dmitriy Purgin
1 point
56 months ago

Hey Sona, thanks for the work, it looks very promising! (pun intended). In your examples, there is an 'onFailed' handler, does it 'consume' the rejection or rejects the promises up the chain as well? In Simon Brunel's QtPromise implementation there is a 'tapFail' that can be injected into the promise chain to make an action on promise rejection, but the rejection will be passed further.I'm a bit worried though that you are haven't taken a look at JavaScript's promises. Asynchronous programming in JavaScript, C#, and Scala are kind of state of the art, so I think you should consider reviewing those approaches to take the best bits into Qt. Additionally, if someone who is familiar with those technologies starts with Qt, they would recognize similar patterns and techniques, so it is definitely worth looking at.

S
Sona Kurazyan
0 points
56 months ago

Thanks for the feedback.

In your examples, there is an 'onFailed' handler, does it 'consume' the rejection or rejects the promises up the chain as well?

Yes, onFailed() 'consumes' the rejection, i.e. if you have future.then(func1).then(func2).onFailed(...).then(func3): if func1 fails with an exception, func2 is skipped and .onFailed() is called, followed by func3 (if .onFailed()doesn't throw).

Additionally, if someone who is familiar with those technologies starts with Qt, they would recognize similar patterns and techniques, so it is definitely worth looking at.

The intention was to improve the existing QFuture and provide the basic functionality to make it more useful. Considering that many C++ users are probably already familiar with std::future and std::promise, transitioning to QFuture/QPromise should require minimal effort. Of course, that doesn't mean that we won't continue improving the existing functionality. Feel free to submit your suggestions here.

F
Flavio
0 points
56 months ago

@Sona have a look at this as suggested by Dmitriy. That simple QPromise should be included in Qt6. This is exactly was I talking about and a very high quality implementation too.

S
Sona Kurazyan
0 points
56 months ago

I've seen it already, it's indeed a great project, but it's up to the author to decide whether to contribute to Qt or not. We tried to provide our own implementation of a similar functionality, and we are open to suggestions to improve the functionality further.

Peter Würtz
0 points
55 months ago

Well, he tried to contribute, but the Qt people abandoned the discussion at some point: https://bugreports.qt.io/browse/QTBUG-80908 That's all fair of course, but implying that the author decided not to contribute this to Qt6 is a bit of a stretch..

Dmitriy Purgin
1 point
56 months ago

Hey Flavio, you should try Simon Brunel's implementation of Promise/A+: https://github.com/simonbrunel/qtpromise I've used it in a couple of projects and I must say I'd prefer it over the implementation in Qt 6. Exactly for the reason you mentioned: it's easier and is based on a specification. So anybody who has ever used promises in JavaScript would find their way with Simon's QtPromise

F
Flavio
0 points
56 months ago

Thanks, interesting. This is what I meant. But I think such a core feature has to be provided by Qt in order to be usable in the long term. Qt's API should make use of these simple promises. Then we can start using them in our own code. Alas they are over engineering it.

Ivan Komissarov
0 points
56 months ago

I am too lazy to look in the implementation but I wonder how do you ensure that continuation of the future is called in the same thread as the function was called. From the higher level point of view, there could be a race - function can exit before continuation is added. Is the thread somehow reserved until all futures leave scope?

S
Sona Kurazyan
0 points
56 months ago

No, the thread is not reserved. If continuation is attached after the parent has already finished, it will run in the thread that owns the parent future. I might consider adding another version of .then() which takes a specific QThread*as parameter, to give more control in such cases (which may be useful for other scenarios as well).

Ivan Komissarov
0 points
56 months ago

Then the doc should be updated, it says > When the asynchronous computation represented by this future finishes, function will be invoked in the same thread in which this future has been running which is not always true=) Maybe it makes sense to use Inherit launch policy for the Function overload [0]? It that case, if future was async, it will be (synchronously) re-scheduled in the pool?

[0] https://github.com/qt/qtbase/blob/dev/src/corelib/thread/qfuture.h#L350

S
Sona Kurazyan
0 points
56 months ago

You're right, I'll update the docs :)

In case of Inherit policy, if the parent has been using Async, the continuation will be launched in a new thread (QThreadPool may decide to re-use the parent's thread, but it's not guaranteed). Spawning a new thread for each continuation may not be what the user always wants, so I don't think making it default is a good idea.

P
Philippe
0 points
56 months ago

Can the mentioned "custom thread pool" have a custom QThread::Priority too?

S
Sona Kurazyan
1 point
56 months ago

No, passing a QThread::Priority is not supported.

Oleg Derevenetz
0 points
56 months ago

Looks similar to QtPromise project, nice one. Any plans to add support for context tracking, something like discussed here:

https://github.com/simonbrunel/qtpromise/issues/35

?

S
Sona Kurazyan
0 points
56 months ago

Thanks for the feedback. Ability to specify a context seems to be useful, created this.

Florian Korsakissok
0 points
55 months ago

It definitely would sound cool if coroutines didn't exist. But considering they will soon get full compiler support in Qt's supported compilers, likely soon after Qt 6 is released, I feel like these improvements will become obsolete some months after being released. Coroutines will make asynchronous code way more concise than any imaginable implementation of promises or futures.

I hope the improvements on futures and promises will not impede the efforts for first class Qt support on coroutines - because personally, that's what I will care about in the near future.