Function after setupUi()



  • Hello, I would like to show progress bar (in Dialog) during app initialization (connecting to server, checking for updates, etc..).

    Problem is that if I create any logic in Dialog, then UI is displayed after logic in Dialog.

    mainwindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include "QThread"
    #include "QTimer"
    
    void MainWindow::AfterUI() {
    
        //Logic after UI
        QThread::sleep(5);
    }
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        QTimer::singleShot(0, this, SLOT(AfterUI()));
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    

    mainwindow.h:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    
    namespace Ui {
    class MainWindow;
    }
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
    public slots:
        void AfterUI();
    
    private:
        Ui::MainWindow *ui;
    };
    
    #endif // MAINWINDOW_H
    

    You can see that in function called AfterUI() I have:

    QThread::sleep(5);
    

    So UI is displayed after sleep function, but I want to display UI before function AfterUI().

    Thank you very much :)


  • Qt Champions 2016

    The UI is displayed when the paint events are processed (which is when the main event loop is spun). So if you block the main event loop (like using QThread::sleep, no events are processed and nothing is displayed. You need to fix your code, so you don't block the event loop.



  • So how can I create function which is called after all widgets are displayed?


  • Qt Champions 2016

    You can't because you're assuming you can know when the last paint event is going to be dispatched and which paint event is "last", which is equivalent to asking a psychic about the future. We are talking event-driven programming here, so just respond to the relevant events. What is it that you want to do in that function anyway?



  • I wonder, would using threads help in this case?



  • @kshegunov said in Function after setupUi():

    You can't because you're assuming you can know when the last paint event is going to be dispatched and which paint event is "last", which is equivalent to asking a psychic about the future. We are talking event-driven programming here, so just respond to the relevant events. What is it that you want to do in that function anyway?

    In function will be connecting to server, getting data from server, parsing data, etc. So I would like to create Dialog with progressbar like loader, which shows you what is program currently doing.

    It is simple loader window, lot of apps use this.

    I am looking for Form_Load function like in C#


  • Qt Champions 2016

    @t0msk
    To be sure main window is fully shown and then app will popup a Dialog over it then you can use

    void MainWindow::showEvent( QShowEvent* event ) {
        QMainWindow::showEvent( event );
        //your code here
    
    } 
    


  • @mrjj

    It doesnt work

    I added into mainwindow.cpp:

    void MainWindow::showEvent( QShowEvent* event ) {
        QMainWindow::showEvent( event );
        //your code here
    
        QThread::sleep(10);
    
        ui->progressBar->setValue(80);
    }
    

    and mainwindow.h:

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        void showEvent(QShowEvent* event);
    

    Problem is that, mainwindow shows after 10 seconds.


  • Qt Champions 2016

    @t0msk

    Hi
    Do not use QThread::sleep(10);
    You Freeze whole appfor 10 secs... :)


  • Qt Champions 2016

    @t0msk said in Function after setupUi():

    I am looking for Form_Load function like in C#

    I don't know C#, but from a quick search it appears you want to put that code in the constructor after the setupUi call. Nothing is shown until the events are processed, so I don't see why you want to know when the window's shown. Just create your dialog and call show() on it (don't call exec() as it will block the event loop). Connect the signals and slots as appropriate for the case. The data fetching and/or processing you can offload to a worker thread (for example take a look at the concurrent framework).

    @mrjj said in Function after setupUi():

    To be sure main window is fully shown and then app will popup a Dialog over it then you can use

    True, however you are probably going to get multiple show events, so then that part of the code would be executed multiple times. Additionally, you may not have a correct geometry at the time as there's no specific documented order of events; it all will depend on the underlying window manager (although from my experience one gets the show event last at least for Win and X11).



  • @mrjj said in Function after setupUi():

    @t0msk

    Hi
    Do not use QThread::sleep(10);
    You Freeze whole appfor 10 secs... :)

    Is there something else that sleep? Is there for example wait? Because sleep can means that app will freeze, but thread is locked in your app, something like mutex if you want to free thread.

    @kshegunov

    My goal is that app will show Dialog where is progress bar and after all data fetching/processing is shown mainwindow and dialog closed.


  • Qt Champions 2016

    @t0msk said in Function after setupUi():

    Is there something else that sleep?

    The point is you should not sleep.

    My goal is that app will show Dialog where is progress bar and after all data fetching/processing is shown mainwindow and dialog closed.

    Fine, so why have a main window at all. Also this in no way contradicts what I wrote, just call show() on whatever widget you want to show and connect the signals and slots appropriately.



  • ok, so I created quite hard function for CPU, without sleep:

    void MainWindow::showEvent( QShowEvent* event ) {
        QMainWindow::showEvent( event );
        //your code here
    
        ui->progressBar->setValue(20);
    
        int i;
        double result;
        QString string;
    
        for(i = 0;i < 5000000;i++) {
    
            result = ((((i * 1337) / 7) * 3) - 5) % 1937;
    
            string = QString::number(result);
    
            ui->label->setText(string);
        }
    
        ui->progressBar->setValue(80);
    }
    

    It took some seconds, BUT window displayed after math, so I cannot see initial value of progress bar (20). Sorry but I am confused.


  • Qt Champions 2016

    Here, however I provide no text here as instruction. You need to read what an event loop is, how it relates to the GUI, what blocking the event loop means and how one can process things without blocking the event loop (multiple topics all available in the documentation).

    #include <QApplication>
    #include <QProgressDialog>
    #include <QTimer>
    
    int main(int argc, char ** argv)
    {
        QApplication application(argc, argv);
    
        QTimer timer;
        timer.setInterval(100); //< Simulates a long running operation in the background
        timer.start();
    
        QProgressDialog dialog(QStringLiteral("Test dialog"), QStringLiteral("Cancel"), 0, 100);
        dialog.show();
    
        int counter = 0;
        QObject::connect(&timer, &QTimer::timeout, &dialog, [&dialog, &timer, &counter] () -> void {
            counter++;
            if (counter >= dialog.maximum())  {
                timer.stop();
                dialog.close();
                return;
            }
    
            dialog.setValue(counter);
        });
    
        QObject::connect(&dialog, &QProgressDialog::canceled, &timer, &QTimer::stop);
        QObject::connect(&dialog, &QProgressDialog::canceled, &application, &QApplication::quit);
    
        return QApplication::exec();
    }
    


  • Hi,

    I would suggest looking into QSplashScreen it might me the class for your problems.

    If I understand it correctly, you want to paint the gui, and than initialize the rest of your program displayed by a progressbar.

    Simplest way to do this would be to call

    QTimer::singleShot(10,this,&YourClass::SetupWithProgressbar);
    

    out of your constructor, right after

    ui->setupUi(this);
    

    but, if your SetupCode is in the same thread as your progressbar, the pbar will most likely not update itself correctly. You'll have to do that manualy.

    I hope this helps.



  • @J.Hilk said in Function after setupUi():

    If I understand it correctly, you want to paint the gui, and than initialize the rest of your program displayed by a progressbar.

    Exactly!

    So my code now looks like this:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    #include "QTimer"
    #include "QThread"
    
    void MainWindow::SetupWithProgressbar() {
    
        ui->progressBar->setValue(20);
    
        int i;
        double result;
        QString string;
    
        for(i = 0;i < 5000000;i++) {
    
            result = ((((i * 1337) / 7) * 3) - 5) % 1937;
    
            string = QString::number(result);
    
            ui->label->setText(string);
        }
    
        ui->progressBar->setValue(80);
    }
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        QTimer::singleShot(10, this, MainWindow::SetupWithProgressbar());
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    

    But I am getting an error:

    error: invalid use of void expression
         QTimer::singleShot(10, this, MainWindow::SetupWithProgressbar());
                                                            ^
    

    I think it is correct.

    @kshegunov said in Function after setupUi():

    Here, however I provide no text here as instruction. You need to read what an event loop is, how it relates to the GUI, what blocking the event loop means and how one can process things without blocking the event loop (multiple topics all available in the documentation).

    #include <QApplication>
    #include <QProgressDialog>
    #include <QTimer>
    
    int main(int argc, char ** argv)
    {
        QApplication application(argc, argv);
    
        QTimer timer;
        timer.setInterval(100); //< Simulates a long running operation in the background
        timer.start();
    
        QProgressDialog dialog(QStringLiteral("Test dialog"), QStringLiteral("Cancel"), 0, 100);
        dialog.show();
    
        int counter = 0;
        QObject::connect(&timer, &QTimer::timeout, &dialog, [&dialog, &timer, &counter] () -> void {
            counter++;
            if (counter >= dialog.maximum())  {
                timer.stop();
                dialog.close();
                return;
            }
    
            dialog.setValue(counter);
        });
    
        QObject::connect(&dialog, &QProgressDialog::canceled, &timer, &QTimer::stop);
        QObject::connect(&dialog, &QProgressDialog::canceled, &application, &QApplication::quit);
    
        return QApplication::exec();
    }
    

    Isnt it quite complicated for such simple thing? :/



  • @t0msk said in Function after setupUi():

    error: invalid use of void expression
         QTimer::singleShot(10, this, MainWindow::SetupWithProgressbar());
                                                            ^
    

    You're using the wrong syntax,

    Use ne new one

    QTimer::singleShot(10, this, &MainWindow::SetupWithProgressbar);
    

    or the old one

    QTimer::singleShot(10, this, SLOT(SetupWithProgressbar()));
    

    If you mix them, the compiler will complain :)



  • @J.Hilk said in Function after setupUi():

    @t0msk said in Function after setupUi():

    error: invalid use of void expression
         QTimer::singleShot(10, this, MainWindow::SetupWithProgressbar());
                                                            ^
    

    You're using the wrong syntax,

    Use ne new one

    QTimer::singleShot(10, this, &MainWindow::SetupWithProgressbar);
    

    or the old one

    QTimer::singleShot(10, this, SLOT(SetupWithProgressbar()));
    

    If you mix them, the compiler will complain :)

    Ah yes there is newer syntax :D So app was compiled successfully, but window displayed without UI (app freezes until math was completed), so i didint see "changing" progress bar.


  • Qt Champions 2016

    @t0msk
    well as long as you keep block the app , it will freeze like that

    When you do
    for(i = 0;i < 5000000;i++) {

    You kill the event loop and hence nothing else work.

    An ugly and not recommended way is to call
    QCoreApplication::processEvents()
    in such loops but its not good design and not needed if you stop blocking it with loops.



  • @mrjj said in Function after setupUi():

    @t0msk
    well as long as you keep block the app , it will freeze like that

    When you do
    for(i = 0;i < 5000000;i++) {

    You kill the event loop and hence nothing else work.

    An ugly and not recommended way is to call
    QCoreApplication::processEvents()
    in such loops but its not good design and not needed if you stop blocking it with loops.

    I know that I am blocking app, but I dont know how can I create some "logic" (because every logic will occupy CPU) and display it by a progressbar.


  • Qt Champions 2016

    @t0msk

    Ok. so u know that the LOOP is the reason it do not work and you see NO change in the progress bar ?

    Anyway, what is wrong with @kshegunov code ?
    its 100% non blocking.



  • @mrjj said in Function after setupUi():

    @t0msk

    Ok. so u know that the LOOP is the reason it do not work and you see NO change in the progress bar ?

    Anyway, what is wrong with @kshegunov code ?
    its 100% non blocking.

    Because I dont understand it, why there is timer? why is he setting a value into dialog and what is it mean?, I am newbie in Qt, so I am looking for simpler solution.

    I used this in C# and it worked out of box.


  • Qt Champions 2016

    @t0msk
    His example just makes the dialog have a counter (progress) as to simulate something is going on. like
    downloading a patch.
    The timer will post a "timeout" to the event loop ( which in this case is NOT blocked) and
    the dialog can update and "do stuff"

    The syntax
    "QObject::connect(&timer, &QTimer::timeout, &dialog, [&dialog, &timer, &counter] () ->"
    just creates a function in the spot. So all inside
    {
    // normal slot code.
    }
    Is just the code you would put in a normal slot function.
    This allows for the logic to be in main and not inside mainwindow.

    So the timer allows to draw and update the Progress while for loops do not as easy.

    Hope this makes it more clear.



  • I personaly would approach this situation differently from what was suggestet so far.

    The "proper" way would be to put your "CPU-heavy function" in a different thread and use Signal/Slots to update the UI.

    something along the lines:

    *.h:

    signals:
        void pBarSetValue(int value);
        void showResult(QString result);
    

    *.cpp

    ui->setupUi(this);
    
    connect(this, &MyClass::pBarSetValue, ui->progressBar, QProgressBar::setValue);
    connect(this, &MyClass::showResult, ui->label, &QLabel::setText);
    
    ui->progressBar->setRange(0,5000000);
    
    
    QtConcurrent::run([=]() {
        for(i = 0;i < 5000000;i++) {
    
              double dResult = ((((i * 1337) / 7) * 3) - 5) % 1937;
    
              emit showResult(QString::number(dResult));
              emit pBarSetValue(i);
        }
    }
    

  • Qt Champions 2016

    @t0msk said in Function after setupUi():

    Isnt it quite complicated for such simple thing? :/

    You have a working example in 15-20 lines, so what is the complicated part?

    Because I dont understand it, why there is timer?

    Because for the example one needs to simulate a long-running operation (as the comment states). In real code the timer would be substituted with a thread to offload the GUI thread and that thread would raise a signal (just like QTimer::timeout) which notifies the GUI thread about the progress.

    I am newbie in Qt, so I am looking for simpler solution.

    Eh? Can't get simpler than this. You need to read the topics I mentioned. Did you?

    I used this in C# and it worked out of box.

    Perhaps that's true, I have no knowledge of C# but I really don't see the relevance here. Qt is a C++ library, so if you want to work with C++ you need to stick to C++'s specifics - memory management, threading, etc.

    @J.Hilk said in Function after setupUi():

    The "proper" way would be to put your "CPU-heavy function" in a different thread and use Signal/Slots to update the UI.

    It would.

    something along the lines

    Your code has one serious drawback, however - you can't stop the operation in the middle if you need for example to quit the application. For this to be feasible one needs to ensure the worker thread's event loop isn't blocked.



  • @mrjj said in Function after setupUi():

    @t0msk
    His example just makes the dialog have a counter (progress) as to simulate something is going on. like
    downloading a patch.
    The timer will post a "timeout" to the event loop ( which in this case is NOT blocked) and
    the dialog can update and "do stuff"

    The syntax
    "QObject::connect(&timer, &QTimer::timeout, &dialog, [&dialog, &timer, &counter] () ->"
    just creates a function in the spot. So all inside
    {
    // normal slot code.
    }
    Is just the code you would put in a normal slot function.
    This allows for the logic to be in main and not inside mainwindow.

    So the timer allows to draw and update the Progress while for loops do not as easy.

    Hope this makes it more clear.

    Must it be in main? I would like to have code from mainwindow in mainwindow.cpp not in main

    @J.Hilk said in Function after setupUi():

    I personaly would approach this situation differently from what was suggestet so far.

    The "proper" way would be to put your "CPU-heavy function" in a different thread and use Signal/Slots to update the UI.

    something along the lines:

    *.h:

    signals:
        void pBarSetValue(int value);
        void showResult(QString result);
    

    *.cpp

    ui->setupUi(this);
    
    connect(this, &MyClass::pBarSetValue, ui->progressBar, QProgressBar::setValue);
    connect(this, &MyClass::showResult, ui->label, &QLabel::setText);
    
    ui->progressBar->setRange(0,5000000);
    
    
    QtConcurrent::run([=]() {
        for(i = 0;i < 5000000;i++) {
    
              double dResult = ((((i * 1337) / 7) * 3) - 5) % 1937;
    
              emit showResult(QString::number(dResult));
              emit pBarSetValue(i);
        }
    }
    

    Thank you, so if I understood correctly, emit will "send" signal to function pBarSetValue() and connect will "catch" this signal with value and redirect it to QProgressBar::setValue() ? :)

    And QtConcurrent::run() will run function in new thread, yes? So it is same solution like this? :

    QThread thread;
    moveToThread(&thread);
    connect(&thread, SIGNAL(started()), this, SLOT(myfunction()));
    thread.start();
    

    Another question what syntax is this QtConcurrent::run( [ = ] () ? I have never seen anything like this before, and I got an error at the end of function:

    error: expected ')' before '}' token
     }
     ^
    

    The last question what happens if computer has only 1 thread CPU?


  • Qt Champions 2016

    @t0msk said in Function after setupUi():

    Must it be in main? I would like to have code from mainwindow in mainwindow.cpp not in main

    Of course not. I had put it in main only to provide a fully self-contained example. You could put it wherever it suits you as long as the function is executed in the context of the main thread.

    Thank you, so if I understood correctly, emit will "send" signal to function pBarSetValue() and connect will "catch" this signal with value and redirect it to QProgressBar::setValue()?

    pBarSetValue is the actual signal. Look here: http://doc.qt.io/qt-5/signalsandslots.html

    And QtConcurrent::run() will run function in new thread, yes?

    Yes.

    So it is same solution like this?

    It's very similar, yes.

    Another question what syntax is this QtConcurrent::run( [ = ] () ?

    It's a lambda (anonymous) function. It's part of the C++11 standard. Look here: http://en.cppreference.com/w/cpp/language/lambda

    I got an error at the end of function

    Because there's a typo at the end of the code - it doesn't close the function arguments' parenthesis. It should be like this:

    QtConcurrent::run([=] () {
        // ...
    });
    

    The last question what happens if computer has only 1 thread CPU?

    If you mean you have a single core CPU, then it's the OS's responsibility to switch between the threads (so called context switches) and execute things (by things I mean the assembly) sequentially, but this doesn't directly affect you. From your perspective as a programmer it still looks like the threads are executing in parallel.


  • Moderators

    @t0msk said in Function after setupUi():

    connect will "catch" this signal with value and redirect it to QProgressBar::setValue()

    No, connect does not catch anything. Connect connects a slot to a signal. After connecting if you emit the signal all connected slots will be executed.



  • @kshegunov

    Thank you for exaplanation :)

    So I used QtConcurrent::run function, and I works as I want (progressbar is changing in realtime) BUT there is another problem (obviously), it is changing but mainwindow is not responding and if I click somewhere in window it just freeze until work thread finishes his job (why is it happening? because "main" thread should handle UI and work thread should handle function, so I dont know why UI is unresponsive, and how can it be fixed?).

    And what is disadvanatge of using QtConcurrent::run method? You said that if I use it, I cant stop worker thread in middle of operation? And if I add any "signal" to stop?

    No I dont have single core CPU, but I just wondered what happens if computer has 1 core / 1 thread CPU, so if it is ok, no problem :D


  • Qt Champions 2016

    @t0msk said in Function after setupUi():

    why is it happening? because "main" thread should handle UI and work thread should handle function, so I dont know why UI is unresponsive, and how can it be fixed?

    Probably flooding the main thread's event loop.

    And if I add any "signal" to stop?

    I don't follow. Add a stop signal where? You run a function imperatively with the proposed QtConcurent::run (as C++ is an imperative language), you can't just break in the middle of it ...



  • @kshegunov said in Function after setupUi():

    Probably flooding the main thread's event loop.

    Okey I optimized a code little bit:

    QtConcurrent::run([=]() {
    
           int i;
           int value;
           double dResult = 1;
    
           for(i = 0;i < 20000000;i++) {
    
                 dResult = qExp(qCos(qTan(qSin(qPow(qSqrt(((((i * 1337) / 7) * 73) * 1329) % 1937),7) * dResult)) / qAsin(qPow(qSin(dResult * i * qTan(1337 * i)), 29))));
    
                 if((i % 200000) == 0) {
                     value = i / 200000;
                     emit showResult(QString::number(dResult));
                     emit pBarSetValue(value);
                 }
    
           }
    
           emit showResult(QString::number(dResult));
           emit pBarSetValue(100);
       });
    

    It tooks few seconds for my CPU, but yea UI is responsible and progressbar is changing in realtime

    Problem was because

    emit showResult(QString::number(dResult));
    emit pBarSetValue(i);
    

    runs 5000000 times

    I don't follow. Add a stop signal where? You run a function imperatively with the proposed QtConcurent::run (as C++ is an imperative language), you can't just break in the middle of it ...

    I dont know, maybe that Quit Application signal will tell to OS scheduler that scheduler has to kill worker thread

    So only disadvantage of that method is that I cannot stop worker thread in middle of operation?

    //EDIT, thanks for help :)


Log in to reply
 

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