Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

TODO list

V tomto díle vytvoříme jednoduchý TODO list

Změna počtu prvků modelu

Dosud jsme pracovali pouze s modely, které měly po celou dobu běhu programu stejný počet prvků. QML část programu se tedy na začátku dotázala, kolik má model prvků a pak o ně dle potřeby zobrazení žádala. Nyní budeme chtít mít možnost, jak počet prvků za běhu programu měnit a to tak, aby se tyto změny ihned projevovaly i v grafickém rozhraní.

Z pohledu samotných dat v modelu je to jednoduché. Data držíme v seznamu, je tedy snadné tento seznam metodou .append(elem) rozšířit, nebo pomocí metody .pop(idx) některý prvek ze seznamu vyhodit. Pokud ale zkusíme udělat jen tuto jednoduchou změnu, zjistíme, že sice se model změnil, ale uživatelské rozhraní stále zobrazuje původní data z modelu. Je tomu tak proto, neboť ListView (nebo jiné view) nedostane žádnou informaci o tom, že se data modelu změnila a tudíž používá ta, která má již načtená. Pokud tedy chceme měnit počet prvků modelu, musíme o tom informovat i QML a to si pak načte a správně zobrazí změněné položky.

Přidávání prvků

Pokud chceme prvky přidávat, musíme před samotnou změnou modelu zavolat metodu beginInsertRows(index,first,last), která vyšle signál o tom, že se budou přidávat do modelu řádky a po jejich přidání budou mít první přidaný řádek index first a poslední přidaný řádek index last. Tento signál zachytí QML a od této chvíle ví, že se data v modelu budou měnit. Poté je možné řádky do modelu přidat a ihned poté je třeba zavolat metodu endInsertRows(), která vyšle signál o tom, že řádky byly vloženy a modifikace modelu ukončena. Tento signál také zachytí QML, dotáže se modelu na aktuální počet prvků a načte a zobrazí přidané prvky.

Pokud přidáváme jeden prvek, bude first a last nastavené na stejnou hodnotu, protože první přidaný prvek je zároveň posledním přidaným prvkem. Pokud chceme vložit prvky na konec, tak bude mít first hodnotu rovnou aktuálnímu počtu řádků, protože první vložený prvek bude právě na indexu odpovídajícímu aktuálnímu počtu řádků.

Metody beginInsertRows a endInsertRows je vhodné volat vždy co nejtěsněji kolem úprav samotného modelu. Mezi začátkem a koncem vkládání je model považovaný za měněný a to může mít důsledky na výkon aplikace.

Odebírání prvků

Odebírání prvků funguje obdobně jako přidávání. Než začneme měnit model, musíme zavolat metodu beginRemoveRows(index, first,last), která vyšle signál o tom, že budou z modelu odebrány řádky a to tak, že první odebraný řádek bude ten, který má aktuálně index first a poslední odebraný řádek bude ten, který má aktuálně index last. Následně je možné řádky odebrat v modelu a bezprostředně po jejich odebrání je potřeba zavolat metodu endRemoveRows(https://doc.qt.io/qtforpython/PySide6/QtCore/QAbstractItemModel.html#PySide6.QtCore.PySide6.QtCore.QAbstractItemModel.endRemoveRows), která vyšle signál, že modifikace modelu byla ukončena. Oba tyto signály zachytí QML a zajistí, aby zobrazené prvky po ukončení odebírání odpovídaly modelu.

Obdobně jako beginInsertRows a endInsertRows by metody beginRemoveRows a endRemoveRows měly být volány co nejtěsněji kolem samotné modifikace modelu.

Složitější změny modelu

Pokud potřebujeme významnějším způsobem změnit model, lze při snaze o zachování jednoduchosti použít postup, kdy nejprve všechny prvky z modelu odebereme, provedeme modifikace a následně všechny prvky po modifikacích přidáme. Tento postup není vhodný pro větší datové sady a časově náročnější modifikace, protože prvky nakrátko zmizí z rozhraní a je výpočetně náročné je všechny z rozhraní odstranit a následně je tam opět přidat. Pro složitější třídění a filtrování je vhodné použít modely k tomu určené (např. QSortFilterProxyModel, ale to je již nad rámec tohoto tutoriálu.

Popis programu

Program zobrazuje seznam úkolů. Tyto úkoly při startu načte ze souboru a umožňuje pomocí textového pole nové úkoly přidávat a hotové úkoly odstraňovat.

Modelem aplikace je třída TaskListModel, která je velmi podobná třídě CityListModel z předminulého dílu. Kromě základních metod potřebných pro správné fungování spolu s QML dále obsahuje tři sloty, které slouží pro modifikaci seznamu. Všiměte si, že tyto sloty berou argumenty, které pak při modifikaci využívají. Slot addTask dostane jako argument řetězec, který přidá do seznamu úkolů jako další úkol, slot deleteTask dostane jako argument index úkolu, který má smazat. Tím pádem nepotřebují tyto sloty žádné další vazby na uživatelské rozhraní, protože rovnou dostanou všechny informace, které ke své funkci potřebují.

Popis grafického rozhraní

Program má dvě grafická rozhraní - jednoduché v souboru simple_view.qml a pokročilé ve view.qml, které ukazuje uživatelsky přívětivější variantu práce se seznamem úkolů.

Jednodušší rozhraní

Toto rozhraní umožňuje přidávat úkoly zapsáním do textového pole a kliknutím na "Přidej úkol" a odebrat úkol jeho zvolením v seznamu úkolů a následným kliknutím na "Odeber zvolený úkol".

Implementace rozhraní je velmi podobná seznamu měst. Za povšimnutí stojí řešení přidávání úkolů, kdy text nově přidávaného úkolu nezískává Python z TextInputu sám, ale řetězec je předán přímo v signálu. Umožňuje to tak větší flexibilitu v grafickém rozhraní, kdy je možné zadávat text různými způsoby a stačí pak jen vyslat správný signál k přidání úkolu. Obdobně rozhraní pro mazání úkolu je pomocí signálu, který obsahuje index úkolu, který má smazat.

Pokročilé rozhraní

Toto rozhraní umožňuje přidávat úkoly zapsáním do textového pole a následným stisknutím Enteru nebo kliknutím na "Přidej úkol". Po přidání úkolu se textové pole smaže. Odebrání úkolů je možné po jednom kliknutím na úkol nebo na zaškrtávací políčko vedle něj, dále lze odebrat všechny úkoly naráz pomocí kliknutí na tlačítko "Odeber všechny úkoly". Seznam úkolů se automaticky zvětšuje tak, aby vyplnil veškeré dostupné volné místo v okně, textové pole na zadávání nových úkolů spolu s tlačítky je zarovnáno na dolní stranu okna.

Aby bylo možné snadno adaptovat uživatelské rozhraní při změně velikosti okna, budeme používat ColumnLayout a RowLayout z modulu QtQuick.Layouts. Oproti jednoduchým komponentám Row a Column umí tyto komponenty roztahovat vložené prvky tak, aby vyplnily okno, zarovnávat je a dělat s nimi i další pokročilé pozicování. Tyto možnosti se nastavují ve vnořených komponentách pomocí atributu Layout.<atribut> a vždy se vztahují k nejbližší nadřazené komponentě typu Layout. Speciálně pozor u RowLayoutu, kde atributy Layout.fillWidth a Layout.alignment rovnou v RowLayoutu se vztahují k umístění komponenty RowLayout v komponentě ColumnLayout, nikoli k prvkům komponenty RowLayout. Lidsky řečeno říkají, že se řádková komponenta má zarovnat dolů a roztáhnout přes celou šířku komponenty ColumnLayout. Atribut fillWidth / fillHeight říká, že má daná komponenta vyplnit celý zbývající prostor, pomocí atributu alignment lze určit, ke které straně / stranám se má zarovnávat.

K zobrazení jednotlivých úkolů slouží komponenta CheckBox, která dělá zatrhávací políčko s popiskem. Aby bylo možné políčko zaškrtnout, je potřeba povolit atribut checkable, ke stavu zaškrtnutí je pak možné přistupovat pomocí atributu checkState, v tomto programu toho ale nevyužíváme. Místo toho hlídáme signál clicked a při jeho vyslání necháme daný úkol smazat.

Aby byl TextInput sloužící k zadávání textu nového úkolu výraznější, je obalen komponentou Rectangle, která mu zařizuje orámování. Tato komponenta získá svou šířku z Layoutu, ale výšku nemá danou, takže je potřeba nějakou zvolit. Protože hned vedle jsou umístěna tlačítka, byla zvolena výška orámovávacího obdélníka jako výška jednoho z tlačítek, aby to vizuálně navazovalo. U samotné vložené komponenty TextInput je potřeba nastavit výšku podle obklopujícího obdélníka a zmenšit ji o rozměry rámečku. Také se hodí samotný text vertikálně vystředit, aby navazoval na text na tlačítkách vedle.

TextInput má nastaven atribut focus, aby bylo možné rovnou začít psát nové úkoly. Dále je využit signál accepted, který je vyvolán při potvrzení enterem. Při jeho zpracování je využit blok, který umožňuje zadat více příkazů jako reakci na signál. V tomto případě je přidán nový úkol se zadaným textem a následně je textové pole smazáno, aby bylo možné psát další úkol.

Zdroje