Kurs Qt – #1 – Czym jest Qt? Pierwsza aplikacja w praktyce

Kurs Qt – #1 – Czym jest Qt? Pierwsza aplikacja w praktyce

Celem tej serii jest przedstawienie środowiska Qt i stworzenie bazy do szybkiego wykorzystywania frameworka Qt w przyszłych projektach.

Sprawdzimy m.in. jak zbudować własną aplikację mobilną, dzięki której możliwe będzie nawiązanie połączenia z Arduino przez UART/Bluetooth.


Tematem tej serii jest pisanie aplikacji pozwalających na utworzenie połączenia pomiędzy panelem operatorskim (komputer PC, telefon z Androidem, komputer klasy SBC), a systemem wbudowanym (urządzenie z mikrokontrolerem). Nie będziemy skupiać się na tworzeniu interfejsów, oczywiście zajmiemy się tym tematem, ale tylko około 20-30% tej serii będzie poświęcone budowie interfejsu (frontend), reszta będzie dotyczyła tworzenia logiki działania aplikacji (backend).

Podczas tej serii zostaną poruszone takie kwestie jak:

  • instalacja i konfiguracja Qt,
  • budowa prostej aplikacji okienkowej w Qt,
  • budowa aplikacji, która komunikuję się poprzez port szeregowy z mikrokontrolerem, wykorzystamy tutaj najpopularniejszą płytkę Arduino UNO (nie trzeba ograniczać się jednak tylko do Arduino, równie dobrze można wykorzystać STM32),
  • konfiguracja Qt do budowania aplikacji na urządzenia mobilne z Androidem,
  • przygotowanie aplikacji mobilnej, która wykorzysta wbudowany w telefon moduł Bluetooth do nawiązania połączenia z Arduino i HC-05.

Artykuły z tej serii przeznaczone są dla każdego, kto chce poznać podstawy Qt pod kątem budowy aplikacji desktopowych i mobilnych, które mają komunikować się z systemami wbudowanymi.

Dlaczego Qt?

Qt to znacznie więcej niż tylko wieloplatformowy pakiet SDK - to strategia technologiczna, która pozwala szybko i ekonomicznie projektować, rozwijać, wdrażać i utrzymywać oprogramowanie, zapewniając jednocześnie bezproblemową obsługę wszystkich urządzeń - tak piszą o Qt jego twórcy.

Logo projektu Qt

Logo projektu Qt

Dlaczego ja wybrałem Qt?

Powód pierwszy: Qt daje mi nieograniczone możliwości do budowy cross-platformowych aplikacji, interfejsów graficznych lub paneli operatorskich. Napisałem aplikację na Windows, muszę ją zbudować na komputerze z Linuksem – nie ma problemu. Raz napisaną aplikację mogę bez większych problemów zbudować na Windowsie, Linuksie, na urządzeniu z Androidem lub iOS.

Powód drugi: Qt w wersji Open Source jest darmowe! Qt wydane jest na kilku licencjach w tym: LGPL3, GPL2 i GPLv3. Szczególnie interesująca jest ta pierwsza, ponieważ pozwala nam na zamknięcie źródła naszego oprogramowania, bez konieczności zakupu komercyjnej licencji. Czyli, możemy na tym zarabiać i jednocześnie chronić naszą pracę (oczywiście jeśli działamy zgodnie z warunkami licencji).

Instalacja i konfiguracja Qt

Instalator Qt pobieramy z oficjalnej strony Qt. Ważne jest, aby pobrać wersję open-source. Polecam obejrzeć poradnik producenta, który przeprowadzi nas przez proces instalacji i konfiguracji:

Istotną częścią procesu instalacji jest wybranie odpowiedniej wersji Qt i narzędzi (patrz na wybrane opcje na poniższym zrzucie ekranu). Polecam wybranie najnowszej stabilnej wersji Qt i odznaczenie wszystkich narzędzi opisanych jako UWP, wystarczy nam jeden kompilator (zaznaczony na rysunku MinGW). Jednocześnie oszczędzi nam to sporo miejsca na dysku.

Wybór wersji Qt i zalecanych narzędzi

Wybór wersji Qt i zalecanych narzędzi

Gdyby, któreś z tych narzędzi było nam później potrzebne to możemy je w każdej chwili zainstalować korzystając z przygotowanego do tego celu narzędzia Qt (Qt Maintenance Tool).

Po udanej instalacji, możemy już uruchomić program Qt Creator – jest to nasze IDE, które zawiera edytor tekstu i narzędzia do tworzenia interfejsu graficznego aplikacji:

Qt Creator - cross-platformowe IDE do rozwoju aplikacji przy wykorzystaniu frameworka Qt

Qt Creator - cross-platformowe IDE do rozwoju aplikacji przy wykorzystaniu frameworka Qt

Pierwsza aplikacja w Qt

Rozpoczynamy od stworzenia nowego projektu:

Tworzenie nowego projektu w Qt

Tworzenie nowego projektu w Qt

Następnie wybieramy typ naszej aplikacji, w tym wypadku wybieramy aplikację Qt Widgets:

Wybór typu aplikacji

Wybór typu aplikacji

Następnie wybieramy nazwę i położenie naszego projektu. Istotne jest to, aby ścieżka projektu nie zawierała spacji! Unikniemy wtedy problemów z budowaniem aplikacji dla urządzeń z Androidem.

Wybór odpowiedniej ścieżki dla projektu

Wybór odpowiedniej ścieżki dla projektu

W kolejnym kroku wybieramy zestawy narzędzi dla naszych docelowych platform. Na razie wybieramy tylko platformę Desktop (zaznaczoną na czerwono). Później będziemy wykorzystywać także narzędzia dla platformy Android (zaznaczone na zielono). Zauważcie, że dostępne narzędzia odpowiadają tym, które wybraliśmy podczas instalacji (podczas swojej instalacji, z której widać tutaj zrzuty ekranu, nie odznaczyłem opcji zawierających w nazwie UWP).

Wybór odpowiedniej platformy (Desktop)

Wybór odpowiedniej platformy (Desktop)

Następnie musimy nazwać naszą domyślną główną klasę, zostawiamy proponowaną MainWindow. Będzie to nasza główna klasa, która zajmuje się obsługą interfejsu graficznego naszej aplikacji. Do niej będziemy też dołączać nasze własne klasy lub klasy Qt.

Wybór nazwy dla klasy głównej aplikacji

Wybór nazwy dla klasy głównej aplikacji

Następnie przechodzimy dalej. Gdy zakończymy proces konfiguracji, wtedy naszym oczom ukaże się edytor tekstowy, zobaczymy też drzewo stworzonego projektu.

Drzewo pierwszej aplikacji w Qt

Drzewo pierwszej aplikacji w Qt

Znajdziemy tam plik z rozszerzeniem .pro, jest to plik, który zawiera konfigurację projektu - są w nim informacje dla specjalnego preprocesora Qt - generalnie nie edytujemy tego pliku bez potrzeby, będziemy tam jedynie dodawać specjalne moduły Qt. Na liście znajdziemy tam też pliki z popularnymi rozszerzeniami .h i .cpp, gdzie będą się znajdować definicje i implementacje naszego programu.

Ostatnim rodzajem plików są pliki z rozszerzeniem .ui. Są to pliki, które korzystają z formatu XML i zawierają informacje o elementach (i ich właściwościach) naszego interfejsu graficznego.

Plik opisujący interfejs programu tworzonego w Qt

Plik opisujący interfejs programu tworzonego w Qt

Tworzenie interfejsu aplikacji w Qt

Tryb Design uruchamia się domyślnie, gdy klikniemy 2x na plik mainwindow.ui, podgląd trybu Design widoczny jest poniżej. Tworzenie interfejsu jest bardzo intuicyjne i proste, polega na przeciąganiu elementów interfejsu (widgetów) z listy po lewej (obszar zaznaczony na zielono) i upuszczaniu ich na obszar głównego widgetu (centralWidget) aplikacji (obszar zaznaczony kolorem czerwonym).

Hierarchię obiektów (elementy i ich zależności) interfejsu znajdziemy w miejscu zaznaczonym na żółto. Ostatnia część okna, zaznaczona na niebiesko, pozwala na sprawdzenie właściwości elementu, który jest aktualnie wybrany.

Podgląd trybu Design przeznaczonego do edycji formularzy (pliki z rozszerzeniem .ui)

Podgląd trybu Design przeznaczonego do edycji formularzy (pliki z rozszerzeniem .ui)

Edycję interfejsu zaczynamy od usunięcia menuBar, mainToolBar i statusBar, nie będziemy ich używać:

Pierwsze kroki podczas edycji interfejsu w Qt - usunięcie zbędnych elementów

Pierwsze kroki podczas edycji interfejsu w Qt - usunięcie zbędnych elementów

Następnie dodamy do naszego interfejsu przycisk – robimy to przez przeciągnięcie elementu, który nazywa się Push Button. Dwukrotne kliknięcie w obszar nazwy przycisku spowoduje pojawienie się kursora edycji tekstu, zmieńmy nazwę przycisku z PushButton na Zamknij.

Umieszczanie elementów interfejsu

Umieszczanie elementów interfejsu

Po umieszczeniu elementu, możemy wprowadzić siatkę. W tym celu klikamy element centralWidgetHierarchii obiektów (albo wybieramy go klikając w obszar naszej aplikacji). Następnie z menu wybieramy przycisk Rozmieść w siatce.

Teraz dodamy kolejne elementy interfejsu – Spacery. Jak sama nazwa wskazuje odpowiadają one za wolne miejsce na naszym interfejsie. Dodajemy je zgodnie z poniższym zrzutem ekranu:

Dodawanie elementów odpowiadających za odstępy

Dodawanie elementów odpowiadających za odstępy

Dobrą praktyką jest nazywanie obiektów w usystematyzowany sposób, ja robię to w ten sposób, że zostawiam człon pushButton (żeby podczas pisania kodu szybko znaleźć interesujący mnie element) i dodaje do jego nazwy, słowny opis jego funkcji (mój przycisk będzie zamykał aplikację) więc dodaję człon Close, uzyskując w ten sposób nazwę pushButtonClose - będzie to ID przycisku, którym będziemy się posługiwać w kodzie. Sposób nazwy obiektu został przedstawiony na powyższym zrzucie.

Teraz przechodzimy do zbudowania naszej aplikacji i jej uruchomienia. Proces ten jest bardzo prosty, wystarczy, że naciśniemy Ctrl + R lub wybierzemy odpowiedni przycisk:

Budowa i uruchomienie aplikacji

Budowa i uruchomienie aplikacji

Rozpocznie to etap kompilacji, a po jej zakończeniu uruchomi się aplikacja. Poniższy zrzut ekranu przedstawia widok uruchomionej aplikacji (obszar zaznaczony na czerwono). Obszar zaznaczony na zielono przedstawia z kolei komunikaty kompilatora, tam zobaczymy ewentualne błędy.

Budowanie i uruchamianie pierwszej aplikacji w Qt

Budowanie i uruchamianie pierwszej aplikacji w Qt

Spróbuj teraz powiększyć okno aplikacji! Co się dzieje z pozycją przycisku Zamknij, czy jego pozycja względem prawego dolnego rogu aplikacji ulega zmianie? Następnie wyłącz rozmieszczanie w siatce (przycisk obok Rozmieszczania w siatce) i znowu uruchom aplikację, sprawdź czy wtedy aplikacja skaluje się poprawnie. Po testach, przywróć umieszczanie w siatce.

Sygnały i sloty - podłączanie przycisków

Skoro mamy już pierwszy przycisk, to wypadałoby, aby jego wciśnięcie powodowało jakąś akcję. W tym celu skorzystamy z mechanizmu sygnałów i slotów Qt.

Mechanizm sygnałów i slotów jest jedną z funkcjonalności Qt, która wyróżnia je na tle podobnych projektów. Mechanizm ten pozwala na komunikację między różnymi obiektami bibliotek Qt, przy tym jest intuicyjny i prosty w użyciu. Ogólna zasada działania tego mechanizmu polega na wysyłaniu sygnału (na jakieś zdarzenie), który powoduje wykonanie zdefiniowanych w slocie akcji:

Przykładowe wykorzystanie mechanizmu sygnałów i slotów

Przykładowe wykorzystanie mechanizmu sygnałów i slotów

Pierwszy sposób wykorzystania mechanizmu sygnałów i slotów

Pierwszy sposób możemy zastosować dla sygnałów i slotów zdefiniowanych dla elementów interfejsu Qt. W tym celu, w trybie Design należy kliknąć w przycisk Modyfikuj sygnały:

Modyfikowanie sygnałów w trybie Design

Modyfikowanie sygnałów w trybie Design

Spowoduje to włączenie nowego, interaktywnego trybu łączenia sygnałów ze slotami. Musimy najechać kursorem na przycisk Zamknij, nacisnąć lewy przycisk myszy i przeciągnąć kursor w stronę obszaru aplikacji (jak na poniższym zrzucie ekranu).

Tryb interaktywnego łączenia sygnałów ze slotami

Tryb interaktywnego łączenia sygnałów ze slotami

Gdy puścimy przycisk myszki otworzy się nam okno dialogowe z wyborem opcji:

Opcje dostępne podczas konfiguracji sygnałów i slotów w Qt

Opcje dostępne podczas konfiguracji sygnałów i slotów w Qt

Wybieramy sygnał clicked(), zaznaczamy checkbox "Pokaż sygnały i sloty klasy Qwidget", a następnie wybieramy slot close(). Klikamy OK. Co teraz zrobiliśmy? Połączyliśmy sygnał kliknięcia przycisku ze slotem zamknięcia aplikacji. Teraz możemy uruchomić aplikację i przetestować działanie przycisku.

Drugi sposób wykorzystania mechanizmu sygnałów i slotów

Drugi sposób możemy zastosować dla sygnałów i slotów zdefiniowanych dla elementów interfejsu Qt, przy czym możemy zdefiniować własne akcje (w slocie). W tym celu stwórz kolejny przycisk, nazwij go OK, następnie kliknij na niego prawym przyciskiem myszy i wybierz opcję "Przejdź do slotu...".

Przechodzenie do slotu dla wybranego przycisku

Przechodzenie do slotu dla wybranego przycisku

Następnie spośród listy slotów wybierz clicked():

Wybór odpowiedniego slotu

Wybór odpowiedniego slotu

Czynność ta przeniesie nas do implementacji wybranego slotu – plik mainwindow.cpp. W tym miejscu musimy umieścić akcje, które mają się wykonać po wciśnięciu przycisku OK. Zatem do dzieła: pomiędzy klamrami wpisz następującą linijkę kodu:

Następnie (u góry pliku mainwindow.cpp) zaimportuj klasę QDebug dodając linijkę:

Twój kod powinien wyglądać mniej więcej tak:

Edycja kodu przykładowej aplikacji

Edycja kodu przykładowej aplikacji

Klasa QDebug zapewnia strumień wyjściowy do debugowania. W naszej implementacji wciśnięcie przycisku spowoduje wyświetlenie w zakładce Komunikaty Aplikacji tekstu: "Wcisnąłeś przycisk OK".

Uruchom aplikację (tak jak poprzednio) i naciśnij przycisk OK, analizując wyjście strumienia w zakładce Komunikaty aplikacji, wynik powinien być następujący:

Efekt działania nowego przycisku

Efekt działania nowego przycisku

Zwróć uwagę jak nazywa się metoda tego slotu:

Możemy to rozumieć w ten sposób: na sygnał clicked() z obiektu pushButtonOK wykonuj akcje z ciała tej metody. Zauważ, że nazwa zawiera nazwę obiektu emitującego sygnał i nazwę sygnału. Jak pewnie się domyślasz, zdefiniowane sygnały i sloty można używać ręcznie przez zdefiniowanie nowych metod w klasie MainWindow. Jedyne co musisz zrobić to sprawdzić jakie sygnały i sloty posiadają dane obiekty (np. w Qt Creatorze jak poprzednio przy wyborze sygnałów/slotów lub w dokumentacji klas Qt) i na tej podstawie stworzyć nową metodę klasy MainWindow.

Przykładowo w pliku mainwindow.h pod klasyfikatorem private slots można dodać:

A w pliku mainwindow.cpp stworzyć odpowiednią implementację:

Trzeci sposób wykorzystania mechanizmu sygnałów i slotów

Trzeci sposób pozwala nam na wykorzystanie naszych sygnałów i slotów. Własne sygnały zdefiniujemy w późniejszych częściach tej serii, na tą chwilę zajmiemy się stworzeniem własnego slotu.

Dodaj kolejny przycisk i nazwij go Test, a nazwę tego obiektu zmień na pushButtonTest. Następnie w pliku mainwindow.h dodaj pod klasyfikatorem private slots linijkę:

Powinno to wyglądać mniej więcej w ten sposób:

Dodawanie własnego slotu

Dodawanie własnego slotu

Następnie stwórz implementację tego slotu w pliku mainwindow.cpp:

Pozostaje jeszcze jedna czynność, którą wcześniej robiło za nas środowisko, czyli faktycznie łączenie sygnałów ze slotami. W konstruktorze MainWindow należy użyć metody klasy QObject - connect(). Klasa QObject jest klasą bazową dla wszystkich klas Qt (na razie, ta informacja nam wystarczy). A zatem, w konstruktorze dodajemy linijkę:

Teraz musimy sobie wyjaśnić za co ten fragment kodu odpowiada. Metoda connect dokonuje fizycznego połączenia sygnału clicked() dodanego przycisku z naszym slotem myCustomSlot().

Składnia metody connect jest następująca:

W naszym przypadku

  • nadawcą jest: ui−>pushButtonTest, poprzez ui (który defacto jest wskaźnikiem na klasę, która zawiera implementację naszych elementów interfejsu) możemy odnosić się do elementów naszego interfejsu. ui−>pushButtonTest jest niczym innym jak wskaźnikiem na obiekt pushButtonTest. Co więcej, metoda connect oczekuje w miejscu argumentu nadawcy, właśnie wskaźnika na obiekt, który ma emitować sygnał,
  • sygnałem jest: sygnał clicked() przycisku pushButtonTest i przekazujemy go w argumencie za pomocą makra SIGNAL,
  • odbiorcą jest: nasza główna klasa MainWindow – to w niej zdefiniowaliśmy nasz slot, przesyłamy ją do metody connect za pomocą wskaźnika this,
  • slotem jest: nasz zdefiniowany slot myCustomSlot, przekazujemy go za pomocą makra SLOT.

Ostatecznie implementacja powinna wyglądać mniej więcej tak:

Ostateczna implementacja nowego mechanizmu

Ostateczna implementacja nowego mechanizmu

Uruchom program i przetestuj go, wynik powinien być następujący:

Efekt działania nowego przycisku

Efekt działania nowego przycisku

Ostatnią ciekawą rzeczą jest wykorzystanie wartości zwracanej przez metodę connect. W tym celu zmień nieco poprzedni kod, zamień:

na:

Wynik tej zamiany powinien być następujący:

Efekt działania przycisku po zamianie programu

Efekt działania przycisku po zamianie programu

Jest to bardzo przydatny zabieg zwłaszcza wtedy, kiedy tworzymy swoje sygnały oraz sloty i zaczynamy łączyć je między różnymi klasami. Czasem nasza próba połączenia sygnału i slotu może się nie powieść. Wtedy ten zabieg oszczędzi nam bardzo dużo czasu podczas szukania błędów, które sprawiają, że nasz program nie działa tak jak chcemy, a także oszczędzi nam czasu na analizowaniu potencjalnych problemów (które, nie zawsze są powodem tego błędu).

Materiały dodatkowe

Najlepszym źródłem informacji o Qt jest niewątpliwie jego obszerna dokumentacja. Wystarczy wpisać interesującą nas klasę w wyszukiwarkę i mamy dostęp do wszystkich jej metod wraz z przykładami.

Bardzo dobre poradniki robi Bryan Cairns, który publikuje swoje filmy na You Tube. Polecam zwłaszcza pierwsze 20 odcinków (są bardzo krótkie i rzeczowe) - przydadzą się w dalszych częściach serii.

Z książek ciekawe wydają się:

Podsumowanie

W tej części dowiedziałeś się czym jest Qt, wiesz jak je zainstalować i skonfigurować. Poznałeś zalety Qt. Nauczyłeś się jak zaprojektować prosty interfejs graficzny, zbudować aplikację i ją uruchomić. Poznałeś bardzo ważny mechanizm w Qt – mechanizm sygnałów i slotów.

Czy artykuł był pomocny? Oceń go:

Średnia ocena / 5. Głosów łącznie:

W następnej części zbudujemy aplikację i zaprojektujemy jej interfejs, który pozwoli nam na uzyskanie połączenia poprzez port szeregowy. Pozwoli to na odbieranie/wysyłanie informacji do mikrokontrolera. Zbudujemy więc prosty panel operatorski, który będzie można wykorzystać do komunikacji z Arduino.

Autor: Mateusz Patyk

Nawigacja kursu


O Autorze

Autorem tej serii wpisów jest Mateusz Patyk, który zawodowo zajmuje się programowaniem systemów wbudowanych oraz rozwijaniem aplikacji na desktopy i urządzenia mobilne. Jego głównymi obszarami zainteresowań są systemy sterowania, egzoszkielety i urządzenia do wspomagania chodu człowieka. Prywatnie miłośnik dobrego kina i gier strategicznych.

android, arduino, programowanie, qt

Trwa ładowanie komentarzy...