Z předchozích lekcí již víme, že příkazy můžeme psát i v okně shellu. Takto jsme postupně napsali příkazy pro import modulu tkinter a nakreslili text, obdélník, čáru a elipsu. Proces v okně shellu je vidět na obrázku:
Výsledek by byl stejný, i kdybychom tento program spustili:
# 16_1.py
import tkinter
canvas = tkinter.Canvas()
canvas.pack()
canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')
Nakreslily se tyto objekty:
Podívejte se znovu do okna shellu na prvním obrázku. Po nakreslení každého z obrazců se na další řádek napíše číslo vytvořeného obrazce (objektu). Všechny objekty, které vytvoříme pomocí příkazů začínajících na canvas.create_, mají po vytvoření přiřazeno číslo. Python čísluje vytvořené objekty plátna postupně od 1. Tato čísla lze použít pro různé změny objektu.
Příkazem canvas.move můžeme přesouvat objekty vytvořené na plátně. Například canvas.move(2, 30, -15) posune objekt s číslem 2 o +30 bodů na ose x a o −15 bodů na ose y (tj. doprava a nahoru) . V našem případě posuneme červený obdélník (má číslo 2).
>>> canvas.move(2, 30, -15)
>>>
Čísla vytvořených objektů si můžeme uložit do proměnných a pak použít proměnnou, ve které si pamatujeme číslo objektu při pohybu.
Následující program si pamatuje proměnné pro každý vytvořený objekt a po stisku šipky doprava zavolá funkci, která jednotlivé objekty posune doprava.
# 16_2.py
import tkinter
canvas = tkinter.Canvas()
canvas.pack()
nadpis = canvas.create_text(200,200,text='nadpis')
obdelnik = canvas.create_rectangle(100,100,150,200,fill='red')
cara = canvas.create_line(50,50,200,150,width=5,fill='blue')
oval = canvas.create_oval(200,100,300,150,fill='green')
def posun_vpravo(event):
canvas.move(nadpis, 5, 0)
canvas.move(obdelnik, 5, 0)
canvas.move(cara, 5, 0)
canvas.move(oval, 5, 0)
canvas.bind_all('<Right>',posun_vpravo)
canvas.mainloop()
Vzhledem k tomu, že vytvořené objekty jsou číslovány kontinuálně od jedničky, je zbytečné pamatovat si ty nakreslené v předchozím programu. Číslování lze také použít k přesunu objektů pomocí cyklu for. Předchozí program lze také napsat následovně:
# 16_3.py
import tkinter
canvas = tkinter.Canvas()
canvas.pack()
canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')
def posun_vpravo(event):
for i in range(1,5):
canvas.move(i,5,0)
canvas.bind_all('<Right>',posun_vpravo)
canvas.mainloop()
Když přesunujeme všechny objekty najednou, můžeme místo konkrétního čísla použít zápis 'all', canvas.move('all',5,0). S tímto zápisem jsme se již setkali v příkazech canvas.delete('all'). Teď už nejspíš správně tušíme, že pokud chceme smazat jen jeden objekt, můžeme příkazem canvas.delete('all') zadat místo parametru 'all' číslo konkrétního objektu.
Následující program přesune všechny nakreslené objekty doprava a doleva jeden náhodně vybraný, jehož počet je mezi 1 a 5 (včetně).
# 16_4.py
import tkinter
import random
canvas = tkinter.Canvas()
canvas.pack()
canvas.create_text(200,200,text='nadpis')
canvas.create_rectangle(100,100,150,200,fill='red')
canvas.create_line(50,50,200,150,width=5,fill='blue')
canvas.create_oval(200,100,300,150,fill='green')
def posun_vpravo(event):
canvas.move('all',5,0)
def posun_vlevo(event):
objekt = random.randint(1,4)
canvas.move(objekt,-5,0)
canvas.bind_all('<Right>', posun_vpravo)
canvas.bind_all('<Left>', posun_vlevo)
canvas.mainloop()
Co udělá předchozí program, když v příkazovém režimu >>> vytvoříme další objekty? Budou se i tyto přesouvat?
Jaký je rozdíl v posouvání pomocí parametru 'all' a posouváním pomocí cyklu v případě, že bychom v příkazovém řádku vytvořili nové objekty?
Co se stane, když budeme posouvat nebo mazat objekt, který jsme už smazali?
Pokud jsme vytvořili obrázek z více objektů, je obtížnější procházet celý obrázek najednou. Potřebovali bychom znát číslo prvního nakresleného objektu tohoto obrázku a počet nakreslených částí. Za předpokladu, že jsme je nakreslili správně za sebou, můžeme takový obrázek posouvat ve smyčce for od čísla prvního objektu k číslu podle počtu částí.
Existuje však i jednodušší možnost. Můžeme označit všechny části obrázku nějakou společnou značkou a pak můžeme procházet objekty, které tuto společnou značku mají. Objekt označíme při jeho vytváření parametrem tags, například
canvas.create_oval(x,y,x+10,y+10, tags='auto')
Při posouvání pak můžeme místo čísla tohoto objektu použít jeho značku. Například tag canvas.move('auto', 5, 0) lze použít i při mazání objektu. V následujícím programu jsme nakreslili auto a vozik a jednotlivé části těchto obrázků jsme označili tagy. Poté je pomocí časovače přesuneme každý zvlášť.
# 16_5.py
import tkinter
canvas = tkinter.Canvas()
canvas.pack()
canvas.create_rectangle(100,150,200,200,fill='blue',tags='vozik')
canvas.create_oval(115,200,140,225,fill='yellow',tags='vozik')
canvas.create_oval(160,200,185,225,fill='yellow',tags='vozik')
canvas.create_oval(200,100,230,130, fill='',width=5, outline='black',tags='kolo')
canvas.create_oval(250,100,280,130, fill='',width=5, outline='black',tags='kolo')
canvas.create_line(215,115,230,70, width=5,tags='kolo')
canvas.create_line(225,90,240,115,265,115,270,85, width=5,tags='kolo')
def posouvej():
canvas.move('kolo',-5,0)
canvas.move('vozik',5,0)
canvas.after(100,posouvej)
posouvej()
canvas.mainloop()
Vozik i auto po určité době opustí obrazovku. Můžeme je chtít zobrazit na opačném konci. Toto lze vyřešit různými způsoby. Protože to chceme řešit pomocí příkazů, které již známe, je nejjednodušší vypočítat aktuální pozici každého obrázku a uložit do proměnné. V případě, že již bude mimo obrazovku, posuneme obrázek o celou šířku obrazovky na opačnou stranu.
# 16_6.py
import tkinter
sirka = 600
canvas = tkinter.Canvas(bg='white', width=sirka, height=250)
canvas.pack()
x_vozik = 100
canvas.create_rectangle(100,150,200,200,fill='blue',tags='vozik')
canvas.create_oval(115,200,140,225,fill='yellow',tags='vozik')
canvas.create_oval(160,200,185,225,fill='yellow',tags='vozik')
x_kolo = 200
canvas.create_oval(200,100,230,130, fill='',width=5,outline='black',tags='kolo')
canvas.create_oval(250,100,280,130, fill='',width=5,outline='black',tags='kolo')
canvas.create_line(215,115,230,70, width=5,tags='kolo')
canvas.create_line(225,90,240,115,265,115,270,85, width=5,tags='kolo')
def posouvej():
global x_vozik, x_kolo
x_kolo = x_kolo-5
canvas.move('kolo',-5,0)
if x_kolo<0:
x_kolo = x_kolo + sirka
canvas.move('kolo',sirka,0)
x_vozik = x_vozik+5
canvas.move('vozik',5,0)
if x_vozik>sirka:
x_vozik = x_vozik - sirka
canvas.move('vozik',-sirka,0)
print("vozik:", x_vozik, "kolo:", x_kolo) #informace si muzeme vypsat i do shellu
canvas.after(100,posouvej)
posouvej()
canvas.mainloop()
V lekci 14 byl program, který animoval míček padající shora. Jeho kód vypadal nějak takto:
# 16_7.py
import tkinter
vyska = 300
canvas = tkinter.Canvas(width=400,height=vyska)
canvas.pack()
def micek():
global x, y, vyska
canvas.delete('all')
canvas.create_oval(x-5, y-5, x+5, y+5,fill="green")
y = y + 5
# Pokud míček spadne pod plátno, vrať ho nahoru
if y > vyska:
y = 5
canvas.afrer(30, micek)
x = 200
y = 5
micek()
canvas.mainloop()
S využitím metody move by vypdal nějak takto:
# 16_8.py
import tkinter
vyska = 300
canvas = tkinter.Canvas(width=400, height=vyska)
canvas.pack()
micek = canvas.create_oval(190, 0, 210, 20, fill="green")
def padani():
global vyska
canvas.move(micek, 0, 5)
x1, y1, x2, y2 = canvas.coords(micek)
# Pokud míček spadne pod plátno, vrať ho nahoru
if y1 > vyska:
canvas.move(micek, 0, -vyska)
canvas.after(30, padani)
padani()
canvas.mainloop()
V programu 16_8.py máme další užitečnou metodu coords pro získání souřadnic objektu (x1, y1, x2, y2). Tato čísla udávají vzdálenost od výchozích souřadnic okna [0,0]. Metoda coords toho umí víc, prozatím toto stačí.
Později se dozvíme, že konstrukce funkce padani() by šla napsat i bez dodatečných proměnných x1, y1, x2 a y2:
def padani():
canvas.move(micek, 0, 5)
# Pokud míček spadne pod plátno, vrať ho nahoru
if canvas.coords(micek)[1] > 300:
canvas.move(micek, 0, -vyska)
Vytvoř program prijmeni_gravitace.py, který po spuštění zobrazí u horního okraje okna tři různé objekty (čtverec, obdélník, kruh). U spodního okraje okna bude vodorovná linka představující zem. Objekty budou různou rychlostí padat dolů (pomocí různých "random" na začátku animace). Jakmile objekt dosáhne země, jeho padání se zastaví. Ostatní stále padají dokud i ony nedosáhnou země.
Následující program posouvá kuličku vodorovně po plátně tam a zpět. Rychlost je zvolena náhodně. V levé polovíně je oranžová, v pravé je zelená. Inverzně se mění barva pozadí:
# 16_9.py
import tkinter
import random
sirka = 400
vyska = 300
canvas = tkinter.Canvas(width=sirka, height=vyska)
canvas.pack()
# vytvoření dvou čtverců
ctverec = canvas.create_oval(50, 130, 90, 170, fill="green")
krok = random.randint(2, 4)
def pohyb():
global ctverec, krok, sirka
# pohyb vodorovného čtverce
canvas.move(ctverec, rychlost, 0)
x1, y1, x2, y2 = canvas.coords(ctverec)
# odraz od okrajů
if x1 < 0 or x2 > 400:
krok = -krok
if x1 < sirka // 2:
canvas.itemconfig(ctverec, fill="orange")
canvas.config(bg="green")
else:
canvas.itemconfig(ctverec, fill="green")
canvas.config(bg="orange")
canvas.after(20, pohyb)
pohyb()
canvas.mainloop()
V programu je využita metoda itemconfig. Ta umožňuje změnit vlastnosti už existujícího objektu na plátně (například barvu, tloušťku čáry, text, font…). Nemusíme objekt mazat a kreslit znovu – jen mu změníme nastavení.
Naopak metoda config umí měnit vlastnosti samotného plátna. V ukázkovém programu canvas.config(bg="orange").
Vytvoř program prijmeni_krizeni.py. Na plátně se pohybují dvě kuličky, každá jedné barvy. Jedna se pohybuje vodorovně tam a zpět konstantní rychlostí danou random na začátku programu. Druhá dělá totéž, jen se pohybuje svisle. Pohyby budou pomalejší (random mezi 2-5, časovač kolem 20). V okamžiku, kdy se potkají, změní barvu.