Шаг 25.
Библиотека Tkinter.
Работа с окнами. Вывод вторичных окон. Вывод модальных вторичных окон

    На этом шаге мы рассмотрим процесс создания таких окон.

    Модальное вторичное окно, в отличие от обычного, при открытии блокирует все остальные окна приложения, в результате чего пользователь теряет доступ к ним. Остальные окна становятся доступными только после закрытия модального окна. Такие окна обычно применяются в качестве диалоговых окон для запроса каких-либо данных, необходимых для дальнейшей работы приложения.

    К сожалению, библиотека Tkinter не предоставляет никаких инструментов для вывода модальных окон. Однако мы можем превратить обычное вторичное окно в модальное, вызвав у его контейнера или у него самого метод grab_set() (см. 11 шаг). Этот метод задает для контейнера или окна режим перехвата событий, в результате чего остальные окна перестают реагировать на действия пользователя. Как только окно, для которого был установлен перехват событий, закрывается и удаляется из памяти, перехват событий перестает работать, и остальные окна приложения становятся доступными для пользователя.

    Есть еще один неприятный момент, связанный с реализацией модальных окон. Если после запуска приложения и вывода модального вторичного окна мы посмотрим на панель задач Windows, то увидим, что там присутствуют оба окна: и главное, и вторичное. Но присутствие модального окна на панели задач говорит о плохом стиле программирования. Чтобы скрыть вторичное окно, следует вызвать у него метод transient() (см. 21 шаг), передав ему ссылку на главное окно. Этот метод, в частности, отменит представление вторичного окна на панели задач.

    Вот фрагмент кода контейнера, который превратит окно, в котором выведен, в модальное:

class Secondary(tkinter.ttk.Frame):
      def __init__(self, master=None):
            .   .   .   .   .
            self.master.transient(parent)
            self.grab_set()

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

import tkinter
import tkinter.ttk 

# Объявляем класс контейнера для вторичного окна
class Secondary(tkinter.ttk.Frame):
      # Конструктор этого класса поддерживает дополнительный параметр
      # parent, с которым передается ссылка на главное окно. Она
      # понадобится нам, чтобы вывести занесенное значение в главном окне
      def __init__(self, master=None, parent=None):
            super().__init__(master)
            #	Сохраним ссылку на главное окно в атрибуте
            self.parent = parent
            self.pack()
            self.create_widgets()
            self.master.title("Вторичное окно")
            self.master.transient(parent)
            self.grab_set()

      def create_widgets(self):
            self.varValue = tkinter.StringVar()
            self.varValue.set("Значение")
            entValue = tkinter.ttk.Entry(self, textvariable=self.varValue)
            entValue.pack()
            btnOK = tkinter.ttk.Button(self, text="OK", command=self.ok)
            btnOK.pack(side="left")

            btnCancel = tkinter.ttk.Button(self, text="Отмена", 
               command=self.master.destroy)
            btnCancel.pack(side="right")

      def ok(self):
            self.parent.lblValue["text"] = self.varValue.get()
            self.master.destroy()

      def show_value(self):
            self.parent.lblValue["text"] = self.varValue.get()

class Application(tkinter.ttk.Frame):
      def __init__(self, master=None):
            super().__init__(master)
            self.pack()
            self.create_widgets()
            self.master.title("Модальное вторичное окно")
            self.master.resizable(False, False)

      def create_widgets(self):
            btnShowWindow = tkinter.ttk.Button(self, text="Вывести окно", 
               command=self.show_window)
            btnShowWindow.pack()
            #	Опция width компонента Label задает ширину надписи
            #	в символах текста
            self.lblValue = tkinter.ttk.Label(self, text="", width=50)
            self.lblValue.pack()

      def show_window(self):
            #	Выводим вторичное окно, не забыв указать в параметре parent
            #	конструктора ссыпку на главное окно
            Secondary(master=tkinter.Toplevel(), parent=self)

root = tkinter.Tk()
app = Application(master=root)
root.mainloop()
Архив с файлом можно взять здесь.

    Результат работы приложения приведен на рисунке 1.


Рис.1. Результат работы приложения

    На следующем шаге мы рассмотрим управление жизненным циклом приложения.




Предыдущий шаг Содержание Следующий шаг