Rozdział 16 · Tworzenie fajnych aplikacji z Tkinter

Debugowanie interfejsu i jakość GUI

Responsywność interfejsu nie jest automatyczną konsekwencją korzystania z Tkinter, lecz wynikiem konkretnych decyzji w kodzie.

Pętla zdarzeń nie może być wypożyczona przez długi czas

Debug Lab 7:time.sleep() wewnątrz callback zamraża interfejs
sleep_v_callback.py
import time

def on_click():
    time.sleep(5)   # „czekać 5 sekund”
    label.config(text=„Gotowe!”)
# На все 5 секунд окно перестаёт перерисовываться и реагировать на клики —
# событийный цикл заблокирован внутри time.sleep(), а не только сам callback.
Co widać na ekranie

time.sleep(...) zatrzymuje cały proces, w którym działa pętla zdarzeń — a Tkinter zwykle działa w jednym wątku. Gdy wątek śpi, pętla nie może obsłużyć w ogóle niczego: ani przerysowania, ani kliknięć, ani zamknięcia okna.

Poprawiony kod
after_vmesto_sleep.py
def on_click():
    label.config(text=„Czekamy...”)
    root.after(5000, lambda: label.config(text=„Gotowe!”))
    # pętla zdarzeń trwa przez te wszystkie 5 sekund
Debug Lab 8: while True zamiast pętli zdarzeń
while_true_gui.py
while True:
    if button_nazhata():
        obrabotat_klik()
# Такого кода не должно быть в Tkinter-приложении вообще —
# у Tk уже есть собственный событийный цикл внутри mainloop().
Co widać na ekranie

Tkinter już zapewnia pełnoprawną pętlę zdarzeń. Niestandardowe while True dla „pollowania”

Poprawiony kod
mainloop_vmesto_while.py
button.config(command=obrabotat_klik)
root.mainloop()   # c obsługuje zdarzenia, w tym kliknięcia na button
Debug Lab 9: Długie obliczenia bezpośrednio w callback
dolgoe_vychislenie.py
def on_click():
    result = obrabotat_million_zapisej()   # sekundy obliczeń wewnątrz callback
    label.config(text=str(result))
# Интерфейс "подвисает" на всё время вычисления — ровно так же,
# как и с time.sleep(), просто по другой причине.
Co widać na ekranie

Każda czasochłonna operacja w callback nie jest tylko time.sleep() — zajmuje cykl zdarzeń na cały ten czas. Dla naprawdę długich zadań: dzielić pracę na części, planowane przez after(), czyli przeniesienie go do osobnego wątku/procesu z bezpiecznym powrotem wyniku, to już zaawansowany temat, który nie wymaga pełnej implementacji w tym kursie.

Poprawiony kod
razbivka_cherez_after.py
def obrabotat_chast(indeks):
    if indeks >= len(dannye):
        label.config(text=„Gotowe”)
        return
    obrabotat_odnu_zapis(dannye[indeks])
    root.after(0, obrabotat_chast, indeks + 1)   # następna część jest o kolejnym cyklu
Aktualizacja widżetu – ze strumienia pętli zdarzeń
Jeśli w twoim kodzie pojawi się podgląd z wątkiem roboczym, wynik powinien być bezpiecznie przekazany z powrotem i widżety aktualizowane z pętli zdarzeń Tkinter, a nie bezpośrednio z innego wątku. Pełna architektura wielowątkowa wykracza poza zakres tego rozdziału.

Testowanie tego, co można przetestować bez okna

test_domennoj_logiki.py
def test_calculate_tip():
    assert calculate_tip(100, 10, 2) == 5.0

test_calculate_tip()
print(„Logika domeny sprawdzona bez pojedynczego widżetu.”)
Lista kontrolna zanim uważysz GUI- projekt gotowy
☐Okno się otwiera
☐Wszystkie widżety są widoczne
☐Kolejność Tab na klawiaturze jest rozsądna
☐Przycisk wyświetla oczekiwany callback
☐Nieprawidłowe wejście jest obsługiwane przez komunikat czystą
☐Zmiana rozmiaru okna nie psuje układu
☐Zamknięcie okna kończy proces w sposób elegancki
☐Dane są przechowywane tam, gdzie są potrzebne
Praktyka: blokujący vs nieblokujący handler
Zweryfikowane bez tkinter - na modelu kolejki zdarzeń
Otwórz praktykę →