I want to create something beautiful, but event processing doesn't let me!



  • Heh, as the title says, I want to create something that I would like very much as a user of my app (LOL).

    This is rather simple: I have a QProgressBar and a QTextBrowser that I want to update during a process.
    The part of the code doing this is the following:
    [code]
    //'results' is the text browser and 'rotate_progress' is the progressbar, these are hidden, so show them
    ui->results->show();
    ui->rotate_progress->show();
    //'imgs' is a QStringList
    int imgs_count=imgs.count();
    if(!imgs_count){
    ui->results->append("<center><b><font size=50 color=red>"+tr("Image list is empty!")+"</font></b></center>");
    return;
    }
    ui->results->append("<b>"+tr("Skipping images without EXIF data")+"</b>");

    QFileInfo info;
    QString extension;
    for(int i=0;i<imgs_count;i++){
        ui->rotate_progress->setValue((i+1)*100/imgs_count);
        if(QFile&#40;imgs.at(i&#41;&#41;.exists(&#41; && !QImage(imgs.at(i&#41;).isNull()){
            info.setFile&#40;imgs.at(i&#41;&#41;;
            extension=info.suffix(&#41;;
            if(extension!="JPEG" && extension!="JPG" && extension!="jpeg" && extension!="jpg"&#41;{
                //here doing some stuff to remove non-wanted images, unrelated to the problem
            }
        }
        else
        {
              //here doing some stuff to remove non-wanted images, unrelated to the problem
        }
    }
    //now imgs stringlist contains only valid images with the jpeg extension.
    for(int i=0;i<imgs_count;i++){
        //outputing all valid images...
        qDebug() << imgs.at(i);
    }
    

    [/code]

    This works just fine, at the non-GUI part of it, the above code is called when a button is pressed.
    Well, when I press this button the program stucks for a while (mainly because the 'imgs' QStringList is quite big) and then, it does all the above things considering the GUI, altogether:
    It shows the progressbar with a value of 100% and the textbrowser with the text "Skipping images without EXIF data".
    The question is, how do I update the GUI so as to show all these changes in 'live' time? I mean, when I call the show(); function, the widgets really to be shown and when I call the setValue() in the progressbar to be updated at this moment? E.g., I never saw how the progressbar's value changed, I only saw it be 100%, never how it got there.

    I've heard of QApplication::processEvents(); but it doesn't work, I don't know why.

    This is an old issue for me that I've still to find a solution, and it bothers me that it is a barrier for me.

    Any help appreciated; thanks.



  • QApplication::processEvents() really is one way to make this work. Another is to offload the heavy lifting to a separate thread, or better yet, several separate threads using QtConcurrent.



  • Hello Andre, I've tried QApplication::processEvents() with no luck so far.
    I don't know if I'm using it right, but I place it right after calling the show(); function for each of the widgets and right after I call the setValue(); for the progressbar. Shouldn't this work?

    Also, I know how to use QtConcurrent and I just tested it, it doesn't work. Now my code looks like:
    [code]
    void Extras::set_progress_value(int progress){
    QCoreApplication::processEvents();
    ui->rotate_progress->setValue(progress);
    QCoreApplication::processEvents();
    }

    void Extras::grab_images(QStringList imgs){
    QCoreApplication::processEvents();
    int imgs_count=imgs.count();
    if(!imgs_count){
    ui->results->append("<center><b><font size=50 color=red>"+tr("Image list is empty!")+"</font></b></center>");
    return;
    }
    QCoreApplication::processEvents();
    ui->results->append("<b>"+tr("Skipping images without EXIF data")+"</b>");
    QCoreApplication::processEvents();

    QFileInfo info;
    QString extension;
    for(int i=0;i<imgs_count;i++){
        QCoreApplication::processEvents();
        QtConcurrent::run(this, &Extras::set_progress_value, (i+1)*100/imgs_count);
        QCoreApplication::processEvents();
        if(QFile&#40;imgs.at(i&#41;).exists() && !QImage(imgs.at(i)).isNull()){
            info.setFile&#40;imgs.at(i&#41;);
            extension=info.suffix();
            if(extension!="JPEG" && extension!="JPG" && extension!="jpeg" && extension!="jpg"){
                //random, mad stuff here xD
            }
        }
    }
    //now imgs stringlist contains only valid images with jpeg extension.
    

    }
    [/code]

    The problem resists! As you see, I call processEvents as usually as possible ! Also I do update the value of the progressbar on a separate thread using the qtconcurrent::run.

    By having no idea how to solve this problem, I literally cannot use progressbars in my programs.

    EDIT:
    Even with using 'qApp->processEvents( QEventLoop::AllEvents );', still nothing :/



  • You cannot update the progressbar from a second thread using QtConcurrent. That is nonsense. Updating the GUI from a non-gui thread is not going to work, and the whole point of using QtConcurrent is to offload the actual work to your threads, not just updating the progress bar.

    I can guarantee you that both methods, QtConcurrent and processEvents() work. If they don't, you are not using them correctly.

    What you need to understand, is what is needed for a progressbar to work. If its value is changed, it will use the eventqueue to request a repaint update for itself. While your loop is running, the eventloop is not processing events, and thus the progressbar will not get its repaint events. So, one way or another, to keep your UI responsive, you need to make sure your eventloop keeps running.

    • processEvents() can be understood as manually giving the eventloop a spin: the currently waiting events in the queue (matching the flags you used) will be processed, nothing more;
    • using (a) separate thread(s) to do the heavy lifting will make sure the eventloop in the GUI thread can spin continuously;
    • chopping the work up in small tasks and using 0-timers to trigger processing of the next part of the task is another way, as after each task you return to the event loop.

    Take your pick. Each of these work, as each of them makes sure the eventloop continues to spin. They have been proven to work in many, many real-life applications, and I have used each of them at different times.

    Your current use of QtConcurrent is nonsensical. You need to remove it. Also, it is enough to call processEvents() once per loop, as long as each iteration of your loop doesn't take more than, say, 1/10th of a second, you won't notice the difference.



  • Oh, thanks a lot Andre, I can now understand better. Sorry for being a bit mulish :P

    All now work like a charm, the user can even press a button while the process is running :D Perfect :D


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.