0

It feels like this question has been asked about a hundred times before (e.g. here) but I haven't found a working solution yet..

I have a Qt5 program (Linux) which takes some time (about 2sec) for initialization. I don't want to spawn a thread (for several reasons) and before initialization is done the program is not usable anyway.

Currently the program starts and it shows a black window, until initialization is done.

I'd like to have the window content be drawn as soon as possible and queue a method which does the rest which gets executed right after the main window has been drawn.

This is what I tried:

class my_window : public QMainWindow {
    Q_OBJECT
    explicit my_window(QWidget *parent = 0) : QMainWindow(parent) {

        initializeUI();

        /// UI is ready and should be drawn. initializeRest() should 
        /// be queued 

        /// tried to repaint() or update() the MainWindow and to 'force'
        /// processing outstanding events - with no effect
        update();
        repaint();
        QApplication::processEvents();

        /// don't call - just queue 
        QMetaObject::invokeMethod(this, "initializeRest", Qt::QueuedConnection);
    }

    void initializeRest() {
        // do stuff which takes a while
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

But the main window stayes black until initializeRest() has been executed.

How can I tell Qt to execute initializeRest() right after the window has been constructed?

I can think of starting a timer (bad, introduces extra latency) or an event handler which reacts on some kind of "WindowDrawn" event (bad, complicated).

What's the Qt-way to do this?

Update:

I've also tried to put the initializeRest() method into the main() function like suggested by Murphy:

my_window::my_window(QWidget *parent = 0) : QMainWindow(parent) {
    initializeUI();
}

int main(int a_argsc, char *a_argsv[]) {
    QApplication l_application(a_argsc, a_argsv);
    my_window mainWindow;
    mainWindow.show();
    QApplication::processEvents();
    mainWindow.initializeRest();
    return l_application.exec();
}

With same results: Waiting for a couple of seconds inside initializeRest() makes show up the initially black main window and be drawn right after initializeRest() returned (which seems to be logical to me because the event loop has not been started yet..)

Community
  • 1
  • 1
frans
  • 8,868
  • 11
  • 58
  • 132
  • 3
    I would suggest having a splash before showing the main window that can give you some time while loading , is that appropriate for you? – Mike Jun 09 '16 at 19:57
  • Well - that might be an option and I would be more what users expect. Currently it would feel a bit like giving up :). I'd really like to know what I should do to invoke this function after showing the UI. – frans Jun 09 '16 at 20:55
  • *before initialization is done the program is not usable anyway.* , so why would you show a window that is not usable for some seconds? I think a splash screen is much more convenient for such a situation. . . – Mike Jun 10 '16 at 10:03
  • You're right - that's the way I will do it and that's why I've upvoted your comment. I just want to know how to convince Qt to just draw a window and run a method *afterwards*. – frans Jun 10 '16 at 10:15

2 Answers2

1

Note: This suggestion doesn't solve the issue; it's left here for completeness.

You can split the startup into smaller steps in main():

  1. Create the QApplication instance.
  2. Instantiate the main window (I'll call the variable mainWindow here). You can safely remove all that repaint-workaround stuff after initializeUI(); from the constructor of your code example.
  3. Call mainWindow.show() to enforce showing the main window, followed by a call to QApplication::processEvents() to enforce the paint events being handled.
  4. Do all the other initialization stuff of your application.
  5. Start the event loop as usual by calling QApplication::exec().

Be aware that with complex applications/main window implementations it can get quite hairy to do everything in the right order; a QSplashScreen would surely be the less tedious solution.

Murphy
  • 3,827
  • 4
  • 21
  • 35
  • Unfortunately this is just calling the `initializeRest()` method from another function. I tried it without success. When I put a brakepoint at the beginning of `initializeRest()` I can still see a black window which hasn't been drawn yet. – frans Jun 10 '16 at 12:59
  • @frans No, it isn't, because you don't call `mainWindow.show()` anywhere. What exactly did you try? Please add it to your question. – Murphy Jun 10 '16 at 13:04
  • I've updated my post - maybe I misunderstood something? – frans Jun 10 '16 at 13:18
  • @frans No, you didn't; that's how it works here for a main window derived from `QDialog` in Qt4 - perhaps there's a difference in handling `QMainWindow` instances when setting it to visible prior to running the event loop? Sorry I can't help you here. – Murphy Jun 10 '16 at 13:27
  • Maybe behavior changed from Qt4 to Qt5. I can't remember having trouble like this.. – frans Jun 10 '16 at 13:29
0

I have the same problem. I think the problem is based not on show() function. Try to run the next code.

If add a button to boxLayout the application starts fast. But if I try setGeometry() the application takes a long time to start.

Narann
  • 819
  • 2
  • 8
  • 20
  • Please include your code as **text** (properly formatted as a code block) - not as a linked image. – Adrian Mole Sep 15 '21 at 15:36
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-ask). – Community Sep 15 '21 at 22:03