W poprzednim wpisie po raz pierwszy poruszyliśmy temat tworzenia interfejsu użytkownika korzystając z frameworka Qt. Tam jednak skupiliśmy się na podejściu Qt Widgets dedykowanym aplikacjom desktopowym. Za to dzisiaj dowiemy się czym jest język QML oraz jak potężne jest to rozwiązanie.
Uwaga
Zawodowo zajmuję się tematem Qt Development. Od czasu gdy napisałem te wpisy minęło kilka lat a ja zdążyłem założyć jednąz najbardziej znanych firm zajmujących się kodzeniem w Qt. Ten tutorial trochę się zaktualizował, więc zachęcam Cię odwiedzić stronę Scythe Studio, bo tam dzielimy się nowym tutorialem na YouTube, w którym temat jest podejmowany.
Zanim zaczniesz
W ramach tego kursu Qt w pierwszej kolejności skupiamy się na tworzeniu interfejsu graficznego korzystając właśnie z tej technologii. Następnie to czytelnicy będą mogli zdecydować, w którą stronę pójdzie kurs. Dołożę wszelkich starań, aby nowe wpisy ukazywały się co czwartek. Jeżeli ten wpis jest pierwszym z tego kursu na jaki trafiasz to sugeruję, abyś zajrzał również do poprzednich. Miłej lektury!
- Aplikacje okienkowe. Czym jest Qt?
- Instalacja frameworka Qt
- Meta-Object Compiler i QObject
- Mechanizm sygnałów i slotów w Qt
- Aplikacje okienkowe w C++ z Qt Widgets
Interfejs użytkownika multiplatformowo – Qt Quick i QML
Qt Quick jest modułem obecnym we frameworku, za którym idzie zupełnie inne podejście w tworzeniu interfejsu użytkownika. Aplikacje bazujące
na nim mogą być uruchamiane na wszystkich platformach wspieranych przez Qt wliczając w to nawet przeglądarkę. Na moduł składają się zarówno klasy dostępne z poziomu C++ jak i wprowadzony właśnie na potrzeby tego modułu język QML.
Frontend aplikacji napisany z wykorzystaniem tego modułu może w prosty sposób komunikować się z backendem napisanym w C++. Dzięki temu, w wyraźny sposób, możemy odseparować sferę wizualną projektu od jego logiki, co znacznie wpływa na elastyczność całej architektury. Podczas tego kursu przekonamy się z jak robić to poprawnie.
Język QML
Język QML (Qt Modeling Language) jest deklaratywnym językiem programowania. W przypadku korzystania z języka tego typu opisujemy efekt jaki chcemy osiągnąć, a nie sam sposób jego osiągnięcia. Korzysta się z niego głównie do implementacji interfejsu użytkownika, lecz logika aplikacji również może zostać oparta na tym właśnie języku. Osobiście tego nie zalecam, ale dobrze żebyś wiedział, że tak możliwość istnieje.
Sam język jest bardzo wydajny chociażby z tego powodu, że został napisany w C++ i na docelowych urządzeniach uruchamiany jest natywnie. Dodatkowo tam gdzie jest to możliwe korzysta się z akceleracji sprzętowej, aby jeszcze bardziej zwiększyć wydajność języka co ma szczególne znaczenie tam gdzie zasobów jest po prostu mało, czyli na urządzeniach wbudowanych. Język korzysta z dobrodziejstw kompilacji Just-In-Time i Ahead-Of-Time.
Jak zaraz sam się przekonasz język QML jest na tyle intuicyjny oraz czytelny, że edytowanie interfejsu użytkownika korzystając z niego nie powinno sprawić większej trudności nikomu wliczając w to na przykład designerów. W dodatku został on zaprojektowany tak, że bardzo łatwo osiągnąć efekt założony w projekcie graficznym. Nawet istnieje rozwiązanie pozwalające wyeksportować projekt z Photoshopa do QML właśnie.

Składnia języka
Można powiedzieć, że język QML przypomina nieco format JSON i takie porównanie wydaje się mieć sens patrząc na kod. Dodatkowo właściwości typów mogą przypominać nam arkusz stylów znany z CSS. Rzućmy okiem jak wyglądałby kod prostej aplikacji napisanej w języku QML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { width: 200 height: 100 anchors.centerIn: parent color: "#16191D" Text { anchors.centerIn: parent text: "Kurs Qt Binarnie.pl" color: "#EBEBEB" font.pointSize: 14 } } } |
Z kolei tak wyglądałaby uruchomiona aplikacja:

Itemy w QML zorganizowane są z uwzględnieniem zachowania drzewiastej struktury. Przekłada się to zastosowanie mechanizmu rodzicielstwa, tak samo jak w przypadku obiektów w C++. Dlatego właśnie, w powyższym przykładzie, linia „anchors.centerIn: parent” pomogła nam odnieść się do rodzica prostokąta, czyli całego okna. Dzięki temu odniesieniu mogliśmy wyśrodkować prostokąt. W podobny sposób można odnieść się do dzieci danego Itemu poprzez odwołanie się do właściwości children tak jak w przykładzie poniżej. Odwołujemy się w nim do szerokości i wysokości tekstu, aby na jego podstawie określić wymiary otaczającego go prostokąta.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { width: children[0].width height: children[0].height anchors.centerIn: parent color: "#16191D" Text { anchors.centerIn: parent text: "Kurs Qt Binarnie.pl" color: "#EBEBEB" font.pointSize: 14 } } } |
Efekt takiego zabiegu:

Większość typów dostępnych w QML bazuje na typie Item, więc w szczególności zapraszam do zapoznania się ze wszystkimi właściwościami tego typu w dokumentacji. Zanim przejdziemy dalej zwróćmy szczególną uwagę na właściwości x oraz y określające współrzędne itemu względem rodzica. Właśnie fakt, że umieszczamy itemy względem rodzica chciałbym podkreślić. Myślę, że jeden rzut oka na kod i efekt wystarczy, aby wszystko stało się jasne.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { height: 100 width: 100 y: 50 color: "red" } Rectangle { height: 100 width: 100 x: 10 y: 40 color: "green" } Rectangle { height: 200 width: 200 x: 50 y: 200 color: "blue" Rectangle { height: 100 width: 100 x: -50 //y: 0 by default color: "yellow" } } } |

Koncepty języka
Język QML rozwijany jest od 2010 roku i od tego czasu namnożyło się różnego rodzaju podmodułów do wykorzystania w aplikacjach Qt Quick. Do nich i do wielu innych zamysłów obecnych w module bazowym będziemy jeszcze wracać – każdy z nich lepiej jest poruszyć oddzielnie. W tym wpisie poprzestaniemy na wymienieniu kilku mniej obszernych, ale nie mniej ważnych konceptów. Całą resztę znajdziesz w spisie treści kursu.
Pozycjonowanie elementów z Anchors
Anchors (z ang. kotwice) to koncept pozycjonowania elementów bazując na ich geometrii i siedmiu niewidocznych liniach względem, których pozycjonowanie się odbywa. Pamiętaj żeby nie bać się zaglądać do dokumentacji, a w szczególności dokumentacji tak dobrej jak ta od Qt 😉 Pod tym linkiem znajdziesz link, gdzie mechanizm jest szczegółowo opisany.

W jednym z przykładów powyżej prostokąt utworzony został z właściwością anchors.centerIn: parent, która posłużyła do wyśrodkowania obiektu względem rodzica. Zamiast rodzica może to być też id innego obiektu. Taki zabieg zaprezentowany jest poniżej:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { id: bigOne height: 200 width: 200 anchors.centerIn: parent color: "#16191d" } Rectangle { height: 100 width: 100 anchors.centerIn: bigOne color: "#b8ce16" } } |
Efekt:

Bądź ostrożny używając właściwości id. W danym pliku .qml może istnieć tylko jeden element o wskazanym id.
Własne properties
Jak już z pewnością zauważyłeś, programowanie interfejsu użytkownika w QML w dużej mierze opiera się na przypisywaniu wartości do właściwości (ang. properties) obecnych w typach. QML wprowadza możliwość definiowania własnych właściwości dla obiektów, które później mogą być używane wielokrotnie. Do tematu z pewnością wrócimy. Na ten moment poprzestaniemy na definiowaniu własnych właściwości dla konkretnych obiektów. Spójrz na poniższy przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { id: greenOne height: 80 width: 100 anchors.centerIn: parent anchors.verticalCenterOffset: -height/2 property bool isDark: false color: isDark ? "#16191d" : "#b8ce16" } Rectangle { id: darkOne height: 80 width: 100 anchors.horizontalCenter: parent.horizontalCenter anchors.top: greenOne.bottom anchors.topMargin: 5 property bool isDark: true color: isDark ? "#16191d" : "#b8ce16" } } |
Efekt:

Jak widzisz nie dość, że stworzyliśmy własną właściwość to jeszcze wykorzystaliśmy ją przy definiowaniu koloru prostokątów.
Właściwości można zdefiniować na kilka sposobów:
1 2 3 4 5 6 7 8 9 10 11 12 |
// ogólny schemat: property type name property int counter property double factor: 1.5 property var listWithStuff: [0, 1, "three", "four"] readonly property url: "https://binarnie.pl" property bool hasSense: { if(false == [] && false == ![]) { return false; } return true } |
A to tylko kilka z możliwości. Jeżeli chcesz dowiedzieć się więcej na temat definiowania właściwości to odsyłam do dokumentacji.
Jak pewnie zauważyłeś definiowane właściwości mogą mieć typ z góry narzucony przez programistę, ale również możemy użyć słowa kluczowego var tak jak w języku JavaScript. Wówczas typ właściwości zostanie wydedukowany na podstawie jej wartości. Listę dostępnych typów znajdziesz również w dokumentacji.
Sygnały i sloty w QML
Omawiany już w ramach tego kursu mechanizm sygnałów i slotów obecny jest również w języku QML. O tym jak definiować własne sygnały jeszcze sobie powiemy, ale jest to sprawa tak kluczowa, że nie mogę tego konceptu po prostu pominąć.
Mechanika stojąca za emitowaniem sygnałów w QML jest taka sama jak w C++. Coś się dzieje – emitowany jest sygnał. Jeśli istnieje zarejestrowany slot – zostaje on wywołany. Rzućmy okiem na poniższy kod i efekt jego działania:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
Window { visible: true width: 640 height: 480 title: qsTr("FirstQuickApp") Rectangle { id: button height: 80 width: 100 anchors.centerIn: parent anchors.verticalCenterOffset: -height / 2 property bool isDark: false color: isDark ? "#16191d" : "#b8ce16" Text { anchors.centerIn: parent text: "Click me" color: button.isDark ? "#EBEBEB" : "#16191d" } } MouseArea { id: mouseArea anchors.fill: button onClicked: { button.isDark = !button.isDark } // opcjonalnie, gdy mamy tylko jedną instrukcję // onClicked: button.isDark = !button.isDark } } |

Obiekt typu MouseArea posiada sygnał cicked(). Aby stworzyć deklarację slotu wewnątrz utworzonego obiektu, należy skorzystać z następującego formatu „on + NazwaSygnału”. Dla sygnału clicked(), nazwa slotu będzie miała postać onClicked. Schemat ten powtarza się dla wszystkich sygnałów. Zawsze jest to nazwa sygnału z wielkiej litery poprzedzona prefiksem „on”. Wówczas możemy napisać wewnątrz własny slot w stylu JavaScript lub odwołać się do istniejącej funkcji.
Innym sposobem jest skorzystanie z elementu Connections:
1 2 3 4 5 6 7 |
Connections { target: mouseArea onClicked: { button.isDark = !button.isDark } } |
Tworzenie projektu z Qt Quick
Jeżeli chcesz stworzyć swój pierwszy projekt bazując na Qt Quick, polecam skorzystać z jednego z gotowych szablonów projektów. Jeżeli korzystasz z Qt Creatora to w menu File wybierz New File or Project a następnie w zakładce Application wybierz jeden z szablonów rozpoczynających się słowami nazwą modułu, czyli Qt Quick.

Gotową aplikację z pustym oknem otrzymasz wybierając opcję podświetloną na zrzucie ekranu. Pozostałe pozwolą na stworzenie aplikacji z gotowymi sposobami organizacji zawartości. Ciekawym narzędziem dostępnym w Qt Creatorze jest QML Scene, czyli narzędzie do prototypowania interfejsu użytkownika napisanego w QML. Chcąc z niego skorzystać należy wybraż następującą opcję na pasku menu: Tools->External->Qt Quick->Qt Quick 2 Preview (qmlscene).
Narzędzie to jest przydatne w momencie, gdy chcemy podejrzeć efekt naszej pracy, bez kompilowania całego projektu.
Podsumowanie
To by było na tyle w tym wpisie dotyczącym podstaw języka QML i modułu Qt Quick. Niemniej to na pewno nie koniec wpisów na ten temat. Możesz spodziewać się kolejnych obszernych materiałów. Już w następnym wpisie wielka konfrontacja Qt Widgets versus Qt Quick i QML. Wymienimy sobie mocne i słabe strony obu rozwiązań i postaramy się wybrać jedyne słuszne o ile to w ogóle możliwe 😊
Jeżeli nie możesz doczekać się kolejnych wpisów to zajrzyj do tej darmowej książki poświęconej QML.
Nie zapomnij też zostawić komentarza i lajka na profilu binarnie.pl na FB.