Qt World Summit: Submit your Presentation

[SOLVED] Displaying progress messages with QTextEdit

  • Hi Qters,

    I would like to display the progress of a certain function by sending messages to a QTextEdit. Currently, I have the QTextEdit set as read-only, and periodically I will update the text displayed by doing something like this:

    QString update3 = "Now reading the file ...";

    Basically, I'm looking to create something similar to "printf" on the command line.

    When I run the app, the contents of the QTextEdit are not updated on screen. Instead, the messages appear in their entirety when the function is completely done. I would like to have the messages updated while the function is still running.

    Perhaps QTextEdit isn't the best way to go. If anyone has experience doing something like this, suggestions are greatly appreciated.

    Thanks in advance!!

  • In order to have any widget updates, the main event loop has to be running. You function is effectively blocking the event loop while it runs and, as such, won't let any interface elements change.

    You have some options, each with their own positives and negatives.

    • You can move your function to a separate thread and periodically emit signals to let your main thread update its widgets. (All GUI code has to run in the main thread.)
    • You could call qApp->processEvents() manually periodically during your function's run.
    • You can break your function up into a series of parts and periodically call one or more slots with a QTimer to do the next part.
    • Probably other ways as well...

  • mlong:
    Thanks for your response!
    I went with your third suggestion, breaking my function into multiple parts and updating the widget using the signal/slot mechanism. It seems to be working fine.

    If you don't mind me asking another question:
    Currently, after I update the contents of the QTextEdit, I "refresh" the view by using

    Basically, every time I make a change to the contents of QTextEdit, I call the show() method in order to refresh the view. Even though it works okay, I feel this might not be the most professional way to do it. Also, I didn't use QTimer, as you suggested in your above message. What are the advantages of using QTimer?

    Thanks again for your assistance

  • If you use, for instance, a QTimer::singleShot(...) with a timeout of 0, it will call the given slot as soon as it can on the event loop. It's kind of a short cut to saying "process your events that are pending, and then call this slot as soon as possible. It's not better or worse, necessarily, just another option.

    Off the top of my head, I wouldn't think that you would need to do anything to manually refresh the QTextEdit, so I'm a little unsure of what to tell you.

    Can you provide a minimal code example of how you're splitting things up and how you're working with setting and updating the QTextEdit?

  • The fact that you did not use a timer (and I assume, also not another method of returning to the event loop) and the fact that you need something like show on the widget to make it update, I think your changes did not reach the target of the whole excersise of breaking up your operation in small chunks yet. The whole point is, that you give your application the opportunity to return to the event loop regulary. The application needs that to stay responsive: to be able to respond to mouse and keyboard operations, to do updates to widgets, to redraw areas that may have become covered and uncovered in the meantime, etc. An application that does not return to the eventloop frequently enough feels unresponsive to a user.

    By using a QTimer as suggested by mlong, you make sure that after each chunk of your process, you give the Qt machinery the opportunity to do its work. After that, your process will continue. That will also ensure that your QTextEdit will just display the updates you make to its text, without you having to call show() on it.

  • The point why the refresh without doing anything does not work is, that you need the repainting stuff, which needs a return to the event loop. I would not use show() to force repainting, I would use repaint() :-) to force it now, but from my POV that's no good design to need that.

    If you have longer running stuff to do, there are IMHO two options:

    • breaking it up like mlong suggested into several steps and doing one part and then (at the end) do a single shot timer or a queued message call to reenter the stuff from the event loop
    • moving the long time operation to a seperate thread ( "look at peppes article":http://developer.qt.nokia.com/wiki/Threads_Events_QObjects ).

  • Thank you all for taking the time to respond to my questions. Unfortunately, I still don't think I've achieved the goal. Below is an outline of my code:
    @FileProcessingDialog::FileProcessingDialog(QDialog *parent) {
    QTimer::singleShot(0, this, SLOT(part1()));
    void FileProcessingDialog::part1() {
    ......do stuff
    QTimer::singleShot (0, this, SLOT(part2()));
    void FileProcessingDialog::part2() {
    .....do stuff
    QTimer::singleShot(0, this, SLOT(part3()));

    I'm not sure if I put the "textEdit_updates->append()" statements in the right place. Do they belong in the slot bodies or in the FileProcessingDialog constructor? Presently, the QTextEdit does not display the messages quickly.
    Again, I really appreciate your helpful responses.

  • I doubt that this is actual code, right?

    How much work is performed in each of your steps?

  • ...this is actually the basic structure of my code. Is there something fundamentally wrong? Please let me know!!

    As for the work done in each slot, it can potentially take several seconds. I would like the QTextEdit to reflect the progress of the functions by displaying messages. Presently, I would consider the user experience minimally acceptable, but it could definitely be better.

  • The "do stuff" parts are the real issue -- what are you doing in there? Can you split that work in smaller chunks? Can you offload it to another thread?

  • The "do stuff" chunks perform some pretty heavy computations, and I've already broken the original code into 6 chunks. Could you please tell whether the code I posted above is correctly implementing the procedure as originally recommended by mlong?
    His suggestion was:
    "You can break your function up into a series of parts and periodically call one or more slots with a QTimer to do the next part."
    Am I still missing the point?
    Thanks in advance.

  • No, the point is there, but ideally every part should take no more than some milliseconds to not be noticeable by the user. If your computations are heavy and you can't break them in an easy way (and/or add calls to processEvents) move them to another thread or process.

  • I implemented a separate thread and the user experience is much improved.
    Thanks so much for everyone's guidance and helpful suggestions!

  • Glad to see it's working!

Log in to reply