user975326
user975326

Reputation: 657

What is the correct way to display widgets without calling QApplication::exec()?

For test purposes I'd like to create and display a widget. For now I only need the widget to render correctly but in the future I may want to extend this so I simulate various events to see how the widget behaves.

From various sources it would appear that the following should work:

QApplication app;

QPushButton button("Hello");
button.show();

// Might also be necessary:
QApplication::processEvents();

But for me the widget does not render correctly. A window is created to display the widget, however it is entirely black.

I can get the widget to render correctly by adding the following lines:

std::this_thread::sleep_for(std::chrono::milliseconds(10));
QApplication::processEvents();

With 10 milliseconds being about the smallest time necessary to get the widget to render correctly.

Does anyone know how to get this to work without the time delay, or know why the delay is necessary?

Upvotes: 3

Views: 915

Answers (3)

ypnos
ypnos

Reputation: 52367

As expertly described by Vincent Fourmond, widgets are not a one-off deal. The GUI is non-blocking and for this, it needs to run in an event loop.

The exec() method starts this event loop which you mimicked by polling. While it is possible to combine Qt's event loop with other event loops, I would recommend you a simpler solution:

Proceed your program within the event loop by calling a method when it starts. Find an excellent answer here on how to do this: https://stackoverflow.com/a/8877968/21974

As you mentioned unit testing, there is also a signal you can use for doing checks at the end of the lifecycle (before widgets are destroyed): QApplication::aboutToQuit. This will happen when the last window is closed (programmatically or by the user).

Upvotes: 0

pptaszni
pptaszni

Reputation: 8303

To test Qt GUI application you need at least QApplication instance and event loop being processed. The fastest way is just use QTEST_MAIN macro, this answer explains in a nice way what it does exactly. However, to have more flexibility (e.g. to use GTest, GMock) you can also simply create QAplication instance in your tests main (no need to call exec).

Then, to have the events processed, you should invoke QTest::qWait. This will process your events for the specified amount of time. It is a good practice to use qWaitFor which accepts a predicate - this way you avoid race conditions in your tests.

In the particular scenario, when you expect some signal to be emitted, you can also use similar functionality of QSignalSpy::wait.

Small example when we want to wait until some parameters are passed from one item to another:

QSignalSpy spy(&widget1, &Widget1::settingsChanged);
widget2->applySettings();
ASSERT_TRUE(spy.wait(5000));
// do some further tests based on the content of passed settings

Upvotes: 4

Vincent Fourmond
Vincent Fourmond

Reputation: 3278

Why don't you want to have the application run exec ? The process of displaying a widget is not "static". You don't "draw" the widget on the screen, but rather you have an application that listen for various events and receives draw events from the windowing manager. The application can only draw the widget when the windowing manager asks it to.

The reason your second code works is that you wait sufficiently long for the windowing manager to have sent the "draw" request in your conditions. This does not guarantee it will always work.

If you want to guarantee the display of the widget, you need to start a loop and wait until you have received at least one draw event, but even that isn't foolproof.

Upvotes: 1

Related Questions