Виртуальная энциклопедия Linux по-русски   Рейтинг@Mail.ru
Главная | Каталог ПО | Каталог ссылок | Библиотека | Е-книги | Форум | Авторское | О сайте | Карта сайта
  Вход для пользователей  
Регистрация
Забыли пароль?
Полезные ссылки
 

UnixForum

Книги по Linux
(с отзывами читателей)




магазин радар детекторов, тест радар детекторов
 

Библиотека сайта или "Мой Linux Documentation Project"

  "Linux по-русски". Применение управляющих размещениями Tkinter

Применение управляющих размещениями Tkinter

Шапошникова С.В., 19.08.2007 г.



Что это?
Grid – плетём сети для создания ячеек
Pack – пакуем вещи: просто и ... в чемоданы
Place – ищем удобное местечко, возможны относительные варианты
Выводы

Что это?

В Tkinter (библиотеке компонентов графического интерфейса, входящей в Python по "умолчанию") есть три стандартных «менеджера геометрии» (англ. «Geometry Manager») - управляющих размещениями: Grid, Pack и Place. Занимаются они тем, что располагают на главном окне остальные виджеты, причем каждый из трех делает это по-своему. Подобным вопросам при невизуальном программировании приходится уделять немалое внимание в связи с отсутствием возможности весело тыкать мышкой по виджетам на панели, а затем сосредоточенно развозить их по окну, как, например, в VisualBasic.

Возможно, наиболее полная информация по управляющим размещениями представлена в «Введение в Tkinter» (автор Fredrik Lundh), главы 28, 35 и 37. Однако, отсутствие примеров в описании менеджера Pack и, так и оставшийся за гранью моего понимания, смысл некоторых англоязычных фраз в описании Place, привели к мысли "поставить несколько опытов" и увидеть на наглядных примерах принципы работы, преимущества и недостатки каждого из трех методов.

Попробуем организовать некий прототип диалогового окна (создание настоящего диалогового окна – это отдельная история) тремя разными способами. За примером далеко ходить не будем ... Таблица -> Вставить -> Строки ...



Образец диалогового окна


Вполне подойдет. Напишем часть кода, которая останется почти неизменной в будущих опытах (шкала рядом с текстовым полем опущена):


# -*- coding: UTF-8 -*-
from Tkinter import *

# Виджеты
root = Tk() # окно
root.title(
"Вставка строки")

l_in=Label(root,text="Вставить") # метки
l_num=Label(root,text="Кол-во")
l_pos=Label(root,text=
"Положение")

e_num=Entry(root, width=5, bg="White") # текстовое поле

pos = IntVar() # радиокнопки
pos.set(0)
r_pos1=Radiobutton(root,text=
"До", variable=pos, value=0)
r_pos2=Radiobutton(root,text=
"После", variable=pos, value=1)

b_ok=Button(root,text=
"OK", width=10) # Кнопки
b_canc=Button(root,text="Отмена", width=10)
b_help=Button(root,text=
"Справка", width=10)

# Размещение виджетов
#...

root.mainloop()

Теперь приступим к делу. Не нарушая логики "An Introduction to Tkinter", начнем с Grid...

Grid – плетём сети для создания ячеек

Grid делит пространство родительского виджета на ячейки, количество которых определяется дочерними виджетами. Ячейка идентифицируется номером строки (row) и номером столбца (column). Ячейки можно объединять как по горизонтали (columnspan), так и по вертикали (rowspan).

В соответствие с вышеприведенным примером разработаем схему, которая поможет определить количество необходимых ячеек, их адреса и объединения:


Схема Grid


«Жилые» ячейки пронумерованы. Первое число – номер строки, второе – столбца (нумерация начинается с нуля).

Теперь очень легко написать вторую часть кода:

...
# Размещение виджетов
l_in.grid(row=0,column=0,columnspan=2)
l_num.grid(row=1,column=0)
e_num.grid(row=1,column=1)
l_pos.grid(row=2,column=0,columnspan=2)
r_pos1.grid(row=3,column=0)
r_pos2.grid(row=4,column=0)
b_ok.grid(row=1,column=2)
b_canc.grid(row=2,column=2)
b_help.grid(row=3,column=2)
...

При выполнении получаем следующее:


Окно, полученное с помощью Grid


Очень даже ничего, но вот положение радио-кнопок относительно друг друга явно не в порядке. Данный казус результат того, что размер ячейки не всегда совпадает с размером виджета; а по-умолчанию положение последнего определяется по центру. Для изменения положения виджета в ячейке используется опция sticky ("липучка"), значение которой может быть любой комбинацией констант S, N, E и W, или NW, NE, SW и SE. Например, W (запад) означает, что виджет должен быть выравнен по левой границе ячейки. W+E – виджет будет вытянут горизонтально и заполнит целую ячейку по данной оси. W+E+N+S означает, что виджет будет расширен в обоих направлениях.

Еще один «недочет» - кнопки расположены слишком близко друг к другу. Исправляется с помощью опций padx и pady, которые позволяют заполнить пустотой место вокруг виджета.

В конечном итоге, я остановилась на таком варианте:

...
# Размещение виджетов
l_in.grid(row=0,column=0,columnspan=2,sticky=W,pady=5)
l_num.grid(row=1,column=0)
e_num.grid(row=1,column=1,sticky=W)
l_pos.grid(row=2,column=0,columnspan=2,sticky=W)
r_pos1.grid(row=3,column=0,sticky=W,padx=5)
r_pos2.grid(row=4,column=0,sticky=W,padx=5)
b_ok.grid(row=1,column=2,padx=10,pady=5)
b_canc.grid(row=2,column=2,padx=10,pady=5)
b_help.grid(row=3,column=2,padx=10,pady=5)
...

Pack – пакуем вещи: просто и ... в чемоданы

Менеджер Pack только и делает, что располагает виджеты друг за другом по той или иной стороне. Опция side может принимать следующие значения: TOP (значение по-умолчанию, а значит можно не указывать), LEFT, BOTTOM и RIGHT. Для дочерних виджетов, располагающихся на одном родительском окне, можно указывать разные стороны, однако при этом можно и не добиться желаемого результата.

Очевидно, что pack () подойдет лишь для более простых схем расположения, а не таких как пример выше. Для того, чтобы все-таки выяснить как работает pack (), разберем более простой пример: расположим четыре рамки сначала горизонтально, затем вертикально и, наконец, попробуем создать расположение «2х2».

Общая часть кода:

from Tkinter import *

root = Tk()

f_1=Frame(root,width=50,height=50,bg="Yellow")
f_2=Frame(root,width=50,height=50,bg="Blue")
f_3=Frame(root,width=50,height=50,bg="Green")
f_4=Frame(root,width=50,height=50,bg="Red")

# Размещение рамок
# ...

root.mainloop()

Размещение рамок:

горизонтально

f_1.pack(side=LEFT)
f_2.pack(side=LEFT)
f_3.pack(side=LEFT)
f_4.pack(side=LEFT)

Окно с горизонтальным располож. рамок

вертикально

f_1.pack()
f_2.pack()
f_3.pack()
f_4.pack()

Окно с вертикальным располож. рамок

более сложное расположение

f_1.pack(side=LEFT)
f_2.pack(side=RIGHT)
f_3.pack(side=TOP)
f_4.pack(side=BOTTOM)

Окно со сложным расположением

Как я ни пыталась проявить чудеса логического мышления, «2х2» так и не вышло. Но, на самом деле, существует известная хитрость, позволяющая использовать Pack где угодно и как угодно. Заключается она в том, что вводятся дополнительные рамки (фреймы). Поэтому для варианта «2х2» код может быть таким:

Размещение «2х2»

from Tkinter import *

root = Tk()

f_01=Frame(root)
f_02=Frame(root)
f_1=Frame(f_01,width=50,height=50,bg="Yellow")
f_2=Frame(f_01,width=50,height=50,bg="Blue")
f_3=Frame(f_02,width=50,height=50,bg="Green")
f_4=Frame(f_02,width=50,height=50,bg="Red")

f_01.pack(side=LEFT)
f_02.pack(side=LEFT)
f_1.pack()
f_2.pack()
f_3.pack()
f_4.pack()

root.mainloop()

Окно с рамками 2х2

Теперь можно реализовать и более сложный пример. Сначала создадим схему:


Схема Pack

Здесь, прямоугольники – это рамки; внешние рамки являются родительскими по отношению к внутренним.

Конечный код может быть таким:

# -*- coding: UTF-8 -*-
from Tkinter import *

# Виджеты
root = Tk() # окно
root.title("Вставка строки")

f_1=Frame(root)
# рамки
f_2=Frame(root)
f_11=Frame(f_1)
f_12=Frame(f_1)
f_13=Frame(f_1)

l_in=Label(f_11,text=
"Вставить --------------------") # метки
l_num=Label(f_12,text="Кол-во")
l_pos=Label(f_13,text=
"Положение -----------------")

e_num=Entry(f_12, width=5, bg=
"White") # текстовое поле

pos = IntVar()
# радиокнопки
pos.set(0)
r_pos1=Radiobutton(f_13,text=
"До ", variable=pos, value=0)
r_pos2=Radiobutton(f_13,text=
"После ", variable=pos, value=1)

b_ok=Button(f_2,text=
"OK", width=10) # Кнопки
b_canc=Button(f_2,text="Отмена", width=10)
b_help=Button(f_2,text=
"Справка", width=10)

# Размещение виджетов
f_1.pack(side=LEFT)
f_2.pack(side=LEFT,fill=Y)
f_11.pack()
f_12.pack()
f_13.pack()

l_in.pack()
l_num.pack(side=LEFT)
e_num.pack(side=LEFT)
l_pos.pack()
r_pos1.pack()
r_pos2.pack()

b_ok.pack()
b_canc.pack()
b_help.pack(side=BOTTOM)

root.mainloop()

Обратите внимание на параметр 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.

Создадим для наглядности общую схему, с указанием ключевых относительных координат...


Схема Place


, а затем напишем код:

# -*- coding: UTF-8 -*-
from Tkinter import *

# Виджеты
root = Tk() # окно
root.title("Вставка строки")

f_1=Frame(root,width=300,height=150) # рамка

l_in=Label(f_1,text="Вставить --------------------") # метки
l_num=Label(f_1,text="Кол-во")
l_pos=Label(f_1,text="Положение -----------------")

e_num=Entry(f_1, width=5, bg="White") # текстовое поле

pos = IntVar() # радиокнопки
pos.set(0)
r_pos1=Radiobutton(f_1,text="До", variable=pos, value=0)
r_pos2=Radiobutton(f_1,text="После", variable=pos, value=1)

b_ok=Button(f_1,text="OK", width=8) # Кнопки
b_canc=Button(f_1,text="Отмена", width=8)
b_help=Button(f_1,text="Справка", width=8)

# Расположение виджетов
f_1.pack()
l_in.place(relx=0.03,rely=0.05)
l_num.place(relx=0.07,rely=0.25)
e_num.place(relx=0.3,rely=0.25)
l_pos.place(relx=0.03,rely=0.5)
r_pos1.place(relx=0.07,rely=0.65)
r_pos2.place(relx=0.07,rely=0.8)
b_ok.place(relx=0.7,rely=0.2)
b_canc.place(relx=0.7,rely=0.4)
b_help.place(relx=0.7,rely=0.7)

root.mainloop()

Обратите внимание, что виджеты размещаются не на главном окне, а рамке f_1. Рамка введена для того, чтобы диалоговое окно приняло нужную форму. К ней применен метод pack(). Хотя нельзя использовать два менеджера внутри одного родительского окна, к данному случаю это правило не относится, т.к. у рамки родителем является главное окно, а у остальных виджетов – рамка.

Размер виджета не указан, а значит равняется их «естественному» размеру. Указание относительных размеров чревато тем, что при изменении размера родительского окна будет изменяться и сам виджет. Указание же абсолютных координат при изменяемом родительском окне также может приводить к нежелательным результатам. Чтобы убедиться воочию в «непредсказуемости» менеджера place, достаточно немного подправить вышеприведенный код. Позволим рамке расширяться и занимать все свободное пространство:

f_1.pack(fill=BOTH,expand=1)

Для кнопок укажем абсолютные координаты и относительные размеры:

b_ok.place(x=200,y=25, relwidth=0.2, relheight=0.2)
b_canc.place(x=200,y=60, relwidth=0.2, relheight=0.2)
b_help.place(x=200,y=95, 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(). Часто оказывается очень удобным, что текстовое поле или картинка увеличиваются при увеличении окна. Конфуз с кнопками легко избежать или поместив их в отдельную рамку, или умело комбинируя опции. Конечно, простота метода при этом исчезает, но зато появляется гибкость.

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


Эта статья еще не оценивалась
Вы сможете оценить статью и оставить комментарий, если войдете или зарегистрируетесь.
Только зарегистрированные пользователи могут оценивать и комментировать статьи.
 
Главная | Каталог ПО | Каталог ссылок | Библиотека | Е-книги | Форум | Авторское | О сайте | Карта сайта
Rambler's Top100
Service   (C) В.А.Костромин, 1999 - 2010 г. Пишите на kos at rus-linux dot net. liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня Рейтинг@Mail.ru