Share This
Связаться со мной
Крути в низ
Categories
//Пишем калькулятор на Python и Tkinter

Пишем калькулятор на Python и Tkinter

Расскажем о преимуществах и недостатках Tkinter, изучим основы создания GUI и разберемся в особенностях компоновки виджетов с помощью pack(), place() и grid(). В конце статьи – 10 практических заданий по разработке GUI для Python-программ.

pishem kalkuljator na python i tkinter d5c4a67 - Пишем калькулятор на Python и Tkinter

← Часть 19 Основы ООП – абстракция и полиморфизм

Графический интерфейс для программы на Python можно создать с помощью одной из специальных GUI библиотек:

  • Tkinter
  • wxPython
  • PyQt
  • PySide
  • Kivy
  • PyGTK

У каждой из этих библиотек есть свои преимущества и недостатки. Только одна из них, Tkinter, входит в стандартную поставку Python. Виджеты Tkinter не отличаются сногсшибательной стильностью – это ее единственный очевидный минус. Преимуществ у Tkinter гораздо больше, чем недостатков. Эта библиотека:

  • Максимально проста в изучении и использовании.
  • Имеет детальную и доступную документацию.
  • Помимо базовых элементов интерфейса, содержит два мощных виджета – Text (многострочное текстовое поле с поддержкой форматирования) и Canvas («холст», на котором можно рисовать и отображать любые графические объекты).
  • Включает в себя модуль ttk, который предоставляет в распоряжение разработчика набор дополнительных виджетов – Combobox, Notebook, Treeview, Progressbar, Scale и другие.
  • Позволяет сделать интерфейс адаптивным.
  • Отлично подходит для начинающих – поэтому на ней мы и сосредоточимся.

Создание окна приложения с Tkinter

Для создания простого окна приложения в Tkinter выполните следующие действия:

  • Импортируйте модуль import tkinter.
  • Создайте новый объект Tk, который представляет собой главное окно приложения.
  • (Опционально) Задайте заголовок окна с помощью метода title() объекта Tk.
  • (Опционально) Установите размер окна с помощью метода geometry() объекта Tk. Метод geometry() принимает строковый параметр в формате "ширинаxвысота".
  • Вызовите метод mainloop() объекта Tk, чтобы запустить основной цикл GUI приложения.

Вот пример кода, который создает простое окно размером 250 на 250 пикселей с заголовком «Мое приложение»:

         import tkinter as tk  # Создайте новый объект Tk root = tk.Tk()  # Задайте заголовок окна root.title("Мое приложение")  # Установите размер окна root.geometry("250x250")  # Запустите основной цикл root.mainloop()      

pishem kalkuljator na python i tkinter 90d5e53 - Пишем калькулятор на Python и Tkinter

Позиционирование окна в центре экрана

Чтобы разместить окно Tkinter приложения в центре экрана, необходимо:

  • Воспользоваться методами winfo_screenwidth() и winfo_screenheight() для получения ширины и высоты экрана соответственно.
  • Передать в метод geometry() координаты x и y, равные половине ширины и высоты экрана за минусом половины ширины и высоты окна приложения.

Код будет выглядеть так:

         import tkinter as tk  # Создаем окно root = tk.Tk()  # Получаем ширину и высоту экрана screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight()  # Вычисляем координаты окна приложения window_width = 500 window_height = 300 x = (screen_width // 2) - (window_width // 2) y = (screen_height // 2) - (window_height // 2) root.geometry(f"{window_width}x{window_height}+{x}+{y}")  # Запускаем программу root.mainloop()      

Теперь окно расположено точно в центре экрана:

pishem kalkuljator na python i tkinter d24bfe2 - Пишем калькулятор на Python и Tkinter

Размещение элементов интерфейса в Tkinter

Элементы интерфейса в Tkinter называются виджетами. Существует три основных способа расположения виджетов на поверхности окна: pack(), place() и grid(). Каждый из этих методов имеет свои преимущества и недостатки, и выбор оптимального способа зависит от конкретной ситуации.

pack() – упорядочивает виджеты по горизонтали или вертикали. Он прост в использовании, не требует дополнительных параметров (указания отступов, конкретной позиции). Подходит для создания простых интерфейсов. Недостаток – с помощью pack() проблематично реализовать сложную компоновку, например, сетку или перекрывание виджетов: для этого нужно комбинировать метод с place().

         import tkinter as tk  root = tk.Tk()  label1 = tk.Label(root, text="Это пример использования pack()") label1.pack()  button1 = tk.Button(root, text="Нажми!") button1.pack()  root.mainloop()      

pishem kalkuljator na python i tkinter 74f2570 - Пишем калькулятор на Python и Tkinter

place() – позволяет задать точное положение и размер каждого виджета в окне. Его используют, когда необходимо точно расположить виджеты или создать перекрывающиеся элементы интерфейса. Недостаток – при изменении размеров окна или содержимого виджетов трудно сохранить расположение элементов.

         import tkinter as tk  root = tk.Tk()  label1 = tk.Label(root, text="Это пример использования place()") label1.place(x=5, y=50)  button1 = tk.Button(root, text="Нажми кнопку") button1.place(x=50, y=100)  root.mainloop()      

pishem kalkuljator na python i tkinter 0418921 - Пишем калькулятор на Python и Tkinter

grid() – упорядочивает виджеты в сетку из рядов и столбцов. Самый гибкий и мощный, позволяет создавать сложные интерфейсы, состоящие из виджетов разных размеров. Сложнее в использовании, чем pack(), поскольку требует больше кода для компоновки виджетов.

         import tkinter as tk  root = tk.Tk() root.title("Пример grid()") root.geometry("250x250")  frame = tk.Frame(root) frame.pack(expand=True)  label1 = tk.Label(frame, text="Имя:") label1.grid(row=0, column=0)  entry1 = tk.Entry(frame) entry1.grid(row=0, column=1)  label2 = tk.Label(frame, text="Email:") label2.grid(row=1, column=0)  entry2 = tk.Entry(frame) entry2.grid(row=1, column=1)  button1 = tk.Button(frame, text="Отправить") button1.grid(row=2, column=1)  root.update_idletasks() width = root.winfo_width() height = root.winfo_height() x = (root.winfo_screenwidth() // 2) - (width // 2) y = (root.winfo_screenheight() // 2) - (height // 2) root.geometry('{}x{}+{}+{}'.format(width, height, x, y))  root.mainloop()      

pishem kalkuljator na python i tkinter 12456e2 - Пишем калькулятор на Python и Tkinter

В целом, pack() хорошо подходит для простых интерфейсов, place() – для более сложных, а grid() используют для создания сложных интерфейсов, которым нужна адаптивность, особое позиционирование или растягивание виджетов на несколько строк / столбцов.

Связывание виджетов с функциями

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

Метод command() используется для прямого связывания функции с нажатием кнопки:

         import tkinter as tk  root = tk.Tk()  def say_hello():     print("Привет!")  button1 = tk.Button(root, text="Поздоровайся", command=say_hello) button1.pack()  root.mainloop()      

pishem kalkuljator na python i tkinter a81dae7 - Пишем калькулятор на Python и Tkinter

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

         import tkinter as tk  root = tk.Tk()  def say_hello(event=None):     print("Привет от метода bind()")  button1 = tk.Button(root, text="Нажми кнопку") button1.bind("<Button-1>", say_hello) button1.pack()  root.mainloop()      

pishem kalkuljator na python i tkinter 93591bf - Пишем калькулятор на Python и Tkinter

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

         import tkinter as tk  root = tk.Tk()  button1 = tk.Button(root, text="Нажми!", command=lambda: print("Привет от анонимной функции!")) button1.pack()  root.mainloop()      

pishem kalkuljator na python i tkinter 3a26df6 - Пишем калькулятор на Python и Tkinter

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

         import tkinter as tk from functools import partial  root = tk.Tk()  def say_hello(name):     print(f"Привет, {name}!")  button1 = tk.Button(root, text="Нажми эту кнопку", command=partial(say_hello, "пользователь")) button1.pack()  root.mainloop()      

pishem kalkuljator na python i tkinter 7eccf5c - Пишем калькулятор на Python и Tkinter

Практика

Задание 1

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

Ожидаемый результат:

pishem kalkuljator na python i tkinter cd4817b - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk  root = tk.Tk()  def button_click():     input_text = entry.get()     print(f"Полученный текст: {input_text}")  entry = tk.Entry(root) button = tk.Button(root, text="Отправить", command=button_click)  entry.pack() button.pack()  root.mainloop()      

Задание 2

Создайте интерфейс для программы, которая изменяет текст виджета Label после нажатия на кнопку.

Ожидаемый результат:

pishem kalkuljator na python i tkinter a4f973c - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk root = tk.Tk() label = tk.Label(root, text="Измени этот текст") label.pack() def change_text():     label.config(text="Это новый текст") button = tk.Button(root, text="Нажми кнопку!", command=change_text) button.pack() root.mainloop()      

Задание 3

Создайте интерфейс для программы, которая получает от пользователя многострочный текст в виджете Text и выводит в виджетах Label количество слов и символов.

Ожидаемый результат:

pishem kalkuljator na python i tkinter 27f97ce - Пишем калькулятор на Python и Tkinter

         import tkinter as tk import tkinter.ttk as ttk  root = tk.Tk() root.geometry("250x150") root.title("Подсчет слов и символов")  def count_words_characters():     sentence = sentence_entry.get("1.0", "end-1c")       words = len(sentence.split())     characters = len(sentence)     words_label.config(text=f"Количество слов: {words}")     characters_label.config(text=f"Количество символов: {characters}")  sentence_entry = tk.Text(root, height=3, wrap="word")   words_label = ttk.Label(root) characters_label = ttk.Label(root) count_button = ttk.Button(root, text="Подсчитать", command=count_words_characters)  sentence_entry.pack() words_label.pack() characters_label.pack() count_button.pack()  root.mainloop()      

Задание 4

Напишите программу для оценки сервиса по шкале от 0 до 100. Используйте виджет Scale (ttk модуль).

Ожидаемый результат:

pishem kalkuljator na python i tkinter ccba9e1 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk root = tk.Tk() label = tk.Label(root, text=f"Ваша оценка от 0 до 100") label.pack() def update_label(value):     label.config(text=f"Ваша оценка: {value}") scale = tk.Scale(root, from_=0, to=100, orient="horizontal", command=update_label) scale.pack() root.mainloop()      

Задание 5

Создайте индикатор выполнения задачи с помощью виджета Progressbar из модуля ttk. Индикатор должен обнуляться спустя 5 секунд.

Ожидаемый результат:

pishem kalkuljator na python i tkinter 69b2388 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk from tkinter import ttk  root = tk.Tk()  progressbar = ttk.Progressbar(root, orient="horizontal", length=200, mode="determinate")  progressbar.start()  def stop_progressbar():     if root.winfo_exists() and progressbar.winfo_exists():         progressbar.stop()         progressbar["value"] = 0            root.quit()         root.destroy()  if root.winfo_exists() and progressbar.winfo_exists():     root.after(5000, stop_progressbar)  progressbar.pack()  def exit_app():     stop_progressbar()  root.protocol("WM_DELETE_WINDOW", exit_app)  root.mainloop()      

Задание 6

Создайте Tkinter интерфейс для программы заказа фруктов. Для вывода списка фруктов используйте виджет Combobox из модуля ttk.

Ожидаемый результат:

pishem kalkuljator na python i tkinter e938850 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk import tkinter.ttk as ttk  root = tk.Tk() root.title("Заказ")  def select_item():     selected_item = combobox.get()     selection_label.config(text=f"Выбрано: {selected_item}, 1 кг")  items = ["Яблоки", "Апельсины", "Виноград", "Персики", "Клубника"] combobox = ttk.Combobox(root, values=items) selection_label = ttk.Label(root) select_button = ttk.Button(root, text="Заказать", command=select_item)  combobox.pack() selection_label.pack() select_button.pack()  root.mainloop()      

Задание 7

Напишите GUI программу, которая позволяет пользователю выбрать цвет из палитры, и отображает его HEX-значение.

Ожидаемый результат:

pishem kalkuljator na python i tkinter 2608603 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk import tkinter.colorchooser as cc  root = tk.Tk() root.geometry("250x200") root.title("Выбор цвета")  def choose_color():     color = cc.askcolor()[1]       color_label.config(text=f"Вы выбрали цвет {color}")       second_label.config(bg=color)    color_button = tk.Button(root, text="Выбрать цвет", command=choose_color) color_label = tk.Label(root, text="Нажмите кнопку, чтобы выбрать цвет") second_label = tk.Label(root, text="tttnttt") color_button.pack(pady=10) color_label.pack() second_label.pack(pady=10)  root.mainloop()      

Задание 8

Напишите программу, которая использует виджеты Spinbox для получения двух чисел a и b (0 <= a <= 100, 0 <= b <= 100), а затем выводит сумму всех целых чисел в диапазоне от a до b включительно.

Ожидаемый результат:

pishem kalkuljator na python i tkinter 95c7f30 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk import tkinter.ttk as ttk  class SumApp:     def __init__(self, root):         self.root = root         root.geometry("250x200")         self.root.title("Сумма чисел")                  self.start_spinbox = ttk.Spinbox(root, from_=0, to=100, increment=1)         self.end_spinbox = ttk.Spinbox(root, from_=0, to=100, increment=1)         self.sum_label = tk.Label(root, text="")         self.calc_button = tk.Button(root, text="Вычислить сумму", command=self.calculate_sum)         self.start_spinbox.pack(pady=10)         self.end_spinbox.pack(pady=10)         self.sum_label.pack(pady=10)         self.calc_button.pack(pady=10)          def calculate_sum(self):         start = int(self.start_spinbox.get())         end = int(self.end_spinbox.get())         sum_ = sum(range(start, end+1))         self.sum_label.configure(text=f"Сумма чисел в диапазоне от {start} до {end}: {sum_}")   root = tk.Tk() app = SumApp(root) root.mainloop()      

Задание 9

Создайте GUI интерфейс для программы-каталога фильмов, книг и мультфильмов. Используйте виджеты Notebook и Treeview для вывода объектов разного типа на отдельных вкладках. Реализуйте всплывающее окно для вывода полной информации об издании.

Ожидаемый результат:

pishem kalkuljator na python i tkinter 131cd79 - Пишем калькулятор на Python и Tkinter

Решение:

         import tkinter as tk from tkinter import ttk  def retrieve_details(event):     item = event.widget.focus()     values = event.widget.item(item)['values']     details = f"Информация об издании:nn"     for i in range(len(values)):         details += f"{event.widget['columns'][i].capitalize()}: {values[i]}n"     popup = tk.Toplevel(root)     popup.title(event.widget.item(item)['text'])     popup.geometry("250x150")     popup_label = ttk.Label(popup, text=details)     popup_label.pack(pady=10)     close_button = ttk.Button(popup, text="Закрыть", command=popup.destroy)     close_button.pack()  root = tk.Tk() root.title("Каталог") root.geometry("600x300")  notebook = ttk.Notebook(root)  movies_tab = ttk.Frame(notebook) books_tab = ttk.Frame(notebook) animation_tab = ttk.Frame(notebook)  notebook.add(movies_tab, text="Фильмы") notebook.add(books_tab, text="Книги") notebook.add(animation_tab, text="Мультфильмы")  movies_desc = ttk.Label(movies_tab, text="Список фильмов") books_desc = ttk.Label(books_tab, text="Список книг") animation_desc = ttk.Label(animation_tab, text="Список мультфильмов")  movies_list = ttk.Treeview(movies_tab) books_list = ttk.Treeview(books_tab) animation_list = ttk.Treeview(animation_tab)  movies_list['columns'] = ('Жанр', 'Режиссер', 'Год') books_list['columns'] = ('Автор', 'Издательство', 'Год') animation_list['columns'] = ('Жанр', 'Студия', 'Рейтинг')  for tree in [movies_list, books_list, animation_list]:     for i, col in enumerate(tree['columns']):         tree.column(col, width=100, anchor='center')         tree.heading(col, text=col.capitalize())     tree.bind("<Double-1>", retrieve_details)  movies_list.insert('', 'end', text='М3ган', values=('Фантастика', 'Джерард Джонстоун', '2022')) books_list.insert('', 'end', text='Перекрестки', values=('Джонатан Франзен', 'Corpus', '2022')) animation_list.insert('', 'end', text='Пиноккио Гильермо Дель Торо', values=('фэнтези', 'Disney+', '8.2'))  movies_desc.pack() movies_list.pack() books_desc.pack() books_list.pack() animation_desc.pack() animation_list.pack()  notebook.pack(fill='both', expand=True)  root.mainloop()      

Задание 10

Напишите GUI калькулятор, который:

  • Выполняет основные арифметические операции – сложение, вычитание, деление и умножение.
  • Поддерживает операции с отрицательными числами, извлечение квадратного корня, деление с остатком и целочисленное деление.

Кроме того, программа должна поддерживать очистку ввода.

Ожидаемый результат:

pishem kalkuljator na python i tkinter a07338f - Пишем калькулятор на Python и Tkinter

Решение:

         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.

***

Содержание самоучителя

  1. Особенности, сферы применения, установка, онлайн IDE
  2. Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
  3. Типы данных: преобразование и базовые операции
  4. Методы работы со строками
  5. Методы работы со списками и списковыми включениями
  6. Методы работы со словарями и генераторами словарей
  7. Методы работы с кортежами
  8. Методы работы со множествами
  9. Особенности цикла for
  10. Условный цикл while
  11. Функции с позиционными и именованными аргументами
  12. Анонимные функции
  13. Рекурсивные функции
  14. Функции высшего порядка, замыкания и декораторы
  15. Методы работы с файлами и файловой системой
  16. Регулярные выражения
  17. Основы скрапинга и парсинга
  18. Основы ООП: инкапсуляция и наследование
  19. Основы ООП: абстракция и полиморфизм
  20. Графический интерфейс на Tkinter

  • 2 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close