В сценарии gui1c.py из примера 7.4 я начал добавлять метки, не сохраняя их в переменных. Этот прием действует и является совершенно допустимым, но поскольку при первом взгляде такой программный код может смутить начинающих программистов, необходимо более подробно объяснить здесь, почему он работает.
В tkinter объекты классов Python соответствуют объектам, выводимым на экран, — мы заставляем объект Python создать объект на экране и вызываем методы объекта Python, чтобы настроить этот экранный объект. Благодаря такому соответствию срок жизни объекта Python должен в целом соответствовать сроку жизни соответствующего ему объекта на экране.
К счастью, сценариям на языке Python обычно не требуется беспокоиться о времени жизни объекта. Обычно они вообще не обязаны сохранять ссылки на создаваемые объекты виджетов, если только не собираются в дальнейшем изменять параметры настройки этих объектов. Например, при программировании с использованием tkinter часто сразу же после создания виджет размещают в интерфейсе, если в дальнейшем ссылка на него не понадобится:
Label(text=’hi’).pack() # ОК
Эта инструкция выполняется как обычно, слева направо. Она сначала создает новую метку и затем немедленно вызывает метод pack нового объекта, чтобы установить его расположение при отображении на экране. Обратите, однако, внимание, что в этом выражении объект Label является временным: так как он не присваивается переменной, в обычной ситуации он должен был бы быть утилизирован сборщиком мусора сразу после выполнения метода pack.
Однако поскольку при создании объектов библиотека tkinter обращается к библиотеке Tk, метка будет нарисована на экране, как положено, несмотря на то, что мы не сохранили в своем сценарии ссылку на соответствующий объект Python. В действительности внутри tkinter объекты виджетов связываются в долгоживущее дерево, которое используется для представления интерфейса, отображаемого на экране, поэтому ссылка на объект Label, созданный этой инструкцией, сохраняется, хотя и не внутри нашего сценария.[XXX]
Иными словами, сценариям обычно не нужно беспокоиться о сроке жизни объектов виджетов, и вполне допустимо создавать виджеты и сразу же размещать их одной и той же инструкцией, не сохраняя ссылки на них в переменных.
Но это не значит, что можно писать такой код:
widget = Label(text=’hi’).pack() # неверно!
…использование графического элемента…
На первый взгляд, эта инструкция должна присвоить только что размещенную в интерфейсе метку переменной widget, но это не так. На самом деле это известная ошибка новичков в tkinter. Метод pack размещает виджет, но не возвращает его. Метод pack возвращает объект None — после этой операции переменная widget будет хранить ссылку на None и все дальнейшие операции с виджетом, использующие эту переменную, окажутся безуспешными. Например, по той же причине следующая команда потерпит неудачу:
Label(text=’hi’).pack().mainloop() # неверно!
Метод pack возвращает None, поэтому попытка обратиться к его атрибуту mainloop возбудит исключение (как и должно быть). Если вы действительно хотите разместить виджет и сохранить ссылку на него, используйте такой способ:
widget = Label(text=’hi’) # тоже правильно
widget.pack()
…использование графического элемента…
Такой способ несколько многословнее, но он более надежен, чем размещение виджета в той же инструкции, которая его создает, и позволяет сохранить ссылку на виджет для последующих операций с ним. Кроме того, такой способ чаще используется на практике при создании более сложных конфигураций и схем размещения виджетов.
С другой стороны, сценарии, занимающиеся компоновкой внешнего вида, часто добавляют виджеты, размещая их раз и навсегда и не нуждаясь в последующей их настройке, — сохранение долгоживущих ссылок в такой программе не имеет смысла и является необязательным.
В главе 8 мы встретимся с двумя исключениями из этого пра-
<* вила. Сценарии должны вручную получать ссылки на объ-
*’Ч’ е •, екты изображений, потому что фактические данные, состав
… ■ ляющие изображение, будут уничтожены, как только сборщик мусора в Python утилизирует объект изображения. Объекты класса Variable из библиотеки tkinter также временно сбрасывают связанные с ними переменные из библиотеки Tk при утилизации, но это случается редко и не так опасно.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011