Програмиране с Python: Графичен интерфейс с Tkinter
Author: Стефан Кънев, Николай Бачийски и Димитър Димитров

# Графичен интерфейс с Tkinter

### „ Програмиране с Python“, ФМИ

#### ['Къньо Стефанов', 'Бачий Николаев', 'Димитър Димитров']

#### 14.05.2008

# Графичен интерфейс в Python

Създаването на графичен интерфейс (GUI) е силно зависимо от операционната система - компоненти като текстови полета, бутони и менюта работят с различни механизми на Linux, Windows и Mac.

Въпреки това, в Python съществуват няколко библиотеки, които работят (горе-долу) платформено независимо.

- [wxPython http://www.wxpython.org/] - базирана на библиотеката wxWidgets
- [PyQT http://wiki.python.org/moin/PyQT] - базирана на QT, важна част от KDE

# Tkinter

Tkinter е порт на Tk за Python. Tk е мултиплатформена графична библиотека. Освен в Python, тя има имплементация в редица други езици - Tcl, Perl, Ruby.

# Hello, word

    from Tkinter import *
    import random

    def wisdom():
      return random.choice([
        "Beautiful is better than ugly.",
        "Now is better than never.",
        "Simple is better than complex.",
        "Complex is better than complicated."])

    root = Tk()

    greeting = Label(root, text="Hello, world!")
    greeting.pack(side=TOP)

    def new_wisdom():
      greeting['text'] = wisdom()

    be_wise = Button(root, text="Kind sir, may I have another?", command=new_wisdom)
    be_wise.pack()

    root.mainloop()

# Widget

Tk програмите се организират чрез дървовидна структура от компоненти. Тези компоненти се наричат `Widget`-и. Всеки `Widget` се конструира с родител и опции. Изключение правят само тези в основата на йерархията - инстанции на `Tk` и `Toplevel`. Те нямат нужда от родител.

    from Tkinter import *

    root = Tk()

    greeting = Label(root, text="I am a label")
    greeting.pack()

    root.mainloop()

# Опции

Всеки `Widget` има набор от опрции (options), които определят как изглежда. Те могат да се настройват с:

    Widget(master, option1=value1, option2=value2)
    widget.config(option1=value1, option2=value2)
    widget['option1'] = value

Могат да се вземат с

    print widget['option1']
    print widget.cget('option1')

# Няколко основни widget-а

- `Button(parent, text=…, command=…)` - бутон, който може да бъде натиснат с мишката
- `Label(parent, text=…, image=…)` - показва текст или картинка
- `Entry(parent)` - показва единично поле за въвеждане на текст
- `Checkbutton(parent, text=…)` - бутон с две състояния - включено и изключено
- `Text(parent)` - компонент за редактиране на богат текст - поддържа цветове, картинки и различни шрифтове
- `Frame(parent)` - генеричен контейнер за `Widget`-и
- `Canvas(parent)` - компонент за чертане на графика

# Геометрии

Когато влагате компоненти един в друг, имате три алтернативи (наречени „геометрии“) за позициониране на децата.

- `pack()` - позволява ви да нареждате компонентите един срямо друг - ляво, дясно, горе или долу
- `grid()` - позволява ви да наредите компонентите в „таблица“
- `place()` - позволява ви директно да задавате координатите на компонентите в пиксели

# Пример за `grid()`

    from Tkinter import *

    root = Tk()

    x = Entry(root)
    y = Entry(root)
    result = Label(root, text="----")
    logarithm = Button(root, text="log",)
    exponent = Button(root, text="exp")

    x.grid(row = 0, column = 0)
    y.grid(row = 0, column = 1)
    logarithm.grid(row = 1, column = 0)
    exponent.grid(row = 1, column = 1)
    result.grid(row = 2, column = 0, columnspan = 2)

    root.mainloop()

# mathline.py v0.2

    from Tkinter import *
    import math

    root = Tk()

    x = Entry(root)
    y = Entry(root)
    result = Label(root, text="----")

    def do_exp():
      result['text'] = str(float(x.get()) ** float(y.get()))

    def do_log():
      result['text'] = str(math.log(float(y.get()), float(x.get())))

    logarithm = Button(root, text="log", command=do_log)
    exponent = Button(root, text="exp", command=do_exp)

    x.grid(row = 0, column = 0)
    y.grid(row = 0, column = 1)
    logarithm.grid(row = 1, column = 0)
    exponent.grid(row = 1, column = 1)
    result.grid(row = 2, column = 0, columnspan = 2)

    root.mainloop()

# Интерфейс за калкулатор

    from Tkinter import *

    class Calculator(Frame):
      def _make_button(self, name, text, row, column, columnspan=1):
        button = Button(self, text=text)
        button.grid(row = row, column = column,
            columnspan = columnspan, sticky=W+E)
        setattr(self, name, button)

      def __init__(self, master, **kwargs):
        Frame.__init__(self, master, **kwargs)

        number = Entry(self, text="", width = 20, justify='right')
        number.grid(row = 0, column = 0, columnspan = 3, stick=W+E)

        for n in range(0, 9):
            self._make_button('button_%s' % (n+1), str(n+1), n / 3 + 1, n % 3)
        self._make_button('result', '=', 0, 4)
        self._make_button('plus', '+', 1, 4)
        self._make_button('minus', '-', 2, 4)
        self._make_button('multiply', '*', 3, 4)
        self._make_button('divide', '/', 4, 4)

        self._make_button('button_0', '0', 4, 0, 2)
        self._make_button('dot', '.', 4, 2)

    root = Tk()

    calc = Calculator(root)
    calc.pack()

    root.mainloop()

# Диалогови прозорци

Може да отваряте диалогови прозорци с `tkMessageBox.тиn(заглавие, съобщение, опции)`, като `тип` може да е:

- `showinfo`
- `showwarning`
- `showerror`
- `askquestion`
- `askokcancel`
- `askyesno`
- `askretrycancel`

    tkMessageBox.showerror("Фал!", "Опа, стана фал!")

# Меню

Може да създавате менюта със `Menu`:

    from Tkinter import *

    def show(text): print text

    root = Tk()
    menu = Menu(root)

    file = Menu(menu)
    file.add_command(label = "Open", command = lambda: show("Open!"))
    file.add_command(label = "Save", command = lambda: show("Save!"))
    file.add_separator()
    file.add_command(label = "Close", command = lambda: show("Close!"))
    menu.add_cascade(label = "File", menu = file)

    about = Menu(menu)
    about.add_command(label = "About", command = lambda: show("About!"))
    menu.add_cascade(label = "Help", menu = about)

    root.config(menu = menu)
    root.mainloop()

# Събития

Може да прихващате събития с `bind(event, handler)`, където `event` е текстов низ идентифициращ събитието, а `handler` е едноаргументна функция, която ще бъде извикана при настъпването на събитието.

    from Tkinter import *

    def handle(event):
      print event.x, event.y

    root = Tk()
    frame = Frame(root, width=200, height=200)
    frame.pack()
    frame.bind("", handle)

    root.mainloop()

# Canvas

`Canvas` е widget, който позволява да чертаете свобоно. Той предлага различни методи, чертаещи различни обекти:

- create_arc
- create_bitmap
- create_image
- create_line
- create_oval
- create_polygon
- create_rectangle
- create_text
- create_window

# Canvas(2)

    from Tkinter import *

    root = Tk()
    canvas = Canvas(root, width=300, height=300)
    canvas.pack()

    canvas.create_line(0, 0, 300, 300, fill='red')
    canvas.create_line(0, 300, 300, 0, fill='blue')

    root.mainloop()

# items

Всеки начертан елемент в `Canvas` има уникален номер. Функциите `create_*` връщат този уникален номер. След това бихте могли да променяте опциите на начертания елемент чрез `canvas.itemconfig(item, options)` или да го триете чрез `canvas.delete(item)`.

# Етикети

Може да маркирате обектите които чертаете със текстови низове, наречени етикети. Това става като подадете опция `tags`, която да съдържа низ (при единичен етикет) или n-орка от низове (при няколко етикета). После може да ги намирате с `canvas.find_withtag(tag)`.

# Още въпроси?

- Страница на курса - http://fmi.py-bg.net/
- Форуми на курса - http://fmi.py-bg.net/forums/
- Въведение в Tkinter