Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

2. díl - Tvorba kalkučky v tkinteru

Python Tkinter Tvorba kalkučky v tkinteru

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V této lekci si zkusíme naprogramovat jednoduchou kalkulačku za použití tlačítek (button) a popisků (label). Ovšem budeme rozlišovat dva druhy widgetů (komponent do GUI aplikace) a to obyčejné z tkinteru a vzhledově lépe vypadající z podknihovny ttk.

Podknihovna reimplementuje všechny hlavní widgety, ale není úplně kompatibilní ve stylování widgetů.

Label

Label umožňuje v aplikaci zobrazit nějaký text. Česky se překládá jako textový popisek. V konstruktoru labelu můžeme přidat další volitelné parametry a tím změnit jeho vlastnosti.

Základní labely

Napřed se podíváme použití obyčejného labelu z tkinteru.

import tkinter

class MainWindow(tkinter.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.parent.title("První GUI aplikace")
        self.create_widgets()

    def create_widgets(self):
        self.label = tkinter.Label(text="Hello world!")
        self.label.grid(row=0, column=0)

root = tkinter.Tk()
app = MainWindow(root)
app.mainloop()

Možné parametry konstruktoru jsou:

  • text - zobrazí daný řetězec
  • background (bg) - barva pozadí
  • foreground (fg) - barva textu
  • wraplenght - počet řádek, na které se text zalomí (defaultně je nastaveno na nulu)

Existují i další, tyto jsou ty nezákladnější. Label v ukázce bude mít bílý text a černé pozadí.

ttk labely

Tyto pokročilejší labely se formátují za pomoci stylů. Vytvoříme si několik druhů stylů s předdefinovanými vlastnostmi a poté určíme každému popisku jeho styl.

Ukázka kódu pro vytvoření stylu a jeho aplikace:

style = Style()
style.configure("BW.Label", background="white", foreground="black")
label = ttk.Label(text="Some text", style="BW.Label")

Vytvořený label bude mít všechny vlastnosti nadefinované stylem. Metoda na konfigurování stylů přijímá jako první parametr název stylu a dále jako klíčové argumenty hodnoty jednotlivých vlastností, které se widgetům nastaví. Normálně je název stylu widgetu stejný jako název třídy widgetu (dá se zjistit metodou winfo_class()) a vlastní styly musí být ve tvaru <prefix>.<nazev tridy>

Tlačítka

Tlačítka jistě znáte. Umožňují po kliknutí provést nějakou akci v programu.

Nejzákladnějšími parametry jsou:

  • text - text zobrazený na tlačítku
  • background (bg) - barva pozadí
  • foreground (fg) - barva textu
  • command - funkce, která se má provést po stisknutí
  • state - stav tlačítka, možnosti jsou "normal" (tlačítko je funkční, ale zatím neaktivní), "active" (tlačítko je funkční a aktivní, např. pokud je nad ním myš) nebo "disabled" (tlačítko je nefunkční)
  • font - bere jako parametr buď řetězec s názvem fontu, nebo n-tici (název_fontu, velikost_fontu, dekorace)
button = tkinter.Button(text="Some text", background="white", command=self.do_something, font=("Arial", 18, "bold"))

Ovšem tato základní tlačítka jsou dosti nevzhledná, proto použijeme ekvivalenty z knihovny ttk.

ttk tlačítka

Základní parametry:

  • text - text zobrazený na tlačítku
  • command - funkce, která se má provést po stisknutí
  • style - název stylu, který se má použít pro tlačítko

Pro více názornosti, jak funguje stylování a bindování příkazů, se vrhneme na kalkulačku.

Kalkulačka

Napřed importujeme obě knihovny:

import tkinter
from tkinter import ttk

a nyní se vrhneme na okno. Okno dědí z ttk.Frame a nastavíme mu titulek "Kalkulacka". Kód pro výpočet vyčleníme do jiné třídy. Dále si připravíme speciální proměnnou, která bude představovat text zobrazený na displeji kalkulačky. Díky tomu se automaticky změní i text labelu, kdykoliv tuto proměnnou změníme. A nakonec zavoláme metodu, ve které vytvoříme obsah okna.

class CalculatorWindow(ttk.Frame):

    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.parent.title("Kalkulacka")
        self.calculator = Calculator()
        self.display_var = tkinter.StringVar(parent, self.calculator.text)
        self.create_widgets()

Nejprve definujeme seznam se znaky, které budou pod displejem. Displej zabere celou první řadu v okně. Dále upravíme styly widgetů a vytvoříme label představující display a tlačítka kalkulačky.

def create_widgets(self):
    buttons = [["7", "8", "9", "/", "("],
               ["4", "5", "6", "*", ")"],
               ["1", "2", "3", "-", "CE"],
               ["0", ".", "=", "+", "C"]
               ]
    self.define_styles()
    self.create_label(columnspan=max(map(len, buttons)))
    self.create_buttons(buttons)

V metodě definujeme dva styly. Defaultní styl pro nějaký prvek má název složený s písmena T a názvu widgetu. Pokud nechcete měnit výchozí styl widgetu, ale mít vlastní, tak existuje konvence, podle které se přidá před název defaultního stylu vlastní prefix. Například "display.TLabel" pro Label.

def define_styles(self):
    style = ttk.Style()
    style.configure("display.TLabel", foreground = "blue",
                    sticky="NSWE", font=15)
    style.configure("TButton", sticky="NSWE")

U labelu nastavujeme textovou proměnnou, kterou bude zobrazovat přes parametr textvariable. Ten přijímá jako parametr proměnnou typu StringVar, kterou jsme definovali již dříve. Pokud změníme její hodnotu přes metodu set, tak se automaticky updatuje i hodnota zobrazená widgetem. Kromě typu StringVar existují ještě typy IntVar, DoubleVar a BooleanVar, všechny dědící z třídy Variable.

def create_label(self, columnspan):
    self.display_label = ttk.Label(style="display.TLabel",
                                   textvariable=self.display_var)
    self.display_label.grid(row=0, column=0, columnspan=columnspan)

Dále musíme každému tlačítku přidat nějakou událost. To lze jednoduše přiřazením funkce do parametru command. Pokud potřebujeme zavolat funkci a předat jí parametry, tak si vypomůžeme lambda funkcí. Problém ale nastává, pokud potřebujeme vykonat více funkcí naráz, lambda funkce dokáže obalit pouze jedinou funkci. To se dá řešit buď vytvořením nové funkce, která uvnitř zavolá požadované funkce, anebo vytvořením speciální třídy.

class MultiCall:

    def __init__(self, *calls):
        self.calls = calls

    def __call__(self, *ignore):
        for func in self.calls:
            func()

Instance třídy dostane v konstruktoru funkce a pokud bude sama zavolaná jako funkce, tak volání propaguje do předaných funkcí. Místo nastavování každého tlačítka zvlášť projdeme dvojrozměrný seznam s tlačítky dvěma for cykly. Pokud potřebujeme předat hodnotu z aktuálního cyklu do lambda funkce, musíme předat proměnnou jako parametr do funkce, čímž se hodnota proměnné zkopíruje, jinak tyto lambda funkce budou sdílet stejnou proměnnou včetně její hodnoty.

def create_buttons(self, buttons):
    for i, row in enumerate(buttons):
        for j, button_text in enumerate(row):
            command = lambda button_text=button_text, *ignore: self.update_text(button_text)
            if button_text == "=":
                command = MultiCall(self.calculator.compute, self.update_text)
            elif button_text == "C":
                command = MultiCall(self.calculator.delete_last, self.update_text)
            elif button_text == "CE":
                command = MultiCall(self.calculator.delete_all, self.update_text)
            button = ttk.Button(text=button_text, command=command)
            button.grid(row = i + 1, column = j, padx = 2, pady = 2)
    for row_id in range(len(buttons)+1):
        self.parent.rowconfigure(row_id, weight=100)
    for column_id in range(len(buttons[0])):
        self.parent.columnconfigure(column_id, weight=100)

A již nám v CalculatorWindow chybí pouze metoda update_text(), která má volitelný parametr text, zastupující nově přidaný znak.

def update_text(self, text=""):
    self.calculator.add_symbols(text)
    self.display_var.set(self.calculator.text)

Nyní se vrhneme na třídu Calculator. V ní si budeme pamatovat text, který je zobrazený na displeji a to, zda-li je kalkulačka ve validním stavu, ve kterém neindikuje chybu.

class Calculator:

    def __init__(self):
        self.text = ""
        self.is_valid = False

    def add_symbols(self, symbols):
        if not self.is_valid and symbols != "":
            self.text = symbols
            self.is_valid = True
        elif len(self.text + symbols) < 20:
            self.text += symbols

    def delete_last(self):
        if self.is_valid:
            self.text = self.text[0:-1]
            if self.text == "":
                self.is_valid = False

    def delete_all(self):
        self.text = ""
        self.is_valid = False

    def compute(self):
        try:
            if self.is_valid:
                self.text = str(eval(self.text))
        except (ZeroDivisionError, SyntaxError, OverflowError, TypeError):
            self.text = "Error"
            self.is_valid = False

Pro jednoduchost používáme metodu eval(). Nyní stačí doplnit řádky na spuštění okna a kalkulačka je kompletní.

root = tkinter.Tk()
app = CalculatorWindow(root)
app.mainloop()

Takto vypadá naše kalkulačka ve Windows 10:

Ukázka formuláře

A to je pro tuto lekci vše, na procvičení si můžete napsat vlastní vyhodnocení výrazu, aby se zamezilo použití potenciálně nebezpečné funkce eval().


 

Stáhnout

Staženo 13x (3.42 kB)
Aplikace je včetně zdrojových kódů v jazyce Python

 

 

Článek pro vás napsal gcx11
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
(^_^)
Miniatura
Předchozí článek
Úvod do tkinteru v Pythonu
Miniatura
Všechny články v sekci
Okenní aplikace v Pythonu pomocí tkinter
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!