Jaki jest prawidłowy sposób wyświetlania widgetów bez wywoływania QApplication :: exec ()?

Nov 20 2020

Do celów testowych chciałbym stworzyć i wyświetlić widżet. Na razie potrzebuję widżetu tylko do poprawnego renderowania, ale w przyszłości mogę chcieć to rozszerzyć, więc symuluję różne zdarzenia, aby zobaczyć, jak zachowuje się widget.

Z różnych źródeł mogłoby się wydawać, że powinny działać:

QApplication app;

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

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

Ale dla mnie widget nie renderuje się poprawnie. Zostanie utworzone okno do wyświetlania widgetu, jednak jest ono całkowicie czarne.

Mogę sprawić, by widżet renderował się poprawnie, dodając następujące wiersze:

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

10 milisekund to mniej więcej najmniejszy czas potrzebny do prawidłowego renderowania widżetu.

Czy ktoś wie, jak sprawić, by to działało bez opóźnienia, czy wie, dlaczego opóźnienie jest konieczne?

Odpowiedzi

4 pptaszni Nov 20 2020 at 17:13

Aby przetestować aplikację Qt GUI, potrzebujesz przynajmniej QApplicationprzetwarzanej instancji i pętli zdarzeń. Najszybszym sposobem jest użycie QTEST_MAINmakra, ta odpowiedź ładnie wyjaśnia, co dokładnie robi. Jednak, aby mieć większą elastyczność (np. Aby używać GTest, GMock), możesz po prostu utworzyć QAplicationinstancję w swoich testach main (nie ma potrzeby dzwonienia exec).

Następnie, aby zdarzenia zostały przetworzone, należy wywołać QTest :: qWait . Spowoduje to przetworzenie Twoich wydarzeń przez określony czas. Dobrą praktyką jest qWaitForakceptacja predykatu - w ten sposób unikniesz wyścigu w swoich testach.

W konkretnym scenariuszu, kiedy spodziewasz się wyemitowania jakiegoś sygnału, możesz również skorzystać z podobnej funkcjonalności QSignalSpy :: wait .

Mały przykład, gdy chcemy poczekać, aż niektóre parametry zostaną przekazane z jednego elementu do drugiego:

QSignalSpy spy(&widget1, &Widget1::settingsChanged);
widget2->applySettings();
ASSERT_TRUE(spy.wait(5000));
// do some further tests based on the content of passed settings
1 VincentFourmond Nov 20 2020 at 16:39

Dlaczego nie chcesz, aby aplikacja działała exec? Proces wyświetlania widgetu nie jest „statyczny”. Nie „rysujesz” widżetu na ekranie, ale raczej masz aplikację, która nasłuchuje różnych zdarzeń i odbiera zdarzenia rysowania od menedżera okien. Aplikacja może narysować widget tylko wtedy, gdy poprosi o to menedżer okien.

Powodem, dla którego drugi kod działa, jest to, że czekasz wystarczająco długo, aż menedżer okien wyśle ​​żądanie „losowania” w Twoich warunkach . Nie gwarantuje to, że zawsze będzie działać.

Jeśli chcesz zagwarantować wyświetlanie widżetu, musisz rozpocząć pętlę i poczekać, aż otrzymasz co najmniej jedno zdarzenie losowania, ale nawet to nie jest niezawodne.

ypnos Nov 20 2020 at 16:58

Jak fachowo opisał Vincent Fourmond, widżety nie są jednorazową transakcją. GUI nie blokuje i w tym celu musi działać w pętli zdarzeń .

exec()Metoda rozpoczyna tę pętlę zdarzeń które naśladować odpytywania. Chociaż możliwe jest połączenie pętli zdarzeń Qt z innymi pętlami zdarzeń, polecam prostsze rozwiązanie:

Kontynuuj swój program w pętli zdarzeń, wywołując metodę podczas jej uruchamiania. Tutaj znajdziesz doskonałą odpowiedź, jak to zrobić:https://stackoverflow.com/a/8877968/21974

Jak wspomniano testów jednostkowych, nie jest też sygnał można wykorzystać do prowadzenia kontroli na koniec cyklu (przed widgety są zniszczone) QApplication::aboutToQuit. Stanie się tak, gdy ostatnie okno zostanie zamknięte (programowo lub przez użytkownika).