На главную страницу | Новости  |  Ссылки | Контакты

Spyphy Farnsworth
Квантовая реальность. Кибернетика. Искусственный интеллект


GUI на Python (Tkinter)

#!/bin/python

Библиотека Tk


Самый простой способ создания GUI-приложение на python - использование модуля tkinter, который по сути является надстройкой над библиотекой Tk.

Простой пример GUI-программы на python, созданный с помощью Tkinter


Рассмотрим минимальный пример:

from tkinter import Tk, Label
root = Tk()
label = Label(root, text='Hello') 
label.pack()
root.mainloop()

Создание GUI с помощью Tk включает в себя следующие основные шаги:


1) Импорт классов виджета из библиотеки:

from tkinter import Tk, Label

или

from tkinter import *

2) Создание главного окна:

root = Tk()

3) Создание виджетов и установка их свойств:

label = Label(root, text='Hello') 

либо

label = Label(root) 
label['text'] = 'Hello'

4) Определение событий и определение обработчиков событий (при необходимости) посредством метода bind или установки параметра command (для кнопки и меню);

5) Расположение (упаковка) виджетов на родительском окне:

label.pack()

6) Отображение главного окна и запуск цикла событий:

root.mainloop()

----------

Замечания:


Шаг 4 не обязателен в случае размещения пассивных элементов (таких как метки Label), но нужен, если мы хотим обрабатывать нажатие кнопок и т.п.

На самом деле создавать родительское окно (root) было тоже не обязательно, если виджет всего один. Так что этот пример с меткой можно было сократь до

from tkinter import Label
label = Label(None, text='Hello')
label.pack()
label.mainloop()

----------

Функция mainloop() запускает цикл обработки событий (посредством "обработного вызова"). Соответственно, чтобы происходили какие-то действия, необходимо зарегистрировать обработчики событий. Есть несколько способов как это сделать.

Обработка нажатий кнопок мыши и клавиш на клавиатуре


Способ 1 - cвойство command


import sys
from tkinter import *

def quit():
	sys.exit()

root = Tk()
but = Button(root, text="Press me to exit", command=quit) 
but.pack()
root.mainloop()

Здесь в качестве параметра мы передает ссылку на функцию (метод), а не результат выполнения функции. Поэтому скобки после quit ставить не нужно(!).

Конечно, здесь можно было и сразу написать


command = sys.exit

Также здесь можно было использовать анонимные функции (т.е. лямбда-выражения):


command = (lambda: sys.exit())

В этом случае скобки после метода уже нужны, что позволяет передавать туда аргументы при необходимости. Однако здесь, как и выше, вызов sys.exit() происходит не в момент создания кнопки, т.е. при выполнения инструкции

but = Button(root, text="Press me", command=(lambda: sys.exit()))

а в момент нажатия кнопки. Это так называемый отложенный вызов функции, который достигается за счет использования лямбда-выражений.

Способ 2 - метод bind


Рассмотрим пример:

import sys
from tkinter import *

def quit(event):
	sys.exit()

root = Tk()
but = Button(root, text="Press me", width=30, height=5, bg="white",fg="blue")
but.bind('', quit)
but.pack()
root.mainloop()

Здесь:

'<Button-1>' - низкоуровневый обработчик событий;

Метод bind связывает между собой 3 базовые компонента событийного программирования: виджет, событие и функцию. Таким образом, общая форма метода bind следующая:


виджет.bind(событие, функция)

При возникновении события будет вызывать соответствующая функция (обработчик события). В отличие от прежнего способа, здесь функция имеет один параметр event - событие.

Вообще, с помощью bind мы можем привязать к виджету не одно событие, а сразу несколько. Например,

but.bind('< Button-1 >', quit)
but.bind('< Double-1 >', somefunc)

Некоторые стандартные события, производимые мышью:


- < Button-1 > - щелчок левой кнопкой мыши

- < Button-2 > - щелчок средней кнопкой мыши

- < Button-3 > - щелчок правой кнопкой мыши

- < Double-Button-1 > - двойной клик левой кнопкой мыши

- < Motion > - движение мыши

События, производимые с помощью клавиатуры


- Буквенные клавиши (например, '< Q >' или просто 'Q').

- Для неалфавитных клавиш существуют специальные зарезервированные слова:

* < Return > - нажатие клавиши Enter;

* < space > - пробел;

- Сочетания клавиш пишутся через тире:

* < Control-Shift > - одновременное нажатие клавиш Ctrl и Shift.

Они устанавливаются для виджета, на котором в данный момент находится фокус:

root.bind('< Return >', quit)

Описание некоторых виджетов


Виджет Text


Пример:

text = Text(root, font=('times',12), width=50, height=15, wrap=WORD)
text.bind('Q', quit)
text.pack()

Рисование


виджет Canvas


Пример:

canv = Canvas(root, width=300, height=200, bg="white", cursor="pencil")
canv.pack()
canv.create_line(20,0,100,50, width=1, fill="green")
canv.create_line(0,0,100,100, width=2, fill="blue", arrow=LAST) 

Tk + PIL


Для работы с файлами JPG понадобятся следующие библиотеки:

$ sudo apt-get install python-imaging-tk

$ sudo apt-get install python3-pil.imagetk

затем коде делаем

import PIL
from PIL.ImageTk import PhotoImage

============================

Практический пример. Рисование с помощью tkinter графика функции, вычисление значений которой занимает много времени


Часто возникает необходимость запустить какой-то долго длящийся процесс вычислений, но так, чтобы само окно при этом не зависало и кнопки реагировали на нажатия, и при этом результат вычислений выводился в процессе работы программы, чтобы не возникало чувства, что программа зависла. Кроме того, может возникнуть необходимость остановить процесс вычислений при необходимости.

В этом случае проще всего (а возможно и единственный вариант) - запустить отдельный поток, используя модуль _thread либо threading. Следующий пример это демонстрирует (будет работать на python3). Эта программа состоит из двух частей (модулей).

Основной модуль graphic.py (для вывода графика на canvas):

import sys
from tkinter import *
import _thread
from calc import func

class Control:
	bool_stop = False

class Graphic:
	x0 = 0
	y0 = 0
	def next_point(self, y1):
		x1 = self.x0 + 2
		canv.create_line(self.x0, self.y0, x1, y1, width=2, fill="red")
		#canv.update() # not nesessary
		self.x0 = x1
		self.y0 = y1

def add_point_on_graphic(x):
	gr.next_point(x)

def quit(event):
	sys.exit()

def start_calc():
	Control.bool_stop = False
	_thread.start_new_thread(func, (add_point_on_graphic, Control))

def method_stop():
	Control.bool_stop = True

root = Tk()
canv = Canvas(root, width=600, height=200, bg="white", cursor="pencil")

but = Button(root, text="Draw", width=30, height=2, bg="white",fg="blue")
but['command'] = (lambda: start_calc())

but_stop = Button(root, text="Control", width=30, height=2, bg="white",fg="blue")
but_stop['command'] = (lambda: method_stop())

but_quit = Button(root, text="Quit", width=30, height=2, bg="white",fg="blue")
but_quit['command'] = (lambda: sys.exit())

canv.create_line(0,100,200,100, width=2, fill="blue", arrow=LAST)

gr = Graphic()

canv.pack()
but.pack()
but_stop.pack()
but_quit.pack()
root.mainloop()

Модуль calc.py для вычисления значения функции func. Эта функция будет запущена в отдельном потоке. При создании потока мы передаем в функцию func ссылку на метод add_point_on_graphic, который добавляет точку на график.

import math
import time
def func(func_add_point_on_graphic, Control):	
	for i in range(0, 2000):		
		if Control.bool_stop: break
		func_add_point_on_graphic(math.sin(i/4.0)*20+20)
		time.sleep(0.05)

Класс Control создан для того, чтобы хранить глобальную переменную bool_stop, что позволяет нашему дочернему потоку узнать, была ли нажата кнопка Stop, останавливающая процесс вычислений.

Конечно, вместо передачи ссылки на функцию func_add_point_on_graphic можно было передать в funш ссылку на экземпляр класса Graphic и через него вызывать метод next_point(x), но здесь продемонстрировал еще один пример.

--------------------------------

Библиотека PyQt


PyQt - это набор Python библиотек для создания графического интерфейса на базе платформы Qt от компании Digia.

В настоящий момент (под Python 3) доступна вервия PyQt5.

Установка:

$ sudo apt-get install python3-pyqt5 pyqt5-dev-tools

Минимальный пример:

 #!/usr/bin/python3
 import sys
 from PyQt5.QtWidgets import QApplication, QWidget
 if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = QWidget()
    w.resize(250, 150)
    w.move(300, 300)
    w.setWindowTitle('Simple')
    w.show()
    sys.exit(app.exec_())

Источники


http://younglinux.info/tkinter/tkinter.php

https://pythonworld.ru/gui/pyqt5-firstprograms.html

- Лутц М., том 1.





galaxy