Języki skryptowe - Python

Wykład 12


  • graficzny interfejs użytkownika

GUI


  • Graphical User Interface
  • Najpopularniejsze biblioteki:
    • Tkinter
    • PyQT
    • PyGTK
    • wxPython
  • Pełna lista: link

PIerwsze okienko


import tkinter as tk

# główne okno programu
# z tytułem, krawędziami itp
# kontener na resztę
root = tk.Tk()

# pętla programu - wyłapuje sygnały
# przerwana przez zamknięcie okna
# lub wywołanie quit()
root.mainloop()  

Tytuł i wymiary


root = tk.Tk()

# tytuł okna
root.title("Hello World!")

# szerokość x wysokość + położenie x + położenie y
root.geometry("400x300+200+200")

root.mainloop()

Klasa App


class App(tk.Tk):
    # App dziedziczy z tkinter.Tk
    def __init__(self, title="Aplikacja"):
        super().__init__() # konstruktor Tk
        self.title(title)  # ustaw tytuł

    def run(self):
        # app.run() będzie się lepiej prezentować
        self.mainloop()

Pierwsza "aplikacja"


app = App("Pierwsza aplikacja")

# app dziedziczy z Tk
# więc ma dostęp do tych samych funkcji

#app.geometry("400x300+200+200")

app.run()

Centrowanie aplikacji


class App(tk.Tk):
    def __init__(self, title="Aplikacja"):
        super().__init__() # konstruktor Tk
        #super().option_add("*font", "Helvetica 24")
        self.title(title)  # ustaw tytuł
        self.center()      # wyśrodkuj

    def run(self): self.mainloop()

    def center(self):
        self.update()
        # szerokość / wysokość okna
        wx = self.winfo_width()
        wy = self.winfo_height()
        # szerokość wysokość ekranu
        sx = self.winfo_screenwidth()
        sy = self.winfo_screenheight()
        # środek ekranu przesunięty o 
        x = (sx - wx) // 2 # połowę szerokośi
        y = (sy - wy) // 2 # połowę wysokości

        self.geometry("{}x{}+{}+{}".format(wx, wy, x, y))

Test


app = App("Wyśrodkowana aplikacja")

app.run()

Pierwszy Frame


# Frame to po prostu prostokątny obszar

root = tk.Tk()
root.geometry("400x300+200+200")

# Frame(parent)
ramka = tk.Frame(root) # umieść ramkę w root
# więcej o pack za chwilę
ramka.pack(fill=tk.BOTH, expand=True)

root.mainloop()

Pierwszy kolorowy Frame


root = tk.Tk()
root.geometry("400x300+200+200")

# Frame(parent, background=color)
ramka = tk.Frame(root, background="red")
ramka.pack(fill=tk.BOTH, expand=True)

root.mainloop()

Aplikacja z ramką


app = App("Jedna ramka")

# App dziedziczy z tk.Tk
# więc też może być argumentem Frame(...)
ramka = tk.Frame(app, background="red")
ramka.pack(fill=tk.BOTH, expand=True)

app.run()

Klasa ramka


# pozwoli zaoszczędzić miejsca na kolejnych slajdach

class Ramka(tk.Frame):
    # nasza ramka dziedziczy z tkinter.Frame
    def __init__(self, parent, color="white"):
        # wywołaj konstruktor Frame
        tk.Frame.__init__(self, parent, background=color)
        # o pack więcej za chwilę
        self.pack(fill=tk.BOTH, expand=True)

Przykład


app = App("Jedna ramka")

# Ramka inicjuje Frame
# i się pakuje w App
ramka = Ramka(app)

app.run()

Dwie ramki


app = App("Jedna ramka")


r1 = Ramka(app) # "white"
r2 = Ramka(app, "red")

app.run()

Pakowanie


  • do rozmieszczania widgetów służy funkcja widget.pack(opcje)
  • dostępne opcje:
    • expand - True / False; domyślnie jak rodzic
    • fill - None (domyślnie) / BOTH / X / Y
    • side - z której strony rodzica zacząć układać: TOP (domyślnie) / BOTTOM / LEFT / RIGHT

Pakowanie ramek


class Ramka(tk.Frame):
    # dodajemy argument kluczowy side
    def __init__(self, parent, color="white", side=tk.TOP):
        tk.Frame.__init__(self, parent, background=color)   
        self.pack(fill=tk.BOTH, expand=True, side=side)

Pakowanie ramek I


app = App("Dwie ramki")

r1 = Ramka(app, "white", tk.LEFT)
r2 = Ramka(app, "red", tk.RIGHT)

app.run()

Pakowanie ramek II


app = App("Trzy ramki")

r1 = Ramka(app, "white", tk.LEFT)
r2 = Ramka(app, "red", tk.RIGHT)
r3 = Ramka(app, "green", tk.TOP)

app.run()

Pakowanie ramek III


app = App("Trzy ramki")

r1 = Ramka(app, "green", tk.TOP) # górna ramka
r2 = Ramka(app) # dolna ramka

# do dolnej ramki wrzucamy lewą i prawą
r21 = Ramka(r2, "white", tk.LEFT)
r22 = Ramka(r2, "red", tk.RIGHT)

app.run()

Pierwszy przycisk


app = App("Aplikacja z przyciskiem")

ramka = Ramka(app)

# Button(parent, text, command)
# app.center bez ()
przycisk = tk.Button(ramka, text="Center", command=app.center)
przycisk.place(x=0, y=0) # pozycja przycisku

app.run()

Więcej o command


def funkcja():
    return "Hello"

# przypisuje odpowiednią funkcję
# która będzie wywołana po naciśnięciu
przycisk1 = tk.Button(command=funkcja)

przycisk1.cget("command")
# przypisuje wynik zwracany przez funkcję
przycisk2 = tk.Button(command=funkcja())

przycisk2.cget("command")

command i argumenty funkcji?


  • a co jeśli chcemy, aby przycisk wywoływał funkcję z jakimś argumentem?
  • np. mamy dwa przyciski i chcemy, aby jeden zmieniał tło na kolor biały a drugi na czerwony
  • chcemy użyć tej samej funkcji, ale z innym argumentem
  • wtedy niezbędne jest wykorzystanie wyrażeń lambda

Wyrażenia lambda


def suma(a, b): # "klasyczna" funkcja
    return a + b
lsuma = lambda a, b: a + b # "anonimowa" funkcja
suma(1, 2) # wywołujemy funkcję suma
lsuma(1, 2) # wywołujemy wyrażenia lambda

Wyrażenie lambda


def suma(a, b):
    return a + b

suma2 = lambda b: suma(2, b) # dodaj 2
suma4 = lambda b: suma(4, b) # dodaj 4
suma2(1)
suma4(1)

Wyrażenie lambda


def change_color(color="white"):
    print("Zmieniam kolor na:", color)

ustaw_zielony = lambda: change_color("zielony")
ustaw_niebieski = lambda: change_color("niebieski")
ustaw_zielony() # change_color("zielony")
ustaw_niebieski() # change_color("niebieski")

Kolor ramki


class Ramka(tk.Frame):

    def __init__(self, parent, color="white", side=tk.TOP):
        tk.Frame.__init__(self, parent, background=color)   
        self.pack(fill=tk.BOTH, expand=True, side=side)

    def change_color(self, color="white"):
        # Frame.config umożliwia zmianę parametrów
        self.config(bg=color) # bg = background
app = App("Jedna ramka")

ramka = Ramka(app) # domyślnie biała
ramka.change_color("red") # zmień kolor

app.run()

Przycisk do koloru


app = App("Przycisk do tła")

ramka = Ramka(app)

# Button(parent, text, command)
przycisk = tk.Button(ramka, text="Red",
                     command=lambda: ramka.change_color("red"))
przycisk.place(x=0, y=0) # pozycja przycisku

app.run()

Kilka przycisków


app = App("Przyciski v1")

ramka = Ramka(app)

but_red = tk.Button(ramka, text="Red",
                    command=lambda: ramka.change_color("red"))
but_red.place(x=0, y=0) # wpisujemy ręcznie

but_blue = tk.Button(ramka, text="Blue",
                     command=lambda: ramka.change_color("blue"))
but_blue.place(x=0, y=50) # absolutne pozycje

but_def = tk.Button(ramka, text="Default",
                    command=lambda: ramka.change_color())
but_def.place(x=0, y=100) # przycisków

app.run()

Kilka przycisków krócej


app = App("Przyciski v2")

ramka = Ramka(app)

# wykorzystamy wyrażenia lambda do generowania przycisków
button = lambda color: tk.Button(ramka, text=color,
                                 command=lambda: ramka.change_color(color))

button("red").place(x=0, y=0)     # wpisujemy ręcznie
button("blue").place(x=0, y=50)   # absolutne pozycje
button("white").place(x=0, y=100) # przyciskow

app.run()

Układanie przycisków


app = App("Przyciski v3")

ramka = Ramka(app)

button("red").pack(side=tk.LEFT)   # pakujemy od lewej
button("blue").pack(side=tk.LEFT)
button("white").pack(side=tk.LEFT)

app.run()

Rozciąganie przycisków


app = App("Przyciski v4")

ramka = Ramka(app)

# czerwony rozciągamy w poziomie
button("red").pack(side=tk.LEFT, fill=tk.X, expand=True)
# niebieski ma stały rozmiar
button("blue").pack(side=tk.LEFT)
# biały rozciądamy w pionie
button("white").pack(side=tk.LEFT, fill=tk.Y, expand=True)

app.run()

Siatka


app = App("Przyciski v5")

ramka = Ramka(app)


# 1 rząd 1 kolumna
button("red").grid(row=0, column=0)
# 1 rząd 2 kolumna
button("blue").grid(row=0, column=1)
# 2 rząd 1 kolumna
button("white").grid(row=1, column=0)
# 2 rząd 2 kolumna
button("black").grid(row=1, column=1)

app.run()

Rozciąganie w siatce


from tkinter import N, S, E, W

app = App("Przyciski v6")

ramka = Ramka(app)

# 1 rząd 1 kolumna
button("red").grid(row=0, column=0, sticky=N+S+E+W)
# 1 rząd 2 kolumna
button("blue").grid(row=0, column=1, sticky=N+S+E+W)
# 2 rząd 1 kolumna
button("white").grid(row=1, column=0, sticky=N+S+E+W)
# 2 rząd 2 kolumna
button("black").grid(row=1, column=1, sticky=N+S+E+W)

app.run()

Pole tekstowe


app = App("Pole tekstowe")

tekst = tk.Entry(app) # pole tekstowe
tekst.pack() # domyślne pakowanie

app.run()

Pole tekstowe - wstaw


app = App("Pole tekstowe - insert")

tekst = tk.Entry(app)
tekst.pack(fill=tk.X, expand=True) # rozciągnij w poziomie

# wstaw początkowy tekst
tekst.insert(0, "Tutaj wpisz cokolwiek...")

app.run()

Usuwanie tekstu


app = App("Pole tekstowe - delete")

tekst = tk.Entry(app)
tekst.pack(fill=tk.X, expand=True)

tekst.insert(0, "Tutaj wpisz cokolwiek...")

# Entry.delete usuwa tekst
przycisk = tk.Button(app, text="clear",
                     command=lambda: tekst.delete(0, tk.END))
przycisk.pack()

app.run()

Pobieranie tekstu


app = App("Pole tekstowe - get")

tekst = tk.Entry(app)
tekst.pack(fill=tk.X, expand=True)

tekst.insert(0, "Tutaj wpisz cokolwiek...")

# Entry.get pobiera tekst
przycisk = tk.Button(app, text="pobierz", command=lambda: print(tekst.get()))
przycisk.pack()

app.run()

Student GUI


class Student:

    def __init__(self, imie, nazwisko, indeks):
        self._imie = imie
        self._nazwisko = nazwisko
        self._indeks = indeks

        self.save() # automatyczny zapis przy tworzeniu studenta

    def save(self):
        with open(".".join([self._imie, self._nazwisko]), 'w') as f:
            f.write(self._indeks)

Główne okno programu


import tkinter as tk

root = tk.Tk()
#root.option_add("*font", "Helvetica 24")
root.title("Student")
root.geometry("800x600+400+300")

# ramka z tłem, do której będziemy wrzucać pola tekstowe
ramka = tk.Frame(root, bg="white")
ramka.pack(fill=tk.BOTH, expand=True)

Entries


# imię
tk.Label(ramka, text="Imię: ").grid(row=0, column=0, sticky="nsew")
imie = tk.Entry(ramka)
imie.grid(row=0, column=1, sticky="nsew")

# nazwisko
tk.Label(ramka, text="Nazwisko: ").grid(row=1, column=0, sticky="nsew")
nazwisko = tk.Entry(ramka)
nazwisko.grid(row=1, column=1, sticky="nsew")

# numer indeksu
tk.Label(ramka, text="Indeks: ").grid(row=2, column=0, sticky="nsew")
indeks = tk.Entry(ramka)
indeks.grid(row=2, column=1, sticky="nsew")

Przycisk save


# wyrażenie lamba generujące studenta
# na bazie wpisów w entries
make_student = lambda: Student(imie.get(), nazwisko.get(), indeks.get())

przycisk = tk.Button(root, text="save", command=make_student)
przycisk.pack()

Test


root.mainloop()
%%bash
cat Jan.Kowalski