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ę →