В Tkinter
(библиотеке компонентов графического интерфейса, входящей в
Python по "умолчанию") есть три стандартных «менеджера
геометрии» (англ. «Geometry Manager») -
управляющих размещениями: Grid, Pack и Place. Занимаются они
тем, что располагают на главном окне остальные виджеты, причем
каждый из трех делает это по-своему. Подобным вопросам при
невизуальном программировании приходится уделять немалое
внимание в связи с отсутствием возможности весело тыкать мышкой
по виджетам на панели, а затем сосредоточенно развозить их по
окну, как, например, в VisualBasic.
Возможно, наиболее
полная информация по управляющим размещениями представлена в
(автор Fredrik Lundh), главы 28, 35 и 37.
Однако, отсутствие примеров в описании менеджера Pack и, так и
оставшийся за гранью моего понимания, смысл некоторых
англоязычных фраз в описании Place, привели к мысли "поставить
несколько опытов" и увидеть на наглядных примерах принципы
работы, преимущества и недостатки каждого из трех методов.
Попробуем организовать некий прототип
диалогового окна (создание настоящего диалогового окна –
это отдельная история) тремя разными способами. За примером
далеко ходить не будем ... Таблица -> Вставить -> Строки
...
Вполне подойдет.
Напишем часть кода, которая останется почти неизменной в будущих
опытах (шкала рядом с текстовым полем опущена):
Теперь приступим к делу. Не
нарушая логики , начнем с Grid...
Grid –
плетём сети для создания ячеек
Grid делит пространство
родительского виджета на ячейки, количество которых определяется
дочерними виджетами. Ячейка идентифицируется номером строки
(row) и номером столбца (column). Ячейки можно
объединять как по горизонтали (columnspan),
так и по вертикали (rowspan).
В
соответствие с вышеприведенным примером разработаем схему,
которая поможет определить количество необходимых ячеек, их
адреса и объединения:
«Жилые»
ячейки пронумерованы. Первое число – номер строки, второе
– столбца (нумерация начинается с нуля).
Очень даже ничего,
но вот положение радио-кнопок относительно друг друга явно не в
порядке. Данный казус результат того, что размер ячейки не
всегда совпадает с размером виджета; а по-умолчанию положение
последнего определяется по центру. Для изменения положения
виджета в ячейке используется опция sticky
("липучка"), значение которой может быть любой
комбинацией констант S, N, E и W, или NW, NE, SW и SE. Например,
W (запад) означает, что виджет должен быть выравнен по левой
границе ячейки. W+E – виджет будет вытянут горизонтально и
заполнит целую ячейку по данной оси. W+E+N+S означает, что
виджет будет расширен в обоих направлениях.
Еще
один «недочет» - кнопки расположены слишком близко
друг к другу. Исправляется с помощью опций padx и pady,
которые позволяют заполнить пустотой место вокруг виджета.
В
конечном итоге, я остановилась на таком варианте:
Менеджер Pack только и делает,
что располагает виджеты друг за другом по той или иной стороне.
Опция side может принимать следующие значения: TOP
(значение по-умолчанию, а значит можно не указывать), LEFT,
BOTTOM и RIGHT. Для дочерних виджетов, располагающихся на одном
родительском окне, можно указывать разные стороны, однако при
этом можно и не добиться желаемого результата.
Очевидно, что pack
() подойдет лишь для более простых схем расположения, а не таких
как пример выше. Для того, чтобы все-таки выяснить как работает
pack (), разберем более простой пример: расположим четыре рамки
сначала горизонтально, затем вертикально и, наконец, попробуем
создать расположение «2х2».
Как я ни пыталась проявить чудеса логического
мышления, «2х2» так и не вышло. Но, на самом деле,
существует известная хитрость, позволяющая использовать Pack где
угодно и как угодно. Заключается она в том, что вводятся
дополнительные рамки (фреймы). Поэтому для варианта «2х2»
код может быть таким:
Обратите
внимание на параметр fill в методе pack (), примененном к
рамке f_2. Он позволяет заполнять виджету свободное пространство
по оси X, Y или обоим направлениям (BOTH). В данном случае, это
позволило f_2 стать по высоте равной f_1; иначе ее высота
равнялась бы суммарной высоте дочерних виджетов (трех кнопок),
что не позволило бы развезти их по разным сторонам.
Place –
ищем удобное местечко, возможны относительные варианты
С помощью Place можно явно
указывать виджету на его место с помощью координат, как в
абсолютных значениях (в пикселях), так и относительно
родительского окна (в долях). Также относительно и абсолютно
можно задавать размер самого виджета.
Перечислю наиболее важные, на
мой взгляд, параметры данного метода:
anchor (якорь) –
определяет часть виджета, для которой задаются координаты.
Принимает значения - N, NE, E, SE,
SW, W, NW, или CENTER. По умолчанию NW (верхний левый угол).
relwidth, relheight
(относительные ширина и высота) – определяют размер
виджета по отношению к его родителю.
relx, rely –
определяют относительную позицию обычно в родительском виджете.
Координата (0;0) –
у левого верхнего угла, (1;1) - у правого нижнего.
width, height –
абсолютный размер в пикселах. Значения по умолчанию (когда
данные опции опущены) приравниваются к "естественному"
размеру виджета.
x, y - абсолютная
позиция в пикселах. Значения по умолчанию приравниваются к 0.
Создадим для наглядности общую схему, с указанием ключевых
относительных координат...
Обратите внимание,
что виджеты размещаются не на главном окне, а рамке f_1. Рамка
введена для того, чтобы диалоговое окно приняло нужную форму. К
ней применен метод pack(). Хотя нельзя использовать два
менеджера внутри одного родительского окна, к данному случаю
это правило не относится, т.к. у рамки родителем является
главное окно, а у остальных виджетов – рамка.
Размер
виджета не указан, а значит равняется их «естественному»
размеру. Указание относительных размеров чревато тем, что при
изменении размера родительского окна будет изменяться и сам
виджет. Указание же абсолютных координат при изменяемом
родительском окне также может приводить к нежелательным
результатам. Чтобы убедиться воочию в «непредсказуемости»
менеджера place, достаточно немного подправить вышеприведенный
код. Позволим рамке расширяться и занимать все свободное
пространство:
f_1.pack(fill=BOTH,expand=1)
Для
кнопок укажем абсолютные координаты и относительные размеры:
Запускаем,
разворачиваем окно на полный экран и удивляемся.
Выводы
Теперь сравним результаты
«опыта» с характеристиками менеджеров, данными в
«Введении в Tkinter».
Fredrik Lundh
характеризует менеджер Grid как самый гибкий и универсальный.
Последнее понятно: его можно применять в любой ситуации и не
бояться неожиданных спецэффектов. А вот что понимать под
гибкостью? В примере, для того, чтобы увеличить расстояние между
кнопками были использованы опции padx и pady. Это привело к
увеличению самих ячеек, и, как следствие, увеличению самого
окна. Конечно, при использовании большего числа параметров и
вычислении соотношений размеров виджета, можно добиться
желаемого результата. Но реализовать схему, где необходима
тонкая настойка положений виджета, может оказаться слишком
сложно. Следует также помнить, что пустые строки и столбцы
игнорируются. Например, если в примере вместо столбца 2 указать
3, ничего не изменится.
В «Введении»
менеджер Pack никак не характеризуется, но автор часто
рекомендует попробовать Grid вместо него. Ясно, что Pack –
это идеальный вариант для простых схем. Разработка более сложных
требуется ввода дополнительных рамок, что приводит к увеличению
кода; хотя понять работу этого метода легче всего. Также,
исключается тонкая подстройка.
Place
характеризуется как самый простой из трех управляющих.
Действительно: указываешь координаты и получаешь готовый
результат. В примере, Place позволил быстро и просто получить
самое изящное и близкое к оригиналу окно. Сложности начинаются,
когда требуется создать обычное окно, изменяемое в размерах.
Lundh рекомендует использовать Place лишь в специфических
случаях. Например, размещение виджета строго по центру
(w.place(relx=0.5,
rely=0.5, anchor=CENTER))
или когда требуется наложить один виджет на другой. Мне же метод
place() нравится куда больше grid(). Часто оказывается очень
удобным, что текстовое поле или картинка увеличиваются при
увеличении окна. Конфуз с кнопками легко избежать или поместив
их в отдельную рамку, или умело комбинируя опции. Конечно,
простота метода при этом исчезает, но зато появляется гибкость.
Понятно, что для каждого
идеален тот управляющий размещениями, пользоваться которым
можешь в совершенстве. Однако, если целью ставить создание
наиболее адекватного кода, необходимо уметь пользоваться тремя
методами и грамотно их комбинировать; при этом никогда не
забывая о том, что нельзя использовать два
менеджера внутри одного родительского окна.
Эта статья еще не оценивалась
Вы сможете оценить статью и оставить комментарий, если
войдете или зарегистрируетесь.
Только зарегистрированные пользователи могут оценивать и комментировать статьи.