Obsah
1. Tvorba grafického uživatelského rozhraní v Pythonu s využitím knihovny Kivy: kontejnery
3. Specifikace šířky okrajů okolo widgetů umístěných do mřížky
4. Modifikace velikosti widgetů umístěných do mřížky
5. Výchozí šířka nebo výška widgetů
7. Nastavení výšky widgetů umístěných do kontejneru typu stack
8. Různé kombinace výšky a šířky widgetů v okně s pevně zadanými rozměry
9. Specifikace pořadí vkládání widgetů do kontejneru
11. Vertikální orientace kontejneru box
12. Složitější UI formulářů a dialogů: kombinace většího množství kontejnerů
13. Kontejner box, do kterého jsou vloženy další kontejnery box
14. Popis UI formulářů a dialogů v jazyce Kv
15. Definice dialogu postaveného na kontejneru typu grid
16. Definice dialogu postaveného na kontejneru typu stack
17. Definice dialogu, který využívá kontejnery typu box uvnitř jiného kontejneru stejného typu
18. Další typy kontejnerů poskytovaných knihovnou Kivy
19. Repositář s demonstračními příklady
1. Tvorba grafického uživatelského rozhraní v Pythonu s využitím knihovny Kivy: kontejnery
Ve třetím článku o tvorbě grafického uživatelského rozhraní v programovacím jazyce Python s využitím knihovny Kivy se budeme zabývat velmi důležitými prvky, na kterých je tvorba uživatelského rozhraní postavena. Jedná se o takzvané kontejnery (containers), které se někdy nazývají správci geometrie (geometry managers) v závislosti na použité UI knihovně nebo frameworku. V tomto článku budeme používat termín „kontejner“.
Připomeňme si, že samotné ovládací prvky (widgety) nejsou na obrazovce (resp. na ploše desktopu) prakticky nikdy zcela osamocené; ostatně většina knihoven pro GUI by samostatný widget ani nedokázala zobrazit. Ve skutečnosti se ovládací prvky téměř vždy nachází v nějakém okně, dialogu či dalším nadřazeném widgetu. Programátoři grafických uživatelských rozhraní se často setkají s pojmem kontejner. Jedná se o komponentu, na kterou lze vkládat různé widgety a mnohdy i další kontejnery.
Obecně tak interně vzniká stromová datová struktura jejíž kořen je představován plochou na obrazovce, na které jsou umístěna okna aplikací (dnes je ovšem i samotná plocha obrazovky součástí větší virtuální plochy zobrazované obecně na více monitorech). V těchto oknech se dále nachází kontejnery a widgety. V mnoha grafických uživatelských rozhraních přitom mohou být vybrané widgety (zdaleka ne však všechny) současně i kontejnery. Kontejnery kromě jiného řeší i rozmístění widgetů na své ploše.
Dnes se seznámíme s trojicí základních kontejnerů, jejichž znalost postačuje pro tvorbu prakticky všech běžných formulářů a dialogů. Jedná se o:
- Grid layout (umístění komponent do pomyslné mřížky)
- Stack layout (komponenty jsou umístěny za sebou nebo pod sebou s tím, že dokážou „přetéct“ na další řádky)
- Box layout (komponenty jsou umístěny buď vedle sebe nebo pod sebe, ovšem nedokážou automaticky „přetéct“ tak, jako v případě předchozího kontejneru).
2. Kontejner grid layout
S kontejnerem nazvaným grid layout jsme se již seznámili, protože byl použit v demonstračních příkladech představených v prvním i ve druhém článku o knihovně Kivy. Připomeňme si, že plocha tohoto kontejneru je rozdělena pomyslnou pravidelnou mřížkou na buňky, do kterých se vkládají jednotlivé ovládací prvky a/nebo jiné kontejnery. Šířka sloupců tabulky popř. výška jejích řádků je odvozena od velikosti widgetů, která je konfigurovatelná. Počet sloupců je nastaven při konstrukci kontejneru, zatímco počet řádků je odvozen od počtu vložených widgetů.
V dnešním prvním demonstračním příkladu vytvoříme mřížku se třemi sloupci (a předem neznámým počtem řádků):
layout = GridLayout(cols=3)
Dále do kontejneru vložíme celkem devět tlačítek, takže vznikne mřížka se třemi řádky:
button = Button(text="1") layout.add_widget(button) ... ... ... button = Button(text="9") layout.add_widget(button)
Při velikosti okna 200×200 délkových jednotek bude výsledné uživatelské rozhraní vypadat takto:
Následuje úplný zdrojový kód dnešního prvního demonstračního příkladu:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = GridLayout(cols=3)
button = Button(text="1")
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4")
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7")
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Programátoři, co nesnášíte BS, ale máte rádi business! Y Soft je česká firma s globálním dopadem (100+ zemí, 1M+ uživatelů a >100% meziroční růst). R&D úplně bez manažerů (130 developerů). Otevíráme 30 pozic pro Cloud a AI: Praha/Brno/Ostrava/remote. Zodpovědnost ano, mikro-management ne. Pojď někam, kde můžeš věci změnit.
3. Specifikace šířky okrajů okolo widgetů umístěných do mřížky
Vlastnosti mřížky, do které se vkládají jednotlivé ovládací prvky, lze do jisté míry ovládat. Prozatím jsme pouze specifikovali počet sloupců tabulky:
layout = GridLayout(cols=3)
V případě potřeby je ovšem možné specifikovat okraje okolo ovládacích prvků i okolo celé mřížky:
layout = GridLayout(cols=3, padding=10, spacing=10)
Výsledkem bude dialog, který bude vypadat následovně:
Hodnoty padding a spacing jsou na sobě nezávislé, takže je možné navrhnout například i tyto dialogy:
Obrázek 3: Nastavení hodnoty padding zatímco hodnota spacing je nulová.
Obrázek 4: Nastavení hodnoty spacing zatímco hodnota padding je nulová.
Opět si pochopitelně ukážeme celý zdrojový kód takto upraveného demonstračního příkladu:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = GridLayout(cols=3, padding=10, spacing=10)
button = Button(text="1")
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4")
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7")
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
4. Modifikace velikosti widgetů umístěných do mřížky
Velikost ovládacích prvků vkládaných do mřížky kontejneru typu grid je možné do určité míry ovlivnit. Při konstrukci většiny widgetů se může zapsat jejich výchozí velikost, tj. šířka a/nebo výška. Pokud nechceme, aby se výpočet velikosti prováděl automaticky, je navíc ještě vhodné nastavit parametr size_hint_x nebo size_hint_y na hodnotu None (a to opět při konstrukci widgetu). Podívejme se na příklad, ve kterém změníme šířku všech tlačítek umístěných do prvního sloupce mřížky:
button = Button(text="1", size_hint_x=None, width=30) ... ... ... button = Button(text="4", size_hint_x=None, width=30) ... ... ... button = Button(text="7", size_hint_x=None, width=30)
Výsledek může vypadat následovně (ovšem vizuální podoba se změní ve chvíli, kdy zmenšíme nebo zvětšíme okno s dialogem):
Úplný zdrojový kód demonstračního příkladu, ve kterém je do dialogu vloženo devět tlačítek, přičemž tlačítka v prvním sloupci mají odlišnou šířku třiceti délkových jednotek, vypadá následovně:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = GridLayout(cols=3, padding=10, spacing=10)
button = Button(text="1", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
5. Výchozí šířka nebo výška widgetů
Výchozí velikost widgetů je možné (i když nepřímo) ovlivnit velikostí buněk pomyslné mřížky, do které se ovládací prvky vkládají. Můžeme si například vynutit nastavení výšky buněk, a to do značné míry nezávisle na tom, jakou velikost ovládacích prvků (například tlačítek) vypočítá knihovna Kivy. Příkladem je nastavení (a vynucení) výšky buněk mřížky na čtyřicet délkových jednotek, což je provedeno přímo při konstrukci kontejneru typu grid layout:
layout = GridLayout(
cols=3,
padding=10,
spacing=10,
row_force_default=True,
row_default_height=40,
)
Povšimněte si, že v tomto případě nebudou tři řádky mřížky nataženy přes celou výšku okna (přesněji řečeno přes výšku, od které se dvakrát odečte deset délkových jednotek rezervovaných pro okraje):
Upravený zdrojový kód demonstračního příkladu bude nyní vypadat takto:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = GridLayout(
cols=3,
padding=10,
spacing=10,
row_force_default=True,
row_default_height=40,
)
button = Button(text="1", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7", size_hint_x=None, width=30)
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
6. Kontejner stack layout
Druhým typem kontejneru, se kterým se v dnešním článku setkáme, je kontejner nazvaný stack layout. Jedná se o poměrně unikátní kontejner, který v mnoha dalších knihovnách pro tvorbu grafického uživatelského rozhraní nenajdeme. Tento typ kontejneru umožňuje jednotlivé komponenty vkládat za sebe (naprosto stejným způsobem, jako znaky v textu) nebo pod sebe. Ovšem pokud se daná komponenta již na aktuální „řádek“ nevejde (přesahuje plochu kontejneru), je automaticky přesunuta na další pomyslný „řádek“. Pokud se tedy komponenty (ovládací prvky nebo vložené kontejnery) vkládají vedle sebe, je chování skutečně do značné míry podobné automatickému zalamování textu do odstavce.
Díky tomu je kontejner stack layout možné s výhodou použít v těch aplikacích, které mají být provozovány jak na desktopech (kde je typicky k dispozici velká volná plocha), tak i na smartphonech (obecně menší volná plocha a tedy potřeba „zalamování“, zejména při orientaci displeje na výšku).
Podívejme se nyní na základní způsob použití tohoto kontejneru. Do plochy dialogu opět vložíme devět tlačítek, u kterých budeme specifikovat jejich požadovanou (relativní) výšku, zatímco šířka bude dopočítána automaticky knihovnou Kivy. Tlačítka budou vkládána za sebe, ovšem pokud by se měla překročit dostupná plocha okna, bude další tlačítko „zalomeno“ na další pomyslný řádek:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.stacklayout import StackLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = StackLayout()
button = Button(text="1", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="2", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="3", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="4", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="5", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="6", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="7", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="8", size_hint=(None, 0.15))
layout.add_widget(button)
button = Button(text="9", size_hint=(None, 0.15))
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Ihned po spuštění tohoto demonstračního příkladu by mělo okno s dialogem vypadat takto – komponenty byly rozmístěny tak, že po vložení dvojice tlačítek došlo k „odřádkování“:
Obrázek 7: Automatické rozmístění tlačítek ve výchozím stavu, kdy je okno široké 200 délkových jednotek.
Ovšem ve chvíli, kdy změníme velikost okna, dojde i k automatickému přemístění komponent, které jsou do kontejneru umístěny:
7. Nastavení výšky widgetů umístěných do kontejneru typu stack
Nepovinným parametrem nazvaným size_hint, který se předává do konstruktoru při tvorbě ovládacích prvků, je možné specifikovat očekávanou šířku a/nebo výšku tohoto prvku. Kvůli tvorbě programů s grafickým uživatelským rozhraním, které mají být přenositelné na různé platformy, mohou být tyto rozměry specifikovány relativně vůči ploše, kterou celý kontejner zabírá. Pokud například nastavíme výšku na hodnotu 1/5, bude se knihovna Kivy snažit o to, aby výška komponenty dosahovala právě 1/5 (tedy 20%) výšky kontejneru (a v případě, že je kontejner umístěn přímo do plochy okna, tak se jedná o 1/5, resp. 20% jeho výšky):
button = Button(text="1", size_hint=(None, 1/5)) layout.add_widget(button)
Požadovaná výška widgetu se uplatní po přidání widgetů do kontejneru typu stack následovně:
Výška tlačítek zůstane zachována i tehdy, pokud se změní velikost okna a dojde k přeuspořádání komponent:
Opět si pochopitelně ukážeme úplný zdrojový kód tohoto demonstračního příkladu:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.stacklayout import StackLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = StackLayout()
button = Button(text="1", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="2", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="3", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="4", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="5", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="6", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="7", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="8", size_hint=(None, 1/5))
layout.add_widget(button)
button = Button(text="9", size_hint=(None, 1/5))
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
8. Různé kombinace výšky a šířky widgetů v okně s pevně zadanými rozměry
Nyní si ukážeme, jakým způsobem lze dosáhnout toho, aby byla tlačítka v dialogu umístěna stejným způsobem, jak je to patrné ze screenshotu:
U všech tlačítek nastavíme relativní šířku a výšku vztaženou k celé ploše kontejneru. Například tlačítko, které má být stejně široké, jako kontejner, ovšem jeho výška má být pětinová, se zkonstruuje takto:
button = Button(text="1", size_hint=(1/1, 1/5)) ... ... ...
Naopak, pokud budeme chtít, aby se dvě tlačítka umístila vedle sebe, musí být jejich relativní šířka rovna 1/2:
button = Button(text="2", size_hint=(1/2, 1/5)) ... ... ... button = Button(text="3", size_hint=(1/2, 1/5)) ... ... ...
Následuje výpis úplného zdrojového kódu tohoto demonstračního příkladu, ve kterém je do kontejneru přidáno všech devět tlačítek:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.stacklayout import StackLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = StackLayout()
button = Button(text="1", size_hint=(1/1, 1/5))
layout.add_widget(button)
button = Button(text="2", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="3", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="4", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="5", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="6", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="7", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="8", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="9", size_hint=(1/1, 1/5))
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Dialog ve chvíli, kdy je jedno z tlačítek stisknuto:
9. Specifikace pořadí vkládání widgetů do kontejneru
Parametrem orientation předávaným do konstruktoru kontejneru lze zvolit, v jakém pořadí budou widgety (či další kontejnery) vkládány do volné plochy. Hodnota tohoto parametru se předává v řetězci:
| Hodnota | Pořadí vkládání |
|---|---|
| „lr-tb“ | left-right, top-bottom (výchozí hodnota) |
| „tb-lr“ | top-bottom, left-right |
| „rl-tb“ | right-left, top-bottom (zrcadlení okolo vertikální osy) |
| „tb-rl“ | top-bottom, right-left |
| „lr-bt“ | left-right, bottom-top |
| „bt-lr“ | bottom-top, left-right |
| „rl-bt“ | right-left, bottom-top |
| „bt-rl“ | bottom-top, right-left |
Prakticky má (alespoň podle mého názoru) význam volit jen mezi hodnotami „lr-tb“ a „rl-tb“. Ostatně vyzkoušejme si, jak bude vypadat dialog, pokud si vyžádáme vkládání komponent v pořadí zprava-doleva a potom shora dolů. Až na hodnotu orientation se tento příklad nijak neliší od příkladu předchozího:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.stacklayout import StackLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = StackLayout(orientation="rl-tb")
button = Button(text="1", size_hint=(1/1, 1/5))
layout.add_widget(button)
button = Button(text="2", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="3", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="4", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="5", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="6", size_hint=(1/3, 1/5))
layout.add_widget(button)
button = Button(text="7", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="8", size_hint=(1/2, 1/5))
layout.add_widget(button)
button = Button(text="9", size_hint=(1/1, 1/5))
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Výsledkem bude dialog, ve kterém jsou tlačítka vertikálně zrcadlena (ovšem ne jejich obsah, pouze umístění v ploše kontejneru):
10. Kontejner box layout
Třetím typem kontejneru, který si dnes popíšeme, je kontejner nazvaný box layout. Při použití tohoto kontejneru jsou jednotlivé ovládací prvky (widgety) nebo další kontejnery umístěny buď vedle sebe nebo pod sebou (orientace je zvolena při jeho konstrukci). Ovšem na rozdíl od kontejneru stack layout zde nedochází k automatickému „zalamování“ komponent na další řádky. Z tohoto pohledu je tedy box layout jedním z nejjednodušších kontejnerů. A jeho jednoduchost je současně i jeho největší předností, protože se tento kontejner typicky velmi snadno kombinuje s dalšími kontejnery (stejného či odlišného typu), což umožňuje tvorbu relativně složitých dialogů a formulářů.
Nejprve si ukažme, jakým způsobem se tento kontejner chová v případě, že při jeho konstrukci neuvedeme žádné další parametry, tedy jaké je jeho výchozí chování:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = BoxLayout()
button = Button(text="1")
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4")
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7")
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (400, 400)
if __name__ == "__main__":
Application().run()
Do kontejneru bylo přidáno devět tlačítek, která byla umístěna vedle sebe. Kontejner navíc vyplnil celou plochu okna, takže se tlačítka příslušně zvýšila:
11. Vertikální orientace kontejneru box
Jak jsme si již řekli v předchozí kapitole, umožňuje kontejner typu box layout umístění komponent vedle sebe nebo pod sebe. Ve výchozím stavu se komponenty umisťují vedle sebe v pořadí zleva doprava, ovšem parametrem orientation předávaným konstruktoru kontejneru lze zajistit vertikální orientaci, tj. umisťování komponent v pořadí shora dolů:
layout = BoxLayout(orientation="vertical")
Výsledný dialog s devíti tlačítky bude nyní vypadat takto:
Obrázek 15: Rozmístění devíti tlačítek na plochu kontejneru typu box layout, použita je vertikální orientace komponent.
Pro úplnost je uveden i celý zdrojový kód takto upraveného demonstračního příkladu:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = BoxLayout(orientation="vertical")
button = Button(text="1")
layout.add_widget(button)
button = Button(text="2")
layout.add_widget(button)
button = Button(text="3")
layout.add_widget(button)
button = Button(text="4")
layout.add_widget(button)
button = Button(text="5")
layout.add_widget(button)
button = Button(text="6")
layout.add_widget(button)
button = Button(text="7")
layout.add_widget(button)
button = Button(text="8")
layout.add_widget(button)
button = Button(text="9")
layout.add_widget(button)
return layout
def on_start(self):
Window.size = (400, 400)
if __name__ == "__main__":
Application().run()
12. Složitější UI formulářů a dialogů: kombinace většího množství kontejnerů
Na první pohled by se mohlo zdát, že kontejner typu box layout vlastně nabízí při tvorbě grafického uživatelského rozhraní jen omezené možnosti. To je do jisté míry pravda, ovšem jeho největší předností je snadná kombinace s dalšími kontejnery. GUI ze screenshotu číslo 16 můžeme například realizovat následujícím způsobem:
- Do plochy okna je vložen kontejner typu box layout nastavený tak, že komponenty budou umístěny pod sebou (orientation=„vertical“).
- První komponentou bude další kontejner typu box layout s výchozím nastavením. Do tohoto „podkontejneru“ vložíme tři tlačítka.
- Druhou komponentou bude další kontejner typu box layout s výchozím nastavením. Nyní do tohoto „podkontejneru“ vložíme tlačítka čtyři.
- Třetí komponentou bude další kontejner typu box layout s výchozím nastavením, do něhož jsou vložena dvě tlačítka.
Výsledkem bude okno s devíti tlačítky rozmístěnými následujícím způsobem:
Samozřejmě můžeme nastavit i parametry padding a spacing. Pokud oba parametry nastavíme na deset délkových jednotek (u hlavního kontejneru), bude celá struktura GUI mnohem více patrná:
13. Kontejner box, do kterého jsou vloženy další kontejnery box
Ukažme si nyní prakticky, jakým způsobem lze programově vytvořit dialogy ze screenshotů 16 a 17 uvedené v předchozí kapitole. Nejprve vytvoříme „hlavní“ kontejner:
layout = BoxLayout(orientation="vertical")
Dále do tohoto kontejneru vložíme tři další kontejnery:
first_panel = BoxLayout(orientation="horizontal") layout.add_widget(first_panel) second_panel = BoxLayout(orientation="horizontal") layout.add_widget(second_panel) third_panel = BoxLayout(orientation="horizontal") layout.add_widget(third_panel)
Nyní do každého z těchto kontejnerů můžeme libovolně vkládat jednotlivé widgety, například:
button = Button(text="1") first_panel.add_widget(button) button = Button(text="9") third_panel.add_widget(button) ... ... ...
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = BoxLayout(orientation="vertical")
first_panel = BoxLayout(orientation="horizontal")
button = Button(text="1")
first_panel.add_widget(button)
button = Button(text="2")
first_panel.add_widget(button)
button = Button(text="3")
first_panel.add_widget(button)
layout.add_widget(first_panel)
second_panel = BoxLayout(orientation="horizontal")
button = Button(text="4")
second_panel.add_widget(button)
button = Button(text="5")
second_panel.add_widget(button)
button = Button(text="6")
second_panel.add_widget(button)
button = Button(text="7")
second_panel.add_widget(button)
layout.add_widget(second_panel)
third_panel = BoxLayout(orientation="horizontal")
button = Button(text="8")
third_panel.add_widget(button)
button = Button(text="9")
third_panel.add_widget(button)
layout.add_widget(third_panel)
return layout
def on_start(self):
Window.size = (400, 300)
if __name__ == "__main__":
Application().run()
Samozřejmě nic nebrání v tom, aby se okolo jednotlivých „podkontejnerů“ vytvořily okraje:
layout = BoxLayout(orientation="vertical", padding=20, spacing=20)
Tím vznikne dialog, který jsme mohli vidět na screenshotu číslo 17:
from kivy.app import App
from kivy.core.window import Window
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class Application(App):
def build(self):
layout = BoxLayout(orientation="vertical", padding=20, spacing=20)
first_panel = BoxLayout(orientation="horizontal")
button = Button(text="1")
first_panel.add_widget(button)
button = Button(text="2")
first_panel.add_widget(button)
button = Button(text="3")
first_panel.add_widget(button)
layout.add_widget(first_panel)
second_panel = BoxLayout(orientation="horizontal")
button = Button(text="4")
second_panel.add_widget(button)
button = Button(text="5")
second_panel.add_widget(button)
button = Button(text="6")
second_panel.add_widget(button)
button = Button(text="7")
second_panel.add_widget(button)
layout.add_widget(second_panel)
third_panel = BoxLayout(orientation="horizontal")
button = Button(text="8")
third_panel.add_widget(button)
button = Button(text="9")
third_panel.add_widget(button)
layout.add_widget(third_panel)
return layout
def on_start(self):
Window.size = (400, 300)
if __name__ == "__main__":
Application().run()
14. Popis UI formulářů a dialogů v jazyce Kv
V úvodním článku o knihovně Kivy jsme si řekli, že grafické uživatelské rozhraní (tj. jednotlivé dialogy a formuláře) je možné vytvořit dvěma různými způsoby. Buď explicitně v Pythonu konstrukcí jednotlivých kontejnerů a programovým vkládáním widgetů do těchto kontejnerů, nebo lze uživatelské rozhraní plně popsat v jazyku Kv. Samotný kód v Pythonu může být minimální, protože pouze potřebujeme načíst příslušné popisy uživatelského rozhraní a popř. naprogramovat reakce na události. Díky existenci jazyka Kv lze popis uživatelského rozhraní zcela oddělit od programové logiky, což přináší větší čitelnost zdrojových kódů aplikace. Navíc je možné vytvářet například interaktivní editory grafického uživatelského rozhraní, které budou pracovat pouze s jazykem Kv (což je do značné míry podobný koncept, jaký používalo Delphi nebo Visual Baisc) atd. V dalších třech kapitolách si ukážeme způsob popisu formulářů založených na všech třech výše popsaných kontejnerech, tj. na grid layout, stack layout i box layout.
15. Definice dialogu postaveného na kontejneru typu grid
V první ukázce definice dialogu v jazyku Kv použijeme kontejner typu grid, do kterého jsou vkládány další komponenty. Povšimněte si, jakým způsobem jsou definovány parametry kontejneru (tj. zejména hodnoty cols, padding a spacing) i toho, že kontejner společně s tlačítky skutečně tvoří stromovou strukturu. U všech tlačítek je definován jejich popisek (text) a u trojice tlačítek v prvním sloupci i jejich očekávaná šířka:
GridLayout:
id: grid_layout
padding: 10
spacing: 10
cols:3
Button:
text: "1"
size_hint_x: None
width: 30
Button:
text: "2"
Button:
text: "3"
Button:
text: "4"
size_hint_x: None
width: 30
Button:
text: "5"
Button:
text: "6"
Button:
text: "7"
size_hint_x: None
width: 30
Button:
text: "8"
Button:
text: "9"
Skript napsaný v Pythonu, který po svém spuštění načte definici dialogu a následně ho zobrazí, vypadá následovně:
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
class Application(App):
def build(self):
builder = Builder.load_file("grid_layout.kv")
return builder
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Dialog, který je zobrazen po spuštění tohoto demonstračního příkladu, by měl vypadat takto:
Obrázek 18: Devět tlačítek v kontejneru typu grid s mřížkou 3×3 buňky.
16. Definice dialogu postaveného na kontejneru typu stack
Následuje výpis obsahu souboru, který obsahuje popis dialogu založeného na kontejneru typu stack. Nastaveny jsou nulové šířky okrajů okolo okna i nulové mezery mezi jednotlivými ovládacími prvky. Následně je na kontejner vloženo celkem devět tlačítek s explicitně nastavenou relativní šířkou a relativní výškou. Povšimněte si, že je možné používat zlomky, z čehož je alespoň náznakem patrné, že Kv je opravdu spíše jazykem, nežli konfiguračním formátem:
StackLayout:
id: stack_layout
padding: 0
spacing: 0
Button:
text: "1"
size_hint: [1, 1/5]
Button:
text: "2"
size_hint: [1/2, 1/5]
Button:
text: "3"
size_hint: [1/2, 1/5]
Button:
text: "4"
size_hint: [1/3, 1/5]
Button:
text: "5"
size_hint: [1/3, 1/5]
Button:
text: "6"
size_hint: [1/3, 1/5]
Button:
text: "7"
size_hint: [1/2, 1/5]
Button:
text: "8"
size_hint: [1/2, 1/5]
Button:
text: "9"
size_hint: [1, 1/5]
Skript, který výše uvedenou definici dialogu načte a dialog zobrazí, vypadá takto:
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
class Application(App):
def build(self):
builder = Builder.load_file("stack_layout.kv")
return builder
def on_start(self):
Window.size = (200, 200)
if __name__ == "__main__":
Application().run()
Výsledkem je okno, které by mělo vypadat následovně:
Obrázek 19: Devět tlačítek s různou šířkou vložených do kontejneru typu stack.
17. Definice dialogu, který využívá kontejnery typu box uvnitř jiného kontejneru stejného typu
V dnešním posledním demonstračním příkladu je ukázáno, jakým způsobem se v jazyku Kv popíše dialog založený na kontejneru typu box, do kterého jsou vloženy další tři kontejnery stejného typu (tyto kontejnery jsou uspořádány vertikálně, tedy pod sebou). A do těchto tří kontejnerů jsou vloženy ovládací prvky – tlačítka. Tato tlačítka jsou uspořádána horizontálně, tedy vedle sebe a v každém kontejneru je jich odlišný počet:
BoxLayout:
id: box_layout
orientation: "vertical"
BoxLayout:
orientation: "horizontal"
Button:
text: "1"
Button:
text: "2"
Button:
text: "3"
BoxLayout:
orientation: "horizontal"
Button:
text: "4"
Button:
text: "5"
Button:
text: "6"
Button:
text: "7"
BoxLayout:
orientation: "horizontal"
Button:
text: "8"
Button:
text: "9"
Skript, který výše takto zapsanou dialogu načte a dialog následně zobrazí, vypadá takto:
from kivy.app import App
from kivy.lang import Builder
from kivy.core.window import Window
class Application(App):
def build(self):
builder = Builder.load_file("box_layout.kv")
return builder
def on_start(self):
Window.size = (400, 300)
if __name__ == "__main__":
Application().run()
Výsledkem bude tento dialog zobrazený na ploše desktopu:
18. Další typy kontejnerů poskytovaných knihovnou Kivy
Kromě třech výše popsaných kontejnerů jsou v knihovně Kivy dostupné i další typy kontejnerů:
- AnchorLayout umožňuje umístění až pěti komponent (či dalších kontejnerů) do pěti oblastí, na které je plocha rozdělena (nahoře, dole, vlevo, vpravo, uprostřed). Obdobu tohoto kontejneru nalezneme i v dalších knihovnách určených pro tvorbu GUI.
- FloatLayout umožňuje „volné“ pozicování jednotlivých komponent na dostupné ploše. To vlastně znamená, že výpočet souřadnic komponent je ponechán na programu (a tedy i na vývojáři).
- RelativeLayout slouží, jak již jeho název naznačuje, pro pozicování jednotlivých komponent na dostupné ploše. Na rozdíl od předchozího kontejneru jsou souřadnice určeny relativně vůči dalším komponentám a nikoli absolutně. Změna pozice jedné komponenty pak vede i k posunu dalších komponent.
- PageLayout je velmi zajímavý kontejner, který umožňuje na ploše okna zobrazit více „virtuálních“ ploch (s widgety nebo i dalšími kontejnery), přičemž je možné se interaktivně mezi jednotlivými plochami přepínat posunem jejich okrajů (swiping).
- ScatterLayout se do značné míry podobá kontejneru RelativeLayout. Rozdíly mezi těmito dvěma kontejnery si popíšeme v navazujícím článku.
19. Repositář s demonstračními příklady
Demonstrační příklady, s nimiž jsme se dnes seznámili a které jsou určeny pro Python 3.11 (a libovolnou vyšší verzi Pythonu) a knihovnu Kivy, jsou dostupné, jak je zvykem, na GitHubu. V tabulce níže jsou uvedeny odkazy na jednotlivé zdrojové kódy i na definice formulářů:
20. Odkazy na Internetu
- Welcome to KivyMD’s documentation!
https://kivymd.readthedocs.io/en/latest/ - Kivy Tutorial
https://www.geeksforgeeks.org/python/kivy-tutorial/ - Stránky projektu Kivy
https://kivy.org/#home - Two Ways To Change Background Colors – Python Kivy GUI Tutorial
https://kivycoder.com/two-ways-to-change-background-colors-python-kivy-gui-tutorial-11/ - 5 Best Ways to Adjust Window Size in Kivy with Python
https://blog.finxter.com/5-best-ways-to-adjust-window-size-in-kivy-with-python/ - Python | Make a simple window using kivy
https://www.geeksforgeeks.org/python/python-make-a-simple-window-using-kivy/ - Python | Ellipse (different polygons) in Kivy
https://www.geeksforgeeks.org/python/python-ellipse-different-polygons-in-kivy/ - Getting Started » Kv Design Language
https://kivy.org/doc/stable/gettingstarted/rules.html - Python | Kivy .kv File
https://www.geeksforgeeks.org/python/python-kivy-kv-file/ - Kivy na GitHubu
https://github.com/kivy/kivy - PySimpleGUI
https://www.pysimplegui.org/en/latest/ - DearPyGui na GitHubu
https://github.com/hoffstadt/DearPyGui - PySimpleGUI Tutorial
https://www.tutorialspoint.com/pysimplegui/index.htm - PySimpleGUI – Canvas Element
https://www.tutorialspoint.com/pysimplegui/pysimplegui_canvas_element.htm - Dokumentace ke knihovně PySimpleGUI
https://www.pysimplegui.org/en/latest/ - Dokumentace ke knihovně DearPyGui
https://dearpygui.readthedocs.io/en/latest/index.html# - The Hitchhiker's Guide to Pyhton: GUI Applications
http://docs.python-guide.org/en/latest/scenarios/gui/ - 7 Top Python GUI Frameworks for 2017
http://insights.dice.com/2014/11/26/5-top-python-guis-for-2015/ - Stránky projektu wxPython
https://wxpython.org/ - wxPython Project Phoenix (na GitHubu)
https://github.com/wxWidgets/Phoenix/blob/wxPython-4.0.3/README.rst - wxPython API Documentation
https://docs.wxpython.org/index.html - wxWidgets
https://wxwidgets.org/ - wxPython 4.0.3 na PyPi
https://pypi.org/project/wxPython/4.0.3/ - wxGlade – a GUI builder for wxWidgets
http://wxglade.sourceforge.net/ - Repositář projektu wxGlade
https://github.com/wxGlade/wxGlade/ - wxGlade’s documentation
http://wxglade.sourceforge.net/docs/index.html - Graphical User Interfaces (GUI)
https://pythonspot.com/gui/ - wxPyWiki
https://wiki.wxpython.org/FrontPage - Getting started with wxPython
https://wiki.wxpython.org/Getting%20Started#A_First_Application:_.22Hello.2C_World.22 - wxPython GUI tutorial
https://pythonspot.com/wxpython-gui-tutorial/ - wxPython tutorial
http://zetcode.com/wxpython/ - Build wxPython On Raspberry Pi
https://wiki.wxpython.org/BuildWxPythonOnRaspberryPi - wxPython History
https://wxpython.org/pages/history/index.html - Installing wxPython 4.0 (Project Phoenix) on Fedora 27
https://blog.wizardsoftheweb.pro/installing-wxpython-on-fedora/ - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - GUI Programming in Python
https://wiki.python.org/moin/GuiProgramming - Cameron Laird's personal notes on Python GUIs
http://phaseit.net/claird/comp.lang.python/python_GUI.html - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Hand Coded GUI Versus Qt Designer GUI
https://stackoverflow.com/questions/387092/hand-coded-gui-versus-qt-designer-gui - Qt Creator Manual
http://doc.qt.io/qtcreator/ - Qt Designer Manual
http://doc.qt.io/qt-5/qtdesigner-manual.html - Qt Creator (Wikipedia)
https://en.wikipedia.org/wiki/Qt_Creator - QIODevice
https://pyside.github.io/docs/pyside/PySide/QtCore/QIODevice.html#PySide.QtCore.QIODevice - QFile
https://pyside.github.io/docs/pyside/PySide/QtCore/QFile.html#PySide.QtCore.QFile - QUiLoader
https://pyside.github.io/docs/pyside/PySide/QtUiTools/QUiLoader.html#PySide.QtUiTools.PySide.QtUiTools.QUiLoader.load - QSvgWidget
https://pyside.github.io/docs/pyside/PySide/QtSvg/QSvgWidget.html - QByteArray
https://pyside.github.io/docs/pyside/PySide/QtCore/QByteArray.html - Differences Between PySide and PyQt
https://wiki.qt.io/Differences_Between_PySide_and_PyQt - PySide 1.2.1 tutorials
https://pyside.github.io/docs/pyside/tutorials/index.html - PySide tutorial
http://zetcode.com/gui/pysidetutorial/ - Drawing in PySide
http://zetcode.com/gui/pysidetutorial/drawing/ - Qt Core
https://pyside.github.io/docs/pyside/PySide/QtCore/Qt.html - Signals & Slots
http://doc.qt.io/qt-4.8/signalsandslots.html - Signals and Slots in PySide
http://wiki.qt.io/Signals_and_Slots_in_PySide - Intro to PySide/PyQt: Basic Widgets and Hello, World!
http://www.pythoncentral.io/intro-to-pysidepyqt-basic-widgets-and-hello-world/ - Leo editor
http://leoeditor.com/ - IPython Qt Console aneb vylepšený pseudoterminál
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-ipython-a-ipython-notebook/#k06 - Python GUI development
http://pythoncentral.io/introduction-python-gui-development/ - Graphic User Interface FAQ
https://docs.python.org/2/faq/gui.html#graphic-user-interface-faq - Tkinter
https://wiki.python.org/moin/Tkinter - Tkinter 8.5 reference: a GUI for Python
http://infohost.nmt.edu/tcc/help/pubs/Tkinter/web/index.html - Tkinter (Wikipedia)
https://en.wikipedia.org/wiki/Tkinter - appJar
http://appjar.info/ - appJar (Wikipedia)
https://en.wikipedia.org/wiki/AppJar - appJar na Pythonhosted
http://pythonhosted.org/appJar/ - appJar widgets
http://appjar.info/pythonWidgets/ - Stránky projektu PyGTK
http://www.pygtk.org/ - PyGTK (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PyGObject
https://wiki.gnome.org/Projects/PyGObject - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - What's on the Kivy Roadmap?
https://github.com/orgs/kivy/discussions/10 - Graphical user interface (Wikipedia)
http://en.wikipedia.org/wiki/Graphical_user_interface - The Real History of the GUI
http://articles.sitepoint.com/article/real-history-gui - History of the graphical user interface (Wikipedia)
http://en.wikipedia.org/wiki/History_of_the_graphical_user_interface - Creating a GUI with Python: A Comprehensive Guide
https://coderivers.org/blog/how-to-create-a-gui-with-python/ - NiceGUI
https://nicegui.io/ - Buildozer’s documentation
https://buildozer.readthedocs.io/en/latest/ - buildozer 1.5.0 na PyPi
https://pypi.org/project/buildozer/ - Kivy 2.3.1 na PyPi
https://pypi.org/project/Kivy/ - pyglet 2.1.11 na PyPi
https://pypi.org/project/pyglet/ - pyglet Documentation
https://pyglet.readthedocs.io/en/latest/ - Kivy: BoxLayout
https://kivy-fork.readthedocs.io/en/latest/api-kivy.uix.boxlayout.html - Box Layout
https://kivy.org/doc/stable/api-kivy.uix.boxlayout.html#module-kivy.uix.boxlayout - Grid Layout
https://kivy.org/doc/stable/api-kivy.uix.gridlayout.html#module-kivy.uix.gridlayout - Stack Layout
https://kivy.org/doc/stable/api-kivy.uix.stacklayout.html#module-kivy.uix.stacklayout - Delphi (software)
https://en.wikipedia.org/wiki/Delphi_(software) - Visual Basic (classic)
https://en.wikipedia.org/wiki/Visual_Basic_(classic)















