Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Refresh display of QTableWidget
QtWS25 Last Chance

Refresh display of QTableWidget

Scheduled Pinned Locked Moved Unsolved General and Desktop
15 Posts 5 Posters 8.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    jcga
    wrote on last edited by
    #1

    Hi,

    I have an apparently simple problem: consider the following code

    QTableWidget* table=new QTableWidget(...);
    // initialisation of table
    QTableWidgetItem* it=NULL;
    QProcess p;
    int i;
    for( i=0; i<n; i++ )
    {
         p.start('some external pgr called');
         it=new QTableWidgetItem("text");
         table->insertRow(i);
         table->setItem(i,0,it);
         table->update();
    }
    

    The problem I get is that 'table' will display the inserted rows only after the for loop is completed. How to force it to update at each pass inside the loop?

    Thanks in advance,

    jclaude

    1 Reply Last reply
    0
    • Chris KawaC Offline
      Chris KawaC Offline
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by
      #2

      It's a wrong question to the problem. You don't want to "force" anything. What you want to do is insert an item into the table when a process starts, so focus on doing that. Also, you should use a separate QProcess object for each process you start. The way you use it is not designed to be "fire and forget".

      Here's a sample that doesn't require "forcing" updates:

      for (int i = 0; i<n; ++i)
      {
          auto p = new QProcess();
          connect(p, qOverload<int>(&QProcess::finished), p, &QProcess::deleteLater);
          connect(p, &QProcess::started, [=] {
              auto it = new QTableWidgetItem("text");
              table->insertRow(i);
              table->setItem(i, 0, it);
          });
          p->start("some external pgr called");
      }
      

      You might also want to handle the errorOccurred signal of the process in some way (e.g. add a different item to the table).

      J 1 Reply Last reply
      4
      • Chris KawaC Chris Kawa

        It's a wrong question to the problem. You don't want to "force" anything. What you want to do is insert an item into the table when a process starts, so focus on doing that. Also, you should use a separate QProcess object for each process you start. The way you use it is not designed to be "fire and forget".

        Here's a sample that doesn't require "forcing" updates:

        for (int i = 0; i<n; ++i)
        {
            auto p = new QProcess();
            connect(p, qOverload<int>(&QProcess::finished), p, &QProcess::deleteLater);
            connect(p, &QProcess::started, [=] {
                auto it = new QTableWidgetItem("text");
                table->insertRow(i);
                table->setItem(i, 0, it);
            });
            p->start("some external pgr called");
        }
        

        You might also want to handle the errorOccurred signal of the process in some way (e.g. add a different item to the table).

        J Offline
        J Offline
        jcga
        wrote on last edited by
        #3

        @Chris-Kawa

        Thanks Chris Kawa for your answer. I could not try your suggestions because my version of Qt (5.6.1) apparently do not recognizes qOverload. I'll update it and try.

        mrjjM 1 Reply Last reply
        0
        • J jcga

          @Chris-Kawa

          Thanks Chris Kawa for your answer. I could not try your suggestions because my version of Qt (5.6.1) apparently do not recognizes qOverload. I'll update it and try.

          mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by mrjj
          #4

          @jcga said in Refresh display of QTableWidget:

          qOverload

          I thinks its included in 5.7 to help using the new syntax.
          You can just use the old syntax to test.

          https://wiki.qt.io/New_Signal_Slot_Syntax

          1 Reply Last reply
          0
          • Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on last edited by
            #5

            qOverload is just a syntax-shortener. You can use the more verbose version if it's not available to you:

            connect(p, static_cast<void(QProcess::*)(int)>(&QProcess::finished), p, &QProcess::deleteLater);
            

            This ugliness is needed because there are two finished signals with different parameters and you need to help compiler choose the right one.
            Hopefully in Qt 6 the overloaded signals will be gone and you'll be able to write just

            connect(p, &QProcess::finished, p, &QProcess::deleteLater);
            
            kshegunovK 1 Reply Last reply
            2
            • Chris KawaC Chris Kawa

              qOverload is just a syntax-shortener. You can use the more verbose version if it's not available to you:

              connect(p, static_cast<void(QProcess::*)(int)>(&QProcess::finished), p, &QProcess::deleteLater);
              

              This ugliness is needed because there are two finished signals with different parameters and you need to help compiler choose the right one.
              Hopefully in Qt 6 the overloaded signals will be gone and you'll be able to write just

              connect(p, &QProcess::finished, p, &QProcess::deleteLater);
              
              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              Or directly bypass the cast by explicitly specifying the template arguments, e.g.:

              connect<void(QProcess::*)(int)>(p, &QProcess::finished, p, &QProcess::deleteLater);
              

              Granted it's only marginally shorter, but is a bit more readable in my opinion.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              0
              • Chris KawaC Offline
                Chris KawaC Offline
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by
                #7

                @kshegunov To each his own, but without really knowing the template implementation of connect it isn't obvious what the template argument really refers to (at least here, where you've got the same class on sending and receiving end).

                kshegunovK 1 Reply Last reply
                0
                • Chris KawaC Chris Kawa

                  @kshegunov To each his own, but without really knowing the template implementation of connect it isn't obvious what the template argument really refers to (at least here, where you've got the same class on sending and receiving end).

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by
                  #8

                  Fair enough. But that would be one of the really beautiful things about templates - having a convoluted implementation and terrible binary incompatible interface, wouldn't it ... no wonder the standards committee are pushing them templates so intently ... ;)

                  For the record the first template argument refers to the signal prototype, while the second refers to the slot prototype.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • J Offline
                    J Offline
                    jcga
                    wrote on last edited by
                    #9

                    I tried the solution suggested by Chris Kawa. The program runs correctly, do what it has to do, but the QWidgetTable display is not updated during the for loop, but only after the loop finishes, as before. I think I implemented strictly the code as indicated by Chris, so I do not understand what happens.

                    1 Reply Last reply
                    0
                    • Chris KawaC Offline
                      Chris KawaC Offline
                      Chris Kawa
                      Lifetime Qt Champion
                      wrote on last edited by
                      #10

                      The program runs correctly, do what it has to do, but the QWidgetTable display is not updated during the for loop, but only after the loop finishes, as before.

                      The code I posted wasn't suppose to update inside the loop, because I don't think it really needs to. Body of that loop is almost empty. Enforcing ui update after every iteration would just artificially slow it down. Usually you want to update ui when some state changes. In this case it's when a process is started, and yes, it happens after the execution leaves your code and gets back to the Qt's event loop.

                      There is a way to use your original code and "force" an update, but that's imho a bad design and I won't suggest how to do that unless you sign the fine print saying "I hereby want to willingly shoot myself in the foot".

                      VRoninV 1 Reply Last reply
                      0
                      • Chris KawaC Chris Kawa

                        The program runs correctly, do what it has to do, but the QWidgetTable display is not updated during the for loop, but only after the loop finishes, as before.

                        The code I posted wasn't suppose to update inside the loop, because I don't think it really needs to. Body of that loop is almost empty. Enforcing ui update after every iteration would just artificially slow it down. Usually you want to update ui when some state changes. In this case it's when a process is started, and yes, it happens after the execution leaves your code and gets back to the Qt's event loop.

                        There is a way to use your original code and "force" an update, but that's imho a bad design and I won't suggest how to do that unless you sign the fine print saying "I hereby want to willingly shoot myself in the foot".

                        VRoninV Offline
                        VRoninV Offline
                        VRonin
                        wrote on last edited by VRonin
                        #11

                        @Chris-Kawa said in Refresh display of QTableWidget:

                        There is a way to use your original code and "force" an update, but that's imho a bad design and I won't suggest how to do that unless you sign the fine print saying "I hereby want to willingly shoot myself in the foot".

                        the way is to add QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); inside the loop but I agree is not the best design

                        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                        ~Napoleon Bonaparte

                        On a crusade to banish setIndexWidget() from the holy land of Qt

                        1 Reply Last reply
                        1
                        • J Offline
                          J Offline
                          jcga
                          wrote on last edited by
                          #12

                          Thanks once more for your answers. Let me be a little bit more specific on what I'a trying to do, and why. As indicated in the sample code I provided, my program calls inside a for loop an external program. This exeternal program essentially reads data from a device, format them and save them to files, on file per loop execution. The tablewidget displays information about the file and the data saved. As reading data from the device can be relatively long (tens of seconds to few minutes), I would like the user to have some feedback on the advancement of the process by seeing the rows display progressively on the table. Thats is why I would like that added table rows become visible to the user during the execution of the loop, not only after. Anyway I also tried to add to my widget a QLabel playing the role of a status bar to indicate what the program is currently doing and I have exaclty the same problem, the QLabel do not updated its text inside the loop, but only after the loop is complete.

                          I'll try the solution proposed by VRonin, evec if not "best design", but I'm affraid it can have some undesirable side effects.

                          1 Reply Last reply
                          0
                          • Chris KawaC Offline
                            Chris KawaC Offline
                            Chris Kawa
                            Lifetime Qt Champion
                            wrote on last edited by Chris Kawa
                            #13

                            I wish VRonin haven't suggested that because that's the easy but messy way out. It leads to poor code that doesn't scale. Soon you'll see problems like something that you wouldn' t want to happen during the loop happens because it is processed when some signal fires etc. You'll design locks and flags around the problem, they will keep interfering with each other, you will add more bloat and the code will become unreadable mess. I've seen this all too many times. The problem is in the larger design of that piece of code.

                            You shouldn't do long operations in a loop in the ui thread. The loop should just start the tasks and exit. The tasks can take long time and they should signal you when they are started or done.
                            If the tasks are external programs then it's easy. Just start all the processes and connect to started/finished signals to update your ui to inform the user. If the tasks are some code in your own app that takes time to execute start them in separate threads and signal when they're done (QtConcurrent might be of use to you). There's also a tasking solution - you put tasks into a queue in a loop and resume execution and worker threads pick up the tasks from the queue and signal their progress/state.

                            1 Reply Last reply
                            3
                            • J Offline
                              J Offline
                              jcga
                              wrote on last edited by
                              #14

                              So I have tested VRonin's suggestions. It works, the program reacts as I wanted it to, and for the moment I have not noted any undesirable side-effect, thanks a lot. I would like to tray Chris' suggestion in his last message, but I don't master multithreading programming, so I have not understood how to implement it. If Chris has some time to spent indicating roughly the way to do it (specially, what function to call, or where to start reading the doc), I can try to find the details in the documentation myself.

                              jclaude

                              VRoninV 1 Reply Last reply
                              0
                              • J jcga

                                So I have tested VRonin's suggestions. It works, the program reacts as I wanted it to, and for the moment I have not noted any undesirable side-effect, thanks a lot. I would like to tray Chris' suggestion in his last message, but I don't master multithreading programming, so I have not understood how to implement it. If Chris has some time to spent indicating roughly the way to do it (specially, what function to call, or where to start reading the doc), I can try to find the details in the documentation myself.

                                jclaude

                                VRoninV Offline
                                VRoninV Offline
                                VRonin
                                wrote on last edited by
                                #15

                                @jcga This https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ should be a good place to start

                                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                                ~Napoleon Bonaparte

                                On a crusade to banish setIndexWidget() from the holy land of Qt

                                1 Reply Last reply
                                0

                                • Login

                                • Login or register to search.
                                • First post
                                  Last post
                                0
                                • Categories
                                • Recent
                                • Tags
                                • Popular
                                • Users
                                • Groups
                                • Search
                                • Get Qt Extensions
                                • Unsolved