Obsah
1. Grafické uživatelské rozhraní v Pythonu: kouzla s kreslicí plochou (2. část)
2. První demonstrační příklad: geometrické tvary, které je možné vložit (vykreslit) na plátno
4. Kruhové výseče a kruhové úseče
6. Čárkované a čerchované úsečky
9. Styly vykreslení uzavřených obrazců
10. Rámce (Frame) aneb kontejner pro další widgety uložený přímo na plátnu
11. Repositář s demonstračními příklady
1. Grafické uživatelské rozhraní v Pythonu: kouzla s kreslicí plochou (2. část)
V předchozí části seriálu o knihovnách určených pro tvorbu grafických uživatelských rozhraní v programovacím jazyku Python jsme se seznámili s takzvanou kreslicí plochou neboli canvasem. Připomeňme si, že canvas slouží jako kontejner, na nějž je možné vkládat různé geometrické tvary, rastrové obrázky nebo text. Jednotlivé tvary jsou v Pythonu reprezentovány plnohodnotnými objekty, takže je možné měnit jejich vlastnosti, naprogramovat reakci na různé události, přemisťovat je nebo je i mazat (u textů je možné změnit i zobrazený řetězec). Canvas je tak možné použít jako základ pro různé systémy typu CAD, editory map, editory schémat atd. Canvas je tak pravděpodobně tou nejlepší technologií, kterou knihovna Tkinter programátorům nabízí, a to i přesto, že není založena na žádné moderní sadě widgetů, ale „pouze“ na knihovně Tk s rozšířením Ttk.
Obrázek 1: Canvas s několika objekty – kruhem, dvojicí úseček a textem.
V následující tabulce (již jsme se s ní ostatně seznámili minule) je ukázáno, jaké objekty je možné na canvas pokládat:
Jméno objektu | Význam |
---|---|
arc | kruhový nebo eliptický oblouk, též kruhová výseč a úseč |
bitmap | bitmapový obrázek |
image | obecně vícebarevný rastrový obrázek |
line | úsečka, lomená úsečka nebo dokonce hladká křivka (spline) (!) |
oval | uzavřená kružnice nebo elipsa |
polygon | uzavřený polygon či tvar vytvořený ze spline křivek |
rectangle | čtverec nebo obdélník |
text | textový řetězec |
frame(window) | vnořené okno (rámec) se samostatným řízením |
Z těchto objektů můžeme vybrat geometrické tvary, které mohou být buď otevřené (liniové) nebo uzavřené (potenciálně vyplněné):
Geometrický tvar | Typ |
---|---|
line | liniový |
arc | liniový (oblouk) i uzavřený (kruhová výseč a úseč) |
rectangle | uzavřený |
polygon | uzavřený |
oval | uzavřený |
Poznámka: u všech uzavřených objektů je možné zakázat jejich výplň. To má dva významy – změní se způsob vykreslení takového geometrického tvaru a navíc bude objekt odlišně reagovat například na přejezd kurzoru myši (neexistující výplň již nebude součástí objektu).
2. První demonstrační příklad: geometrické tvary, které je možné vložit (vykreslit) na plátno
V dnešním prvním demonstračním příkladu je ukázán způsob vykreslení základních geometrických tvarů, ať již otevřených (liniových), tak i uzavřených. Kromě toho je na plátno vykreslena i jednoduchá mřížka; tu použijeme i v příkladech následujících. Význam některých nastavení bude popsán v dalším textu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_line(0, 0, 100, 100, fill='red', width=2, dash=8) canvas.create_arc(100, 1, 200, 100, outline='blue', start=45, extent=180, style=tkinter.ARC, width=2) canvas.create_oval(200, 1, 300, 100) canvas.create_oval(325, 25, 375, 75, fill="#a0a0ff") canvas.create_rectangle(50, 125, 150, 175, fill="#a0a0ff") canvas.create_text(300, 150, text="Hello world!", font="Helvetica 20") canvas.create_polygon(50, 225, 200, 300, 50, 375, fill="#80ff80") canvas.create_polygon(250, 225, 400, 300, 250, 375, fill="black", outline="red", width="5") root.mainloop()
Obrázek 2: Screenshot prvního demonstračního příkladu s geometrickými tvary a textem vloženými na canvas.
3. Oblouky
Poněkud zvláštní postavení mezi grafickými entitami má objekt pojmenovaný arc, pomocí kterého lze, jak již ostatně jeho název napovídá, vytvářet několik geometrických tvarů, konkrétně oblouky a také kruhové či eliptické výseče. S využitím arc je tedy možné vytvořit jak liniový objekt, tak i objekt plošný (a potenciálně vyplněný). Příkaz pro vytvoření a zobrazení oblouku vypadá následovně:
canvas.create_arc(x1, y1, x2, y2, další nepovinné volby)
popř.:
canvas.create_arc(seznam souřadnic, další nepovinné volby)
V případě, že skutečně potřebujeme vytvořit oblouk a nikoli kruhovou výseč nebo úseč, musí se použít nepovinný (pojmenovaný) parametry style, jehož hodnota musí být nastavena na konstantu tkinter.ARC:
canvas.create_arc(0, 0, 100, 100, outline='red', style=tkinter.ARC, width=2)
Pomocí souřadnic [x1, y1] a [x2, y2] se specifikuje obalový obdélník oblouku (jedná se o dva protilehlé vrcholy). Pokud se zadá obdélník se stejně dlouhými hranami, je vytvořen kruhový oblouk, v opačném případě se jedná o oblouk eliptický. Nejdůležitějšími nepovinnými volbami jsou start=hodnota a extent=hodnota (nikoli extend, sám tuto chybu dělám velmi často). Těmito volbami se udává počáteční a koncový úhel – mezi zadanými úhly bude oblouk vytvořen. Pomocí voleb outline, fill, stipple atd. je možné nastavit způsob zobrazení obrysů i výplně oblouků.
Obrázek 3: Screenshot druhého demonstračního příkladu s různými oblouky. Povšimněte si vlivu parametrů width a dash na tvar oblouku.
Opět se podívejme na demonstrační příklad, po jehož spuštění se na kreslicí plochu vloží několik oblouků s různými geometrickými a grafickými vlastnostmi:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_arc(0, 0, 100, 100, outline='red', style=tkinter.ARC, width=2) canvas.create_arc(100, 100, 200, 200, outline='blue', start=45, style=tkinter.ARC, width=2) canvas.create_arc(200, 0, 300, 100, outline='brown', extent=180, style=tkinter.ARC, width=2) canvas.create_arc(300, 100, 400, 200, outline='green', start=45, extent=270, style=tkinter.ARC, width=2) canvas.create_arc(0, 200, 100, 300, outline='red', style=tkinter.ARC, dash=8, width=2) canvas.create_arc(100, 300, 200, 400, outline='blue', start=45, style=tkinter.ARC, dash=8, width=10) canvas.create_arc(200, 200, 300, 300, outline='green', start=45, extent=270, style=tkinter.ARC, dash=3, width=50) canvas.create_arc(290, 290, 390, 390, outline='brown', extent=270, style=tkinter.ARC, dash=80, width=20) root.mainloop()
4. Kruhové výseče a kruhové úseče
Pokud při použití metody canvas.create_arc() nebudeme specifikovat hodnotu pojmenovaného parametru style, popř. mu přiřadíme hodnotu tkinter.PIESLICE, vykreslí se kruhová výseč:
Obrázek 4: Několik kruhových výsečí (pro oblouk o 180° není rozdíl mezi výsečí a úsečí patrný).
Poznámka: výseč ve skutečnosti nemusí být pouze kruhová, protože se, podobně jako u oblouku, tvar specifikuje s využitím souřadnic protilehlých vrcholů obalového obdélníku, takže lze tvořit i eliptickou výseč.
Čtvrtý obrázek byl vykreslen následujícím skriptem:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_arc(0, 0, 100, 100, fill='#ff8080') canvas.create_arc(100, 100, 200, 200, fill='#8080ff', start=45) canvas.create_arc(200, 0, 300, 100, fill='#80ffff', extent=180) canvas.create_arc(300, 100, 400, 200, fill='#ffff80', start=45, extent=270) canvas.create_arc(0, 200, 100, 300, fill='#ff8080', start=90, extent=270) canvas.create_arc(100, 300, 200, 400, fill='#8080ff', start=90+45, extent=270) canvas.create_arc(200, 200, 300, 300, fill='#80ffff', start=180, extent=180) canvas.create_arc(300, 300, 400, 400, fill='#ffff80', start=-45, extent=90) root.mainloop()
Knihovna Tkinter umožňuje, aby metoda canvas.create_arc() alternativně vykreslila i kruhovou úseč. Postačuje použít pojmenovaný nepovinný parametr style=tkinter.CHORD:
canvas.create_arc(300, 100, 400, 200, fill='#ffff80', start=45, extent=270, style=tkinter.CHORD)
Obrázek 5: Několik kruhových úsečí (pro oblouk o 180° není rozdíl mezi výsečí a úsečí patrný).
Pátý obrázek byl vykreslen následujícím skriptem:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_arc(0, 0, 100, 100, fill='#ff8080', style=tkinter.CHORD) canvas.create_arc(100, 100, 200, 200, fill='#8080ff', start=45, style=tkinter.CHORD) canvas.create_arc(200, 0, 300, 100, fill='#80ffff', extent=180, style=tkinter.CHORD) canvas.create_arc(300, 100, 400, 200, fill='#ffff80', start=45, extent=270, style=tkinter.CHORD) canvas.create_arc(0, 200, 100, 300, fill='#ff8080', start=90, extent=270, style=tkinter.CHORD) canvas.create_arc(100, 300, 200, 400, fill='#8080ff', start=90+45, extent=270, style=tkinter.CHORD) canvas.create_arc(200, 200, 300, 300, fill='#80ffff', start=180, extent=180, style=tkinter.CHORD) canvas.create_arc(300, 300, 400, 400, fill='#ffff80', start=-45, extent=90, style=tkinter.CHORD) root.mainloop()
5. Styly liniových tvarů
U všech vykreslovaných liniových tvarů je možné specifikovat styl vykreslování. Styly se zadávájí pomocí nepovinných (pojmenovaných) parametrů:
Jméno parametru | Význam |
---|---|
fill | barva úsečky (již známe) |
width | šířka liniového tvaru (opět již známe) |
dash | styl vykreslení (čárkovaná, čerchovaná, …), viz následující kapitolu |
cap | styl zakončení objektů s šířkou větší než jeden pixel |
join | styl spojení navazujících úseček s šířkou větší než jeden pixel |
Hodnoty parametru cap:
Hodnota | Význam |
---|---|
tkinter.BUTT | úsečka je ukončena přesně v koncových bodech |
tkinter.PROJECTING | úsečka je ukončena za koncovými body ve vzdálenosti odpovídající šířce/2 |
tkinter.ROUND | oba konce úsečky jsou zaobleny (polokruh) |
Hodnoty parametru join (viz též http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/cap-join-styles.html):
Hodnota | Význam |
---|---|
tkinter.ROUND | v místě lomu polyčáry je umístěn kruh o průměru odpovídajícímu její šířce |
tkinter.BEVEL | lom polyčáry je „useknut“ v koncovém bodě |
tkinter.MITER | hrany úseček jsou protaženy až do místa, kde se protínají |
V dalším příkladu je použití některých stylů vykreslení ukázáno. Povšimněte si především způsobu použití stylů join a cap při navazování úseček:
Obrázek 6: Různé styly vykreslení na sebe navazujících úseček (polyčar). U spodních dvanácti polyčar je bílou barvou naznačena „kostra“ polyčáry spojující koncové body (vrcholy)
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_line(10, 10, 90, 90) canvas.create_line(110, 10, 190, 90, fill='#8080ff') canvas.create_line(210, 10, 290, 90, fill='#ffff80', width=8) canvas.create_line(310, 10, 390, 90, fill='#80aa80', width=8, dash=15) canvas.create_line(10, 110, 90, 190, fill='red', width=12) canvas.create_line(110, 110, 190, 190, fill='red', width=12, cap=tkinter.BUTT) canvas.create_line(210, 110, 290, 190, fill='red', width=12, cap=tkinter.PROJECTING) canvas.create_line(310, 110, 390, 190, fill='red', width=12, cap=tkinter.ROUND) canvas.create_line(10, 110, 90, 190, fill='white') canvas.create_line(110, 110, 190, 190, fill='white') canvas.create_line(210, 110, 290, 190, fill='white') canvas.create_line(310, 110, 390, 190, fill='white') canvas.create_line(10, 210, 50, 290, 90, 210, fill='red', width=12) canvas.create_line(110, 210, 150, 290, 190, 210, fill='red', width=12, cap=tkinter.BUTT) canvas.create_line(210, 210, 250, 290, 290, 210, fill='red', width=12, cap=tkinter.PROJECTING) canvas.create_line(310, 210, 350, 290, 390, 210, fill='red', width=12, cap=tkinter.ROUND) # pomocne usecky canvas.create_line(10, 210, 50, 290, 90, 210, fill='white') canvas.create_line(110, 210, 150, 290, 190, 210, fill='white') canvas.create_line(210, 210, 250, 290, 290, 210, fill='white') canvas.create_line(310, 210, 350, 290, 390, 210, fill='white') canvas.create_line(10, 310, 50, 390, 90, 310, fill='red', width=12) canvas.create_line(110, 310, 150, 390, 190, 310, fill='red', width=12, join=tkinter.ROUND) canvas.create_line(210, 310, 250, 390, 290, 310, fill='red', width=12, join=tkinter.BEVEL) canvas.create_line(310, 310, 350, 390, 390, 310, fill='red', width=12, join=tkinter.MITER) # pomocne usecky canvas.create_line(10, 310, 50, 390, 90, 310, fill='white') canvas.create_line(110, 310, 150, 390, 190, 310, fill='white') canvas.create_line(210, 310, 250, 390, 290, 310, fill='white') canvas.create_line(310, 310, 350, 390, 390, 310, fill='white') root.mainloop()
6. Čárkované a čerchované úsečky
Při kresbě liniových obrazců nebo okrajů plošných obrazců je možné zvolit vzorek (styl) úseček – plná (výchozí nastavení), čárkovaná, čerchovaná, střídavá apod. K nastavení vzorku vykreslení úsečky slouží nepovinné parametry pojmenované dash a dashoff. Pokud se do parametru dash předá jediná číselná hodnota, vytvoří se čárkovaná úsečka s délkou jednotlivých úseků odpovídajících specifikované hodnotě. Je také možné zvolit si jiné jednotky, například centimetry, potom se ovšem namísto číselné hodnoty předává řetězec (viz úvodní část tohoto seriálu).
Pokud se však do parametru dash předá n-tice, budou jednotlivé prvky n-tice postupně interpretovány jako délka viditelné části úsečky, délka neviditelné části, opět délka viditelné části atd. Tímto způsobem lze tedy vytvořit například i čerchovanou čáru, tečkovanou apod. Pomocný pojmenovaný parametr dashoff specifikuje posun (offset) vzorku, přičemž posun může být kladný i záporný (to se hodí v těch případech, kdy potřebujeme, aby byl začátek úsečky tvořen neviditelným segmentem):
Demonstrační příklad, v němž se používají nepovinné parametry pojmenované dash a dashoff, vypadá následovně:
Obrázek 7: Úsečky vykreslené pomocí segmentů rozdílné délky.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_line(10, 10, 90, 90) canvas.create_line(110, 10, 190, 90, fill='#8080ff') canvas.create_line(210, 10, 290, 90, fill='#ffff80', width=8) canvas.create_line(310, 10, 390, 90, fill='#80aa80', width=8, dash=15) canvas.create_line(10, 110, 90, 190, width=2, dash=(12, 3)) canvas.create_line(110, 110, 190, 190, width=2, dash=(9, 6)) canvas.create_line(210, 110, 290, 190, width=2, dash=(6, 9)) canvas.create_line(310, 110, 390, 190, width=2, dash=(3, 12)) canvas.create_line(10, 210, 90, 290, width=2, dash=(12, 2, 2, 2)) canvas.create_line(110, 210, 190, 290, width=2, dash=(12, 2, 4, 2)) canvas.create_line(210, 210, 290, 290, width=2, dash=(12, 4, 2, 4)) canvas.create_line(310, 210, 390, 290, width=2, dash=(12, 2, 2, 2, 2, 2)) canvas.create_line(10, 310, 90, 390, width=2, dash=(12, 2, 2, 2), dashoff=0) canvas.create_line(110, 310, 190, 390, width=2, dash=(12, 2, 4, 2), dashoff=5) canvas.create_line(210, 310, 290, 390, width=2, dash=(12, 4, 2, 4), dashoff=10) canvas.create_line(310, 310, 390, 390, width=2, dash=(12, 2, 2, 2, 2, 2), dashoff=-5) root.mainloop()
7. Kresba šipek
V některých aplikacích může být velmi užitečná podpora pro kresbu šipek k libovolné úsečce. Lze specifikovat, který konec (či konce) úsečky má být opatřen šipkou:
canvas.create_line(110, 50, 190, 50, arrow=tkinter.FIRST) canvas.create_line(210, 50, 290, 50, arrow=tkinter.LAST) canvas.create_line(310, 50, 390, 50, arrow=tkinter.BOTH)
Také je možné nastavit tvar šipky pomocí tří vzdáleností d1, d2 a d3, které se předávají pomocí n-tice tak, jak je to ukázáno níže:
canvas.create_line(10, 350, 90, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 10, 10))
Díky tomu je možné vytvořit šipky prakticky jakéhokoli žádaného tvaru, o čemž se můžete přesvědčit pohledem na tento obrázek:
Poznámka: šipky jsou samozřejmě vykresleny korektně i ve chvíli, kdy je úsečka kreslena pod jiným úhlem.
Obrázek 8: Uživatelsky definované šipky.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_line(10, 50, 90, 50) canvas.create_line(110, 50, 190, 50, arrow=tkinter.FIRST) canvas.create_line(210, 50, 290, 50, arrow=tkinter.LAST) canvas.create_line(310, 50, 390, 50, arrow=tkinter.BOTH) canvas.create_line(10, 150, 90, 150, width=5, ) canvas.create_line(110, 150, 190, 150, width=5, arrow=tkinter.FIRST) canvas.create_line(210, 150, 290, 150, width=5, arrow=tkinter.LAST) canvas.create_line(310, 150, 390, 150, width=5, arrow=tkinter.BOTH) canvas.create_line(10, 250, 90, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 10, 10)) canvas.create_line(110, 250, 190, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 20, 10)) canvas.create_line(210, 250, 290, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 5, 10)) canvas.create_line(310, 250, 390, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 0, 10)) canvas.create_line(10, 350, 90, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 10, 10)) canvas.create_line(110, 350, 190, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 20, 10)) canvas.create_line(210, 350, 290, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 5, 10)) canvas.create_line(310, 350, 390, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 0, 10)) root.mainloop()
8. Uzavřené obrazce
Kromě již popsaných kruhových výsečí a kruhových úsečí je možné pracovat i s dalšími uzavřenými (a tedy potenciálně vyplněnými) geometrickými tvary. Dalším jednoduchým uzavřeným obrazcem je obdélník, který je vytvořen pomocí metody canvas.create_rectangle. Vytvoření se provádí následovně:
canvas.create_rectangle(20, 220, 80, 280, fill='#ff8080')
Podobně jako u oblouku, i u obdélníků (a samozřejmě i čtverců) je možné volit styl výplně a obrysu.
Dále je možné pracovat s kruhem či elipsou. Ty se vytváří s využitím metody nazvané canvas.create_oval(), například:
canvas.create_oval(x1, y1, x2, y2, fill='#8080ff')
Souřadnice [x1, y1] a [x2, y2] udávají protilehlé vrcholy obalového obdélníka, podobně jako jsme to již viděli v případě oblouku. Pokud má obalový obdélník stejně dlouhé hrany, vykreslí se kruh či kružnice (podle nastavení vyplňování), v opačném případě se vykreslí elipsa. Způsob vyplnění tohoto typu objektu je stejný, jako u předchozích dvou typů uzavřených geometrických tvarů.
Nejsložitější uzavřené tvary se tvoří s využitím metody canvas.create_polygon(). Při vytváření tohoto objektu se může zadat prakticky libovolné množství souřadnic vrcholů. Buď se všechny souřadnice specifikují přímo:
canvas.create_polygon(310, 20, 390, 20, 350, 80, fill='#ffff80', outline='black')
nebo (a v praxi mnohem častěji) pomocí seznamu souřadnic.
canvas.create_polygon(seznam_se_souřadnicemi, fill='#ffff80', outline='black')
Polygon může být buď vyplněný nebo prázdný. Kromě klasického polygonu, jehož hrany jsou tvořeny polyčárou, je možné vytvářet i tvar ohraničený spline křivkami. Nastavení spline křivek se děje pomocí voleb smooth (povoluje či zakazuje spline křivky) a splinesteps hodnota (způsob rozdělení ideální křivky na úsečkové segmenty).
Obrázek 9: Uzavřené tvary.
Opět se podívejme na jednoduchý demonstrační příklad, v němž jsou tyto objekty vykresleny:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_rectangle(10, 30, 90, 70, fill='#ff8080') canvas.create_rectangle(30, 110, 70, 190, fill='#ff8080') canvas.create_rectangle(20, 220, 80, 280, fill='#ff8080') canvas.create_oval(110, 30, 190, 70, fill='#8080ff') canvas.create_oval(130, 110, 170, 190, fill='#8080ff') canvas.create_oval(120, 220, 180, 280, fill='#8080ff') canvas.create_polygon(210, 20, 290, 20, 250, 80, fill='#ffff80') canvas.create_polygon(310, 20, 390, 20, 350, 80, fill='#ffff80', outline='black') canvas.create_polygon(210, 120, 250, 140, 290, 120, 250, 180, fill='#80ff80') canvas.create_polygon(310, 120, 350, 140, 390, 120, 350, 180, fill='#80ff80', outline='black') canvas.create_polygon(210, 220, 290, 220, 250, 280, fill='#ffff80', smooth=1) canvas.create_polygon(310, 220, 390, 220, 350, 280, fill='#ffff80', outline='black', smooth=1) canvas.create_polygon(210, 320, 250, 340, 290, 320, 250, 380, fill='#80ff80', smooth=1) canvas.create_polygon(310, 320, 350, 340, 390, 320, 350, 380, fill='#80ff80', outline='black', smooth=1) root.mainloop()
9. Styly vykreslení uzavřených obrazců
(Nejenom) u uzavřených obrazců můžeme zvolit, jaká vlastnost se má změnit ve chvíli, kdy se nad obrazcem pohybuje kurzor myši:
Vlastnost neaktivní entity | Vlastnost po najetí myší |
---|---|
fill | activefill |
outline | activeoutline |
width | activewidth |
dash | activedash |
Obrázek 10: Uzavřené tvary s explicitně zadanými vlastnostmi.
Následující příklad je tedy interaktivní, protože se jednotlivé entity změní ve chvíli, kdy na ně najedete kurzorem myši:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys WIDTH = 400 HEIGHT = 400 GRID_SIZE = 100 def exit(): sys.exit(0) def basic_canvas(root, width, height, grid_size): canvas = tkinter.Canvas(root, width=width, height=height, background='#ccffcc') canvas.pack() draw_grid(canvas, width, height, grid_size) return canvas def draw_grid(canvas, width, height, grid_size): for x in range(0, width, grid_size): canvas.create_line(x, 0, x, height, dash=7, fill="gray") for y in range(0, height, grid_size): canvas.create_line(0, y, width, y, dash=7, fill="gray") root = tkinter.Tk() canvas = basic_canvas(root, WIDTH, HEIGHT, GRID_SIZE) canvas.create_rectangle(10, 30, 90, 70, fill='#ff8080', width=2, activefill='white') canvas.create_rectangle(110, 30, 190, 70, fill='#ff8080', width=2, dash=(5, 5), activedash=1) canvas.create_rectangle(30, 110, 70, 190, fill='#ff8080', activeoutline='yellow') canvas.create_rectangle(20, 220, 80, 280, fill='#ff8080', activeoutline='yellow', activewidth='5') canvas.create_oval(130, 110, 170, 190, fill='#8080ff', width=2, activedash=(10, 10)) canvas.create_oval(120, 220, 180, 280, fill=None, activefill='#8080ff') canvas.create_rectangle(210, 30, 290, 70, fill=None, width=2, activefill='white') canvas.create_rectangle(310, 30, 390, 70, fill=None, width=2, dash=(5, 5), activedash=1) canvas.create_rectangle(230, 110, 270, 190, fill=None, activeoutline='yellow', width=5) canvas.create_rectangle(220, 220, 280, 280, fill=None, activeoutline='yellow', activewidth='5') canvas.create_oval(330, 110, 370, 190, fill=None, width=2, activedash=(10, 10)) canvas.create_oval(320, 220, 380, 280, fill=None, activefill='#8080ff', width=5) canvas.create_line(10, 330, 90, 370, fill='#80ff80', width=2, activefill='white') canvas.create_line(110, 330, 190, 370, fill='#80ff80', width=20, activefill='white') canvas.create_line(210, 330, 290, 370, fill='#80ff80', width=20, activefill='white', dash=10) root.mainloop()
10. Rámce (Frame) aneb kontejner pro další widgety uložený přímo na plátnu
Nejpodivnějším objektem, který můžeme na canvas položit, je takzvaný rámec neboli frame. Rámec je z pohledu uživatele tvořen obdélníkem s volitelnou barvou pozadí a konfigurovatelným reliéfem; z pohledu programátora se pak jedná o kontejner, na který je možné vkládat další widgety, například tlačítka apod. To je další důkaz toho, že canvas není pouhou „glorifikovanou bitmapou“, ale integrální součástí grafického uživatelského rozhraní, které je možné v Tkinteru vytvořit.
V následujícím příkladu je vytvořen prázdný rámec, se zvoleným reliéfem. Kromě plátna jsou do GUI aplikace vloženy další dva widgety – textové návěští a tlačítko. Ty jsou ovšem vloženy přímo do hlavního okna aplikace, takže jsou zobrazeny pod canvasem (protože canvas byl pomocí .pack do okna vložen jako první):
Obrázek 11: Prázdný rámec na canvasu, řídicí prvky jsou zobrazeny pod canvasem.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() canvas = tkinter.Canvas(root, width=300, height=300, background='white') canvas.pack() canvas.create_line(0, 150, 80, 20, 220, 280, 300, 150, dash=10) canvas.create_line(0, 150, 80, 20, 220, 280, 300, 150, smooth=True, width=2, fill="red") nested_window = tkinter.Frame(relief=tkinter.RAISED) canvas.create_window(150, 150, width=150, height=150, window=nested_window) label = tkinter.Label(root, text="Hello world!") button = tkinter.Button(root, text="Close window", command=exit) label.pack() button.pack() root.mainloop()
V příkladu dalším je již vše jinak – dva widgety (textové návěští a tlačítko) jsou nyní vloženy na rámec umístěný na canvasu:
Obrázek 12: Řídicí prvky jsou vloženy do rámce na canvasu.
Opět následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python import tkinter from tkinter import ttk import sys def exit(): sys.exit(0) root = tkinter.Tk() canvas = tkinter.Canvas(root, width=300, height=300, background='white') canvas.pack() canvas.create_line(0, 150, 80, 20, 220, 280, 300, 150, dash=10) canvas.create_line(0, 150, 80, 20, 220, 280, 300, 150, smooth=True, width=2, fill="red") nested_window = tkinter.Frame(relief=tkinter.RAISED) canvas.create_window(150, 150, width=150, height=150, window=nested_window) label = tkinter.Label(nested_window, text="Hello world!") button = tkinter.Button(nested_window, text="Close window", command=exit) label.pack() button.pack() root.mainloop()
11. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů naleznete pod následujícími odkazy:
Obrázek 13: Bitmapa se specifikovanou barvou popředí a pozadí (ukázka z dalšího pokrčování seriálu).
Obrázek 14: Texty s různě nastavenými kotvicími body (ukázka z dalšího pokrčování seriálu).
12. Odkazy na Internetu
- Hra Breakout napísaná v Tkinteri
https://www.root.cz/clanky/hra-breakout-napisana-v-tkinteri/ - Hra Snake naprogramovaná v Pythone s pomocou Tkinter
https://www.root.cz/clanky/hra-snake-naprogramovana-v-pythone-s-pomocou-tkinter/ - The Tkinter Canvas Widget
http://effbot.org/tkinterbook/canvas.htm - Ovládací prvek (Wikipedia)
https://cs.wikipedia.org/wiki/Ovl%C3%A1dac%C3%AD_prvek_%28po%C4%8D%C3%ADta%C4%8D%29 - Rezervovaná klíčová slova v Pythonu
https://docs.python.org/3/reference/lexical_analysis.html#keywords - TkDocs: Styles and Themes
http://www.tkdocs.com/tutorial/styles.html - Drawing in Tkinter
http://zetcode.com/gui/tkinter/drawing/ - Changing ttk widget text color (StackOverflow)
https://stackoverflow.com/questions/16240477/changing-ttk-widget-text-color - 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/ - 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/ - 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/ - 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 Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro - PyQt (Wikipedia)
https://cs.wikipedia.org/wiki/PyGTK - Stránky projektu PySide
https://wiki.qt.io/PySide - PySide (Wikipedia)
https://en.wikipedia.org/wiki/PySide - Stránky projektu Kivy
https://kivy.org/#home - Kivy (framework, Wikipedia)
https://en.wikipedia.org/wiki/Kivy_(framework) - QML Applications
http://doc.qt.io/qt-5/qmlapplications.html - KDE
https://www.kde.org/ - Qt
https://www.qt.io/ - GNOME
https://en.wikipedia.org/wiki/GNOME - Category:Software that uses PyGTK
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGTK - Category:Software that uses PyGObject
https://en.wikipedia.org/wiki/Category:Software_that_uses_PyGObject - Category:Software that uses wxWidgets
https://en.wikipedia.org/wiki/Category:Software_that_uses_wxWidgets - GIO
https://developer.gnome.org/gio/stable/ - GStreamer
https://gstreamer.freedesktop.org/ - GStreamer (Wikipedia)
https://en.wikipedia.org/wiki/GStreamer - Wax Gui Toolkit
https://wiki.python.org/moin/Wax - Python Imaging Library (PIL)
http://infohost.nmt.edu/tcc/help/pubs/pil/ - Why Pyjamas Isn’t a Good Framework for Web Apps (blogpost z roku 2012)
http://blog.pyjeon.com/2012/07/29/why-pyjamas-isnt-a-good-framework-for-web-apps/