В данной статье речь пойдет о создании простейшей игры с помощью wxPython. Причем упор сделан далеко не на интересный геймплей, а на основные моменты организации некоторых элементов интерфейса (виджетов) и обработчиков событий.
wxPython — это кросс-платформенная библиотека инструментов для разработки кроссплатформенных приложений на языке программирования Python, в частности для построения графического интерфейса пользователя (GUI).
Как установить библиотеку написано здесь.
Суть будущего приложения удивительно проста и заключается в следующем — есть слайдер с численными значениями, есть таймер и есть огромное желание двигать этот слайдер вправо и влево до тех пор пока не сработает таймер. Числа, которые пробегаются при движении слайдера складываются, а полученная сумма — это и есть итоговый результат игры. Несомненно, идея игры далека от идеала, но зато позволяет оценить насколько просто можно манипулировать виджетами wxPython.
Итак, первые две строчки кода приложения совершенно стандартны:
Далее, если позволите, начну описание кода немного нестандартным способом — с конца.
Если программа запускается не как импортированный модуль, то выполняется функция main(). Что же она делает? Ниже определение этой функции:
Переменная app — экземпляр класса App. Этот класс наследуется от класса wx.App пакета wx и непосредственно отвечает за работу нашего графического приложения. С помощью выражения app.MainLoop() мы создаем жизненный цикл нашего приложения, в течение которого программа ждет наших действий, реагирует на них должным образом, ну и в общем — работает!
Что из себя представляет класс App()? Вот он:
Как видим строк совсем мало благодаря тому, что все свойства и методы наследуются от класса wx.App. Мы не переопределяли метод __init__, а просто написали, что же должно произойти в момент вызова класса. Итак в момент инициализации класса App происходит следующее: создается фрейм (окно) с заданными параметрами. Создается оно с помощью создания экземпляра класса Frame, указания этому экземпляру появиться на экране и быть при этом нашим главным окном. Всё понятно кроме одного. Что же за класс Frame такой?
Frame — это самый массивный класс этого приложения. Он отвечает за всю логику нашей игры — за таймеры, за нажатия кнопок, за отображение этих кнопок в конце концов!
Далее приведен полный код класса, он хоть и достаточно длинный, но многие фрагменты в нем аналогичны друг другу:
Класс Frame расширяет класс wx.Frame библиотеки wx. В начале записаны переменные класса, зачем они нужны — указано в комментариях. Теперь разберем основные моменты класса.
Посмотрим на то как внутри метода App создавался экземпляр класса Frame:
В параметрах классу передаем родителя экземпляра(в данном случае None, так как создаем главное окно), id экземпляра("-1" автоматически проставляет id), строку заголовока окна, позицию левого верхнего угла, размер окна.
Все эти параметры передаются методу __init__ класса Frame.
Далее создаем панель, на которую будем добавлять элементы интерфейса с указанием родителя — в данном случае self:
Теперь уже можно добавлять виджеты на только что созданную панель:
Всего тут 4 различных виджета: текст, кнопка, слайдер и таймер. Они создаются как экземпляры классов библиотеки wx (wx.TextCtrl, wx.Button, wx.Slider, wx.Timer соответственно) c параметрами, практически совпадающими с теми, которые передавались для инициализации класса Frame — родитель, id, положение, размер. Для кнопок дополнительно указан параметр label(надпись на кнопке). Для слайдера — минимальное и максимальное численно значение(1 и 100 соответственно), а также style со значениями констант библиотеки wx.
Теперь вешаем на созданные элементы обработчики событий:
Тут всё просто — сначала пишем тип события, потом название функции, которая будет это событие обрабатывать, и, наконец, элемент, который это событие вызывает.
Наверное, самое интересное — это обработка события движения слайдера — функция OnSlide. Она до неприличия короткая — всего 2 строчки:
Помимо ключевого слова self мы передаем в нее переменную event, содержащую в себе информацию о событии. Это стандартный способ объявления обработчиков событий. Функция не делает ничего больше, кроме как добавляет текущее значение слайдера к общей сумме.
При нажатии на кнопку goButton вызывается следующая функция:
Игра начинается!!! Вообще, играть долго в это невозможно:) Хотя есть повод поразмыслить каким образом двигать слайдер, чтобы побольше очков набрать.
Наконец, когда истекает время основного таймера, мы должны завершить игру и показать набранное количество очков. За это отвечает функция OnUpdate:
Сначала останавливаем таймеры, затем переменной заносим значение набранной суммы в переменную класса last_result, обнуляем сумму, восстанавливаем значение обратного отсчета, скрываем кнопку stop и показываем кнопку go. В завершении показываем всплывающее окошко с набранными очками.
Пара других обработчиков описываться подробно не будут, один из них OnStop — просто прерывает игру, а другой OnCount — отвечает за отображение обратного отсчета до конца основного времени.
Целью статьи не было открыть что-либо новое, а просто познакомить с wxPython тех, кто с ним еще не сталкивался. Все значения позиций и размеров в статье абсолютны, гораздо удобней использовать так называемые сайзеры(sizers), которые позволят масштабировать приложение.
Чтобы запустить игру можно просто скопировать в один питоновский файлик первые 5 кодовых врезок (в последовательности 1,4,5,2,3 ) или скачать отсюда
В написании статьи очень помог потрясающий ресурс:
wxPyWiki
а также книга wxPython in Action
Спасибо за внимание.
wxPython — это кросс-платформенная библиотека инструментов для разработки кроссплатформенных приложений на языке программирования Python, в частности для построения графического интерфейса пользователя (GUI).
Как установить библиотеку написано здесь.
Суть будущего приложения удивительно проста и заключается в следующем — есть слайдер с численными значениями, есть таймер и есть огромное желание двигать этот слайдер вправо и влево до тех пор пока не сработает таймер. Числа, которые пробегаются при движении слайдера складываются, а полученная сумма — это и есть итоговый результат игры. Несомненно, идея игры далека от идеала, но зато позволяет оценить насколько просто можно манипулировать виджетами wxPython.
Итак, первые две строчки кода приложения совершенно стандартны:
#coding:utf-8<br/>
import wx # импорт предварительно установленной библиотеки wxPython
Далее, если позволите, начну описание кода немного нестандартным способом — с конца.
if __name__=='__main__':<br/>
main()
Если программа запускается не как импортированный модуль, то выполняется функция main(). Что же она делает? Ниже определение этой функции:
def main():<br/>
app = App()<br/>
app.MainLoop()
Переменная app — экземпляр класса App. Этот класс наследуется от класса wx.App пакета wx и непосредственно отвечает за работу нашего графического приложения. С помощью выражения app.MainLoop() мы создаем жизненный цикл нашего приложения, в течение которого программа ждет наших действий, реагирует на них должным образом, ну и в общем — работает!
Что из себя представляет класс App()? Вот он:
class App(wx.App):<br/>
def OnInit(self):<br/>
self.frame = Frame(None,-1,"Move It",(0,0),(500,300)) # создание окна<br/>
self.frame.Show(True) # отображение окна<br/>
self.SetTopWindow(self.frame) # указываем, что только что созданное окно - главное<br/>
return True # ну не False же возвращать, правда?:)
Как видим строк совсем мало благодаря тому, что все свойства и методы наследуются от класса wx.App. Мы не переопределяли метод __init__, а просто написали, что же должно произойти в момент вызова класса. Итак в момент инициализации класса App происходит следующее: создается фрейм (окно) с заданными параметрами. Создается оно с помощью создания экземпляра класса Frame, указания этому экземпляру появиться на экране и быть при этом нашим главным окном. Всё понятно кроме одного. Что же за класс Frame такой?
Frame — это самый массивный класс этого приложения. Он отвечает за всю логику нашей игры — за таймеры, за нажатия кнопок, за отображение этих кнопок в конце концов!
Далее приведен полный код класса, он хоть и достаточно длинный, но многие фрагменты в нем аналогичны друг другу:
class Frame(wx.Frame):<br/>
static_duration = 10 # значение таймера <br/>
duration = static_duration # текущее значение таймера<br/>
sum = 0 # количество набранных очков<br/>
last_result = 0 # результат последней игры<br/>
str = '' # строка для удобства вывода результатов<br/>
<br/>
def __init__(self,parent = None,id = -1,title = '',pos = (0,0),size = (300,200)):<br/>
wx.Frame.__init__(self,parent,id,title,pos,size)<br/>
self.panel = wx.Panel(self)<br/>
<br/>
self.text = wx.TextCtrl(self.panel,-1,'Пока нет результатов',pos=(10,210),size=(480,30))<br/>
self.text.SetBackgroundColour('#DDFFEE')<br/>
self.text.SetEditable(False)<br/>
<br/>
self.goButton = wx.Button(self.panel,label="ready! steady! GO!",id=-1,pos=(150,50),size=(200,40))<br/>
self.Bind(wx.EVT_BUTTON,self.OnGo,self.goButton)<br/>
<br/>
self.stopButton = wx.Button(self.panel,label="stop",id=-1,pos=(150,50),size=(200,40))<br/>
self.Bind(wx.EVT_BUTTON,self.OnStop,self.stopButton)<br/>
self.stopButton.Show(False)<br/>
<br/>
self.slider = wx.Slider(self.panel,-1,50,1,100,pos=(100,150),size = (300,-1),style=wx.SL_AUTOTICKS|wx.SL_LABELS)<br/>
self.slider.SetTickFreq(5,1)<br/>
self.Bind(wx.EVT_SLIDER,self.OnSlide,self.slider)<br/>
<br/>
self.timer = wx.Timer(self,-1)<br/>
self.Bind(wx.EVT_TIMER, self.OnUpdate, self.timer)<br/>
<br/>
self.timerCounter = wx.Timer(self,-1)<br/>
self.Bind(wx.EVT_TIMER, self.OnCount, self.timerCounter)<br/>
<br/>
self.count = wx.TextCtrl(self.panel,-1,'',pos=(235,100),size=(30,30))<br/>
self.count.SetBackgroundColour('#DDFFEE')<br/>
self.count.SetEditable(False)<br/>
self.count.Show(False)<br/>
<br/>
def OnGo(self,event):<br/>
self.count.Show(True)<br/>
self.stopButton.Show(True)<br/>
self.goButton.Show(False)<br/>
self.sum = 0<br/>
self.timer.Start(self.static_duration*1000)<br/>
self.timerCounter.Start(1000)<br/>
<br/>
def OnUpdate(self,event):<br/>
self.timerCounter.Stop()<br/>
self.timer.Stop()<br/>
self.count.SetValue(str(0))<br/>
self.str+=str(self.sum)+' | '<br/>
if len(self.str)>60:<br/>
self.str = str(self.last_result)+' | '+str(self.sum)+' | '<br/>
self.text.SetValue(self.str)<br/>
self.last_result = self.sum<br/>
self.sum = 0<br/>
self.duration = self.static_duration<br/>
self.goButton.Show(True)<br/>
self.stopButton.Show(False)<br/>
wx.MessageBox("Ваш результат "+str(self.last_result))<br/>
<br/>
def OnCount(self,event):<br/>
self.duration-=1<br/>
self.count.SetValue(str(self.duration))<br/>
<br/>
def OnStop(self,event):<br/>
self.timerCounter.Stop()<br/>
self.timer.Stop()<br/>
self.sum = 0<br/>
self.stopButton.Show(False)<br/>
self.goButton.Show(True)<br/>
self.duration = self.static_duration<br/>
<br/>
def OnSlide(self,event):<br/>
self.sum+=self.slider.GetValue()
Класс Frame расширяет класс wx.Frame библиотеки wx. В начале записаны переменные класса, зачем они нужны — указано в комментариях. Теперь разберем основные моменты класса.
Посмотрим на то как внутри метода App создавался экземпляр класса Frame:
self.frame = Frame(None,-1,"Move It",(0,0),(500,300))
В параметрах классу передаем родителя экземпляра(в данном случае None, так как создаем главное окно), id экземпляра("-1" автоматически проставляет id), строку заголовока окна, позицию левого верхнего угла, размер окна.
Все эти параметры передаются методу __init__ класса Frame.
Далее создаем панель, на которую будем добавлять элементы интерфейса с указанием родителя — в данном случае self:
self.panel = wx.Panel(self)
Теперь уже можно добавлять виджеты на только что созданную панель:
self.text = wx.TextCtrl(self.panel,-1,'Пока нет результатов',pos=(10,210),size=(480,30)) <br/>
self.goButton = wx.Button(self.panel,label="ready! steady! GO!",id=-1,pos=(150,50),size=(200,40)) <br/>
self.stopButton = wx.Button(self.panel,label="stop",id=-1,pos=(150,50),size=(200,40)) <br/>
self.slider = wx.Slider(self.panel,-1,50,1,100,pos=(100,150),size = (300,-1),style=wx.SL_AUTOTICKS|wx.SL_LABELS) <br/>
self.timer = wx.Timer(self,-1)<br/>
self.timerCounter = wx.Timer(self,-1)<br/>
self.count = wx.TextCtrl(self.panel,-1,'',pos=(235,100),size=(30,30))
Всего тут 4 различных виджета: текст, кнопка, слайдер и таймер. Они создаются как экземпляры классов библиотеки wx (wx.TextCtrl, wx.Button, wx.Slider, wx.Timer соответственно) c параметрами, практически совпадающими с теми, которые передавались для инициализации класса Frame — родитель, id, положение, размер. Для кнопок дополнительно указан параметр label(надпись на кнопке). Для слайдера — минимальное и максимальное численно значение(1 и 100 соответственно), а также style со значениями констант библиотеки wx.
Теперь вешаем на созданные элементы обработчики событий:
self.Bind(wx.EVT_BUTTON,self.OnGo,self.goButton)<br/>
self.Bind(wx.EVT_BUTTON,self.OnStop,self.stopButton)<br/>
self.Bind(wx.EVT_SLIDER,self.OnSlide,self.slider)<br/>
self.Bind(wx.EVT_TIMER, self.OnUpdate, self.timer)<br/>
self.Bind(wx.EVT_TIMER, self.OnCount, self.timerCounter)
Тут всё просто — сначала пишем тип события, потом название функции, которая будет это событие обрабатывать, и, наконец, элемент, который это событие вызывает.
Наверное, самое интересное — это обработка события движения слайдера — функция OnSlide. Она до неприличия короткая — всего 2 строчки:
def OnSlide(self,event):<br/>
self.sum+=self.slider.GetValue()
Помимо ключевого слова self мы передаем в нее переменную event, содержащую в себе информацию о событии. Это стандартный способ объявления обработчиков событий. Функция не делает ничего больше, кроме как добавляет текущее значение слайдера к общей сумме.
При нажатии на кнопку goButton вызывается следующая функция:
def OnGo(self,event):<br/>
self.count.Show(True) # показываем обратный отсчет<br/>
self.stopButton.Show(True) # показываем кнопку stop<br/>
self.goButton.Show(False) # скрываем кнопку start<br/>
self.sum = 0 # обнуляем сумму очков<br/>
self.timer.Start(self.static_duration*1000) # запускаем основной таймер<br/>
self.timerCounter.Start(1000) # запускаем таймер обратного отсчета до конца игры
Игра начинается!!! Вообще, играть долго в это невозможно:) Хотя есть повод поразмыслить каким образом двигать слайдер, чтобы побольше очков набрать.
Наконец, когда истекает время основного таймера, мы должны завершить игру и показать набранное количество очков. За это отвечает функция OnUpdate:
def OnUpdate(self,event):<br/>
self.timerCounter.Stop()<br/>
self.timer.Stop()<br/>
self.last_result = self.sum<br/>
self.sum = 0<br/>
self.duration = self.static_duration<br/>
self.goButton.Show(True)<br/>
self.stopButton.Show(False)<br/>
wx.MessageBox("Ваш результат "+str(self.last_result))
Сначала останавливаем таймеры, затем переменной заносим значение набранной суммы в переменную класса last_result, обнуляем сумму, восстанавливаем значение обратного отсчета, скрываем кнопку stop и показываем кнопку go. В завершении показываем всплывающее окошко с набранными очками.
Пара других обработчиков описываться подробно не будут, один из них OnStop — просто прерывает игру, а другой OnCount — отвечает за отображение обратного отсчета до конца основного времени.
Целью статьи не было открыть что-либо новое, а просто познакомить с wxPython тех, кто с ним еще не сталкивался. Все значения позиций и размеров в статье абсолютны, гораздо удобней использовать так называемые сайзеры(sizers), которые позволят масштабировать приложение.
Чтобы запустить игру можно просто скопировать в один питоновский файлик первые 5 кодовых врезок (в последовательности 1,4,5,2,3 ) или скачать отсюда
В написании статьи очень помог потрясающий ресурс:
wxPyWiki
а также книга wxPython in Action
Спасибо за внимание.