На этом шаге мы приведем приложение для интерактивного построения графиков.
В примере ниже показана возможность совместного использования графики matplotlib и элементов управления tkinter. В верхней части окна расположено текстовое поле (элемент Entry), предназначенное для ввода выражения относительно переменной х, график которого нужно построить. В два текстовых поля справа вводятся значения параметров а и b, задающие интервал [a,b] изменения аргумента х. Нажатие клавиши Enter или кнопки "График" строит кривую.
from tkinter import * from numpy import * from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from tkinter.messagebox import showerror import warnings def evaluate(event): try: mystr = entry.get() exec('f = lambda x:' + mystr, globals()) a = float(strA.get()) b = float(strB.get()) X = linspace(a, b, 300) Y = [f(x) for x in X] ax.clear() # очистить графическую область ax.plot(X, Y, linewidth=2) ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5) canvasAgg.draw() # перерисовать "составной" холст return except: # реакция на любую ошибку showerror('Ошибка', "Неверное выражение или интервал [a,b].") def evaluate2(event): # чтобы кнопка отжималась при ошибке root.after(100, evaluate, event) root = Tk() root.wm_title("График функции") warnings.filterwarnings("error") frameUp = Frame(root, relief=SUNKEN, height=64) frameUp.pack(side=TOP, fill=X) Label(frameUp, text="Выражение: ").place(x=20, y=4, width=100, height=25) Label(frameUp, text="Начало интервала a:").place(x=250, y=4, width=140, height=25) Label(frameUp, text="Конец интервала b:").place(x=370, y=4, width=140, height=25) entry = Entry(frameUp, relief=RIDGE, borderwidth=4) entry.bind("<Return>", evaluate) entry.place(x=6, y=30, width=250, height=25) strA = StringVar() strA.set(0) entryA = Entry(frameUp, relief=RIDGE, borderwidth=4, textvariable=strA) entryA.place(x=280, y=30, width=80, height=25) entryA.bind("<Return>", evaluate) strB = StringVar() strB.set(1) entryB = Entry(frameUp, relief=RIDGE, borderwidth=4, textvariable=strB) entryB.place(x=400, y=30, width=80, height=25) entryB.bind("<Return>", evaluate) fig = Figure(figsize=(5, 4), dpi=100, facecolor='white') ax = fig.add_subplot(111) canvasAgg = FigureCanvasTkAgg(fig, master=root) canvasAgg.draw() canvas = canvasAgg.get_tk_widget() canvas.pack(fill=BOTH, expand=1) btn = Button(root, text='График') btn.bind("<Button-1>", evaluate2) btn.pack(ipady=2, pady=4, padx=10) root.bind('<Control-z>', lambda event: root.destroy()) root.mainloop()
Результат работы приложения приведен на рисунке 1.
Рис.1. Результат работы приложения
Прокомментируем приведенный пример.
Программа начинается с импортирования необходимых модулей и функций. Далее мы создаем функцию evaluate(). Она рисует график кривой и вызывается при нажатии на клавишу Enter.
def evaluate(event): try: mystr = entry.get() exec('f = lambda x:' + mystr, globals()) a = float(strA.get()) b = float(strB.get()) X = linspace(a, b, 300) Y = [f(x) for x in X] ax.clear() # очистить графическую область ax.plot(X, Y, linewidth=2) ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5) canvasAgg.draw() # перерисовать "составной" холст return except: # реакция на любую ошибку showerror('Ошибка', "Неверное выражение или интервал [a,b].")
Инструкция
mystr = entry.get()
'f = lambda x:' + mystr
exec('f = lambda x:' + mystr, globals())
С текстовыми виджетами entryA и entryB, в которые вводятся границы интервала [a, b] изменения независимой переменной, мы связали переменные слежения strA и strB (см. код далее). Две последующие инструкции преобразуют содержимое этих переменных в числовые значения а и b.
Выражения, вводимые пользователем в текстовом виджете entry, а также текстовые поля а и bмогут содержать синтаксические или другие ошибки. Поэтому описанные выше инструкции следует помещать в конствукции try ... except, используемые для обработки исключительных ситуаций. В нашем примере реакция на любую ошибку пользователя состоит в отображении окна сообщений showerror().
Используя значения а и b, мы создаем список X значений аргумента и список Y значений функции f. Перед последующим построением графическая область очищается от графика, нарисованного ранее. Инструкция
ax.plot(X, Y, linewidth=2)
ax.grid(color='b', alpha=0.5, linestyle='dashed', linewidth=0.5)
def evaluate2(event): # чтобы кнопка отжималась при ошибке root.after(100, evaluate, event)
Функция evaluate2() является копией функции evaluate(), но запускается с задержкой 100 миллисекунд. Она вызывается при нажатии кнопки "График", а задержка нужна для того, чтобы процедуры перерисовки кнопки успевали ее "отжать", если в функции evaluate() сгенерирована исключительная ситуация (ошибка).
После создания функций идет код построения окна приложения:
root = Tk()
root.wm_title("График функции")
Следующая инструкция
warnings.filterwarnings("error")
Далее идут инструкции создания интерфейса программы. Мы создаем рамку Frame с метками и текстовыми виджетами, предназначенными для ввода выражения и переменных а и b. К текстовым виджетам прикрепляем функцию evaluate() обработки события <Return> нажатия на клавишу Enter.
frameUp = Frame(root, relief=SUNKEN, height=64) frameUp.pack(side=TOP, fill=X) Label(frameUp, text="Выражение: ").place(x=20, y=4, width=100, height=25) Label(frameUp, text="Начало интервала a:").place(x=250, y=4, width=140, height=25) Label(frameUp, text="Конец интервала b:").place(x=370, y=4, width=140, height=25) entry = Entry(frameUp, relief=RIDGE, borderwidth=4) entry.bind("<Return>", evaluate) entry.place(x=6, y=30, width=250, height=25) strA = StringVar() strA.set(0) entryA = Entry(frameUp, relief=RIDGE, borderwidth=4, textvariable=strA) entryA.place(x=280, y=30, width=80, height=25) entryA.bind("<Return>", evaluate) strB = StringVar() strB.set(1) entryB = Entry(frameUp, relief=RIDGE, borderwidth=4, textvariable=strB) entryB.place(x=400, y=30, width=80, height=25) entryB.bind("<Return>", evaluate)
Для текстовых виджетов entryA и entryB также создаются переменные слежения strA и strB. После этого создается графическое окно Figure, которое помещается на "составной" холст FigureCanvasTkAgg:
fig = Figure(figsize=(5, 4), dpi=100, facecolor='white')
ax = fig.add_subplot(111)
canvasAgg = FigureCanvasTkAgg(fig, master=root)
canvasAgg.draw()
canvas = canvasAgg.get_tk_widget()
canvas.pack(fill=BOTH, expand=1)
Затем создается кнопка Button построения графика, к которой присоединяется функция evaluate2() обработки "клика":
btn = Button(root, text='График') btn.bind("<Button-1>", evaluate2) btn.pack(ipady=2, pady=4, padx=10)
Завершается код двумя стандартными инструкциями. Одна подключает процедуру destroy() завершения приложения к комбинации клавиш CTRL+Z. Другая - запускает цикл обработки сообщений:
root.bind('<Control-z>', lambda event: root.destroy()) root.mainloop()
На следующем шаге мы рассмотрим виджет Text.