-3

There is a lot of discussion (e.g. here) going on about spinning or moving busy indicators in a GUI but I could not find a single one that clearly states that it is impossible to re-paint/update any content in a GUI application while the main thread is blocked.

This question is actually a general one and is not necessarily directly related to Qt GUI applications.

When the main thread of a GUI is performing a blocking operation no events are processed and no content can be re-painted. There are two "recommended" approaches:

  1. Using worker threads
  2. Splitting the work in chunks and updating the UI "just not that often"

The problem is that there are operations that simply cannot be moved to worker threads or any other asynchronous mechanism. The best example is the serialization and de-serialization of the UI itself. In both scenarios the user must not click happily in the UI and change settings while the settings, properties, etc. are taken from the widgets (during the saving) or applied to the widgets (during the loading). This is true for the threading approach as well as the splitting into chunks. Edit: Another example is the application of stylesheets to a complete GUI. Imagine that a user wants so select a "dark" scheme. All of the widgets need to be updated and the currently visible ones needs to be re-painted. During this step the event loop cannot run. A similar discussion can be found here.

In my case I'm working on an embedded device so there is another approach:

  1. Directly overwriting a specific region of the framebuffer

This approach feels very ugly and comes with a lot of problematic scenarios and surly involves lots of debugging.

The last but sad approach:

  1. Do not use any moving/updating content at all and just show something static such as "...in progress..."

Well, this is just sad...

Do you agree on these observations? Does anyone know of a different approach or concept in general un-related to Qt?

Community
  • 1
  • 1
FrozenTarzan
  • 834
  • 10
  • 30
  • I'm not sure about general not related to Qt, but there is `processEvents` function in Qt that will do exactly what you need. – Bearded Beaver Apr 06 '17 at 12:38
  • `processEvents` re-enters the event loop, and is unnecessary. If you can use `processEvents`, you can already chunk the operation. – Kuba hasn't forgotten Monica Apr 06 '17 at 12:39
  • This question is too broad, and is a bad case of an XY problem. You're discussing *specific solutions* without describing in detail the **specific problem** you're solving. If you're asking **specifically** how to nicely de-serialize the UI in an asynchronous fashion that provides for good user experience - just ask about that, and not about some ill-defined "general" problem that would need coverage in a dozen separate questions. **What is your particular scenario that you're having a problem with?** Be exact about deserialization: everyone means 10 different things when they use that term. – Kuba hasn't forgotten Monica Apr 06 '17 at 12:44
  • @KubaOber: thanks for your responses. You seem to really dislike my question and the style I'm asking it. What I'm describing is a scenario, maybe my example is not perfect but the scenario simply exists. Just take it as the description of a mathematical problem that you need to solve You cannot change its description :-) This question is about GUI programming concepts. Of course it is somewhat broad :-) – FrozenTarzan Apr 06 '17 at 13:35
  • @BeardedBeaver: thanks for your response but processEvents does not do what I want. It simply pushes the execution of the event loop which is not recommended and in addition does not solve the problem. When you have a longer operation that performs slow operations in Qt itself (or the GUI framework itself) you cannot call processEvents, there are no "chunks" that can be intercepted (as Kuba Ober pointed out). – FrozenTarzan Apr 06 '17 at 13:37
  • "What I'm describing is a scenario, maybe my example is not perfect but the scenario simply exists" Your scenario exists because you've messed up some other part of your application and you steadfastly refuse to tell us about that real issue, and instead you're trying to come up with kludges to what's likely a very simple core issue. I've faced many problems like that where the sought solution has nothing to do with the real problem. Are you trying to dynamically change stylesheets or styles all the time? – Kuba hasn't forgotten Monica Apr 06 '17 at 13:57
  • Please add Qt version and the platform backend you're using to the question. – Kuba hasn't forgotten Monica Apr 06 '17 at 16:45

1 Answers1

1

The problem is that there are operations that simply cannot be moved to worker threads or any other asynchronous mechanism.

I can't agree. These operations should be split into blocking and non-blocking operations. Everything that blocks should be handled asynchronously, or, if no non-blocking APIs are available, handed off to a worker thread.

The best example is the serialization and de-serialization of the UI itself.

I find it a particularly poor example mainly because I've yet to run into a need for blocking the GUI, and serialization doesn't call for it.

In both scenarios the user must not click happily in the UI and change settings while the settings, properties, etc. are saved or loaded.

Construction and destruction of the widgets should be very quick, if that's what you mean by "deserializing" the UI. Recall that the blocking I/O and long parsing has been done in another thread. Almost all Qt widgets certainly are quick to set up, and those that are not are a necessary evil that you have no choice but to live with. If you have your own widgets that do blocking operations like disk or registry access in their constructors or event handlers (plenty of such "code" exists), fix them.

If you're merely talking about setting widget values, this again is super-quick and can be done all in one batch. You will probably need a viewmodel to asynchronously interface between the view (widgets, QML view, or a QAbstractItemView) and the data source.

Disk I/O and parsing/output of the on-disk representation belongs in a separate worker. Once you create an internal representation, it can be passed to the gui thread to build the widget tree or populate the interface.

You should implement thread-safe models using QAbstractItemModel or a similar interface, and populate them in a worker thread, and let the GUI thread react to the updates in real time.

If you wish to indicate to the user that a part of the interface is not usable yet, e.g. while a model gets populated, you could do so via an overlay, making sure that you temporarily disable the focus on the widgets that are under the overlay. This is not hard and I'd find it amusing if your entire question could be reduced to "how do I make a part of the UI inaccessible while it's being modified".

The key thing missing is that the UI should handle asynchronously reacting to a model changing its state. For all I care, it could take an hour to load the data needed to fully populate the model. It doesn't mean that your application should be unresponsive. Simply make the parts of the UI that can't be interacted with inaccessible for interaction. Ensure that other parts of the application that need the configuration data are similarly either inaccessible or in a partially usable state that will asynchronously revert to full state once the configuration becomes available.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • -1 for "I can't agree". You cannot not agree on how my setup looks like :-) You tell me about the performance of my embedded platform although you don't know it. Applying application wide styling and re-painting is extremely costly, for example. The question is if there exist concepts that can handle my limited scenarios. Maybe I could have given better examples. You should have asked ;-) – FrozenTarzan Apr 06 '17 at 13:50
  • One solution, for example, would be a separate application hovering on top of the other that is only displayed when the actual GUI app is busy. The communication could be done via some kind of network communication. This seems to be ugly as hell but would in fact solve the problem:-) – FrozenTarzan Apr 06 '17 at 13:50
  • "Applying application wide styling and re-painting is extremely costly, for example." How long of a time are we talking here? How often do you change the styling of the application? You're doing something else wrong, but it's impossible to tell without you telling us exactly what goes on. My point is that when you do things right, it's not supposed to be a problem. That's why I don't agree that you have the problem you claim you do: as you said yourself, you've got other problems that should be fixed first, but we can't help you unless you help us. – Kuba hasn't forgotten Monica Apr 06 '17 at 13:53
  • It's the first time I encounter such a discussion behavior on stackoverflow :-( The question directly talks about a blocked main thread. It does not matter why it is blocked. I ask because I wanted to find a concept for such scenarios. I think that my "second application" approach should make my point even clearer :-) – FrozenTarzan Apr 06 '17 at 13:57
  • "It does not matter why it is blocked". It matters because you've taken it for a given that it's blocked. And I don't buy it. It shouldn't be hard to convince everyone that the problem you have is insurmountable and must be tackled as-is. Any workarounds are likely to be way uglier than whatever problem you're really having that blocks the main thread, and even then they may require architectural changes to split widget painting across threads etc. (yes, it's possible). – Kuba hasn't forgotten Monica Apr 06 '17 at 13:58
  • Reading in between of your somewhat aggressive posts I found your actual answer: "Almost all Qt widgets certainly are quick to set up, and those that are not are a necessary evil that you have no choice but to live with.". That's the point, I'm working on a platform where I can actually measure single constructor calls of Qt's widgets. This discussion was about concepts to inform the user about such longer operations not about how bad it is to use Qt on such low performance platforms. So your answer simply is: "I don't know of any" which is perfectly fine :-) – FrozenTarzan Apr 06 '17 at 14:15
  • What you're not telling us is what exactly is slow and in what circumstances. It should be quick to demonstrate. E.g. if widgets take `x` microseconds to construct, you can easily mock up a `QWidget` that busy-loops using `QElapsedTimer` or `QThread::usleep`. You'd then have an actual code to show the problem, and a very good question. Instead you're generalizing and making it hard to tackle your real problem. What I was mentioning was occasional slow widgets like web view. You need some other approach, but I can't divine it unless you mock-up the setup you have problems with. – Kuba hasn't forgotten Monica Apr 06 '17 at 16:41
  • Also, if you don't have one already: get yourself a profiling build of Qt - a release build with full debug information. Use that to build your application, so that you're not facing slowness merely due to the debug build's slowness. And then profile your problem and show where the time is spent – Kuba hasn't forgotten Monica Apr 06 '17 at 16:44