пятница, 18 декабря 2009 г.

еще раз о wx, но глубже

Этот пост снова посвящен работе с библиотекой wx, и неким закреплением темы рекурсии и функций – все это в одном посте, да простят мне все те, кто ждет от меня советов и трюков именно по Blender’у – Вам сюда (http://f1cd.ru/soft), а здесь я показываю азы, не столько Python’а, сколько программирования вообще – что поделать, учитель – он и в Африке учитель. Особенно приятно, что задаются вопросы именно по программированию. Поэтому, начинающие кодеры, этот пост для Вас. Тема и пример успешно обкатаны и перекочевали в Python и Blender (как среду программирования) из Pascal’я. Хотя, все, что когда-то было уже сделано, я не люблю – нет простора для воображения и творческого подхода.
В этом посте я покажу, как сделать оболочку для калькулятора, используя библиотеку wx, но сам калькулятор делать не стану. Не потому, что я такой вредный, а чтобы дать читателю простор воображения. Начнем с некоторого анализа предстоящей работы. Нам нужно будет сделать графическую оболочку, которая содержит кнопки (будем использовать термины Windows – и называть любой графический объект окном). Сами по себе кнопки на окне или фрейме интереса не представляют. Интерес представляет связь между графическим объектом и реакцией на воздействие на этот объект. Говоря проще, как кнопка будет реагировать на нажатие – вот здесь простор воображения, но Трояны писать мы не станем. Как работает библиотека, как происходит связь объектов и действий – обо всем этом я писал. Поэтому сразу переходим к обсуждению логики работы нашего имитатора калькулятора.



Он содержит поле для ввода\вывода информации и клавиши, разделить их можно на две группы:

• Клавиши для ввода данных;
• Клавиши, для выполнения операций

Операции у нас будут только унарные – нахождение факториала числа, и нахождение суммы всех положительных чисел до заданного. Работать мы будем только с целыми числами, хотя «точка» для создания дробных предусмотрена. Еще есть две кнопки для очистки текстового поля. Собственно говоря все, чтобы работать с бинарными операциями (сложение, вычитание, умножение и деление), надо просто завести еще две переменные для хранение второго операнда (чтобы знать два числа, с которыми будем работать) и переменную для хранения самой операции, которую надо будет произвести.
Клавиши ввода работают очень просто, добавляют то, что на них написано, в текстовое поле. Поэтому мы здесь пойдем на хитрость. И одну и ту же операцию назначим для ряда окон. Работают они одинаково, различие в том, что они добавляют свой Title в текстовое поле. Сгруппируем все клавиши ввода в одно событие, а потом просто, когда это событие произойдет, определим, какое окно его вызвало, и добавим Title с этого окна в поле ввода\вывода. Вроде бы все просто.
Рассмотрим весь этот алгоритм реализованным.

self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button7)

Подробнее о Bind я уже писал – смотрите мои посты. Так же Bind будет выглядеть для всех остальных окно ввода, разница только в имени окна. Теперь процедура добавления заголовка окна:

def OnDigitClick(self, event):
# Получаем список всех окно, которые содержит panel.
children = self.panel.GetChildren()
# Находим окно что, вызвало событие.
for child in children:
if child.GetId() == event.GetId():
# Знак, который нужно вывести, содержится в label'e виждета.
self.textCtrlInfo.AppendText(child.GetLabel())


Остальной код труда не составит, для понимания. Две идентичные функции у меня реализованы разными методами, чтобы показать, что получить одни и тот же результат можно получить разными путями – выбор за Вами.

# -*- coding: cp1251 -*-

import wx

class Frame_main(wx.Frame):

def __init__(self, parent):
self._init_ctrls(parent)

def _init_ctrls(self, prnt):
''' Инициализирует все видимые компоненты. '''


# Создаём главное окно, которое не может изменять размер и разворачиваться на
# весь экран.
wx.Frame.__init__(self, id=-1, parent=prnt,
pos=wx.Point(422, 270), size=wx.Size(258, 222),
style=wx.DEFAULT_FRAME_STYLE & (~(wx.MAXIMIZE_BOX | wx.RESIZE_BORDER)),
title='Calculator')
self.SetClientSize(wx.Size(250, 218))

# Созаём панель - на ней будут распологаться все остальные окна.
self.panel = wx.Panel(self)
# создаем кнопки
self.button0 = wx.Button(id=-1, label='0',
name='button_0', parent=self.panel, pos=wx.Point(8, 144),
size=wx.Size(40, 23), style=0)

self.button1 = wx.Button(id=-1, label='1',
name='button_1', parent=self.panel, pos=wx.Point(8, 112),
size=wx.Size(40, 23), style=0)

self.button2 = wx.Button(id=-1, label='2',
name='button_2', parent=self.panel, pos=wx.Point(56, 112),
size=wx.Size(40, 23), style=0)

self.button3 = wx.Button(id=-1, label='3',
name='button_3', parent=self.panel, pos=wx.Point(104, 112),
size=wx.Size(40, 23), style=0)

self.button4 = wx.Button(id=-1, label='4',
name='button_4', parent=self.panel, pos=wx.Point(8, 80),
size=wx.Size(40, 23), style=0)

self.button5 = wx.Button(id=-1, label='5',
name='button_5', parent=self.panel, pos=wx.Point(56, 80),
size=wx.Size(40, 23), style=0)

self.button6 = wx.Button(id=-1, label='6',
name='button_6', parent=self.panel, pos=wx.Point(104, 80),
size=wx.Size(40, 23), style=0)

self.button7 = wx.Button(id=-1, label='7',
name='button_7', parent=self.panel, pos=wx.Point(8, 48),
size=wx.Size(40, 23), style=0)

self.button8 = wx.Button(id=-1, label='8',
name='button_8', parent=self.panel, pos=wx.Point(56, 48),
size=wx.Size(40, 23), style=0)

self.button9 = wx.Button(id=-1, label='9',
name='button_9', parent=self.panel, pos=wx.Point(104, 48),
size=wx.Size(40, 23), style=0)

self.buttonFact = wx.Button(id=-1, label='!',
name='button_fact', parent=self.panel, pos=wx.Point(56, 144),
size=wx.Size(40, 23), style=0)

self.buttonDot = wx.Button(id=-1, label='.',
name='button_dot', parent=self.panel, pos=wx.Point(104, 144),
size=wx.Size(40, 23), style=0)

self.buttonSum = wx.Button(id=-1, label='Sum',
name='button_Sum', parent=self.panel, pos=wx.Point(152, 48),
size=wx.Size(40, 23), style=0)


self.buttonErase = wx.Button(id=-1, label='<-',
name='button_erase', parent=self.panel, pos=wx.Point(200, 48),
size=wx.Size(40, 23), style=0)

self.buttonCleanUp = wx.Button(id=-1, label='C',
name='button_clean_up', parent=self.panel, pos=wx.Point(200, 80),
size=wx.Size(40, 23), style=0)

# соам поле для ввоа\вывода результатов

self.textCtrlInfo = wx.TextCtrl(id=-1,
name='textCtrl_info', parent=self.panel, pos=wx.Point(8, 8),
size=wx.Size(232, 21), style=0, value='')

# Максимальная длина вводимой строки.
self.MaxLength = 30
self.textCtrlInfo.SetMaxLength(self.MaxLength)
self.errorStatusBar = self.CreateStatusBar()
#==========================
# Устанавливаем обработчики событий
#==========================
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button0)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button1)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button2)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button3)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button4)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button5)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button6)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button7)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button8)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.button9)
self.Bind(wx.EVT_BUTTON, self.OnDigitClick, self.buttonDot)
self.Bind(wx.EVT_BUTTON, self.OnButtonSumClick, self.buttonSum)
self.Bind(wx.EVT_BUTTON, self.OnOButtonFactClick, self.buttonFact)
self.Bind(wx.EVT_BUTTON, self.OnButtonEraseClick, self.buttonErase)
self.Bind(wx.EVT_BUTTON, self.OnButtonCleanUpClick, self.buttonCleanUp)
self.Bind(wx.EVT_TEXT, self.OnTextCtrlInfoText, self.textCtrlInfo)

def OnDigitClick(self, event):
# Получаем список всех окно, которые содержит panel.
children = self.panel.GetChildren()
# Находим окно что, который вызвал событие.
for child in children:
if child.GetId() == event.GetId():
# Знак, который нужно вывести, содержится в label'e виждета.
self.textCtrlInfo.AppendText(child.GetLabel())

def OnButtonEraseClick(self, event):
''' Стираем последний символ в строке.'''
oldValue = self.textCtrlInfo.GetValue()
self.textCtrlInfo.SetValue(oldValue[:len(oldValue) - 1])


def OnButtonCleanUpClick(self, event):
''' Очищаем все данные. '''
self.textCtrlInfo.SetValue('0')
self.errorStatusBar.SetStatusText('')

def OnTextCtrlInfoText(self, event):
''' Очищает строку сообщений об ошибках как только начинаем ввод нового
числа. '''
self.errorStatusBar.SetStatusText('')

def OnOButtonFactClick(self, event):
''' считаем факториал числа '''
try:
number = float(self.textCtrlInfo.GetValue())
# Не выводим '.', если число целое.
number = fact(number)
number = NormalizeNumber(number)
self.textCtrlInfo.SetValue(str(number))
except (TypeError, ValueError):
self.errorStatusBar.SetStatusText

def OnButtonSumClick(self, event):
''' Меняем знак числа. '''
try:
number = float(self.textCtrlInfo.GetValue())
# Не выводим '.', если число целое.
sum = 0
while number > 0:
sum = sum + number
number = number -1

number = sum
number = NormalizeNumber(number)
self.textCtrlInfo.SetValue(str(number))
except (TypeError, ValueError):
self.errorStatusBar.SetStatusText('ОШИБКА! Введите число правильно.')

def NormalizeNumber(number):
'''
Превращает float в int, если число целое.
'''
if not (number % 1):
number = int(number)
return number

def fact(n):
if n == 0:
result = 1
else:
result = n*fact(n-1)
return result

#======================================================================
#
#=======================основной блок==================================
#
#======================================================================

class CalcApp(wx.App):
def OnInit(self):
self.main = Frame_main(None)
self.main.Show()
self.SetTopWindow(self.main)
return True

def main():
application = CalcApp(0)
application.MainLoop()

if __name__ == '__main__':
main()


На скриншоте привожу самый спорный пример кода.



Обратите на него внимание. Здесь показано, как строить классы. А пока все. До новых встреч. И на прощание, в школы с 2010 год приходит Linux – очень бы хотел услышать Ваши мысли по этому поводу. Удачного плавания в мире бесплатного софта. До скорых встеч.

Комментариев нет:

Отправить комментарий