Last modified: November 07, 2024

This article is written in: 🇵🇱

Tkinter - Tworzenie interfejsów graficznych

Tkinter jest standardowym modułem Pythona służącym do tworzenia interfejsów graficznych użytkownika (GUI). Dzięki niemu możemy szybko i efektywnie tworzyć aplikacje okienkowe, które są interaktywne i przyjazne dla użytkownika. W poniższych sekcjach omówimy szczegółowo, jak zainicjalizować okno główne, dodawać do niego elementy, układać widżety oraz obsługiwać zdarzenia.

Inicjalizacja okna głównego

Pierwszym krokiem w tworzeniu aplikacji graficznej w Pythonie z użyciem Tkinter jest zaimportowanie modułu i zainicjalizowanie głównego okna aplikacji.

import tkinter as tk

root = tk.Tk()                # Tworzy główne okno aplikacji
root.title("Moja aplikacja")   # Ustawia tytuł okna na "Moja aplikacja"
root.geometry("300x200")       # Ustawia wymiary okna: szerokość = 300, wysokość = 200
root.mainloop()                # Uruchamia główną pętlę aplikacji

Przykładowe okno Tkinter

Wyjaśnienie krok po kroku:

  1. import tkinter as tk — Importujemy moduł Tkinter z aliasem tk, co upraszcza odwoływanie się do elementów Tkintera.
  2. root = tk.Tk() — Inicjalizujemy główne okno aplikacji, które jest kontenerem dla wszystkich widżetów.
  3. root.title("Moja aplikacja") — Ustawiamy tytuł okna, który pojawi się na pasku tytułowym.
  4. root.geometry("300x200") — Ustalamy rozmiar okna w formacie "szerokośćxwysokość".
  5. root.mainloop() — Rozpoczyna główną pętlę zdarzeń, dzięki czemu aplikacja pozostaje aktywna i może reagować na interakcje.

Dodawanie elementów do okna

Po utworzeniu głównego okna możemy dołączać do niego różne elementy interfejsu, zwane widżetami. Tkinter oferuje bogaty zestaw widżetów, takich jak przyciski, etykiety, pola tekstowe itp.

Przykład - Dodanie przycisku

Przyciski umożliwiają użytkownikowi interakcję z aplikacją, wywołując określone funkcje po kliknięciu.

def on_button_click():
    print("Kliknięto przycisk!")

button = tk.Button(root, text="Kliknij", command=on_button_click)
button.pack(pady=20)

Przycisk Tkinter

Wyjaśnienie:

Przykład - Dodanie etykiety

Etykiety służą do wyświetlania tekstu lub obrazów.

label = tk.Label(root, text="To jest etykieta", font=("Arial", 14))
label.pack(pady=20)

Etykieta Tkinter

Wyjaśnienie:

Układanie i kompozycja widżetów w Tkinter

Tworzenie przejrzystego i estetycznego interfejsu w Tkinter opiera się na odpowiednim układaniu widżetów. Tkinter oferuje trzy główne metody rozmieszczania elementów w oknie: pack(), grid() oraz place(). Każda z tych metod służy innym celom i posiada unikalne parametry umożliwiające elastyczną kontrolę nad układem widżetów.

I. Metoda pack()

Metoda pack() automatycznie rozmieszcza widżety w interfejsie, układając je jeden za drugim zgodnie z ustaloną orientacją. Domyślnie widżety są rozmieszczane w pionie. Chociaż metoda ta jest łatwa w użyciu, oferuje ograniczoną kontrolę nad dokładnym pozycjonowaniem widżetów, co może być mniej wygodne w bardziej złożonych interfejsach.

Poniższy przykład pokazuje podstawowe użycie pack() do ułożenia dwóch przycisków po lewej i prawej stronie okna:

import tkinter as tk

root = tk.Tk()
button1 = tk.Button(root, text="Button 1")
button2 = tk.Button(root, text="Button 2")

button1.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
button2.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

root.mainloop()

Układ przycisków po lewej i prawej stronie

W tym przykładzie oba przyciski są umieszczone po przeciwnych stronach (LEFT i RIGHT) oraz wypełniają całą przestrzeń pionową i poziomą dzięki parametrom fill=tk.BOTH oraz expand=True.

Parametry metody pack():

Parametr Opis Przykład
side Określa, z której strony widżet będzie pakowany. Możliwe wartości to: tk.TOP, tk.BOTTOM, tk.LEFT, tk.RIGHT. side=tk.LEFT
fill Określa, czy widżet ma wypełnić przestrzeń w kierunku poziomym (tk.X), pionowym (tk.Y) lub obu (tk.BOTH). fill=tk.BOTH
expand Jeśli ustawione na True, widżet rozszerzy się, aby wypełnić dostępną przestrzeń w ramach swojego kontenera. expand=True
padx Dodaje poziomy odstęp (padding) po obu stronach widżetu. padx=10
pady Dodaje pionowy odstęp (padding) nad i pod widżetem. pady=5

Przykład 1: Tworzenie listy przycisków w pionie z odstępami

W tym przykładzie umieszczamy pięć przycisków jeden pod drugim, każdy z niewielkim odstępem (pady=5), aby wyglądały bardziej estetycznie.

import tkinter as tk

root = tk.Tk()
for i in range(5):
    button = tk.Button(root, text=f"Button {i+1}")
    button.pack(fill=tk.X, pady=5)  # Pionowy odstęp między przyciskami

root.mainloop()

Przyciski ułożone pionowo z odstępami

Dzięki ustawieniu fill=tk.X, przyciski wypełniają całą dostępną szerokość okna, co pozwala uzyskać bardziej zorganizowany wygląd.

Przykład 2: Tworzenie prostego menu poziomego

W tym przykładzie tworzymy prosty pasek menu umieszczony u góry okna. Każdy przycisk w menu jest ustawiony w jednej linii dzięki side=tk.LEFT oraz dodaniu poziomego odstępu (padx=5), aby oddzielić przyciski.

import tkinter as tk

root = tk.Tk()
menu_bar = tk.Frame(root)
menu_bar.pack(side=tk.TOP, fill=tk.X)

btn1 = tk.Button(menu_bar, text="Home")
btn1.pack(side=tk.LEFT, padx=5)

btn2 = tk.Button(menu_bar, text="Settings")
btn2.pack(side=tk.LEFT, padx=5)

root.mainloop()

Proste menu poziome

W tym przypadku fill=tk.X w menu_bar sprawia, że pasek menu rozciąga się na całą szerokość okna, podczas gdy poszczególne przyciski Home i Settings są ułożone poziomo z niewielkim odstępem.

II. Metoda grid()

Metoda grid() umożliwia organizację widżetów w siatce, przypisując je do określonych wierszy (row) i kolumn (column). Dzięki temu jest idealna do tworzenia bardziej precyzyjnych układów, takich jak formularze, tabele, czy skomplikowane interfejsy, które wymagają dokładnego rozmieszczenia elementów.

Poniższy przykład pokazuje podstawowe użycie grid() do stworzenia prostego formularza z polami dla nazwy użytkownika i hasła:

import tkinter as tk

root = tk.Tk()
label1 = tk.Label(root, text="Username")
entry1 = tk.Entry(root)
label2 = tk.Label(root, text="Password")
entry2 = tk.Entry(root)

label1.grid(row=0, column=0, sticky="w")
entry1.grid(row=0, column=1)
label2.grid(row=1, column=0, sticky="w")
entry2.grid(row=1, column=1)

root.mainloop()

Formularz logowania w siatce

W tym przykładzie etykiety są umieszczone w lewej kolumnie (column=0), a pola wejściowe w prawej (column=1). sticky="w" powoduje, że etykiety są wyrównane do lewej strony komórki.

Parametry metody grid():

Parametr Opis Przykład
row Określa numer wiersza, w którym ma się znaleźć widżet. row=0
column Określa numer kolumny, w której ma się znaleźć widżet. column=1
sticky Ustawia, do której krawędzi komórki ma przylegać widżet (n, s, e, w lub ich kombinacja). sticky="w"
padx Dodaje poziomy odstęp (padding) po obu stronach widżetu w komórce. padx=5
pady Dodaje pionowy odstęp (padding) nad i pod widżetem w komórce. pady=5
columnspan Określa liczbę kolumn, które widżet ma zajmować (przydatne dla elementów zajmujących więcej przestrzeni). columnspan=2
rowspan Określa liczbę wierszy, które widżet ma zajmować. rowspan=2

Przykład 1: Tworzenie prostego formularza

Poniżej przedstawiono przykład tworzenia bardziej rozbudowanego formularza, gdzie etykiety i pola tekstowe są rozmieszczone w układzie tabelarycznym.

labels = ["Name", "Email", "Age"]
for i, text in enumerate(labels):
    label = tk.Label(root, text=text)
    label.grid(row=i, column=0, sticky="w")
    entry = tk.Entry(root)
    entry.grid(row=i, column=1)

Prosty formularz z polami dla imienia, emaila i wieku

Tutaj etykiety są wyrównane do lewej (sticky="w"), a każde pole tekstowe jest przypisane do kolumny po prawej stronie, co tworzy przejrzysty i uporządkowany wygląd formularza.

Przykład 2: Rozciąganie elementu na dwie kolumny

W przypadku, gdy widżet, taki jak etykieta statusu, ma zajmować więcej miejsca (np. całą szerokość okna), można użyć parametru columnspan, aby rozciągnąć go na kilka kolumn.

status_label = tk.Label(root, text="Status: Ready", bg="lightgrey")
status_label.grid(row=3, column=0, columnspan=2, sticky="we")  # Rozciągnięcie na 2 kolumny

Etykieta statusu rozciągnięta na dwie kolumny

Dzięki columnspan=2, etykieta statusu zajmuje obie kolumny, co pozwala jej się rozciągnąć na całą szerokość. sticky="we" sprawia, że widżet przylega do lewej i prawej strony, co dodatkowo poprawia estetykę.

III. Metoda place()

Metoda place() umożliwia precyzyjne pozycjonowanie widżetów za pomocą współrzędnych x i y, co pozwala na pełną kontrolę nad ich umiejscowieniem w oknie. Jest to przydatne, gdy chcemy rozmieścić widżety w dokładnie określonych miejscach lub tworzyć bardziej nietypowe układy. Wadą tej metody jest konieczność znajomości wymiarów okna oraz widżetów, co sprawia, że metoda place() jest mniej elastyczna od pack() czy grid().

Poniższy przykład pokazuje, jak umieścić przycisk w określonej pozycji za pomocą x=50 oraz y=100.

import tkinter as tk

root = tk.Tk()
button = tk.Button(root, text="Click me")
button.place(x=50, y=100)

root.mainloop()

Pozycjonowanie przycisku w oknie za pomocą współrzędnych

W tym przykładzie przycisk jest umieszczony w pozycji (50, 100) względem lewego górnego rogu okna.

Parametry metody place():

Parametr Opis Przykład
x Pozycja widżetu na osi X w pikselach (od lewej krawędzi okna). x=100
y Pozycja widżetu na osi Y w pikselach (od górnej krawędzi okna). y=50
relx Pozycja widżetu na osi X jako część całkowitej szerokości (np. relx=0.5 umieszcza na środku). relx=0.5
rely Pozycja widżetu na osi Y jako część całkowitej wysokości (np. rely=0.5 umieszcza na środku). rely=0.5
anchor Punkt odniesienia, względem którego widżet jest pozycjonowany. Możliwe wartości: n, s, e, w, center itd. anchor="center"
width Szerokość widżetu w pikselach. width=200
height Wysokość widżetu w pikselach. height=50

Przykład 1: Pozycjonowanie elementu na środku okna

Poniżej znajduje się przykład ustawienia przycisku w samym środku okna. Parametry relx=0.5 i rely=0.5 umieszczają przycisk na 50% szerokości i 50% wysokości okna, a anchor="center" sprawia, że środek przycisku jest odniesieniem tej pozycji.

button = tk.Button(root, text="Center")
button.place(relx=0.5, rely=0.5, anchor="center")

Pozycjonowanie przycisku na środku okna

Przykład 2: Tworzenie layoutu na podstawie współrzędnych

W tym przykładzie rozmieszczamy kilka etykiet w oknie, każdą w różnych pozycjach zdefiniowanych przez x i y, co tworzy bardziej nieregularny układ.

label1 = tk.Label(root, text="Label 1")
label1.place(x=10, y=10)

label2 = tk.Label(root, text="Label 2")
label2.place(x=10, y=50)

label3 = tk.Label(root, text="Label 3")
label3.place(x=100, y=100)

Układ etykiet na różnych współrzędnych

Dzięki zastosowaniu x i y, każda etykieta jest umieszczona w precyzyjnie określonej pozycji, tworząc unikalny układ na potrzeby bardziej skomplikowanych projektów interfejsu.

Organizacja widżetów za pomocą ramek

Ramki (Frame) są kontenerami, które grupują widżety i pomagają organizować bardziej złożone układy.

Przykład użycia ramek

import tkinter as tk

root = tk.Tk()

# Tworzenie ramki
frame = tk.Frame(root, bg="lightgray", bd=5, relief="ridge")
frame.pack(pady=20, padx=20)

button1 = tk.Button(frame, text="Przycisk 1")
button2 = tk.Button(frame, text="Przycisk 2")

# Umieszczanie przycisków w siatce wewnątrz ramki
button1.grid(row=0, column=0, padx=10)
button2.grid(row=0, column=1, padx=10)

root.mainloop()

Ramka Tkinter

Parametry ramki:

Parametr Opis
bg Ustawia kolor tła ramki.
bd Określa szerokość obramowania ramki.
relief Styl obramowania (np. sunken, raised, groove, ridge), co dodaje wizualne wyróżnienie.

Zalety ramek:

Wskazówki:

Obsługa zdarzeń

Interaktywność aplikacji zależy od jej zdolności do reagowania na zdarzenia, takie jak kliknięcia myszy czy naciśnięcia klawiszy. Tkinter pozwala nam obsługiwać te zdarzenia za pomocą metody bind().

Popularne Zdarzenia

Poniżej kilka popularnych zdarzeń w Tkinter i ich opisy:

Kliknięcia myszy:

Symbol Opis
<Button-1> Kliknięcie lewym przyciskiem myszy.
<Button-3> Kliknięcie prawym przyciskiem myszy.
<Double-Button-1> Podwójne kliknięcie lewym przyciskiem.

Ruch myszy:

Symbol Opis
<Enter> Kursor wchodzi na obszar widżetu.
<Leave> Kursor opuszcza obszar widżetu.
<Motion> Ruch kursora nad widżetem.

Klawiatura:

Symbol Opis
<Key> Naciśnięcie dowolnego klawisza.
<Return> Naciśnięcie klawisza Enter.
<Escape> Naciśnięcie klawisza Esc.

Metoda bind()

Aby powiązać zdarzenie z konkretną funkcją obsługującą, używamy metody bind(). Funkcje te przyjmują argument event, który zawiera szczegóły zdarzenia (takie jak pozycja kursora czy informacje o klawiszach modyfikujących).

Przykład obsługi zdarzeń myszy

Poniższy kod reaguje na to, czy kursor znajduje się nad przyciskiem, wypisując odpowiednie komunikaty.

from tkinter import Tk, Button

def on_mouse_enter(event):
    print("Myszka najechała na przycisk")

def on_mouse_leave(event):
    print("Myszka opuściła przycisk")

root = Tk()
button = Button(root, text="Najedź na mnie!")
button.bind("<enter>", on_mouse_enter)
button.bind("<leave>", on_mouse_leave)
button.pack()
root.mainloop()

Wyjaśnienie:

Obsługa zdarzeń klawiatury

Możemy reagować na naciśnięcie klawiszy, np. wyświetlając komunikat, gdy użytkownik naciśnie konkretny klawisz.

Przykład obsługi klawisza

def on_key_press(event):
    print(f"Naciśnięto klawisz: {event.char}")

root = Tk()
root.bind("<key>", on_key_press)
root.mainloop()

Wyjaśnienie:

Łączenie wielu zdarzeń

Tkinter pozwala przypisać wiele różnych zdarzeń do tego samego widżetu lub jedną funkcję do obsługi zdarzeń na wielu widżetach.

Przykład — Łączenie zdarzeń na przycisku

Poniższy przykład przedstawia reakcję na różne kliknięcia przycisków myszy.

def on_left_click(event):
    print("Lewy przycisk myszy")

def on_right_click(event):
    print("Prawy przycisk myszy")

button = Button(root, text="Kliknij mnie")
button.bind("<button-1>", on_left_click)   # Kliknięcie lewym przyciskiem
button.bind("<button-3>", on_right_click)  # Kliknięcie prawym przyciskiem
button.pack()

Obsługa zdarzeń przy użyciu dekoratorów

Domyślnie Tkinter nie obsługuje dekoratorów dla zdarzeń, ale można napisać własny dekorator event_handler, który uprości wiązanie zdarzeń.

Przykład własnego dekoratora event_handler

def event_handler(event_name):
    def decorator(func):
        def wrapper(event):
            return func(event)
        wrapper.event_name = event_name
        return wrapper
    return decorator

class MyApp:
    def __init__(self, root):
        self.button = Button(root, text="Kliknij mnie")
        self.button.pack()
        self.bind_events()

    def bind_events(self):
        for attr in dir(self):
            func = getattr(self, attr)
            if callable(func) and hasattr(func, 'event_name'):
                self.button.bind(func.event_name, func)

    @event_handler("<button-1>")
    def on_click(self, event):
        print("Przycisk został kliknięty")

root = Tk()
app = MyApp(root)
root.mainloop()

Wyjaśnienie:

  1. Dekorator event_handler dodaje atrybut event_name do funkcji, wskazując typ obsługiwanego zdarzenia.
  2. Metoda bind_events przeszukuje metody klasy MyApp i przypisuje zdarzenia do tych, które mają atrybut event_name.
  3. @event_handler("<Button-1>") powiązuje funkcję on_click z lewym przyciskiem myszy.

Uwaga: To rozwiązanie wymaga dobrej znajomości Pythona i stosowane jest w bardziej złożonych aplikacjach.

Zamykanie aplikacji na zdarzenie

Zamknięcie aplikacji można obsłużyć za pomocą przycisku wywołującego metodę root.destroy() lub na przykład na zdarzenie klawiaturowe.

Przykład — Dodanie przycisku zamykającego aplikację

import tkinter as tk

def close_app():
    root.destroy()

root = tk.Tk()
button_exit = tk.Button(root, text="Zamknij", command=close_app)
button_exit.pack()
root.mainloop()

Przycisk zamykający aplikację

Wyjaśnienie:

Alternatywne zamknięcie aplikacji

Możemy także przypisać zamknięcie aplikacji do klawisza, na przykład Escape.

root.bind("<escape>", lambda event: root.destroy())

Spis Treści

    Tkinter - Tworzenie interfejsów graficznych
    1. Inicjalizacja okna głównego
    2. Dodawanie elementów do okna
    3. Układanie i kompozycja widżetów w Tkinter
      1. I. Metoda pack()
      2. II. Metoda grid()
      3. III. Metoda place()
    4. Organizacja widżetów za pomocą ramek
    5. Obsługa zdarzeń
    6. Obsługa zdarzeń klawiatury
    7. Łączenie wielu zdarzeń
    8. Obsługa zdarzeń przy użyciu dekoratorów
    9. Zamykanie aplikacji na zdarzenie