Obsah
1. Kreslicí plátno v knihovně PySimpleGUI
3. Specifikace souřadnic a rozměrů grafických prvků
4. Seznam podporovaných grafických objektů, které lze vykreslit
5. Prázdné plátno na ploše okna
6. Vykreslení základních grafických prvků na kreslicí plochu
10. Odstranění všech grafických tvarů z plátna
11. Upravený příklad s podporou vykreslení kružnic, polyčar a spline křivek
12. Oblouky, kruhové výseče a kruhové úseče
13. Rozšíření demonstračního příkladu o kruhové výseče a úseče
14. Nastavení stylů vykreslovaných prvků
16. Styl ukončení liniových obrazců a způsob vykreslení jejich zalomení
17. Vykreslení polyčar s různým stylem zalomení a vykreslení koncových prvků
18. Vykreslení úseček různým stylem (plná, čárkovaná, čerchovaná…)
19. Repositář s demonstračními příklady
1. Kreslicí plátno v knihovně PySimpleGUI
Práci s grafikou, konkrétně s 2D grafikou, můžeme chápat ve dvou úrovních. Na vyšší úrovni manipulujeme s jednotlivými geometricky popsanými tvary, jakými jsou například úsečka, obdélník, kruh, text či spline křivka. Každému tvaru může být přiřazeno značné množství atributů, jedná se například o tloušťku čáry, kterou je vykreslena hranice objektu, barva a styl výplně uzavřených objektů či font vykreslovaného textu. Na úrovni nižší se může manipulovat přímo s pixely, které tvoří zobrazovaný rastrový obrázek.
V knihovně PySimpleGUI je pro práci na zmíněné vyšší úrovni určen widget canvas spolu s dalšími objekty, na nižší úrovni pak widgety (resp. přesněji řečeno objekty) bitmap, image a photo. Objekt typu image přitom zapouzdřuje více typů obrázků, od monochromatického BitmapImage přes vícebarevný PhotoImage až po uživatelsky vytvářené formáty.
2. Ovládací prvek canvas
Jak jsme si již řekli v úvodní kapitole, je pro práci s grafikou na vyšší úrovni nabízen v knihovně PySimpleGUI widget canvas, česky bychom mohli tento název přeložit jako (malířské) plátno. Nenechte se ovšem zmýlit tímto názvem, který má v jiných programových knihovnách odlišný (většinou jednodušší) význam, zde se jedná o velmi mocný widget, který současně slouží jako kontejner pro další objekty.
Vkládané objekty si své vlastnosti zachovávají, tj. je možné s nimi i po jejich vykreslení interaktivně i programově pohybovat, měnit jejich vlastnosti apod. K tomu musíme připočítat možnost uložit celé plátno do PostScriptového souboru se zachováním vektorových charakteristik plátna (tj. neprovádí se ztrátový převod na bitmapy). To mj. znamená, že se vlastnosti canvasu do určité míry podobají použití SVG na webových stránkách; naproti tomu „webový canvas“ je vlastně pouze bitmapa, do níž se objekty vykreslují a ihned po jejich vykreslení se jejich vlastnosti ztratí (zůstanou jen barevné pixely).
3. Specifikace souřadnic a rozměrů grafických prvků
Každý objekt, který je na kreslicí plátno umístěn, musí mít specifikovány souřadnice počátečního a koncového bodu, v případě lomených čar, spline křivek a polygonů se samozřejmě specifikuje bodů více. Souřadnice bodů mohou být zadány pomocí více jednotek, jež se rozlišují podle jednoho písmene zapsaného za numerickou hodnotou (hodnoty jsou samozřejmě platné pouze při správně nakalibrované obrazovce, což kupodivu mnoho systémů dodnes nedodržuje):
Přípona | Význam |
---|---|
m | milimetry |
c | centimetry |
i | palce |
p | body (implicitní hodnota) |
4. Seznam podporovaných grafických objektů, které lze vykreslit
V následující tabulce jsou vypsány ty grafické objekty, které je možné na plátno pokládat a tím pádem je vykreslovat (a popř. s nimi dále manipulovat):
Jméno objektu | Význam |
---|---|
arc | kruhový nebo eliptický oblouk |
bitmap | bitmapový obrázek |
image | obecně vícebarevný rastrový obrázek |
line | úsečka, lomená úsečka nebo dokonce hladká spline křivka (!) |
oval | uzavřená kružnice nebo elipsa (nenechte se zmást matoucím názvem) |
polygon | uzavřený polygon či tvar vytvořený ze spline křivek |
rectangle | čtverec nebo obdélník |
text | textový řetězec |
window | vnořené okno se samostatným řízením |
5. Prázdné plátno na ploše okna
Již minule jsme si ukázali, jakým způsobem se kreslicí plátno vkládá na plochu okna. Připomeňme si, že se plátno chová stejně jako jakýkoli jiný prvek grafického uživatelského rozhraní – je tedy součástí layoutu okna. Většinou však musíme explicitně specifikovat rozměry plátna, které se typicky uvádí v pixelech:
right_column = [ [ sg.Canvas(background_color='white', size=(320, 240)), ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ]
Výsledek může vypadat následovně:
Obrázek 1: Prázdné plátno umístěné do okna.
Úplný zdrojový kód příkladu, který po svém spuštění zobrazí okno a na něm (kromě dalších ovládacích prvků) i kreslicí plátno:
import PySimpleGUI as sg # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Exit", size=(8, 0))], ] right_column = [ [ sg.Canvas(background_color='white', size=(320, 240)), ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] # vytvoření okna s ovládacími prvky window = sg.Window("Window #30", layout, use_custom_titlebar=False) # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # po přečtení události okno zavřeme window.close()
6. Vykreslení základních grafických prvků na kreslicí plochu
V následujícím demonstračním příkladu je ukázáno jednoduché použití plátna, na které jsou s využitím metody create_TYP_OBJEKTU vloženy jednoduché objekty – zde konkrétně dvojice úseček. Vzhledem k tomu, že se při specifikaci souřadnic neuvádí u číselných hodnot žádná přípona (používáme celá čísla), předpokládá se, že jsou všechny hodnoty zapsané v bodech (pixelech). To také znamená, že na různých obrazovkách a při různých rozlišeních budou mít nakreslené objekty jinou velikost, což nám v tomto případě nemusí vadit.
# reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # získat velikost plátna (v pixelech) size = canvas.get_size() # vykreslit dvojici úseček tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0)
Obrázek 2: Dvojice úseček vykreslená na plátno.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
import PySimpleGUI as sg # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=(8, 0))], [sg.Button("Exit", size=(8, 0))], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas): """Vykreslení úseček na plátno.""" # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # získat velikost plátna (v pixelech) size = canvas.get_size() # vykreslit dvojici úseček tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0) # vytvoření okna s ovládacími prvky window = sg.Window("Window #31", layout, use_custom_titlebar=False, finalize=True) # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas) window.refresh() # po přečtení události okno zavřeme window.close()
7. Lomené čáry (polyčáry)
Pro práci s neuzavřenými křivkami je možné použít geometrický objekt line. Na první pohled se jedná o velmi jednoduchý objekt, ve skutečnosti s ním však lze vytvářet i velmi složité obrazce složené například ze spline křivek. V nejjednodušším případě se pomocí objektu line vykreslí pouze jedna úsečka:
def draw_lines(canvas, tkcanvas): """Vykreslení úseček na plátno.""" size = canvas.get_size() tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0)
Dále je možné specifikovat více bodů (vrcholů), což značí, že se na plátno vykreslí místo jedné úsečky lomená čára (polyčára, polyline):
def draw_polyline(canvas, tkcanvas): """Vykreslení polyčáry na plátno.""" border = 1 size = canvas.get_size() tkcanvas.create_line( border, size[1]-border, size[0]/3, border, size[0]*2/3, size[1]-border, size[0]-border, border)
Obrázek 3: Polyčára.
Obrázek 4: Spline křivka se stejnými vrcholy, jako předchozí polyčára.
8. Spline křivky
Při vytváření polyčar je možné specifikovat poměrně velké množství voleb. Pravděpodobně nejpoužívanější jsou volby fill (volba barvy úsečky), width (tloušťka čáry), joinstyle (způsob ukončení hran) a arrow (vykreslení šipek na konci čar). Kromě toho je také možné specifikovat, že se má místo lomené čáry vykreslit spline křivka. To zajišťuje volba smooth s booleovskou hodnotou a popř. i splinesteps s celočíselnou hodnotou, kterou se zadává, na kolik úsečkových segmentů má být každá část lomené čáry rozdělena. Způsob vykreslení spline křivky na canvas je ukázán v další funkci:
def draw_spline(canvas, tkcanvas): """Vykreslení spline křivky na plátno.""" border = 1 size = canvas.get_size() tkcanvas.create_line( border, size[1]-border, size[0]/3, border, size[0]*2/3, size[1]-border, size[0]-border, border, smooth=smooth)
9. Kružnice, kruh nebo elipsa
Všechny tři tvary vypsané v nadpisu této kapitoly, tedy jak kružnice, tak i kruh či elipsa, se na plochu kreslicího plátna přidávají stejnou metodou. Ta se jmenuje poněkud nepřesně create_oval, ovšem výsledkem je namísto oválu skutečně kružnice nebo elipsa. Dalšími parametry je posléze možné zvolit, jestli se má daný geometrický útvar vykreslit vyplněný (tedy kruh a vyplněná elipsa) nebo nevyplněný (tedy kružnice a nevyplněná elipsa):
def draw_ellipse(canvas, tkcanvas): """Vykreslení oválu na plátno.""" border = 5 size = canvas.get_size() tkcanvas.create_oval(border, border, size[0]-border, size[1]-border)
Obrázek 5: Vykreslení kružnice na plátno.
10. Odstranění všech grafických tvarů z plátna
Jak jsme si již řekli v úvodním textu, je nutné kreslicí plátno realizované v knihovně PySimpleGUI (a interně řešené přes knihovnu TkInter) chápat nikoli jako pouhou tabuli nebo papír, na který se vykreslí tvar, jenž je převeden na sadu pixelů. Spíše se jedná o kontejner, do kterého se vkládají geometrické tvary a plátno je automaticky vizualizuje. Později je možné měnit parametry těchto tvarů nebo je z plátna zcela odstranit. Vymazání plátna, tj. odstranění všech geometrických tvarů, které jsou zde umístěny, se provede následovně:
def clear_canvas(tkcanvas): """Vymazání všech objektů z kreslicího plátna.""" tkcanvas.delete("all")
11. Upravený příklad s podporou vykreslení kružnic, polyčar a spline křivek
Nyní již nastal čas si uvést upravený zdrojový kód demonstračního příkladu, který uživatelům umožní vykreslovat úsečky, polyčáry, spline křivky a kružnice. Kreslicí plátno lze kdykoli smazat použitím k tomu určeného tlačítka.
Úplný zdrojový kód takto upraveného příkladu vypadá následovně:
import PySimpleGUI as sg # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) button_size = (10, 0) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=button_size)], [sg.Button("Draw ellipse", size=button_size)], [sg.Button("Draw polyline", size=button_size)], [sg.Button("Draw spline", size=button_size)], [sg.Button("Clear canvas", size=button_size)], [sg.Button("Exit", size=button_size)], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas, tkcanvas): """Vykreslení úseček na plátno.""" size = canvas.get_size() tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0) def draw_ellipse(canvas, tkcanvas): """Vykreslení oválu na plátno.""" border = 5 size = canvas.get_size() tkcanvas.create_oval(border, border, size[0]-border, size[1]-border) def draw_polyline(canvas, tkcanvas, smooth): """Vykreslení polyčáry na plátno.""" border = 1 size = canvas.get_size() tkcanvas.create_line( border, size[1]-border, size[0]/3, border, size[0]*2/3, size[1]-border, size[0]-border, border, smooth=smooth) def clear_canvas(tkcanvas): """Vymazání všech objektů z kreslicího plátna.""" tkcanvas.delete("all") # vytvoření okna s ovládacími prvky window = sg.Window("Window #32", layout, use_custom_titlebar=False, finalize=True) # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas, tkcanvas) elif event == "Draw ellipse": draw_ellipse(canvas, tkcanvas) elif event == "Draw polyline": draw_polyline(canvas, tkcanvas, False) elif event == "Draw spline": draw_polyline(canvas, tkcanvas, True) elif event == "Clear canvas": clear_canvas(tkcanvas) window.refresh() # po přečtení události okno zavřeme window.close()
12. Oblouky, kruhové výseče a kruhové úseče
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ů.
V případě, že 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č.
def draw_pieslices(canvas, tkcanvas): """Vykreslení kruhových výsečí na plátno.""" tkcanvas.create_arc(1, 1, 100, 100, fill="#ff8080") tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45) tkcanvas.create_arc(200, 1, 300, 100, fill="#80ffff", extent=180) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270) tkcanvas.create_arc(1, 200, 100, 300, fill="#ff8080", start=90, extent=270) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90)
Obrázek 6: Kruhové výseče.
Knihovna Tkinter (z níž PySimpleGUI canvas používá) umožňuje, aby metoda canvas.create_arc() alternativně vykreslila i kruhovou úseč. Postačuje použít pojmenovaný nepovinný parametr style=tkinter.CHORD:
def draw_chords(canvas, tkcanvas): """Vykreslení kruhových úsečí na plátno.""" tkcanvas.create_arc(0, 0, 100, 100, fill="#ff8080", style=tkinter.CHORD) tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45, style=tkinter.CHORD) tkcanvas.create_arc(200, 0, 300, 100, fill="#80ffff", extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(0, 200, 100, 300, fill="#ff8080", start=90, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90, style=tkinter.CHORD)
Obrázek 6: Kruhové úseče.
13. Rozšíření demonstračního příkladu o kruhové výseče a úseče
Opět si tedy můžeme rozšířit původní demonstrační příklad, tentokrát o možnost vykreslovat kruhové výseče a úseče. Grafické uživatelské rozhraní příkladu se samozřejmě muselo změnit (přidala se nová tlačítka).
Úplný zdrojový kód takto upraveného příkladu vypadá následovně:
import PySimpleGUI as sg import tkinter # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) button_size = (10, 0) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=button_size)], [sg.Button("Draw ellipse", size=button_size)], [sg.Button("Draw polyline", size=button_size)], [sg.Button("Draw spline", size=button_size)], [sg.Button("Draw pieslices", size=button_size)], [sg.Button("Draw chords", size=button_size)], [sg.Button("Clear canvas", size=button_size)], [sg.Button("Exit", size=button_size)], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas, tkcanvas): """Vykreslení úseček na plátno.""" size = canvas.get_size() tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0) def draw_ellipse(canvas, tkcanvas): """Vykreslení oválu na plátno.""" border = 5 size = canvas.get_size() tkcanvas.create_oval(border, border, size[0]-border, size[1]-border) def draw_pieslices(canvas, tkcanvas): """Vykreslení kruhových výsečí na plátno.""" tkcanvas.create_arc(1, 1, 100, 100, fill="#ff8080") tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45) tkcanvas.create_arc(200, 1, 300, 100, fill="#80ffff", extent=180) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270) tkcanvas.create_arc(1, 200, 100, 300, fill="#ff8080", start=90, extent=270) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90) def draw_chords(canvas, tkcanvas): """Vykreslení kruhových úsečí na plátno.""" tkcanvas.create_arc(0, 0, 100, 100, fill="#ff8080", style=tkinter.CHORD) tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45, style=tkinter.CHORD) tkcanvas.create_arc(200, 0, 300, 100, fill="#80ffff", extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(0, 200, 100, 300, fill="#ff8080", start=90, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90, style=tkinter.CHORD) def draw_polyline(canvas, tkcanvas, smooth): """Vykreslení polyčáry na plátno.""" border = 1 size = canvas.get_size() tkcanvas.create_line( border, size[1]-border, size[0]/3, border, size[0]*2/3, size[1]-border, size[0]-border, border, smooth=smooth) def clear_canvas(tkcanvas): """Vymazání všech objektů z kreslicího plátna.""" tkcanvas.delete("all") # vytvoření okna s ovládacími prvky window = sg.Window("Window #33", layout, use_custom_titlebar=False, finalize=True) # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas, tkcanvas) elif event == "Draw ellipse": draw_ellipse(canvas, tkcanvas) elif event == "Draw polyline": draw_polyline(canvas, tkcanvas, False) elif event == "Draw spline": draw_polyline(canvas, tkcanvas, True) elif event == "Draw pieslices": draw_pieslices(canvas, tkcanvas) elif event == "Draw chords": draw_chords(canvas, tkcanvas) elif event == "Clear canvas": clear_canvas(tkcanvas) window.refresh() # po přečtení události okno zavřeme window.close()
14. Nastavení stylů vykreslovaných prvků
Grafické objekty vkládané na plátno mohou mít nastaveno velké množství vlastností, jak je ukázáno na modifikaci předchozího příkladu (nejsou zde ukázány zdaleka všechny vlastnosti, ty jsou uvedeny a podrobně popsány v helpu):
Vlastnost | Popis |
---|---|
outline | barva obrysu |
fill | barva výplně |
width | šířka obrysu |
dash | vzorek při vykreslování úseček a polyčar |
font | popis použitého fontu |
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 |
arrow | doplnění šipky na začátek či konec |
15. Vykreslení š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:
tkcanvas.create_line(110, 50, 190, 50, arrow=tkinter.FIRST) tkcanvas.create_line(210, 50, 290, 50, arrow=tkinter.LAST) tkcanvas.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:
tkcanvas.create_line(10, 350, 90, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 10, 10))
V následující funkci je provedeno vykreslení šipek, a to několika různými způsoby:
def draw_arrows(canvas, tkcanvas): """Vykreslení šipek.""" tkcanvas.create_line(10, 50, 90, 50) tkcanvas.create_line(110, 50, 190, 50, arrow=tkinter.FIRST) tkcanvas.create_line(210, 50, 290, 50, arrow=tkinter.LAST) tkcanvas.create_line(310, 50, 390, 50, arrow=tkinter.BOTH) tkcanvas.create_line(10, 150, 90, 150, width=5) tkcanvas.create_line(110, 150, 190, 150, width=5, arrow=tkinter.FIRST) tkcanvas.create_line(210, 150, 290, 150, width=5, arrow=tkinter.LAST) tkcanvas.create_line(310, 150, 390, 150, width=5, arrow=tkinter.BOTH) tkcanvas.create_line(10, 250, 90, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 10, 10)) tkcanvas.create_line(110, 250, 190, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 20, 10)) tkcanvas.create_line(210, 250, 290, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 5, 10)) tkcanvas.create_line(310, 250, 390, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 0, 10)) tkcanvas.create_line(10, 350, 90, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 10, 10)) tkcanvas.create_line(110, 350, 190, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 20, 10)) tkcanvas.create_line(210, 350, 290, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 5, 10)) tkcanvas.create_line(310, 350, 390, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 0, 10))
Obrázek 8: Šipky různých stylů a tvarů.
A takto vypadá celý zdrojový kód demonstračního příkladu doplněného o možnost vykreslení šipek na kreslicí plátno:
import PySimpleGUI as sg import tkinter # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) button_size = (10, 0) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=button_size)], [sg.Button("Draw ellipse", size=button_size)], [sg.Button("Draw polyline", size=button_size)], [sg.Button("Draw spline", size=button_size)], [sg.Button("Draw pieslices", size=button_size)], [sg.Button("Draw chords", size=button_size)], [sg.Button("Draw arrows", size=button_size)], [sg.Button("Clear canvas", size=button_size)], [sg.Button("Exit", size=button_size)], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas, tkcanvas): """Vykreslení úseček na plátno.""" size = canvas.get_size() tkcanvas.create_line(0, 0, size[0]-1, size[1]-1) tkcanvas.create_line(0, size[1]-1, size[0]-1, 0) def draw_ellipse(canvas, tkcanvas): """Vykreslení oválu na plátno.""" border = 5 size = canvas.get_size() tkcanvas.create_oval(border, border, size[0]-border, size[1]-border) def draw_pieslices(canvas, tkcanvas): """Vykreslení kruhových výsečí na plátno.""" tkcanvas.create_arc(1, 1, 100, 100, fill="#ff8080") tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45) tkcanvas.create_arc(200, 1, 300, 100, fill="#80ffff", extent=180) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270) tkcanvas.create_arc(1, 200, 100, 300, fill="#ff8080", start=90, extent=270) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90) def draw_chords(canvas, tkcanvas): """Vykreslení kruhových úsečí na plátno.""" tkcanvas.create_arc(0, 0, 100, 100, fill="#ff8080", style=tkinter.CHORD) tkcanvas.create_arc(100, 100, 200, 200, fill="#8080ff", start=45, style=tkinter.CHORD) tkcanvas.create_arc(200, 0, 300, 100, fill="#80ffff", extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 100, 399, 199, fill="#ffff80", start=45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(0, 200, 100, 300, fill="#ff8080", start=90, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(100, 300, 199, 399, fill="#8080ff", start=90 + 45, extent=270, style=tkinter.CHORD) tkcanvas.create_arc(200, 200, 299, 299, fill="#80ffff", start=180, extent=180, style=tkinter.CHORD) tkcanvas.create_arc(300, 300, 399, 399, fill="#ffff80", start=-45, extent=90, style=tkinter.CHORD) def draw_arrows(canvas, tkcanvas): """Vykreslení šipek.""" tkcanvas.create_line(10, 50, 90, 50) tkcanvas.create_line(110, 50, 190, 50, arrow=tkinter.FIRST) tkcanvas.create_line(210, 50, 290, 50, arrow=tkinter.LAST) tkcanvas.create_line(310, 50, 390, 50, arrow=tkinter.BOTH) tkcanvas.create_line(10, 150, 90, 150, width=5) tkcanvas.create_line(110, 150, 190, 150, width=5, arrow=tkinter.FIRST) tkcanvas.create_line(210, 150, 290, 150, width=5, arrow=tkinter.LAST) tkcanvas.create_line(310, 150, 390, 150, width=5, arrow=tkinter.BOTH) tkcanvas.create_line(10, 250, 90, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 10, 10)) tkcanvas.create_line(110, 250, 190, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 20, 10)) tkcanvas.create_line(210, 250, 290, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 5, 10)) tkcanvas.create_line(310, 250, 390, 250, width=2, arrow=tkinter.LAST, arrowshape=(10, 0, 10)) tkcanvas.create_line(10, 350, 90, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 10, 10)) tkcanvas.create_line(110, 350, 190, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 20, 10)) tkcanvas.create_line(210, 350, 290, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 5, 10)) tkcanvas.create_line(310, 350, 390, 350, width=2, arrow=tkinter.BOTH, arrowshape=(10, 0, 10)) def draw_polyline(canvas, tkcanvas, smooth): """Vykreslení polyčáry na plátno.""" border = 1 size = canvas.get_size() tkcanvas.create_line( border, size[1]-border, size[0]/3, border, size[0]*2/3, size[1]-border, size[0]-border, border, smooth=smooth) def clear_canvas(tkcanvas): """Vymazání všech objektů z kreslicího plátna.""" tkcanvas.delete("all") # vytvoření okna s ovládacími prvky window = sg.Window("Window #34", layout, use_custom_titlebar=False, finalize=True) # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas, tkcanvas) elif event == "Draw ellipse": draw_ellipse(canvas, tkcanvas) elif event == "Draw polyline": draw_polyline(canvas, tkcanvas, False) elif event == "Draw spline": draw_polyline(canvas, tkcanvas, True) elif event == "Draw pieslices": draw_pieslices(canvas, tkcanvas) elif event == "Draw chords": draw_chords(canvas, tkcanvas) elif event == "Draw arrows": draw_arrows(canvas, tkcanvas) elif event == "Clear canvas": clear_canvas(tkcanvas) window.refresh() # po přečtení události okno zavřeme window.close()
16. Styl ukončení liniových obrazců a způsob vykreslení jejich zalomení
Styl zakončení objektů s šířkou větší, než je jeden pixel, se řídí nepovinným parametrem cap. Naproti tomu parametr join určuje, jak se mají napojit navazující úsečky v polyčáře, které jsou opět širší 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:
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í |
17. Vykreslení polyčar s různým stylem zalomení a vykreslení koncových prvků
Styly vykreslení úseček, polyčar či oblouků, které jsme si popsali v předchozí kapitole, použijeme v dnešním předposledním demonstračním příkladu, který po svém spuštění vykreslí následující tvary:
Obrázek 9: Vykreslení polyčar s různým stylem zalomení a vykreslení koncových prvků.
Ze zdrojového kódu demonstračního příkladu je zřejmé, jakým způsobem (nepovinné parametry) se tyto styly vykreslování specifikují:
import PySimpleGUI as sg import tkinter # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=(8, 0))], [sg.Button("Exit", size=(8, 0))], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas): """Vykreslení úseček na plátno.""" # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # úsečky s nastavenými grafickými vlastnostmi tkcanvas.create_line(10, 10, 90, 90) tkcanvas.create_line(110, 10, 190, 90, fill="#8080ff") tkcanvas.create_line(210, 10, 290, 90, fill="#ffff80", width=8) tkcanvas.create_line(310, 10, 390, 90, fill="#80aa80", width=8, dash=15) tkcanvas.create_line(10, 110, 90, 190, fill="red", width=12) tkcanvas.create_line(110, 110, 190, 190, fill="red", width=12, cap=tkinter.BUTT) tkcanvas.create_line(210, 110, 290, 190, fill="red", width=12, cap=tkinter.PROJECTING) tkcanvas.create_line(310, 110, 390, 190, fill="red", width=12, cap=tkinter.ROUND) tkcanvas.create_line(10, 110, 90, 190, fill="white") tkcanvas.create_line(110, 110, 190, 190, fill="white") tkcanvas.create_line(210, 110, 290, 190, fill="white") tkcanvas.create_line(310, 110, 390, 190, fill="white") tkcanvas.create_line(10, 210, 50, 290, 90, 210, fill="red", width=12) tkcanvas.create_line(110, 210, 150, 290, 190, 210, fill="red", width=12, cap=tkinter.BUTT) tkcanvas.create_line(210, 210, 250, 290, 290, 210, fill="red", width=12, cap=tkinter.PROJECTING) tkcanvas.create_line(310, 210, 350, 290, 390, 210, fill="red", width=12, cap=tkinter.ROUND) # pomocné úsečky tkcanvas.create_line(10, 210, 50, 290, 90, 210, fill="white") tkcanvas.create_line(110, 210, 150, 290, 190, 210, fill="white") tkcanvas.create_line(210, 210, 250, 290, 290, 210, fill="white") tkcanvas.create_line(310, 210, 350, 290, 390, 210, fill="white") tkcanvas.create_line(10, 310, 50, 390, 90, 310, fill="red", width=12) tkcanvas.create_line(110, 310, 150, 390, 190, 310, fill="red", width=12, join=tkinter.ROUND) tkcanvas.create_line(210, 310, 250, 390, 290, 310, fill="red", width=12, join=tkinter.BEVEL) tkcanvas.create_line(310, 310, 350, 390, 390, 310, fill="red", width=12, join=tkinter.MITER) # pomocné úsečky tkcanvas.create_line(10, 310, 50, 390, 90, 310, fill="white") tkcanvas.create_line(110, 310, 150, 390, 190, 310, fill="white") tkcanvas.create_line(210, 310, 250, 390, 290, 310, fill="white") tkcanvas.create_line(310, 310, 350, 390, 390, 310, fill="white") # vytvoření okna s ovládacími prvky window = sg.Window("Window #35", layout, use_custom_titlebar=False, finalize=True) # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas) window.refresh() # po přečtení události okno zavřeme window.close()
18. Vykreslení úseček různým stylem (plná, čárkovaná, čerchovaná…)
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.
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):
Obrázek 10: Vykreslení úseček různým stylem (plná, čárkovaná, čerchovaná…).
Demonstrační příklad, v němž se používají nepovinné parametry pojmenované dash a dashoff, vypadá následovně:
import PySimpleGUI as sg import tkinter # kreslicí plátno canvas = sg.Canvas(background_color='#ccffcc', size=(400, 400)) # ovládací prvky, které se mají zobrazit v okně left_column = [ [sg.Button("Draw lines", size=(8, 0))], [sg.Button("Exit", size=(8, 0))], ] right_column = [ [ canvas ], ] layout = [ [ sg.Frame("Commands", left_column), sg.Frame("Canvas", right_column), ], ] def draw_lines(canvas): """Vykreslení úseček na plátno.""" # reference na plátno z knihovny Tk tkcanvas = canvas.TKCanvas # úsečky s nastavenými grafickými vlastnostmi tkcanvas.create_line(10, 10, 90, 90) tkcanvas.create_line(110, 10, 190, 90, fill="#8080ff") tkcanvas.create_line(210, 10, 290, 90, fill="#ffff80", width=8) tkcanvas.create_line(310, 10, 390, 90, fill="#80aa80", width=8, dash=15) tkcanvas.create_line(10, 110, 90, 190, width=2, dash=(12, 3)) tkcanvas.create_line(110, 110, 190, 190, width=2, dash=(9, 6)) tkcanvas.create_line(210, 110, 290, 190, width=2, dash=(6, 9)) tkcanvas.create_line(310, 110, 390, 190, width=2, dash=(3, 12)) tkcanvas.create_line(10, 210, 90, 290, width=2, dash=(12, 2, 2, 2)) tkcanvas.create_line(110, 210, 190, 290, width=2, dash=(12, 2, 4, 2)) tkcanvas.create_line(210, 210, 290, 290, width=2, dash=(12, 4, 2, 4)) tkcanvas.create_line(310, 210, 390, 290, width=2, dash=(12, 2, 2, 2, 2, 2)) tkcanvas.create_line(10, 310, 90, 390, width=2, dash=(12, 2, 2, 2), dashoff=0) tkcanvas.create_line(110, 310, 190, 390, width=2, dash=(12, 2, 4, 2), dashoff=5) tkcanvas.create_line(210, 310, 290, 390, width=2, dash=(12, 4, 2, 4), dashoff=10) tkcanvas.create_line(310, 310, 390, 390, width=2, dash=(12, 2, 2, 2, 2, 2), dashoff=-5) # vytvoření okna s ovládacími prvky window = sg.Window("Window #36", layout, use_custom_titlebar=False, finalize=True) # obsluha smyčky událostí (event loop) while True: # přečtení jediné události event, values = window.read() print("Event: ", event, " Values: ", values) # reakce na událost "uzavření okna" if event in {"Exit", sg.WIN_CLOSED}: break elif event == "Draw lines": draw_lines(canvas) window.refresh() # po přečtení události okno zavřeme window.close()
19. Repositář s demonstračními příklady
Všechny Pythonovské skripty, které jsme si v předmiminulém, minulém i dnešním článku ukázali, naleznete na adrese https://github.com/tisnik/most-popular-python-libs. Následují odkazy na jednotlivé příklady. Pro jejich spuštění je pochopitelně nutné mít nainstalován balíček PySimpleGUI:
20. Odkazy na Internetu
- PySimpleGUI
https://www.pysimplegui.org/en/latest/ - Kivy na GitHubu
https://github.com/kivy/kivy - 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 Kivy
https://kivy.org/#home - Stránky projektu PyQt
https://riverbankcomputing.com/software/pyqt/intro