
V návaznosti na předchozí návod, jak vytvořit semafor, je zde další návod. Tentokrát budeme ovládat blikání každé diody zvlášť. Pro každou diodu je vytvořeno vlastní vlákno.
Tento kód ukazuje příklad práce s více vlákny v Pythonu. Každá LED bliká ve vlastním výpočetním vlákně, Kzatímco hlavní vlákno běží jako GUI tkinter. Postupně si vysvětlíme, co se děje v rámci běhu více vláken,
Základní princip více vláken
V Pythonu se běh více vláken řeší knihovnou threading. Každé vlákno běží souběžně s ostatními, ale vždy dělí ten samý proces. V našem případě:
- Hlavní vlákno:
- běží
root.mainloop()– čeká na kliknutí myší, klávesnici apod.
- běží
- Další vlákna (
green_worker,yellow_worker,red_worker):- samostatně blikají svou LED bez toho, aby se GUI zaseklo.
Kdyby blikání bylo přímo v hlavním cyklu Tkinteru, program by se „zamrznul“ v sleep() a neakceptoval by žádné kliknutí. Více vláken umožňuje, že blikání běží na pozadí, GUI zůstává aktivní.
Vytvoření nového vlákna
Tahle konstrukce se v kódu objevuje pro každou LED:
pythongreen_worker = threading.Thread(target=worker_green, daemon=True)
green_worker.start()
target=worker_greenznamená, že po spuštění vlákna bude spouštět funkciworker_green().daemon=Trueoznačuje vlákno jako démon, tzn. když se ukončí hlavní program (např. při zavření okna), všechna vlákna démon automaticky skončí. To je vhodné pro blikání LED, aby neblikala navždy.start()spustí vlákno.
Každá LED má tedy svoji kopii threading.Thread(target=worker_...), takže běží jako samostatné vlákno.
Komunikace mezi vlákny
Aby se blikání mohlo vypnout bezpečně, vlákna spolu komunikují. K tomu slouží threading.Event():
pythongreen_stop = threading.Event()
Eventje jakýsi signál, který může být nastaven („set“) nebo ne (výchozí stav je „cleared“).- Uvnitř funkce
worker_green():
pythondef worker_green():
while not green_stop.is_set():
green.on()
sleep(0.5)
green.off()
sleep(0.5)
green_stop.is_set()kontroluje, zda bylo blikání požádáno o ukončení.- Pokud tlačítko zavolá
green_stop.set(), cyklus se vejde do podmínkyis_set()a skončí.
Analogicky platí pro yellow_stop a red_stop.
Bezpečné zastavení vláken
Když uživatel klikne na tlačítko pro zastavení blikání, např. pro zelenou:
pythongreen_stop.set()
green_worker.join()
green_stop.set()nastaví signál. V další iteraciwhileveworker_green()se cyklus přeruší.green_worker.join()způsobí, že hlavní vlákno čeká, dokudgreen_workerneprojedereturna vlákno skutečně skončí.
Díky join() je v kódu zajištěné, že nezůstanou „viset“ stará vlákna, když se další znovu spouštějí.
Bezpečnost vícevláknového řešení
- Každé vlákno má svůj vlastní cyklus
whilea svůj vlastnísleep(). To znamená, že LED mohou blikat současně a nezávisle. Můžete i nastavit u každé LED jinou frekvenci blikání. V příkladu kódu je nastaveno blikání všech s opakováním 1 vteřina. green.off(),yellow.off()apod. pracují přímo s GPIO, ale protožegpiozeroje v tomto scénáři “bezpečně” navržený pro jednoduché aplikace (nepotřebuje synchronizaci kritických sekcí), může být použit i v více vláknech.- Změna stavu tlačítek (
btn_green.config(...)) je už provedena v hlavním vlákně (Tkinter), takže GUI neovlivňují vlákna s blikáním, všechny změny jsou bezpečně řízeny z hlavního vlákna.
Výhody více vláken
- Každá LED může blikat vlastním frekvencí.
- Jedno vlákno může být vypnuto, druhé spuštěno, aniž by ovlivnilo ostatní.
- Tkinter může běžet v hlavním vlákně a reagovat na akce bez zpoždění.
Během práce s více vlákny je důležité si vždy pamatovat, že:
- vlákna nemusí běžet „paralelně“ v pravém významu (kvůli GIL v Pythonu),
- ale v praxi je to zde dostačující pro oddělení úkolů jako GUI a fyzické výstupy.
Celý kód
import tkinter as tk
from gpiozero import LED
from time import sleep
import threading
GREEN_PIN = 22
YELLOW_PIN = 27
RED_PIN = 17
green = LED(GREEN_PIN)
yellow = LED(YELLOW_PIN)
red = LED(RED_PIN)
green_worker = None
green_stop = threading.Event()
yellow_worker = None
yellow_stop = threading.Event()
red_worker = None
red_stop = threading.Event()
def blink_green():
global green_worker, green_stop
if green_worker is not None:
green_stop.set()
green_worker.join()
green_worker = None
green_stop.clear()
green.off()
btn_green.config(text="Bliká: zelená", bg="lightgray")
else:
green_worker = threading.Thread(target=worker_green, daemon=True)
green_stop.clear()
green_worker.start()
btn_green.config(text="Stop zelená", bg="green")
def blink_yellow():
global yellow_worker, yellow_stop
if yellow_worker is not None:
yellow_stop.set()
yellow_worker.join()
yellow_worker = None
yellow_stop.clear()
yellow.off()
btn_yellow.config(text="Bliká: žlutá", bg="lightgray")
else:
yellow_worker = threading.Thread(target=worker_yellow, daemon=True)
yellow_stop.clear()
yellow_worker.start()
btn_yellow.config(text="Stop žlutá", bg="yellow")
def blink_red():
global red_worker, red_stop
if red_worker is not None:
red_stop.set()
red_worker.join()
red_worker = None
red_stop.clear()
red.off()
btn_red.config(text="Bliká: červená", bg="lightgray")
else:
red_worker = threading.Thread(target=worker_red, daemon=True)
red_stop.clear()
red_worker.start()
btn_red.config(text="Stop červená", bg="red")
def worker_green():
while not green_stop.is_set():
green.on()
sleep(0.5)
green.off()
sleep(0.5)
def worker_yellow():
while not yellow_stop.is_set():
yellow.on()
sleep(0.5)
yellow.off()
sleep(0.5)
def worker_red():
while not red_stop.is_set():
red.on()
sleep(0.5)
red.off()
sleep(0.5)
def all_off():
# vypneme všechna blikání a všechny LED
flags = [green_stop, yellow_stop, red_stop]
workers = [green_worker, yellow_worker, red_worker]
for flag, worker, led, btn, color_text in zip(
flags, workers, [green, yellow, red], [btn_green, btn_yellow, btn_red],
["Bliká: zelená", "Bliká: žlutá", "Bliká: červená"]
):
if worker is not None:
flag.set()
worker.join()
led.off()
btn.config(text=color_text)
def on_close():
all_off()
root.destroy()
root = tk.Tk()
root.title("Více blikajících LED")
root.geometry("320x260")
root.resizable(False, False)
btn_green = tk.Button(root, text="Bliká: zelená", width=24, command=blink_green)
btn_yellow = tk.Button(root, text="Bliká: žlutá", width=24, command=blink_yellow)
btn_red = tk.Button(root, text="Bliká: červená", width=24, command=blink_red)
btn_all_off = tk.Button(root, text="Vypnout vše a zavřít", width=24, command=on_close)
btn_green.pack(pady=8)
btn_yellow.pack(pady=8)
btn_red.pack(pady=8)
btn_all_off.pack(pady=16)
root.protocol("WM_DELETE_WINDOW", on_close)
root.mainloop()


