воскресенье, 11 октября 2009 г.

О совершенно посторонних библиотеках

Несколько постов подряд я пытаюсь показать, как можно использовать Blender не по назначению. Я стараюсь показать, что Blender так же является мощной средой программирования. Получилось мне это показать? Может, и да. Но лично я бы не поверил в то, что Blender можно использовать как независимую студию программирования, пусть даже на динамично развивающемся языке Python, если каждый рабочий скрипт начинается с такой строчки: import Blender. Подозрительно как-то, Вы не находите? Вот нашел. Итак, мы имеем скрипт, переворачивающий число – записывающий последовательность цифр в обратном порядке, если хотите. Вообще, любую самую тривиальную задачу можно сформулировать очень и очень красиво, но суть данного поста вот в чем – ответить на вопрос, возможно ли использовать Blender, как полноценная студия программирования, но при этом не использовать библиотеку Blender. Все это надо, чтобы в корне отсечь, возможности оппонента говорить, что без библиотеке Blender (import Blender) Blender (уже редактор) как студию программирования использовать нечего.
А за одно попробуем уйти от интерфейса командной строки и Blender’a – будет еще один повод поговорить о независимости Blender’а. Вы думаете невозможно? Возможно все, главное хотеть.



Как Вам интерфейс для нашей программки-переворотчика чисел? Симпатичный, правда?
Написан он с помощью библиотеки wx. Скачать эту библиотеку можно отсюда http://wxPython.org. Я использую вариант этой библиотеки для Windows, но на сайте можно найти варианты библиотеки и для других систем.
Часть кода можно увидеть не на скриншоте, но все же лучше продублирую код целиком:

import wx

'''
autor : ArkSmoke
url : http://arkpython.blogspot.com
date : 10.09
data : work with wx in Blender

'''


class MyWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, id = -1, parent = None, pos = wx.Point(0,0), size = wx.Size(250,150), title = 'My first Window with Blender')
self.panel = wx.Panel(self)
self.buttonResult = wx.Button(id=-1, label='Work',name='button_work', parent=self.panel, pos=wx.Point(100, 70), size=wx.Size(50, 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='1234567')
self.Bind(wx.EVT_BUTTON, self.OnOperationClick, self.buttonResult)

def OnOperationClick(self,event):
try:
number = int(self.textCtrlInfo.GetValue())
except (TypeError, ValueError):
print('error')
return
dt = number
td = 0
while dt > 0:
td = td * 10 +(dt % 10)
dt = dt // 10

self.textCtrlInfo.SetValue(str(td))

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

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

main()

Ну как Вам – скрипт в 46 строк уже более или менее серьезная вещь. Но давайте посмотрим, как он работает. Главная функция в нем – main(). Вы же помните логику работы всех приложений с окнами?
Пока существует окно, отслеживаются и обрабатываются все события, связанные с этим окном. К слову сказать, закрытие окна тоже событие, которое, впрочем, может не останавливать работу программы, но об этом говорить мы не станем.
Цикл обработки событий в Delphi осуществляется следующим образом:

While GetMessage(Msg,0,0,0) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;


простите не сумел удержаться, что не вставить кусок из этого языка – часто еще думаю именно на Pascal’е. Но это цикл для работы с GUI, если же Вы используете интерфейс командной строки, то для этой цели можно использовать бесконечный цикл while. Оговорив условия выхода – например, команда Quit, или номер пункта меню, соответствующий выходу из цикла – простор воображения.
Но все это я рассказал для того, чтобы Вы вспомнили принцип работы программ – работать до тех пор, пока не получена команда прекратить работу. Отсутствие события – тоже событие, которое обрабатывается определенным образом, например, переход в программу скринсайвер. Логично? Логично. Едем дальше.
Нечто подобное происходит и в Python при использовании библиотеки wx – этим занимается MainLoop(), но, для начала, нужно указать с каким именно окном мы работаем. Первая строчка функции main()

application = CalcApp()

CalcApp() – это уже наш собственный класс, в котором мы создаем окно и все события, связанные с ним. Собственно говоря, у нас есть всего одно событие – нажатие на кнопку. А вот само окно и все объекты, находящиеся на окне создавать придется соответствующими функциями: wx.Button – для кнопки и wx.TextCtrl – для текстового поля.
В классе CalcApp() основой является окно - MyWindow(). Тоже самодельный класс, именно в нем и описано все, что касается окна.

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


Остальное нужно отображения нашего окошка, чтобы оно было видимым или нет. Вы только вдумайтесь невидимые окна! Но о хулиганстве писать не буду.
Переходим к классу MyWindow(). Он состоит из описания главного окна и всех компонентов, которые располагаются на главном окне.

wx.Frame.__init__(self, id = -1, parent = None, pos = wx.Point(0,0), size = wx.Size(250,150), title = 'My first Window with Blender')
self.panel = wx.Panel(self)
self.buttonResult = wx.Button(id=-1, label='Work',name='button_work', parent=self.panel, pos=wx.Point(100, 70), size=wx.Size(50, 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='1234567')

Самое главное в этом классе – связь между событием – нажатием кнопки и самой этой кнопки.

self.Bind(wx.EVT_BUTTON, self.OnOperationClick, self.buttonResult)

Функция Bind() осуществляет эту связь. Первый параметр показывает от какого объекта приходит событие, второй параметр – какое именно событие, тритий – уточнят имя побеспокоенного объекта.
Соответственно реакция объекта располагается во втором параметре.

def OnOperationClick(self,event):
try:
number = int(self.textCtrlInfo.GetValue())
except (TypeError, ValueError):
print('error')
return
dt = number
td = 0
while dt > 0:
td = td * 10 +(dt % 10)
dt = dt // 10

self.textCtrlInfo.SetValue(str(td))

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

Вот вроде бы и все. Blender может прекрасно работать и с библиотеками, которые не входят в поставку как самого Blender’a, так и самого Python. Хотя, может быть, с библиотекой wx работать удобнее, используя некие студии, позволяющие делать интерфейсы будущих программ визуально, но любая такая студия, берущая на себя рутинную работу, добавляет свой код, который может увеличить, как объем скрипта, так и сказаться на скорости его работы. В общем, приобретая что-то, что-то обязательно теряешь. Всегда все можно прописать руками, тогда уже знаешь каждую строчку и ориентироваться легче, хотя на первый взгляд это кажется и дольше сложнее. Но, где сложнее, там достойнее.

четверг, 8 октября 2009 г.

О многообразии нахождения команд Змеем

Не знаю, как Вам, а мне всегда было интересно, как осуществляется связь между различными частями одной программы. Стоит оговориться, что здесь под программой я имел ввиду, именно код, а не исполняемый файл, или слепок памяти. А вообще с терминологий надо быть предельно аккуратными и тщательно подбирать каждое слово, претендующие на термин. Увы, аккуратностью я страдаю, особенно, когда работа нацелена на результат.
Не будем вдаваться в подробности организации пространства имен в Python. Откуда и как наш змей берет имена. Давайте лучше посмотрим, как мы можем ему показать, откуда эти имена взять. Этим занимается инструкция – import. Ну Вы с ней знакомы. А теперь давайте попробуем вызвать команду Register(), с параметром None, вместо всех запрашиваемых параметров.
Функция Register позволяет использовать None вместо ее параметров, но злоупотреблять этой возможностью мы не станем. В дальнейшем. В этом же посте мы передадим три параметра None, чтобы не возникло ошибок при работе скрипта, но при этом он ничего не сделал бы.
Итак. в прошлом посте мы просто вызвали Register с нужными параметрами. Попробуйте в том скрипте (см. предыдущий пост) заменить все в Register параметром None. Ошибки не возникло, но и работы тоже. Хотя скрипт корректно выполнил свою работу. Чтобы в этом убедиться, допишите после Register(None, None, None) инструкцию print(‘done’). Теперь просто проверим консоль и убедимся – скрипт работает верно.
Создаем новый скрипт. И пытаемся в нем вызвать Register(None, None, None). ЄAaолучилось? У меня влезла ошибка. Ну как так Register is not defined? В прошлый раз же он был вполне defined. Ну в прошлый раз у нас и билиотека Blender была imported =) импортирована. Импортируем.


import Blender
Register(None,None,None)


И снова наша функция не определена. Но она же точно есть – прошлый скрипт тому пример. Стираем наш Register(), от которого толку нет и так – он все еще не определен. И работаем с Boender. Просто попробуем найти в нем эту функцию.

Blender.Draw.Register(None,None,None)

Нашли. Заработала наша функция.
Продолжаем исследовать дальше. Сотрем Blender перед первой точной. Что за чудеса? На этот раз не определен Draw. Попробуем определить его.

import Blender
from Blender import Draw
Draw.Register(None,None,None)

Скрипт работает снова. Теперь попытаемся вызывать наш Register без всяких там точек. Для этого модифицируем 2ю строку.

import Blender
from Blender.Draw import *
Register(None,None,None)

Вот и вся любовь. Register стал определенным.

А теперь посчитайте, сколькими различными способами, мы вызвали одну функцию? Вот сколько путей сделать одно и тоже действие, и соответственно, при каждом действии код будет выглядеть иначе, хотя делать будет одно и тоже. Что выбирать, зависит только от Ваших предпочтений. Удачи Вам и до скорых встреч.

воскресенье, 4 октября 2009 г.

Необычная среда для обычных задач

Я давно перестал верить глазам своим: одну причину я уже демонстрировал, теперь хочу поговорить о еще одной. Так мне кто-нибудь сможет ответить: Blender – это редактор трехмерной графики с возможностью программирования или – среда разработки на Python с мощными графическими возможностями. До недавнего времени, я считал, что первый вариант. Однако, программирование графики – сама по себе, задача не из простых. В некоторых средах программирования разработка графики – дело, требующие хорошей подготовки. Меня поймут те, кому доводилось писать графику, используя библиотеки DirectX или OpenGL. Bender не знаком с Direct’ом, но зато очень близко знает OpenGL. Если в Blender можно программно рисовать, то с задачами на расчеты он должен справляться вполне успешно. Проверим это предположение. Напишем в Blender следующий скрипт:

dt = 1234567
td = 0

while dt > 0:
td = td * 10 +(dt % 10)
dt = dt // 10

print (td)


Как видно, простая школьная задача по программированию решена: данное число записано задам наперед. Этот скрипт продемонстрировал нам возможность решать в Blender целый класс школьных задач по программированию. В частности, мы можем узнать, является ли данное число числом-перевертышем или же является данное число счастливым числом (естественно, сделав необходимые проверки).
В этом скрипте мне не нравится один момент: всегда ли нужно, проверяя новое число, вводить его в скрипт или же есть какая другая возможность? Она есть – и не одна. Можно соорудить свой собственный интерфейс для комфортной работы со скриптом. Правда в большинстве школьных задач, даже олимпиадного уровня, не требуется создания GUI, а ввод и вывод данных осуществляется в текстовые или типизированные файлы. Так как, я уже не школьник, а Blender обладает мощными графическими возможностями, то сооружать я буду именно GUI. Придется мудрить со скриптом, создавая оболочку для контроля его выполнения. А когда нас пугала работа с API – функциями? Никогда. Даже всегда интересовала.



Довольно достойно получилось. Как Вы считаете? Скрипт выкладываю полностью – основная его ценность, конечно, не в решении задачи, которую решать в Blender’е мало кому придет голову, а в том, что основная нагрузка данного скрипта состоит именно в создании GUI.

import Blender
from Blender.BGL import *
from Blender.Draw import *

'''
autor : ArkSmoke
url : http://arkpython.blogspot.com
date : 10.09
'''
DATA = Create(1234567)
ATAD = Create(0)

EVENT_NOEVENT = 1
EVENT_EXIT = 2
EVENT_DRAW = 3

def draw():

global DATA
global EVENT_NOEVENT,EVENT_EXIT,EVENT_DRAW

glClear(GL_COLOR_BUFFER_BIT)
DATA = Number("No. of sides: ", EVENT_NOEVENT, 10, 55, 210, 18, DATA.val, 10, 99999999, "Number of sides of out polygon");
Button("Draw",EVENT_DRAW, 10, 10, 80, 18)
Button("Exit",EVENT_EXIT, 140, 10, 80, 18)

def event(evt, val):
if (evt == QKEY and not val):
Exit()

def bevent(evt):
global DATA
global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT

if (evt == EVENT_EXIT):
Exit()
elif (evt== EVENT_DRAW):
ref(DATA.val)


def ref(dt):
td = 0

while dt > 0:
td = td * 10 +(dt % 10)
dt = dt // 10
print('done ',td)

Register(draw, event, bevent)


К вопросу о GUI в Blender я еще вернусь, как и к вопросу о решении олимпиадных задач в нем. Но всему - свое время. От простого к сложному.