Rozdział 16 · Tworzenie fajnych aplikacji z Tkinter

tk i ttk: klasyczne i tematyczne widżety

Zaczynając od tej sekcji, w nowych przykładach wolimy ttk, gdzie istnieje tematyczny odpowiednik — ale najpierw przyjrzyjmy się, jak widżety wyglądają razem.

Tkinter Prezentacja Widgetów

Zanim rozłożymy szczegóły każdego widżetu osobno (sekcje 16.13–16.22), oto jak wyglądają Spójrzcie razem, w jednym prawdziwym oknie:

Showcase: Label, Entry, Button, Checkbutton, Radiobutton, Combobox, Spinbox, Scale, Progressbar, Notebook w jednym oknie
Prawdziwe okno z reprezentatywnym zestawem widżetów rozdziałów. Przykład pojawienia się w środowisku kursu. Czcionki, rozmiary i motywy mogą się nieco różnić w zależności od twojego systemu operacyjnego.

Później w tym rozdziale każdy z tych widżetów otrzyma osobną sekcję z własną stany i przykłady kodeksów.

tk i ttk to dwa zestawy widgetów

tkttk
Co to jestklasyczne widgety Tkintertematycznie (themed) osadzone na Tk
Importimport tkinter as tkfrom tkinter import ttk
WyglądStały styl klasycznymoże dostosować się do tematu/platformy
Stylizacjafg=/bg=przez ttk.Style()
Ta sama forma (Label, Entry, Button) złożona dwukrotnie: w klasycznym tk i w tematyzowanym ttk
W tym temacie różnica jest ledwo zauważalna – powinna być: ttk nie gwarantuje „bardziej nowoczesnego”
tk_vs_ttk.py
import tkinter as tk
from tkinter import ttk

root = tk.Tk()

ttk.Label(root, text=„Nowoczesna ttk.Label”).pack()
ttk.Button(root, text="ttk.Button").pack()
ttk.Entry(root).pack()
W przypadku nowego kodu istnieje ttk, gdzie jest analogia
Od tej sekcji, w nowych przykładach, preferujemy ttk.Label, ttk.Button, ttk.Entry inne tematyczne widgety, gdzie istnieje tematyczny odpowiednik. Ale ttk nie zastępuje cały Tk w całości — widżety takie jak tk.Text, tk.Canvas oraz tk.Menu pozostają klasyczne, bo nie mają tematycznego odpowiednika.
ttk nie gwarantuje „bardziej nowoczesnego”
Motyw ttk zależy od systemu operacyjnego i zainstalowanych motywów Tk. Na niektórych platformach/motywach różnica między tk a ttk jest niemal niewidoczna wizualnie (jak na powyższym zrzucie ekranu) – powiedz "tematyczny" zamiast "nowoczesny" czy "ładniejszy".
Nie używaj fg/bg na widżetach ttk jak na klasycznych
Classic tkwidżety rozumieją fg=/bg= bezpośrednio. Widgety ttk są stylizowane inaczej — poprzez obiekt ttk.Style() (następna sekcja). Nie przenosz dosłownie nawyków klasycznych Tk na ttk.

from tkinter import * — nie w stylu produkcyjnym

Import: gwiazdka → jawne nazwy
Nie polecam
from tkinter import *

root = Tk()
Button(root, text=„Guzik”)   # skąd Button pochodzi, nie jest oczywiste
Polecam
import tkinter as tk
from tkinter import ttk

root = tk.Tk()
ttk.Button(root, text=„Guzik”)   # źródło nazwy jest od razu widoczne
Co używać dzisiaj: from tkinter import * działa, ale utrudnia czytanie kodu: nie jest jasne, do którego modułu należy każda nazwa, a łatwo przypadkowo nakładać się na wbudowane nazwy Python. W przykładach kursów, począwszy od tego rozdziału, pojawiają się tylko jawne importy.

ttk.Style() — stylizacja widgetów tematycznych

Zwykły przycisk obok przycisku stylizowanego przez Accent.TButton
Po lewej stronie jest styl domyślny, po prawej niestandardowy styl nazwany. Przykład wyglądu w środowisku pola. Czcionki, rozmiary i motywy mogą się nieco różnić w zależności od twojego systemu operacyjnego.
ttk_style.py
style = ttk.Style()
print(style.theme_use())   #active temat, np. "clam" lub "default"

style.configure(
    "Accent.TButton",
    font=("TkDefaultFont", 11, "bold"),
    padding=8,
)

button = ttk.Button(
    root,
    text=„Ratuj”,
    style="Accent.TButton",
)
ttk.Style() nie jest CSS
style.configure(...) akceptuje tylko opcje, które dany widżet i aktywny motyw Tk obsługują, nie jest uniwersalnym językiem stylów CSS-, gdzie można zmienić dowolną właściwość dowolnego elementu. Styl nazwany ("Accent.TButton") jest stosowane przez style= przy samym widżecie.
Cartesian Purple
#5B24F9
Cartesian Pink
#DB2777
Success Green
#059669
Warning Amber
#B45309
Kolor – najpierw widziany, potem numeryczny
Zanim napiszesz #5B24F9 w kodzie, spójrz na rzeczywisty wzór kolorystyczny powyżej — łatwiej jest zapamiętać i odróżniać podobne odcienie niż na podstawie samych kodów szesnastkowych.

Canvas — rysowanie dowolnych kształtów

Canvas z linią, prostokątem, owalem i etykietą współrzędnych
Poniżej znajduje się wynik kodu. Przykład wyglądu w środowisku kursu. Czcionki, rozmiary i motywy mogą się nieco różnić w zależności od Twojego systemu operacyjnego.
canvas_basics.py
canvas = tk.Canvas(root, width=260, height=170, background="white")
canvas.pack(padx=10, pady=10)

canvas.create_line(10, 10, 240, 10, fill="#5B24F9", width=2)
rect_id = canvas.create_rectangle(10, 30, 90, 90, outline="#DB2777", width=2)
canvas.create_oval(130, 30, 210, 90, outline="#059669", width=2)
canvas.create_text(130, 130, text=„(0,0) - Lewy górny róg”)
Oś Y rośnie w dół, a nie w górę
Pochodzenie (0, 0) jest w lewym górnym rogu Canvas. x rozrasta się w prawo, oraz y rośnie na ziemię — jest to przeciwieństwem zwykłego systemu współrzędnych matematycznych, w którym y rośnie w górę. Tak działa ekranowy system współrzędnych w większości bibliotek graficznych, nie tylko Tkinter.
create_* zwraca identyfikator elementu
rect_id = canvas.create_rectangle(...)Canvas przechowuje wyrysowane elementy i zwraca identyfikator, dzięki któremu możesz później zmienić lub usunąć ten konkretny element (canvas.itemconfig(rect_id, ...), canvas.delete(rect_id)) — przydatne w przyszłych interfejsach gier.
Canvas nie jest drugim Turtle
Canvas jest zwykłym Tkinter widżetem do rysowania dowolnych kształtów w oknie aplikacji, a nie osobnego modułu z własnym światem współrzędnych, takiego jak turtle (rozdziały 6-7). To krótkie wprowadzenie, a nie pełny kurs Canvas.

PhotoImage — obrazy w widżetach

Label z małym wygenerowanym obrazem szachownicy
Obraz jest generowany bezpośrednio w Python, bez pliku zewnętrznego. Przykład wyglądu w środowisku kursu. Na Twoim systemie operacyjnym czcionki, rozmiary i motyw mogą się nieco różnić.
photoimage.py
image = tk.PhotoImage(file="icon.png")
label = ttk.Label(root, image=image)
label.pack()
Debug Lab 2: Obraz znika, mimo że kod jest „poprawny”
photoimage_propadaet.py
def build_ui():
    image = tk.PhotoImage(file="icon.png")   # funkcje zmienna lokalne
    label = ttk.Label(root, image=image)
    label.pack()

build_ui()
# Окно открывается, но картинка не отображается —
# на месте изображения пусто.
Co widać na ekranie

Po ukończeniu build_ui() lokalna nazwa image znika. Jeśli na obiekcie PhotoImage nie pozostaje już żadnych odwołań Pythona, obiekt może zostać zwolniony — widżet przechowuje odniesienie do niego tylko w Tcl, a nie w samym Pythonie. Dlatego aplikacja powinna przechowywać odniesienie do PhotoImage tyle czasu, ile obraz powinien być wyświetlany.

Poprawiony kod
photoimage_fixed.py
class App:
    def __init__(self, root):
        self.logo_image = tk.PhotoImage(file="icon.png")   # przechowywane w self —
        self.logo_label = ttk.Label(root, image=self.logo_image)   # żyje tak długo, jak self
        self.logo_label.pack()
Praktyka: zbieramy formularz z tk i ttk
Moduł tkinter otwiera natywne okno Python — wykonaj lokalnie w VS Code, PyCharm lub Jupyter
Praktyka kursuje lokalnie
Otwórz praktykę →