После того как
мы с вами разобрались с созданием меню и панелей инструментов, пришло время
познакомиться с принципами размещения различных виджетов (кнопок, текстовых
полей, списков и т.п) в области окна. В самом простом варианте мы можем
использовать абсолютное позиционирование этих элементов. Например, сделать так.
Создать два виджета для отображения картинок:
img1 = wx.StaticBitmap(self, wx.ID_ANY, wx.Bitmap("images/js.jpg"))
img2 = wx.StaticBitmap(self, wx.ID_ANY, wx.Bitmap("images/python.jpg"))
и разместить их
в следующих позициях:
img1.SetPosition( (10, 10) )
img2.SetPosition((300, 40))
Далее, укажем
размер окна:
и запустим
программу. Увидим следующий результат:
Как вы
понимаете, при таком подходе есть следующие существенные недостатки:
-
при
изменении размеров окна виджеты не меняют своих размеров и положения;
-
такой
интерфейс будет по разному выглядеть на разных платформах (мобильных, десктопных,
планшетных);
-
изменение
размеров отдельных виджетов может нарушить всю конструкцию интерфейса;
-
при
изменении схемы размещения элементов нам придется заново переделывать весь
интерфейс.
Сайзеры (sizers)
Для решения этих
проблем в wxPython используется
механизм на основе так называемых сайзеров – специальных объектов,
которые располагают виджеты в соответствии с предопределенной схемой (layout). Давайте на
примере посмотрим работу основных из них.
BoxSizer
Этот объект
позволяет располагать виджеты в столбец или в строку (по умолчанию в строку).
Сначала мы должны его создать:
Затем, добавить
виджеты:
vbox.Add(img1, wx.ID_ANY)
vbox.Add(img2, wx.ID_ANY)
И после этого
указать нашему окну использовать этот сайзер:
Теперь, при
запуске программы, мы увидим, что наши изображения автоматически располагаются
в строку и выравниваются по центру.
Однако, это не
лучшая практика использования сайзеров. Обычно, их связывают не с самим окном,
а с панелью, которую добавляют в это окно:
Затем, наши
виджеты связываем с этой панелью:
img1 = wx.StaticBitmap(panel, wx.ID_ANY, wx.Bitmap("images/js.jpg"))
img2 = wx.StaticBitmap(panel, wx.ID_ANY, wx.Bitmap("images/python.jpg"))
И устанавливаем
сайзер:
При запуске
видим практически ту же картину (только цвет фона немного изменился).
Если же изменить
позиционирование по вертикали:
vbox = wx.BoxSizer(wx.VERTICAL)
то наши виджеты
автоматически выстроятся по вертикали. Видите как это удобно.
Вернем
горизонтальное положение и у каждого виджета при добавлении укажем вот такой
флаг:
vbox.Add(img1, wx.ID_ANY, wx.EXPAND)
vbox.Add(img2, wx.ID_ANY, wx.EXPAND)
Это означает,
что размеры каждой ячейки сайзера должны быть максимальны и, в частности, это
предотвратит наложение виждетов друг на друга. Чтобы это лучше увидеть, давайте
создадим виджет в виде панели и расположим его по центру нашего окна:
mp = wx.Panel(panel)
mp.SetBackgroundColour('#FFFFE5')
vbox.Add(mp, wx.ID_ANY, wx.EXPAND | wx.ALL, 20)
Смотрите, мы
здесь указали два стиля: EXPAND и ALL, а затем,
поставили отступ в 20 пикселей. Благодаря стилю ALL отступы будут
применены ко всем четырем сторонам нашей панели, а EXPAND развернет эту
панель на максимально возможный размер. В результате при изменении размеров
окна панель также будет менять свои размеры:
Далее, для
демонстрации возможностей использования сайзера, построим вот такой интерфейс:
Мы здесь сначала
создаем общий сайзер
panel = wx.Panel(self)
vbox = wx.BoxSizer(wx.VERTICAL)
в котором и
будут располагаться все наши виджеты по вертикали. Затем, в первую ячейку
вкладываем еще один сайзер
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
с горизонтальным
расположением элементов. В первую ячейку помещаем обычный текст «Путь к файлу»
с отступом справа 8 пикселей, а во вторую – поле ввода:
st1 = wx.StaticText(panel, label='Путь к файлу:')
tc = wx.TextCtrl(panel)
hbox1.Add(st1, flag=wx.RIGHT, border=8)
hbox1.Add(tc, proportion=1)
Параметр proportion=1 задает
растяжение этого поля по горизонтали на максимальную длину. (По умолчанию его
значение равно 0). После формирования сайзера hbox1 добавляем его
в сайзер vbox:
vbox.Add(hbox1, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, border=10)
Устанавливаем
флаг EXPAND и отступы по 10
пикселей слева, справа и сверху.
В следующую
ячейку сайзера vbox помещаем текст «Содержимое файла», делая отступы со
всех сторон по 10 пикселей:
st2 = wx.StaticText(panel, label='Содержимое файла')
vbox.Add(st2, flag=wx.EXPAND| wx.ALL, border=10)
По аналогии,
ниже размещаем многострочное поле ввода:
tc2 = wx.TextCtrl(panel, style=wx.TE_MULTILINE)
vbox.Add(tc2, proportion=1, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.EXPAND, border=10)
И
в конце размещаем две кнопки:
btnOk = wx.Button(panel, label='Да', size=(70, 30))
btnCn = wx.Button(panel, label='Отмена', size=(70, 30))
в еще одном
вложенном горизонтальном сайзере с отступами слева 10 пикселей:
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
hbox2.Add(btnOk, flag=wx.LEFT, border=10)
hbox2.Add(btnCn, flag=wx.LEFT, border=10)
Размещаем его в
основном блоке, выравнивая по правому краю и с отступами справа и снизу по 10
пикселей:
vbox.Add(hbox2, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.RIGHT, border=10)
panel.SetSizer(vbox)
Запускаем
приложение и видим ожидаемый результат:
Давайте для
примера еще изменим размер шрифта в нашем интерфейсе. Для этого запишем такие
строчки:
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
font.SetPointSize(12)
panel.SetFont(font)
То есть, для
нашей панели мы установили шрифт в 12 пунктов. Запустим программу и видим, что
у всех виджетов шрифт был автоматически увеличен до указанного размера. При
этом наш интерфейс не «поплыл» а остался в рамках указанной разметки. Вот еще
одно наглядное преимущество сайзеров по сравнению с абсолютным
позиционированием.
На следующем
занятии мы продолжим эту тему и поговорим о других классах управления разметкой.