GUI Калькулятор на Python tkinter

Статьи
Автор Admin На чтение 9 мин Просмотров 8.5к. Опубликовано 02.03.2023
Введение
В ходе статьи напишем GUI калькулятор на языке программирования Python с использованием модуля tkinter.
Написание GUI калькулятора
Для начала импортируем библиотеку tkinter:
from tkinter import *
Создадим объект класса Tk(), укажем разрешение окна 268×288, заголовок «Калькулятор» и запретим возможность изменять разрешение окна:
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) root.mainloop()
Создадим виджет Frame() и отобразим его методом grid():
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") root.mainloop()
Добавим текстовое поле (виджет Entry()) на фрейм, укажем шрифт Arial 15 размера жирного начертания, толщину 24 и запретим возможность писать в нём. Отобразим его методом pack():
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) root.mainloop()
Далее создадим кортеж, в котором будут храниться кнопки будущего калькулятора:
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) root.mainloop()
Создадим пустую переменную expression строкового типа данных. Она нам пригодится для избежания ошибок в будущем. Также добавим кнопку, которая в последующем
будет служить для очищения текстового поля:
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") root.mainloop()
Теперь добавим основные кнопки калькулятора с помощью цикла. И в основном, и во вложенном циклах будет по 4 итерации. Во вложенном цикле создаётся кнопка, с шириной 2, и высотой 3. Текстом будет являться поиндексное значение из кортежа buttons. Команда будет задаваться с помощью анонимной функции lambda, позиционироваться кнопки будут с помощью метода grid():
from tkinter import * root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()
Создадим функцию btn_click(), и в качестве аргумента укажем item. Сделаем переменную expression глобальной, после чего добавим конструкцию try … except:
from tkinter import * def btn_click(item): global expression try: pass except: pass root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()
В try мы даём возможность писать в текстовое поле, приведя параметр state в normal. Далее к expression прибавляем item. И вводим результат в текстовое поле:
from tkinter import * def btn_click(item): global expression try: input_field['state'] = "normal" expression += item input_field.insert(END, item) except: pass root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()
Зададим условие, что если на;ата кнопка «равно», то будет подсчитываться результат методом eval(). После чего результат будет выведен в текстовое поле, а expression обнулится. В конце try снова замораживаем текстовое поле:
from tkinter import * def btn_click(item): global expression try: input_field['state'] = "normal" expression += item input_field.insert(END, item) if item == '=': result = str(eval(expression[:-1])) input_field.insert(END, result) expression = "" input_field['state'] = "readonly" except: pass root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()
В except мы будем ловить ошибку ZeroDivisionError, и говорить, что деление на 0 запрещено.
Также добавим ошибку синтаксиса, при которой будет просто выводиться слово «Ошибка»:
from tkinter import * def btn_click(item): global expression try: input_field['state'] = "normal" expression += item input_field.insert(END, item) if item == '=': result = str(eval(expression[:-1])) input_field.insert(END, result) expression = "" input_field['state'] = "readonly" except ZeroDivisionError: input_field.delete(0, END) input_field.insert(0, 'Ошибка (деление на 0)') except SyntaxError: input_field.delete(0, END) input_field.insert(0, 'Ошибка') root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()
Теперь добавим функцию для очищения текстового поля. Внутри неё делаем переменную expression глобальной, опять же размораживаем текстовое поле, очищаем его, и снова замораживаем:
from tkinter import * def btn_click(item): global expression try: input_field['state'] = "normal" expression += item input_field.insert(END, item) if item == '=': result = str(eval(expression[:-1])) input_field.insert(END, result) expression = "" input_field['state'] = "readonly" except ZeroDivisionError: input_field.delete(0, END) input_field.insert(0, 'Ошибка (деление на 0)') except SyntaxError: input_field.delete(0, END) input_field.insert(0, 'Ошибка') def bt_clear(): global expression expression = "" input_field['state'] = "normal" input_field.delete(0, END) input_field['state'] = "readonly" root = Tk() root.geometry("268x288") root.title("Калькулятор") root.resizable(0, 0) frame_input = Frame(root) frame_input.grid(row=0, column=0, columnspan=4, sticky="nsew") input_field = Entry(frame_input, font='Arial 15 bold', width=24, state="readonly") input_field.pack(fill=BOTH) buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) expression = "" button = Button(root, text='C', command=lambda: bt_clear()) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): Button(root, width=2, height=3, text=buttons[row][col], command=lambda row=row, col=col: btn_click(buttons[row][col])).grid(row=row + 2, column=col, sticky="nsew", padx=1, pady=1) root.mainloop()

Заключение
В ходе статьи мы с Вами написали код для GUI калькулятора на языке программирования Python с использованием модуля tkinter. Надеюсь Вам понравилась статья, желаю удачи и успехов!
Калькулятор
Многие программисты стараются изучать языки программирования с помощью написания достаточно простых программ. Один из вариантов – написание калькулятора. Конечно, можно посчитать в отладчике Python или запустив консоль. Но гораздо лучше написать на python свой калькулятор с графическим интерфейсом.
Считаем в консоле
Чтобы посчитать математические выражения можно запустить консоль. Запустить python. После этого набираем математические выражения и получаем ответ. Для этого даже не надо уметь программировать.

Делаем простой калькулятор
Лучше всего закреплять свои знания по программированию с помощью написания простых программ. Таких приложений можно придумать много – календарь, программа для хранения заметок, получение прогноза погоды.

Можно написать программу, которая делает скриншоты и сохраняет полученные изображения в папку. В любом случае, надо выбрать какое-нибудь не сложное задание, чтобы не закопаться в нем. Потом его можно будет расширить и сделать по-настоящему полезное приложение.
В нашем случае мы разберем, как создать простой графический калькулятор на Python 3. Для реализации графического интерфейса воспользуемся стандартным пакетом Tkinter. Он входит в состав Python 3. Соответственно, если у вас установлен Python, то дополнительно не надо ничего устанавливать.
В первых строках файла calculator.py подключаем библиотечные функции:
- Tkinter для графического интерфейса;
- Decimal для вычислений с большей точность, так как точности float не достаточно.
Импорт библиотек и исходные данные
Создаем окно приложения — объект Tk с заголовком Calculator. Во вложенном кортеже buttons будут храниться обозначения для кнопок. В список stack будем добавлять введенные числа и операции, которые надо совершить. activeStr предназначен для хранения набираемого числа.
from tkinter import * from decimal import * root = Tk() root.title('Calculator') buttons = (('7', '8', '9', '/', '4'), ('4', '5', '6', '*', '4'), ('1', '2', '3', '-', '4'), ('0', '.', '=', '+', '4') ) activeStr = '' stack = []
Вычисление результата
Функция calculate получает из списка stack операнды и операцию которую над ними надо произвести. Результат отображается в надписи label. Получать из списка строки будем с помощью метода pop.
def calculate(): global stack global label result = 0 operand2 = Decimal(stack.pop()) operation = stack.pop() operand1 = Decimal(stack.pop()) if operation == '+': result = operand1 + operand2 if operation == '-': result = operand1 - operand2 if operation == '/': result = operand1 / operand2 if operation == '*': result = operand1 * operand2 label.configure(text=str(result))
Обработка нажатия
В функции click выполняется обработка нажатой клавиши. В качестве ее аргумента передается текст, отображаемый на кнопке, которую нажали. Хотелось бы хранить вводимое значение прямо в надписи, а не создавать для этого отдельную переменную. Но так не получается из-за алгоритма работы. После того как посчитан результат, он записывается в надписи. При попытке после этого начать вводить новое число, оно бы дописывало прежний результат.
В списке с операторами и командами для калькулятора не обязательно их будет 3. Но при обработке с помощью метода pop, будут рассматриваться 3 последних введенных значения. А после проведения расчета список очистится. Далее в него добавляется полученный результат, на случай если пользователь нажмет на калькуляторе клавишу операции сразу, а не будет вводить новое число.
def click(text): global activeStr global stack if text == 'CE': stack.clear() activeStr = '' label.configure(text='0') elif '0' = 2: stack.append(label['text']) calculate() stack.clear() stack.append(label['text']) activeStr = '' if text != '=': stack.append(text) else: if text != '=': stack.append(label['text']) stack.append(text) activeStr = '' label.configure(text='0')
Внешний вид
Теперь займемся оформлением внешнего вида калькулятора и зададим обработку нажатия кнопок. Создаем надпись для вывода набираемых значений и результатов. В цикле создаем кнопки. Расположение кнопок и надписи осуществляется в табличном виде с помощью упаковщика grid. И в завершении запускаем цикл обработки событий mainloop.
label = Label(root, text='0', width=35) label.grid(row=0, column=0, columnspan=4, sticky="nsew") button = Button(root, text='CE', command=lambda text='CE': click(text)) button.grid(row=1, column=3, sticky="nsew") for row in range(4): for col in range(4): button = Button(root, text=buttons[row][col], command=lambda row=row, col=col: click(buttons[row][col])) button.grid(row=row + 2, column=col, sticky="nsew") root.grid_rowconfigure(6, weight=1) root.grid_columnconfigure(4, weight=1) root.mainloop()
У надписи выставлена ширина 35, для того, чтобы оформление кнопок подстраивалось под надпись. И в результате кнопки при этом значении лучше выглядят.
Для того, чтобы кнопки правильно работали, пришлось для каждой из кнопок создать свою функцию с помощью lambda.
По аналогии приведенного кода python калькулятора можно сдель, допустим, календарь. Для этого надо будет запрашивать текущую дату у операционной системы. Открывать нужный месяц, рассчитывать какие числа выпадут на понедельники, какой год високосный. Сделать возможность менять год и месяцы.
Как сделать калькулятор в python с графическим интерфейсом
Позиционирование окна в центре экрана
Чтобы разместить окно Tkinter приложения в центре экрана, необходимо:
- Воспользоваться методами winfo_screenwidth() и winfo_screenheight() для получения ширины и высоты экрана соответственно.
- Передать в метод geometry() координаты x и y, равные половине ширины и высоты экрана за минусом половины ширины и высоты окна приложения.
Код будет выглядеть так:
Размещение элементов интерфейса в Tkinter
Элементы интерфейса в Tkinter называются виджетами. Существует три основных способа расположения виджетов на поверхности окна: pack(), place() и grid(). Каждый из этих методов имеет свои преимущества и недостатки, и выбор оптимального способа зависит от конкретной ситуации.
pack() – упорядочивает виджеты по горизонтали или вертикали. Он прост в использовании, не требует дополнительных параметров (указания отступов, конкретной позиции). Подходит для создания простых интерфейсов. Недостаток – с помощью pack() проблематично реализовать сложную компоновку, например, сетку или перекрывание виджетов: для этого нужно комбинировать метод с place ().
place() – позволяет задать точное положение и размер каждого виджета в окне. Его используют, когда необходимо точно расположить виджеты или создать перекрывающиеся элементы интерфейса. Недостаток – при изменении размеров окна или содержимого виджетов трудно сохранить расположение элементов.
grid() – упорядочивает виджеты в сетку из рядов и столбцов. Самый гибкий и мощный, позволяет создавать сложные интерфейсы, состоящие из виджетов разных размеров. Сложнее в использовании, чем pack(), поскольку требует больше кода для компоновки виджетов.

В целом, pack() хорошо подходит для простых интерфейсов, place() – для более сложных, а grid() используют для создания сложных интерфейсов, которым нужна адаптивность, особое позиционирование или растягивание виджетов на несколько строк / столбцов.
Связывание виджетов с функциями
Чтобы при нажатии кнопки выполнялось какое-то действие, нужно связать кнопку с определенной функцией. Чаще всего для этого используются методы command () и bind (), но при необходимости к виджетам Tkinter можно привязывать выполнение анонимных и частично примененных функций. Проиллюстрируем примерами.
Метод command () используется для прямого связывания функции с нажатием кнопки:

Метод bind(), по сравнению с command(), отличается большей гибкостью: его можно использовать для связывания функции с любым событием, происходящим в виджете – с нажатием кнопки, движением мыши, нажатием клавиши, изменением размера окна и так далее:

Назначение кнопке анонимной функции:

Связывание кнопки с частично примененной функцией:

Практика
Задание 1
Создайте Tkinter интерфейс для программы, которая получает от пользователя текст с помощью виджетов Entry и Button , а затем выводит полученную строку в терминале.
Ожидаемый результат:









import tkinter as tk from tkinter import ttk from math import sqrt class Calculator: def __init__(self, master): self.master = master self.master.title("Калькулятор") self.master.geometry("380x140") self.number_entry = ttk.Entry(self.master, width=20) self.number_entry.grid(row=0, column=0, columnspan=5, padx=5, pady=5) self.button_1 = ttk.Button(self.master, text="1", command=lambda: self.button_click(1)) self.button_2 = ttk.Button(self.master, text="2", command=lambda: self.button_click(2)) self.button_3 = ttk.Button(self.master, text="3", command=lambda: self.button_click(3)) self.button_4 = ttk.Button(self.master, text="4", command=lambda: self.button_click(4)) self.button_5 = ttk.Button(self.master, text="5", command=lambda: self.button_click(5)) self.button_6 = ttk.Button(self.master, text="6", command=lambda: self.button_click(6)) self.button_7 = ttk.Button(self.master, text="7", command=lambda: self.button_click(7)) self.button_8 = ttk.Button(self.master, text="8", command=lambda: self.button_click(8)) self.button_9 = ttk.Button(self.master, text="9", command=lambda: self.button_click(9)) self.button_0 = ttk.Button(self.master, text="0", command=lambda: self.button_click(0)) self.button_clear = ttk.Button(self.master, text="C", command=self.button_clear) self.button_add = ttk.Button(self.master, text="+", command=self.button_add) self.button_equal = ttk.Button(self.master, text="=", command=self.button_equal) self.button_subtract = ttk.Button(self.master, text="-", command=self.button_subtract) self.button_multiply = ttk.Button(self.master, text="*", command=self.button_multiply) self.button_divide = ttk.Button(self.master, text="/", command=self.button_divide) self.button_floor_div = ttk.Button(self.master, text="//", command=self.button_floor_div) self.button_modulus = ttk.Button(self.master, text="%", command=self.button_modulus) self.button_sqrt = ttk.Button(self.master, text="√", command=self.button_sqrt) self.button_neg = ttk.Button(self.master, text="+/-", command=self.button_neg) self.button_1.grid(row=1, column=0) self.button_2.grid(row=1, column=1) self.button_3.grid(row=1, column=2) self.button_add.grid(row=1, column=3) self.button_floor_div.grid(row=1, column=4) self.button_4.grid(row=2, column=0) self.button_5.grid(row=2, column=1) self.button_6.grid(row=2, column=2) self.button_subtract.grid(row=2, column=3) self.button_modulus.grid(row=2, column=4) self.button_7.grid(row=3, column=0) self.button_8.grid(row=3, column=1) self.button_9.grid(row=3, column=2) self.button_multiply.grid(row=3, column=3) self.button_sqrt.grid(row=3, column=4) self.button_clear.grid(row=4, column=0) self.button_0.grid(row=4, column=1) self.button_equal.grid(row=4, column=2) self.button_divide.grid(row=4, column=3) self.button_neg.grid(row=4, column=4) self.f_num = 0 self.math = "" def button_click(self, number): current = self.number_entry.get() self.number_entry.delete(0, tk.END) self.number_entry.insert(0, str(current) + str(number)) def button_clear(self): self.number_entry.delete(0, tk.END) def button_add(self): first_number = self.number_entry.get() self.math = "addition" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_equal(self): second_number = self.number_entry.get() self.number_entry.delete(0, tk.END) if self.math == "addition": self.number_entry.insert(0, self.f_num + int(second_number)) if self.math == "multiplication": self.number_entry.insert(0, self.f_num * int(second_number)) if self.math == "division": self.number_entry.insert(0, self.f_num / int(second_number)) if self.math == "floor_div": self.number_entry.insert(0, self.f_num // int(second_number)) if self.math == "modulus": self.number_entry.insert(0, self.f_num % int(second_number)) def button_subtract(self): first_number = self.number_entry.get() self.math = "subtraction" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_multiply(self): first_number = self.number_entry.get() self.math = "multiplication" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_divide(self): first_number = self.number_entry.get() self.math = "division" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_floor_div(self): first_number = self.number_entry.get() self.math = "floor_div" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_modulus(self): first_number = self.number_entry.get() self.math = "modulus" self.f_num = int(first_number) self.number_entry.delete(0, tk.END) def button_sqrt(self): number = float(self.number_entry.get()) result = sqrt(number) if result.is_integer(): self.number_entry.delete(0, tk.END) self.number_entry.insert(0, int(result)) else: self.number_entry.delete(0, tk.END) self.number_entry.insert(0, result) def button_neg(self): current = self.number_entry.get() if current.startswith("-"): current = current[1:] else: current = "-" + current self.number_entry.delete(0, tk.END) self.number_entry.insert(0, current) if __name__ == '__main__': root = tk.Tk() calc = Calculator(root) root.mainloop()
Подведем итоги
В этой статье мы рассмотрели основы создания GUI для Python -программ и научились использовать основные виджеты Tkinter . В следующей главе будем изучать основы разработки игр с Pygame .
- Особенности, сферы применения, установка, онлайн IDE
- Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
- Типы данных: преобразование и базовые операции
- Методы работы со строками
- Методы работы со списками и списковыми включениями
- Методы работы со словарями и генераторами словарей
- Методы работы с кортежами
- Методы работы со множествами
- Особенности цикла for
- Условный цикл while
- Функции с позиционными и именованными аргументами
- Анонимные функции
- Рекурсивные функции
- Функции высшего порядка, замыкания и декораторы
- Методы работы с файлами и файловой системой
- Регулярные выражения
- Основы скрапинга и парсинга
- Основы ООП: инкапсуляция и наследование
- Основы ООП: абстракция и полиморфизм
- Графический интерфейс на Tkinter
- Основы разработки игр на Pygame
- Основы работы с SQLite
- Основы веб-разработки на Flask
- Основы работы с NumPy
- Основы анализа данных с Pandas
Пишем десктоп-приложение на Python с помощью Tkinter
Знакомимся с библиотекой Tkinter — пишем на Python кросс-платформенный калькулятор, который рассчитывает вес человека.


Иллюстрация: Merry Mary для Skillbox Media

Антон Яценко
Изучает Python, его библиотеки и занимается анализом данных. Любит путешествовать в горах.
Десктопные приложения пишут на разных языках программирования: C++, C#, C, Python и других. Начинающим разработчикам проще всего использовать Python и его библиотеки для работы над графическими интерфейсами.
Одна из таких библиотек — Tkinter. Она входит в стандартный пакет Python и позволяет создавать приложения для Windows, mac OS и Linux. Давайте разберёмся, как устроена эта библиотека, и напишем десктопный калькулятор, помогающий рассчитать вес человека.
Что такое GUI и как с ним работать в Python
GUI (Graphical User Interface) — это графический интерфейс пользователя, оболочка программы, с которой мы взаимодействуем с помощью клавиатуры и мыши. На современных операционных системах почти все программы работают с графическим интерфейсом, и мы каждый день сталкиваемся с GUI: читаем статьи в браузере, набираем текст в редакторе или играем в игры.
Противоположность графическому интерфейсу — командная строка, позволяющая управлять приложением с помощью текстовых команд. Такой интерфейс реализован в терминале macOS и командной строке Windows.
Для работы с GUI в Python есть четыре библиотеки:
Мы выбрали Tkinter, потому что она не требует дополнительной установки и позволяет быстро создавать приложения с простым графическим интерфейсом.
Знакомимся с Tkinter
Tkinter — это удобный интерфейс для работы со средствами Tk. Приложения, созданные на основе этой библиотеки, кросс-платформенные, то есть могут запускаться на разных операционных системах.
Схематично работу с Tkinter можно представить в виде четырёх шагов:

Что здесь происходит:
- Мы подключаем библиотеку Tkinter с помощью директивы import.
- Создаём главное окно приложения, в котором будут размещаться все графические элементы.
- Добавляем виджеты — визуальные элементы, выполняющие определённые действия.
- Создаём главный цикл событий — он включает в себя все события, происходящие при взаимодействии пользователя с интерфейсом.
Ключевые объекты в работе с Tkinter — виджеты. Это аналоги тегов из HTML, которые позволяют создавать интерактивные и неинтерактивные элементы, например надписи или кнопки. Всего их 18, но чаще всего используют следующие:
- Button — кнопки;
- Canvas — «холст», на котором рисуют графические фигуры;
- Entry — виджет для создания полей ввода;
- Label — контейнер для размещения текста или изображения;
- Menu — виджет для создания пунктов меню.
Понять работу с виджетами легче всего на практике. Но прежде чем к ней приступить, обсудим идею нашего первого десктопного приложения.
Создаём калькулятор для расчёта индекса массы тела
Мы напишем калькулятор индекса массы тела. ИМТ — это важный медицинский показатель, который позволяет оценить, есть ли у человека избыточный вес или ожирение. Он рассчитывается по следующей формуле:
ИМТ = вес (в кг) / рост 2 (в метрах)
Результаты расчётов оценивают с помощью специальной таблицы. У врачей она имеет много градаций, мы же воспользуемся упрощённой версией:

Шаг 1
Запускаем Python и импортируем Tkinter
Писать код на Python лучше всего в специальной IDE, например в PyCharm или Visual Studio Code. Они подсвечивают синтаксис и предлагают продолжение кода — это сильно упрощает работу программиста. Весь код из этой статьи мы писали в Visual Studio Code.
Библиотека Tkinter предустановлена в Python. Поэтому её нужно только импортировать:
Шаг 2
Делаем эскиз интерфейса и продумываем логику приложения
Прежде чем писать код, необходимо ответить на несколько вопросов:
- Какие данные мы хотим получить от пользователя и в каком виде?
- Какое событие будет запускать расчёт ИМТ: нажатие кнопки, получение приложением всех необходимых данных или что-то другое?
- Как будем показывать результат?
В нашем случае необходимо получить от пользователя вес и рост в виде целых чисел. При этом вес должен быть введён в килограммах, а рост — в сантиметрах. ИМТ будет рассчитываться по нажатии кнопки, а результат — выводиться во всплывающем окне в виде значения ИМТ и категории, к которой он относится.
Схематично графический интерфейс нашего калькулятора будет выглядеть так:

Теперь попробуем реализовать интерфейс и работу калькулятора с помощью Python и Tkinter.
Шаг 3
Создаём основное окно и указываем название приложения
После импорта библиотеки в Python загрузим её методы:

Мы не указали размер окна, поэтому название приложения не помещается в него полностью. Исправим это с помощью метода geometry:

Шаг 4
Создаём виджет Frame для контроля за расположением элементов
В окне приложения необходимо разместить несколько элементов с нашего эскиза: два поля ввода информации с подписями и одну кнопку. Важно, чтобы поля не накладывались друг на друга и не уходили за пределы окна. В Tkinter для этого есть несколько методов:
- pack — используется, когда мы работаем с контейнерами для элементов. Позволяет позиционировать кнопки, надписи или другие элементы внутри контейнеров.
- place — позволяет позиционировать элементы, указывая точные координаты.
- grid — размещает элементы по ячейкам условной сетки, разделяющей окно приложения.
Мы воспользуемся комбинацией методов pack и grid. Для начала создадим виджет Frame для размещения надписей, полей ввода и кнопок. Подробное описание работы виджета есть в документации. Мы же используем только два свойства: padx и pady.
Обозначим отступы по вертикали и горизонтали в 10 пикселей для элементов, которые будут расположены внутри Frame:

Сейчас элемент расположен в центре окна, но он займёт правильное положение, когда мы напишем другие элементы.
Добавим вторую надпись о весе аналогичным образом, но при позиционировании в grid укажем следующую, четвёртую строку:

Теперь добавим поля для ввода пользовательской информации, используя виджет Entry:

Всё получилось. Остаётся по аналогии добавить поле ввода веса:

Теперь добавим кнопку, которая будет запускать расчёт ИМТ. Сделаем это с помощью виджета Button:

Теперь в приложении есть все графические элементы. Остаётся лишь написать код, который будет получать информацию из виджетов Entry и рассчитывать индекс массы тела.
Шаг 6
Получаем информацию из виджетов Entry и рассчитываем индекс массы тела
Напишем простую функцию и разберём её построчно:
Всё работает. Функция получает данные из полей ввода и рассчитывает индекс массы тела, показывая результат на экране.
Приведём код полностью без комментариев
from tkinter import * from tkinter import messagebox def calculate_bmi(): kg = int(weight_tf.get()) m = int(height_tf.get())/100 bmi = kg/(m*m) bmi = round(bmi, 1) if bmi < 18.5: messagebox.showinfo('bmi-pythonguides', f'ИМТ = соответствует недостаточному весу') elif (bmi > 18.5) and (bmi < 24.9): messagebox.showinfo('bmi-pythonguides', f'ИМТ = соответствует нормальному весу') elif (bmi > 24.9) and (bmi < 29.9): messagebox.showinfo('bmi-pythonguides', f'ИМТ = соответствует избыточному весу') else: messagebox.showinfo('bmi-pythonguides', f'ИМТ = соответствует ожирению') window = Tk() window.title('Калькулятор индекса массы тела (ИМТ)') window.geometry('400x300') frame = Frame( window, padx=10, pady=10 ) frame.pack(expand=True) height_lb = Label( frame, text="Введите свой рост (в см) " ) height_lb.grid(row=3, column=1) weight_lb = Label( frame, text="Введите свой вес (в кг) ", ) weight_lb.grid(row=4, column=1) height_tf = Entry( frame, ) height_tf.grid(row=3, column=2, pady=5) weight_tf = Entry( frame, ) weight_tf.grid(row=4, column=2, pady=5) cal_btn = Button( frame, text='Рассчитать ИМТ', command=calculate_bmi ) cal_btn.grid(row=5, column=2) window.mainloop()
Что дальше?
Узнать о возможностях Tkinter и особенностях работы с виджетами можно в официальной документации. А если хотите найти больше реальных примеров для практики, советуем две книги:
- Python GUI Programming with Tkinter. Develop responsive and powerful GUI applications with Tkinter, Алан Мур.
- Tkinter GUI Programming by Example, Дэвид Лав.
Больше интересного про код в нашем телеграм-канале. Подписывайтесь!