Диалоговые (всплывающие) окна / tkinter 9

Скачайте код уроков с GitLab: https://gitlab.com/PythonRu/tkinter-uroki

Окна уведомлений

Распространенный сценарий применения диалоговых окон — уведомление пользователей о событиях, которые происходят в приложении: сделана новая запись или произошла ошибка при попытке открыть файл. Рассмотрим базовые функции Tkinter, предназначенные для отображения диалоговых окон.

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

Окна предупреждений

При запуске этого примера стоит обратить внимание на то, что каждое окно воспроизводит соответствующий платформе звук и показывает перевод метки в зависимости от языка системы:

Окна предупреждений

Эти диалоговые окна открываются с помощью функций showinfo, showwarning и showerror из модуля tkinter.messagebox:


import tkinter as tk
import tkinter.messagebox as mb class App(tk.Tk):
def __init__(self):
super().__init__()
btn_info = tk.Button(self, text="Информационное окно",
command=self.show_info)
btn_warn = tk.Button(self, text="Окно с предупреждением",
command=self.show_warning)
btn_error = tk.Button(self, text="Окно с ошибкой",
command=self.show_error)

opts = {'padx': 40, 'pady': 5, 'expand': True, 'fill': tk.BOTH}
btn_info.pack(**opts)
btn_warn.pack(**opts)
btn_error.pack(**opts)

def show_info(self):
msg = "Ваши настройки сохранены"
mb.showinfo("Информация", msg)

def show_warning(self):
msg = "Временные файлы удалены не правильно"
mb.showwarning("Предупреждение", msg)

def show_error(self):
msg = "Приложение обнаружило неизвестную ошибку"
mb.showerror("Ошибка", msg) if __name__ == "__main__":
app = App()
app.mainloop()

Как работают окна с уведомлениями

В первую очередь нужно импортировать модуль tkinter.messagebox, задав для него алиас mb. В Python2 этот модуль назывался tkMessageBox, поэтому такой синтаксис позволит изолировать проблемы совместимости.

Каждое окно обычно выбирается в зависимости от информации, которую нужно показать пользователю:

  • showinfo — операция была завершена успешно,
  • showwarning — операция была завершена, но что-то пошло не так, как планировалось,
  • showerror — операция не была завершена из-за ошибки.

Все три функции получают две строки в качестве входящих аргументов: заголовок и сообщение.

Сообщение может быть выведено на нескольких строках с помощью символа новой строки \n.

Окна выбора ответа

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

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

В этой программе рассмотрим остальные диалоговые функции из модуля tkinter.messagebox. Каждая кнопка включает метки с описанием типа окна, которое откроется при нажатии:

Окна выбора ответа

У них есть кое-какие отличия, поэтому стоит попробовать каждое из окон, чтобы разобраться в них.

Как и в прошлом примере сначала нужно импортировать tkinter.messagebox с помощью синтаксиса import … as и вызывать каждую из функций вместе с title и message:


import tkinter as tk
import tkinter.messagebox as mb class App(tk.Tk):
def __init__(self):
super().__init__()
self.create_button(mb.askyesno, "Спросить Да/Нет",
"Вернет True или False")
self.create_button(mb.askquestion, "Задать вопрос ",
"Вернет 'yes' или 'no'")
self.create_button(mb.askokcancel, "Спросить Ок/Отмена",
"Вернет True или False")
self.create_button(mb.askretrycancel, "Спросить Повтор/Отмена",
"Вернет True или False")
self.create_button(mb.askyesnocancel, "Спросить Да/Нет/Отмена",
"Вернет True, False или None")

def create_button(self, dialog, title, message):
command = lambda: print(dialog(title, message))
btn = tk.Button(self, text=title, command=command)
btn.pack(padx=40, pady=5, expand=True, fill=tk.BOTH) if __name__ == "__main__":
app = App()
app.mainloop()

Как работают вопросительные окна

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

Выбор файлов и папок

Диалоговые окна для выбора файлов позволяют выбирать один или несколько файлов из файловой системы. В Tkinter эти функции объявлены в модуле tkinter.filedialog, который также включает окна для выбора папок. Он также позволяет настраивать поведение нового окна: например, фильтрация по расширению или выбор папки по умолчанию.

В этом приложении будет две кнопки. Первая, «Выбрать файл», откроет диалоговое окно для выбора файла. По умолчанию в окне будут только файлы с расширением .txt:

Выбор файлов и папок

Вторая — «Выбор папки». Она будет открывать похожее диалоговое окно для выбора папки.

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

Первая кнопка будет вызывать функцию askopenfilename, а вторая — askdirectory:


import tkinter as tk
import tkinter.filedialog as fd class App(tk.Tk):
def __init__(self):
super().__init__()
btn_file = tk.Button(self, text="Выбрать файл",
command=self.choose_file)
btn_dir = tk.Button(self, text="Выбрать папку",
command=self.choose_directory)
btn_file.pack(padx=60, pady=10)
btn_dir.pack(padx=60, pady=10)

def choose_file(self):
filetypes = (("Текстовый файл", "*.txt"),
("Изображение", "*.jpg *.gif *.png"),
("Любой", "*"))
filename = fd.askopenfilename(title="Открыть файл", initialdir="/",
filetypes=filetypes)
if filename:
print(filename)

def choose_directory(self):
directory = fd.askdirectory(title="Открыть папку", initialdir="/")
if directory:
print(directory) if __name__ == "__main__":
app = App()
app.mainloop()

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

Как работают окна выбора файлов и папок

Создадим первое диалоговое окно с помощью функции askopenfilename, которая возвращает строку с полным путем к файлу. Она принимает следующие опциональные аргументы:

  • title — название для диалогового окна.
  • initialdir — начальная папка.
  • filetypes — последовательность из двух строк. Первая — метка с типом файла в читаемом формате, а вторая — шаблон для поиска совпадения с названием файла.
  • multiple — булево значение для определения того, может ли пользователь выбирать несколько файлов.
  • defaultextension — расширение, добавляемое к файлу, если оно не было указано явно.

В этом примере задаем корневую папку в качестве начальной, а также название. В кортеже типов файлов есть следующие три варианта: текстовые файлы, сохраненные с расширением .txt, изображения с .jpg, .gif и .png, а также подстановочный знак («*») для всех файлов.

Стоит обратить внимание на то, что эти шаблоны не обязательно соответствуют формату данных в файле, поскольку есть возможность переименовать его с другим расширением:



filetypes = (("Текстовый файл", "*.txt"),
("Изображение", "*.jpg *.gif *.png"),
("Любой", "*"))
filename = fd.askopenfilename(title="Открыть файл", initialdir="/",
filetypes=filetypes)

Функция askdirectory также принимает параметры title и initialdir, а также булев параметр mustexist для определения того, должны ли пользователи выбирать существующую папку:


directory = fd.askdirectory(title="Открыть папку", initialdir="/")

Модуль tkinter.filedialog включает вариации этих функций, которые позволяют прямо получать объекты файлов.

Например, askopenfile возвращает объект файла, который соответствует выбранному вместо того чтобы вызывать open с путем возвращающим askopenfilename. Но при этом все еще придется проверять, не было ли окно отклонено перед вызовом файловых методов:


import tkinter.filedialog as fd

filetypes = (("Текстовый файл", "*.txt"),)
my_file = fd.askopenfile(title="Открыть файл", filetypes=filetypes)
if my_file:
print(my_file.readlines())
my_file.close()

Сохранение данных в файл

Помимо выбора существующих файлов и папок также есть возможность создавать новые, используя диалоговые окна Tkinter. Они позволят сохранять данные, сгенерированные приложением, позволяя пользователям выбирать название и местоположение нового файла.

Будем использовать диалоговое окно «Сохранить файл» для записи содержимого виджета Text в текстовый файл:

Сохранение данных в файл

Для открытия такого диалогового окна используется функция asksavefile из модуля tkinter.filedialog. Она создает объект файла с режимом записи ('w') или None, если окно было закрыто:


import tkinter as tk
import tkinter.filedialog as fd class App(tk.Tk):
def __init__(self):
super().__init__()
self.text = tk.Text(self, height=10, width=50)
self.btn_save = tk.Button(self, text="Сохранить", command=self.save_file)

self.text.pack()
self.btn_save.pack(pady=10, ipadx=5)

def save_file(self):
contents = self.text.get(1.0, tk.END)
new_file = fd.asksaveasfile(title="Сохранить файл", defaultextension=".txt",
filetypes=(("Текстовый файл", "*.txt"),))
if new_file:
new_file.write(contents)
new_file.close() if __name__ == "__main__":
app = App()
app.mainloop()

Как работает сохранение фалов

Функция asksavefile принимает те же опциональные параметры, что и askopenfile, но также позволяет добавить расширение файла по умолчанию с помощью параметра defaultextension.

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

С помощью объекта файла можно записать содержимое виджета Text. Нужно лишь не забывать закрывать файл для освобождения ресурсов, которые использовал объект:


contents = self.text.get(1.0, tk.END)
new_file.write(contents)
new_file.close()

Благодаря последней программе стало понятно, что askopenfile возвращает объект файла, а не его название. Также есть функция asksaveasfilename, которая возвращает путь к выбранному файлу. Ее можно использовать для изменения пути или выполнения валидации перед открытием файла для записи.

Максим
Я создал этот блог в 2018 году, чтобы распространять полезные учебные материалы, документации и уроки на русском. На сайте опубликовано множество статей по основам python и библиотекам, уроков для начинающих и примеров написания программ.
Мои контакты: Почта
admin@pythonru.comAlex Zabrodin2018-10-26OnlinePython, Programming, HTML, CSS, JavaScript