Виды сайзеров

Продолжаем изучение схем расстановки виджетов с помощью сайзеров. На предыдущем занятии мы с вами рассмотрели построение интерфейса с использованием BoxSizer, далее поговорим о GridSizer.

GridSizer

Он позволяет создавать двумерную сетку ячеек с одинаковой шириной и одинаковой высотой. Общий синтаксис следующий:

wx.GridSizer(rows=1, cols=0, vgap=0, hgap=0)

  • rows – число строк (отсчет начинается с 1);
  • cols – число столбцов (отсчет идет с 0);
  • vgap – интервал между ячейками по вертикали;
  • hgap – интервал между ячейками по горизонтали.

Давайте в качестве примера создадим вот такой импровизированный калькулятор:

Вначале создадим BoxSizer с вертикальным расположением элементов:

vbox = wx.BoxSizer(wx.VERTICAL)

И в первой ячейке разместим поле ввода:

tc = wx.TextCtrl(panel)
vbox.Add(tc, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)

Вторая ячейка будет содержать сайзер:

gbox = wx.GridSizer(5, 4, 5, 5)

состоящий из 5 строк и 4 столбцов. Расстояния между ячейками 5 пикселей по горизонтали и по вертикали. Помещаем в этот сайзер все необходимые кнопки:

gbox.AddMany([ (wx.Button(panel, label='Cls'), 0, wx.EXPAND),
            (wx.Button(panel, label='Bck'), 0, wx.EXPAND),
            (wx.StaticText(panel), wx.EXPAND),
            (wx.Button(panel, label='Close'), 0, wx.EXPAND),
            (wx.Button(panel, label='7'), 0, wx.EXPAND),
            (wx.Button(panel, label='8'), 0, wx.EXPAND),
            (wx.Button(panel, label='9'), 0, wx.EXPAND),
            (wx.Button(panel, label='/'), 0, wx.EXPAND),
            (wx.Button(panel, label='4'), 0, wx.EXPAND),
            (wx.Button(panel, label='5'), 0, wx.EXPAND),
            (wx.Button(panel, label='6'), 0, wx.EXPAND),
            (wx.Button(panel, label='*'), 0, wx.EXPAND),
            (wx.Button(panel, label='1'), 0, wx.EXPAND),
            (wx.Button(panel, label='2'), 0, wx.EXPAND),
            (wx.Button(panel, label='3'), 0, wx.EXPAND),
            (wx.Button(panel, label='-'), 0, wx.EXPAND),
            (wx.Button(panel, label='0'), 0, wx.EXPAND),
            (wx.Button(panel, label='.'), 0, wx.EXPAND),
            (wx.Button(panel, label='='), 0, wx.EXPAND),
            (wx.Button(panel, label='+'), 0, wx.EXPAND) ])

Там где кнопки нет идет текстовый элемент без содержимого. Каждая кнопка привязана к панели и занимает весь допустимый размер (флаг EXPAND).

Добавляем этот объект во вторую ячейку сайзера vbox с отступами 10 пикселей с каждой стороны:

vbox.Add(gbox, flag=wx.EXPAND | wx.ALL, border=10)

Запускаем программу и видим результат. Но здесь есть один недостаток: когда мы меняем окно по вертикали кнопки остаются неизменными. Исправим это, добавив свойство proportion=1:

vbox.Add(gbox, proportion=1, flag=wx.EXPAND | wx.ALL, border=10)

Теперь интерфейс занимает все доступное пространство. Вот так работает GridSizer.

FlexGridSizer

Следующий FlexGridSizer работает аналогично GridSizer, но в отличие от предыдущего здесь ширина (или высота) у соответствующих групп ячеек может быть разная.

Давайте представим, что мы делаем форму для ввода регистрационной информации пользователя и она должна выглядеть вот так:

Для начала создадим такой сайзер с 4 строками, 2 столбцами и расстояниями между ячейками 10 пикселей:

fb = wx.FlexGridSizer(4, 2, 10, 10)

Далее, используя уже знакомый метод AddMany() добавим все виджеты в данный объект:

fb.AddMany([ (wx.StaticText(panel, label="Ф.И.О.:")), 
             (wx.TextCtrl(panel), wx.ID_ANY, wx.EXPAND),
             (wx.StaticText(panel, label="email:")), 
             (wx.TextCtrl(panel), wx.ID_ANY, wx.EXPAND),
             (wx.StaticText(panel, label="Адрес:")), 
             (wx.TextCtrl(panel), wx.ID_ANY, wx.EXPAND),
             (wx.StaticText(panel, label="О себе:")), 
             (wx.TextCtrl(panel, style=wx.NB_MULTILINE), wx.ID_ANY, wx.EXPAND)
           ])

И свяжем панель с этим сайзером:

panel.SetSizer(fb)

Запустим программу и увидим вот такой результат:

Кажется это не то, что мы бы хотели. Для начала укажем, что второй столбец следует расширять по максимуму. Воспользуемся методом:

AddGrowableCol(self, col, proportion=0)

и через него зададим proportion=1 для второго столбца (col=1), получим:

fb.AddGrowableCol(1, 1)

Теперь все наши текстовые элементы достигают конца окна. Далее, вот этот последний элемент с текстом «О себе» следует расширить до нижней границы окна. Это можно сделать с помощью похожего метода:

AddGrowableRow(self, row, proportion=0)

и для row=3 укажем proportion=1:

fb.AddGrowableRow(3, 1)

Все, теперь наши виджеты выглядят так, как этого мы и хотим. Но нам нужны еще отступы от границ окна. Для этого мы поместим сайзер fb в другой сайзер BoxSizer и уже в нем укажем необходимые отступы:

hbox = wx.BoxSizer()
hbox.Add(fb, proportion=1, flag=wx.EXPAND|wx.ALL, border=10)

Свяжем панель с этим новым объектом:

panel.SetSizer(hbox)

и получим желаемый результат.

GridBagSizer

Это, наверное, самый гибкий сайзер среди всех в wxPython. Он динамически создает таблицу ячеек, у которых могут быть разные размеры (также как и у FlexGridSizer). При его создании, мы указываем только два параметра:

GridBagSizer(vgap, hgap)

расстояния между ячейками в пикселах. А, затем, вызывая метод:

Add(self, item, pos, span=wx.DefaultSpan, flag=0, border=0, userData=None)

можем добавлять виджет в позицию

pos=(row, col)

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

И расположим все эти виджеты вот в такой сетке:

Ее мы как раз и создадим с помощью GridBagSizer. Создадим его:

gr = wx.GridBagSizer(5, 5)

Мы здесь указываем отступы между ячейками в 5 пикселей. Далее, создадим текстовый виджет:

text = wx.StaticText(panel, label="Email:")

и добавим его в первую ячейку:

gr.Add(text, pos=(0, 0), flag=wx.TOP | wx.LEFT | wx.BOTTOM, border=5)

Смотрите, здесь параметр pos=(0,0) как раз и указывает первую строку и первый столбец, т.е. первую ячейку нашего сайзера.

Создадим текстовое поле ввода:

tc = wx.TextCtrl(panel)

и добавим его во вторую строку и так, чтобы он охватывал 5 ячеек по столбцам:

gr.Add(tc, pos=(1, 0), span=(1, 5), flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=5)

Это делается через параметры pos=(1,0) и span(1,5). Последний означает, что мы будем захватывать одну строку и 5 столбцов. Как раз получим единую горизонтальную ячейку нашей таблицы.

Далее создаем две кнопки:

button1 = wx.Button(panel, label="Восстановить", size=(120, 28))
button2 = wx.Button(panel, label="Отмена", size=(90, 28))

и добавляем их в четвертую строку и 4-е, 5-е ячейки (не забываем, что отсчет идет с нуля):

gr.Add(button1, pos=(3, 3))
gr.Add(button2, pos=(3, 4), flag=wx.RIGHT|wx.BOTTOM, border=10)

Осталось связать наш сайзер с панелью:

panel.SetSizer(gr)

и запустить программу. Смотрите, ячейки не распахиваются на все окно, а группируются вначале:

Поправим это. Во-первых, сделаем второй столбец максимальным по ширине. Для этого воспользуемся методом класса GridBagSizer

AddGrowableCol(col)

в котором указывается столбец для масштабирования. В нашем случае запишем строчку:

gr.AddGrowableCol(1)

Запустим программу и видим, что теперь все растягивается по ширине, как и задумывалось. По аналогии растянем по высоте третью строку с помощью похожего метода:

AddGrowableRow(row)

получим:

gr.AddGrowableRow(2)

Теперь наш интерфейс выглядит в полном соответствии с задумкой.

Итак, на этом и предыдущем занятиях мы с вами рассмотрели принципы размещения виджетов с разными схемами расположения, используя сайзеры:

BoxSizer, GridSizer, FlexGridSizer, GridBagSizer

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

https://docs.wxpython.org

Видео по теме