Библиотека сайта rus-linux.net
Применение управляющих размещениями TkinterШапошникова С.В., 19.08.2007 г. |
|
||||||||||||||||
Что это? В Tkinter (библиотеке компонентов графического интерфейса, входящей в Python по "умолчанию") есть три стандартных «менеджера геометрии» (англ. «Geometry Manager») - управляющих размещениями: Grid, Pack и Place. Занимаются они тем, что располагают на главном окне остальные виджеты, причем каждый из трех делает это по-своему. Подобным вопросам при невизуальном программировании приходится уделять немалое внимание в связи с отсутствием возможности весело тыкать мышкой по виджетам на панели, а затем сосредоточенно развозить их по окну, как, например, в VisualBasic. Возможно, наиболее полная информация по управляющим размещениями представлена в «Введение в Tkinter» (автор Fredrik Lundh), главы 28, 35 и 37. Однако, отсутствие примеров в описании менеджера Pack и, так и оставшийся за гранью моего понимания, смысл некоторых англоязычных фраз в описании Place, привели к мысли "поставить несколько опытов" и увидеть на наглядных примерах принципы работы, преимущества и недостатки каждого из трех методов. Попробуем организовать некий прототип диалогового окна (создание настоящего диалогового окна – это отдельная история) тремя разными способами. За примером далеко ходить не будем ... Таблица -> Вставить -> Строки ... |
|
||||||||||||||||
|
|
|
|||||||||||||||
Вполне подойдет. Напишем часть кода, которая останется почти неизменной в будущих опытах (шкала рядом с текстовым полем опущена):
Теперь приступим к делу. Не нарушая логики "An Introduction to Tkinter", начнем с 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 – пакуем вещи: просто и ... в чемоданы Менеджер Pack только и делает, что располагает виджеты друг за другом по той или иной стороне. Опция side может принимать следующие значения: TOP (значение по-умолчанию, а значит можно не указывать), LEFT, BOTTOM и RIGHT. Для дочерних виджетов, располагающихся на одном родительском окне, можно указывать разные стороны, однако при этом можно и не добиться желаемого результата. Очевидно, что pack () подойдет лишь для более простых схем расположения, а не таких как пример выше. Для того, чтобы все-таки выяснить как работает pack (), разберем более простой пример: расположим четыре рамки сначала горизонтально, затем вертикально и, наконец, попробуем создать расположение «2х2». Общая часть кода: from
Tkinter import * Размещение рамок: |
|||||||||||||||||
горизонтально |
f_1.pack(side=LEFT) |
|
|||||||||||||||
вертикально |
f_1.pack() |
|
|||||||||||||||
более сложное расположение |
f_1.pack(side=LEFT) |
|
|||||||||||||||
Как я ни пыталась проявить чудеса логического мышления, «2х2» так и не вышло. Но, на самом деле, существует известная хитрость, позволяющая использовать Pack где угодно и как угодно. Заключается она в том, что вводятся дополнительные рамки (фреймы). Поэтому для варианта «2х2» код может быть таким: |
|||||||||||||||||
Размещение «2х2» |
from
Tkinter import * |
|
|||||||||||||||
Теперь можно реализовать и более сложный пример. Сначала создадим схему: |
|||||||||||||||||
|
|
||||||||||||||||
Здесь, прямоугольники – это рамки; внешние рамки являются родительскими по отношению к внутренним. Конечный код может быть таким: #
-*- coding: UTF-8 -*- Обратите внимание на параметр fill в методе pack (), примененном к рамке f_2. Он позволяет заполнять виджету свободное пространство по оси X, Y или обоим направлениям (BOTH). В данном случае, это позволило f_2 стать по высоте равной f_1; иначе ее высота равнялась бы суммарной высоте дочерних виджетов (трех кнопок), что не позволило бы развезти их по разным сторонам. Place – ищем удобное местечко, возможны относительные варианты С помощью Place можно явно указывать виджету на его место с помощью координат, как в абсолютных значениях (в пикселях), так и относительно родительского окна (в долях). Также относительно и абсолютно можно задавать размер самого виджета. Перечислю наиболее важные, на мой взгляд, параметры данного метода:
Создадим для наглядности общую схему, с указанием ключевых относительных координат... |
|||||||||||||||||
|
|
|
|||||||||||||||
, а затем напишем код: #
-*- coding: UTF-8 -*- Обратите внимание, что виджеты размещаются не на главном окне, а рамке f_1. Рамка введена для того, чтобы диалоговое окно приняло нужную форму. К ней применен метод pack(). Хотя нельзя использовать два менеджера внутри одного родительского окна, к данному случаю это правило не относится, т.к. у рамки родителем является главное окно, а у остальных виджетов – рамка. Размер виджета не указан, а значит равняется их «естественному» размеру. Указание относительных размеров чревато тем, что при изменении размера родительского окна будет изменяться и сам виджет. Указание же абсолютных координат при изменяемом родительском окне также может приводить к нежелательным результатам. Чтобы убедиться воочию в «непредсказуемости» менеджера place, достаточно немного подправить вышеприведенный код. Позволим рамке расширяться и занимать все свободное пространство: f_1.pack(fill=BOTH,expand=1) Для кнопок укажем абсолютные координаты и относительные размеры: b_ok.place(x=200,y=25,
relwidth=0.2, relheight=0.2) Запускаем, разворачиваем окно на полный экран и удивляемся. Теперь сравним результаты «опыта» с характеристиками менеджеров, данными в «Введении в 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(). Часто оказывается очень удобным, что текстовое поле или картинка увеличиваются при увеличении окна. Конфуз с кнопками легко избежать или поместив их в отдельную рамку, или умело комбинируя опции. Конечно, простота метода при этом исчезает, но зато появляется гибкость. Понятно, что для каждого идеален тот управляющий размещениями, пользоваться которым можешь в совершенстве. Однако, если целью ставить создание наиболее адекватного кода, необходимо уметь пользоваться тремя методами и грамотно их комбинировать; при этом никогда не забывая о том, что нельзя использовать два менеджера внутри одного родительского окна. |