Stany i przekształcenia w QML to kolejny temat realizowany w ramach Kursu Qt na binarnie.pl. Wraz z kolejnymi wpisami twoja wiedza na temat tego frameworka z pewnością rośnie, więc grzechem z mojej strony byłoby uszczuplanie zestawu twoich umiejętności o wiedzę opisaną w temacie wpisu!
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 z Qt Quick. Język QML
- Podstawowe elementy w Qml
Stany w QML
Zaczniemy od zdefiniowana sobie czym jest State w kontekście tworzenia interfejsu graficznego z wykorzystaniem języka QML. Typ State służy do definiowania właściwości elementów, które mają być wykorzystane w danym stanie tego elementu. Wykorzystanie stanów opiera się na właściwościach state oraz states typu Item, po którym to dziedziczą elementy widzialne modułu Qt Quick.
Wartości tekstowa właściwości state określa stan w jakim aktualnie znajduje się obiekt, a za to właściwość states zawiera listę stanów w jakich może znaleźć się element nie licząc domyślnego. Każdy element domyślnie znajduje się w stanie „” i wszystkie właściwości zdefiniowane bezpośrednio w obiekcie zaliczają się jako domyślne wartości w tym stanie właśnie.
Przejdźmy jednak do kodu:
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 42 43 44 |
Window { id: window visible: true width: 440 height: 360 title: "Kurs Qt - Binarnie.pl" Rectangle { id: background anchors.fill: parent } Rectangle { id: rectangle anchors.centerIn: parent width: 100 height: 100 color: "#16191d" states: [ State { name: "clicked" PropertyChanges { target: rectangle color: "#b8ce16" rotation: 45 } PropertyChanges { target: background color: "beige" } } ] MouseArea { id: mouseArea anchors.fill: parent onClicked: { rectangle.state = rectangle.state == "clicked" ? "" : "clicked" } } } } |
Jak sam się pewnie domyślasz na podstawie samego kodu, program wyświetla domyślnie ciemny kwadrat umieszczony w samym centrum okna. Zdefiniowaliśmy listę stanów dla tego kwadratu tworząc jednoelementową listę obiektów typu State. W momencie, gdy mamy tylko jeden stan poza domyślnym to równie dobrze moglibyśmy do wartości states przypisać pojedynczy obiekt State, ale my jesteśmy dobroduszni i tak nie robimy.
W jaki sposób zmieniamy wartości właściwości? Wystarczy utworzyć obiekty typu PropertyChanges, gdzie po ustawieniu właściwości target na wskazany element, możemy ustalać wartości jego właściwości jakie przybierze on w wybranym stanie. Zmieniamy kolor prostokąta na zielony i obracamy go o 45 stopni. Podejście do stanów w QML jest dość liberalne – możemy zmieniać właściwości innych elementów niż ten, dla którego stan jest definiowany. Widać to patrząc na drugi obiekt PropertyChanges.
Efekt naszych starań wygląda następująco.
Jak widać po naciśnięciu na kwadrat przechodzi on ze stanu „” do stanu „clicked” i na odwrót. Efekt osiągnięty.
Rozszerzanie stanów
Załóżmy hipotetycznie, że mielibyśmy wiele stanów, które różniłyby się od siebie jedynie szczegółami. Wówczas musielibyśmy przepisywać wielokrotnie te same wartości, ale na szczęście jest szybszy sposób, aby taki scenariusz rozwiązać, a mianowicie mowa o właściwości extend. Wartość tej właściwości może przechowywać nazwę innego stanu. Wówczas nowy stan będzie zawierał wszystko to co jego stan bazowy w połączeniu z nowymi zmianami, jeżeli zdecydujesz się je dodać.
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
Window { id: window visible: true width: 440 height: 360 title: "Kurs Qt - Binarnie.pl" Rectangle { id: background anchors.fill: parent } Rectangle { id: rectangle anchors.centerIn: parent width: 100 height: 100 color: "#16191d" property int counter: -1 states: [ State { name: "even" when: rectangle.counter % 2 == 0 PropertyChanges { target: rectangle color: "#b8ce16" rotation: 45 } PropertyChanges { target: background color: "beige" } }, State { name: "odd" extend: "even" when: rectangle.counter % 2 == 1 PropertyChanges { target: rectangle color: "gray" } } ] MouseArea { id: mouseArea anchors.fill: parent onClicked: { if (rectangle.state != "") { rectangle.state = "" } else { rectangle.counter++ } } } } } |
Dlatego w przykładzie powyżej stan „odd” również zmieni kolor tła na beżowy i obróci kwadrat, ale za to kolor kwadratu ustawiony zostanie na szary. Zauważ też, że skorzystaliśmy z właściwości when typu State. Właściwość ta pozwala w łatwy sposób określić warunek kiedy dany stan staje się aktywny.
Kiedy korzystać ze stanów?
Ze stanów skorzystać warto wówczas, gdy chcemy zaprezentować użytkownikowi interfejs użytkownika mniej lub bardziej różniący się z uwagi na występujące w aplikacji okoliczności. Stany są o tyle pożyteczne, że pozwalają definiować zmiany poza funkcjami JavaScript. Dzięki stanom nie musimy definiować długich i skomplikowanych warunków przy ustalaniu wartości niektórych argumentów. Jeśli nie zadbamy o te warunki, będą się one rozwijać tworząc ostatecznie, takie oto potworki:
color: isDevice13Inch ? (isSomeModeEnabled ? „green” : „red”) : (isSomeOtherModeEnabled ? „blue” : „yellow”)
Przy jednej właściwości może nie robi to większej różnicy, ale widziałem bardziej skomplikowane warunki, które w łatwy sposób można byłoby zastąpić właśnie stanami.
Przykładowy use case:
Masz do zaimplementowania interfejs użytkownika. Jego wygląd zależałby od tego, czy użytkownik kupił wersję premium aplikacji. Niektóre elementy możesz ukryć,schować lub też zmienić w zależności od tego czynnika i w efekcie twój kod może być znacznie uproszczony.
Przekształcenia w QML
Stany i przekształcenia w QML zawsze idą ze sobą w parze, ponieważ przekształcenia wykorzystuje się do zdefiniowania animacji jakie mają zostać uruchomione w trakcie zmieniania stanu. Obiekty rozszerzające typ Item posiadają właściwość nazwaną transitions, do której można przypisać pojedyncze przekształcenie lub też całą ich listę tak jak w przypadku stanów.
Animacjom bardziej szczegółowo poświęcę następny wpis, więc na ten moment będziemy je wykorzystywać bez wchodzenia w detale.
Powiedzmy, że chcielibyśmy dodać przekształcenie do pierwszego przykładu z tego wpisu. Wystarczy w elemencie rectangle dodać poniższy kod:
1 2 3 4 5 6 7 8 9 10 11 |
transitions: [ Transition { NumberAnimation { properties: "rotation" duration: 500 } ColorAnimation { duration: 500 } } ] |
W tej jednoelementowej liście znajduje się jeden obiekt typu Transition, który to z kolei zawiera dwie animacje. Pierwsza z nich odpowiada za animowanie obrotu kwadratu, a druga za płynne przejście z jednego koloru do drugiego. Zauważ, że kolor animowany jest zarówno dla wycentrowanego kwadratu, jak i dla tła, gdyż dla obu tych elementów kolor jest zmieniany w ramach stanu „clicked”. Przekształcenie jest uruchamiane automatycznie przy każdej zmianie stanu.
Proste? Jasne, że tak! 😊
Przekształcenia między wyszczególnionymi stanami
Domyślnie przekształcenie wykonywane jest w momencie zmiany pomiędzy wszystkimi stanami, ponieważ typ Transition posiada właściwości from oraz to. Obie te właściwości domyślnie ustawione są na „*” co oznacza tyle co „any”, czyli przekształcenie zachodzi od każdego stanu do każdego stanu.
Powiedzmy, że chcemy, aby kwadrat obracał się o 45 stopni tak jak dotychczas. Dziać się ma to podczas przechodzenia ze stanu domyślnego do stanu „clicked”, ale za to wracając do poprzedniego stanu nie chcemy, aby obrócił się tak samo, a zrobił pełne „koło”. W tym celu jako listę przekształceń wykorzystamy poniższy kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
transitions: [ Transition { from: ""; to: "clicked" NumberAnimation { properties: "rotation" duration: 500 } }, Transition { from: "clicked"; to: "" NumberAnimation { properties: "rotation" duration: 2500 from: 45 to: 360 } } ] |
Jak widzisz dodaliśmy dodatkowe przekształcenie i zmieniliśmy wartości właściwości from oraz to, dla wcześniejszego przekształcenia . Dzięki temu pierwsze przekształcenie zostanie wykonane przy przechodzeniu ze stanu domyślnego do stanu po kliknięciu kwadratu myszką, a drugie przekształcenie w odwrotnej sytuacji. Animacja umieszczona w drugim przekształceniu również doczekała się zmian wartości from oraz to, ale w tym przypadku wskazują one na to w jakim przedziale obracać kwadrat (od 45 stopni do 360).
Podsumowanie
Celem tego wpisu było pokazanie Ci możliwości konfiguracji właściwości elementów przy wykorzystaniu mechanizmu stanów oraz wskazanie jak możesz przechodzenie pomiędzy poszczególnymi stanami urozmaicić za pomocą przekształceń. Mam nadzieję, że wpis przypadł Ci do gustu, a dla stanów znajdziesz zastosowanie w swoich projektach. Zapraszam też na fanpage binarnie.pl, dzięki czemu zawsze będziesz na bieżąco z materiałami publikowanymi na blogu.