Базовые виджеты wxPython

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

В самом начале у нас будет вот такая заготовка:

import wx
 
class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        super().__init__(parent, title=title, size=(600, 450))
 
app = wx.App()
frame = MyFrame(None, 'wxPython')
frame.Show()
app.MainLoop()

И в окно MyFrame будем постепенно добавлять различные элементы управления. Начнем со статусной строки. Она создается с помощью метода CreateStatusBar класса Frame:

        self.sb = self.CreateStatusBar()
        self.sb.SetStatusText("Текст в статусной строке")

Здесь метод SetStatusText позволяет поместить текстовую информацию в статусную строку. Если теперь запустить программу, то увидим окно со статусной строкой и текстом «Текст в статусной строке».

Разумеется, это простейший пример использования данного виджета. Полный набор методов и свойств объекта wx.StatusBar можно найти на странице официальной документации:

docs.wxpython.org/wx.StatusBar.html

Далее, разместим в окне панели и сайзер BoxSizer, который свяжем с этой панелью:

        panel = wx.Panel(self)
        vbox = wx.BoxSizer(wx.VERTICAL)
        panel.SetSizer(vbox)

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

wx.StaticText

Создадим следующий виджет wx.StaticText, который отображает статический текст в заданной позиции. Общий конструктор этого класса, следующий:

StaticText(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, name=StaticTextNameStr)

  • parent – родитель, на котором размещается виджет;
  • id – идентификатор элемента;
  • label – текст, отображаемый этим виджетом;
  • pos, size – позиция и размер виджета (в пикселах);
  • style – стилизация элемента.

В нашем случае мы его создадим так:

st = wx.StaticText(panel, label="Адрес: ")

и добавим в сайзер BoxSizer, который автоматически разместит его в нужной позиции нашего окна:

vbox.Add(st, flag=wx.ALL, border=10)

При запуске программы увидим текст «Адрес: » в верхнем левом угле фрейма. Полная документация данного виджета доступна на странице:

https://docs.wxpython.org/wx.StaticText.html

wx.TextCtrl

Далее разместим текстовое поле ввода. Оно создается с помощью класса wx.TextCtrl и имеет такой конструктор:

TextCtrl(parent, id=ID_ANY, value="", pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=TextCtrlNameStr)

Все параметры здесь вам уже знакомы, поэтому перейдем сразу к примеру его использования. В нашей программе мы его создадим так:

inp = wx.TextCtrl(panel, value="г. Москва")

и поместим следующим элементом в BoxSizer:

vbox.Add(inp, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=10)

При запуске программы увидим поле ввода с начальным текстом «г. Москва». Документацию по этому виджету смотрите здесь:

docs.wxpython.org/wx.TextCtrl.html

wx.StaticLine

После текстового поля ввода разместим горизонтальную черту (линию), которая создается с помощью конструктора

StaticLine(parent, id=ID_ANY, pos=DefaultPosition, size=DefaultSize, style=LI_HORIZONTAL, name=StaticLineNameStr)

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

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

Смотрите, мы здесь в одной строке сразу создали StaticLine и поместили в BoxSizer. Так тоже можно делать, если не предполагается дополнительной работы с виджетом. Ну а его подробная документация доступна по адресу:

https://docs.wxpython.org/wx.StaticLine.html

wx.ToggleButton

Данный класс представляет собой кнопки, которые можно переводить в нажатое положение и возвращать в исходное (отжатое). Чтобы создать вот такую конфигурацию расположения элементов, воспользуемся GridBagSizer. Но вначале создадим кнопки-переключатели. Конструктор следующий:

ToggleButton(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, val=DefaultValidator, name=CheckBoxNameStr)

В нашей программе мы их создадим так:

rtb = wx.ToggleButton(panel, id = B_RED, label='red', pos=(20, 25))
gtb = wx.ToggleButton(panel, id = B_GREEN, label='green', pos=(20, 60))
btb = wx.ToggleButton(panel, id = B_BLUE, label='blue', pos=(20, 100))

А вначале объявим эти уникальные id:

B_RED = 1
B_GREEN = 2
B_BLUE = 3

Затем, создадим специальный объект

self.col = wx.Colour(0, 0, 0)

для работы с цветом панели (ее фон будет меняться в зависимости от нажатых кнопок-переключателей). И саму панель:

self.pn = wx.Panel(panel)
self.pn.SetBackgroundColour(self.col.GetAsString())

Мы здесь вызываем метод SetBackgroundColour чтобы установить начальный фон у панели. Теперь, все созданные виджеты следует добавить в GridBagSizer:

vb1 = wx.GridBagSizer(10, 10)
vb1.Add(rtb, (0, 0)); vb1.Add(gtb, (1, 0)); vb1.Add(btb, (2, 0))
vb1.Add(self.pn, (0, 1), (3, 1), flag=wx.EXPAND)
vb1.AddGrowableCol(1)

В конструкторе этого сайзера мы указываем отступы между ячейками в 10 пикселей по вертикали и горизонтали, затем в столбцы добавляем три кнопки-переключатели, а затем, создаем еще одну ячейку, которая располагается в следующем столбце и охватывает три ячейки по вертикали. Устанавливаем флаг wx.EXPAND, чтобы растянуть ее на всю доступную ширину и высоту и еще устанавливаем proportion=1 для этого второго столбца с помощью метода AddGrowableCol. О том как в деталях работает этот сайзер мы с вами уже говорили на одном из предыдущих занятий.

Добавим эту заготовку следующим элементом в BoxSizer:

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

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

Если пощелкать по кнопкам, то они будут то нажиматься, то отжиматься. Далее, ниже добавим еще горизонтальную разделительную линию:

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

Давайте теперь добавим функционал нашим кнопкам и свяжем с ними обработчик onToggle:

rtb.Bind(wx.EVT_TOGGLEBUTTON, self.onToggle)
gtb.Bind(wx.EVT_TOGGLEBUTTON, self.onToggle)
btb.Bind(wx.EVT_TOGGLEBUTTON, self.onToggle)

А сам метод реализуем следующим образом:

    def onToggle(self, e):
        btn = e.GetEventObject()
        val = 255 if btn.GetValue() else 0
        id = btn.GetId()
 
        r = self.col.Red()
        g = self.col.Green()
        b = self.col.Blue()
 
        if id == B_RED:
            self.col.Set(val, g, b)
        elif id == B_BLUE:
            self.col.Set(r, g, val)
        elif id == B_GREEN:
            self.col.Set(r, val, b)
 
        self.pn.SetBackgroundColour(self.col)
        self.pn.Refresh()

Здесь первой строкой мы получаем ссылку на объект, от которого пришло событие, т.е. одну из трех кнопок-переключаетлей. Затем, с помощью метода GetValue определяем: нажата ли кнопка. Данный метод возвращает True, если кнопка нажата и False в противном случае. Используя тернарный условный оператор, переменной val присваивается 255 для нажатой кнопки и 0 для отжатой. Наконец, в переменной id сохраняет id текущей кнопки.

Следующими тремя строчками получаем текущие значения r, g и b объекта wx.Colour и далее проверяем: если нажата (или отжата) кнопка для красной компоненты, то меняем этот цвет в нашем объекте. Аналогично для двух других цветов: синего и зеленого. После этого панели применяем новый полученный цвет и обновляем ее (перерисовываем) методом Refresh.

Если теперь запустить программу и понажимать на наши кнопки, то фон панели будет меняться. Подробную документацию по wx.ToggleButton смотрите на странице:

https://docs.wxpython.org/wx.ToggleButton.html

wx.StaticBox

Далее мы создадим вот такой фрагмент интерфейса (см. рисунок ниже). И начнем с виджета StaticBox, который представляет собой рамку с неким заголовком:

StaticBox(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, name=StaticBoxNameStr)

Эту рамку (а также радио-кнопки) мы разместим на отдельной панели:

pn2 = wx.Panel(panel)
wx.StaticBox(pn2, label="Ваш пол:", size=(150, 50))

Мы здесь указываем заголовок «Ваш пол:» и размер рамки 150х50 пикселей.

Документацию по виджету смотрите на странице:

docs.wxpython.org/wx.StaticBox.html

wx.RadioButton

Далее на панели pn2 разместим радио-кнопки и укажем их положение так, чтобы они оказались внутри StaticBox:

rd1 = wx.RadioButton(pn2, label="Муж", pos=(10, 20), style=wx.RB_GROUP)
rd2 = wx.RadioButton(pn2, label="Жен", pos=(100, 20))

Здесь style=wx.RB_GROUP у первой кнопки означает начало группы для RadioButton. Общий синтаксис конструктора данного виджета, следующий:

RadioButton(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=RadioButtonNameStr)

После создания кнопок, добавим панель pn2 со всеми элементами в горизонтальный BoxSizer:

vb2 = wx.BoxSizer(wx.HORIZONTAL)
vb2.Add(pn2)

Полная документация по RadioButton доступна на странице:

https://docs.wxpython.org/wx.RadioButton.html

wx.CheckBox

Следующий элемент, который мы разместим – это кнопка с флажком (CheckBox). Синтаксис ее конструктора такой:

CheckBox(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=CheckBoxNameStr)

Создадим этот элемент и установим по умолчанию флажок:

agree = wx.CheckBox(panel, label="Согласен на обработку")
agree.SetValue(True)

Добавим этот элемент в горизонтальный сайзер:

vb2.Add(agree, flag = wx.LEFT|wx.TOP, border=20)

Документация виджета на странице:

docs.wxpython.org/wx.CheckBox.html

wx.ComboBox и wx.SpinCtrl

Следующий виджет ComboBox представляет собой выпадающий список и может быть создан со следующими параметрами:

ComboBox(parent, id=ID_ANY, value="", pos=DefaultPosition, size=DefaultSize, choices=[], style=0, validator=DefaultValidator, name=ComboBoxNameStr)

Здесь value – это начальное выбранное значение списка, а choices – упорядоченный список строк, которые и формируют данный список. Создадим его в нашей программе следующим образом:

links = ["Телефон", "E-mail", "Skype"]
cb = wx.ComboBox(panel, pos=(50, 30), choices=links, style=wx.CB_READONLY)
cb.SetSelection(0)

Мы здесь сначала объявили список из строк, затем вызвали конструктор класса ComboBox и через параметр choices указали ссылку на список. Также установили стиль CB_READONLY (только для чтения), т.е. элементы выпадающего списка можно только выбирать, но не вводить с клавиатуры. Последняя строчка определяет индекс выбранного элемента по умолчанию: 0 – первая строка «Телефон».

Подробная документация на странице:

https://docs.wxpython.org/wx.ComboBox.html

Следующий элемент SpinCtrl создает текстовое поле для ввода чисел со стрелками вверх/вниз:

SpinCtrl(parent, id=ID_ANY, value="", pos=DefaultPosition, size=DefaultSize, style=SP_ARROW_KEYS, min=0, max=100, initial=0, name="wxSpinCtrl")

Параметры его конструктора вполне очевидны и в программе мы его создадим так:

sc = wx.SpinCtrl(panel, value='0', min=-100, max=100)

то есть, начальное значение будет равно 0, минимальное -100, максимальное +100. Документация по виджету на странице:

https://docs.wxpython.org/wx.SpinCtrl.html

Добавим эти два элемента в горизонтальный сайзер:

vb2.Add(cb, flag=wx.LEFT | wx.TOP, border=15)
vb2.Add(sc, flag=wx.LEFT | wx.TOP, border=15)

А, затем, добавим его в горизонтальный BoxSizer:

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

Запустим программу и увидим вот такое окно:

wx.Gauge

Следующий элемент прогресс бар определяется классом Gauge и имеет следующий конструктор:

Gauge(parent, id=ID_ANY, range=100, pos=DefaultPosition, size=DefaultSize, style=GA_HORIZONTAL, validator=DefaultValidator, name=GaugeNameStr)

Здесь параметр range определяет числовое значение, которое будет ассоциироваться с полной полосой (со 100%). Создадим этот виджет следующим образом:

self.gauge = wx.Gauge(panel, range=100)

и добавим его в следующую ячейку BoxSizer:

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

Подробная документация на странице:

https://docs.wxpython.org/wx.Gauge.html

wx.Button

Под прогресс баром разместим две кнопки «Старт» и «Стоп», используя класс Button, который мы уже много раз использовали. Синтаксис его конструктора следующий:

Button(parent, id=ID_ANY, label="", pos=DefaultPosition, size=DefaultSize, style=0, validator=DefaultValidator, name=ButtonNameStr)

Вызовем этот конструктор в нашей программе со следующими параметрами:

bStart = wx.Button(panel, label="Старт")
bStop = wx.Button(panel, label="Стоп")

и разместим их в нашем окне с центрированием по горизонтали:

hb3 = wx.BoxSizer()
hb3.AddMany([(bStart, 0, wx.LEFT|wx.RIGHT, 10), (bStop, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM, 10)])
vbox.Add(hb3, flag=wx.ALIGN_CENTRE)

И ниже горизонтальную разделительную линию:

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

При запуске программы увидим следующий результат:

Подробную документацию по кнопкам смотрите на странице:

https://docs.wxpython.org/wx.Button.html

Давайте теперь на эти кнопки повесим обработчики onStart и onStop, чтобы при нажатии на кнопку «Старт» у нас заполнялась полоса прогресс бара:

bStart.Bind(wx.EVT_BUTTON, self.onStart)
bStop.Bind(wx.EVT_BUTTON, self.onStop)

Здесь нам понадобится вспомогательный объект

self.timer = wx.Timer(self)

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

self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)

и в этом обработчике будем увеличивать переменную:

self.count = 0
self.gauge.SetValue(self.count)

на единицу и менять величину прогресс бара:

    def OnTimer(self, e):
        self.count = self.count + 1
        self.gauge.SetValue(self.count)
 
        if self.count >= 100:
            self.timer.Stop()

Как только дойдем до предельного значения 100, то таймер будет остановлен методом Stop.

Теперь все готово для написания обработчиков onStart и onStop:

    def onStop(self, e):
        self.timer.Stop()
        self.count = 0
 
    def onStart(self, e):
        if self.count > 100:
            return
 
        self.timer.Start(100)

Запускаем программу и тестируем действие кнопок.

wx.Slider

Последний виджет, который мы рассмотрим на данном занятии – это Slider, создающий бегунок. Конструктор его класса следующий:

Slider(parent, id=ID_ANY, value=0, minValue=0, maxValue=100, pos=DefaultPosition, size=DefaultSize, style=SL_HORIZONTAL, validator=DefaultValidator, name=SliderNameStr)

В нашем окне мы его определим вот с такими параметрами:

sld = wx.Slider(panel, value=200, minValue=150, maxValue=500, style=wx.SL_HORIZONTAL)

Добавим его в наш интерфейс:

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

И свяжем с ним обработчик OnSliderScroll:

sld.Bind(wx.EVT_SCROLL, self.OnSliderScroll)

Через этот метод мы будем отображать значение слайдера в статусной строке:

    def OnSliderScroll(self, e):
        val = e.GetEventObject().GetValue()
        self.sb.SetStatusText("Slider: "+str(val))

Запускаем программу и видим все наши виджеты и с некоторыми из них можем взаимодействовать. Подробная документация по Slider доступна на странице:

https://docs.wxpython.org/wx.Slider.html

Конечно, это только некоторые, базовые виджеты wxPython. Полный их список можно найти вот на этой странице

https://docs.wxpython.org/wx.Control.html

в разделе «Субклассы» (Subclasses). Их принцип работы и взаимодействия с ними похож на те, что мы рассмотрели на этом занятии. Поэтому, я думаю, их применение на практике не вызовет у вас больших затружнений.

Видео по теме