Jaki jest prawidłowy sposób wyświetlania widgetów bez wywoływania QApplication :: exec ()?
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
Aby przetestować aplikację Qt GUI, potrzebujesz przynajmniej QApplication
przetwarzanej instancji i pętli zdarzeń. Najszybszym sposobem jest użycie QTEST_MAIN
makra, 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ć QAplication
instancję 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 qWaitFor
akceptacja 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
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.
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).