Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > Skyggedans

Оценить эту запись

SWPy - синхронно-асинхронный Flash проектор для Python

Запись от Skyggedans размещена 09.10.2009 в 20:06
Обновил(-а) Skyggedans 10.10.2009 в 12:07

Доброго времени суток, дорогая общественность!

Этот блог я хочу посвятить своему проекту SWPy. SWPy расшифровывается как ScreenWeaver for Python, и является портом довольно известного проекта ScreenWeaver HX, за авторством еще более известного Николя Канасье, который также является создателем языка haXe и виртуальной машины Neko.
Побуждающим фактором к началу работ над данным проектом послужил мой повышенный интерес к замечательному языку Python и связанность с ним моей рабочей деятельности, а так же мой интерес к Flash-технологиям и к становящейся все более популярной теме RIA приложений.
Не секрет, что при всех своих достоинствах Python до сих пор не имеет вменяемого GUI Toolkit-а, за исключением разве что PyQt. С мультимедиа возможностями там тоже дела обстоят не менее грустно.

Итак, как это все должно работать:
Как уже упоминалось, за основу берется уже упомянутый тут ScreenWeaver HX от француза, точнее только те модуля, которые отвечают за инициализацию/деинициализацию, отрисовку окон, загрузку флэша и т.д. Обертку над всем этим для интеграции в Питон я и делаю.
Python я решил использовать изначально 3-й, чтоб потом не пришлось заново все на него портировать.
Для отображения флэша используется обычный браузерный Flash Plugin, он же NPSWF32.dll в Винде, он же libflashplayer.so в Линухе. Никакого ActiveX - библиотека в будущем планируется кроссплатформенной.
Взаимодействие между флэшом и питоном осуществляется через External API. Для сериализации и десериализации я вначале планировал использовать Pickle, поскольку в питоне он есть нативный, то есть весьма быстрый, уже даже для AS написал аналоги Pickler и Unpickler, но потом впомнил про либу PyAMF, которую я раньше использовал для вэб-сервисов, но производительность оставляла желать лучшего, но оказалось что у нее есть и намного более быстрый нативный вариант. Таким образом процессы сериализации/десериализации на обеих сторонах будут происходить на полной скорости.

На данный момент уже реализован следующий функционал:
- импорт модуля
- инициализация (загрузка и запуск Flash Plugin-a)
- создание окошек
- загрузка SWF-ок
- проходят вызовы из флэша через ExternalInterface.call() в питон.

Последнюю неделю воюю с обратными вызовами из питона во флэш. Во флэше регистрирую метод через ExternalInterface.addCallback() а в модуле пытаюсь вызвать его через метод Invoke() скриптового объекта, который я запрашиваю у плагина. Но хоть кол на голове теши, он мне возвращает FALSE. Причем в оригинальном ScreenWeaver-е эта конструкция работает на ура.
Я уже весь код перекопал, везде все правильно инициализируется, структуры все заполняются, вызов производится из главного трэда, как это положено. А ничего не работает. Код функции, оставшийся без изменений от оригинального проекта:
Цитата:
char *flashp_call_in(flash *f, const char *s, const char *params) {
NPVariant p[2], r;
char *result;
if( f->object == NULL )
return NULL;
p[0].type = NPVariantType_String;
p[0].value.stringValue.utf8characters = s;
p[0].value.stringValue.utf8length = strlen(s);
p[1].type = NPVariantType_String;
p[1].value.stringValue.utf8characters = params;
p[1].value.stringValue.utf8length = strlen(params);
r.type = NPVariantType_Void;
if( !f->object->_class->invoke( f->object, (NPIdentifier)SPECIAL_IDENTIFIER, (NPVariant *)p, 2, &r ) ) <- здесь ошибка
return NULL;
if( r.type == NPVariantType_String )
result = strdup(r.value.stringValue.utf8characters);
DoReleaseVariant(&r);
return result;
}
Если есть люди с опытом разработки плагинов с использованием NPAPI, то буду рад любым советам.
В общем, как разберусь с этой багой, продолжу
Всего комментариев 16

Комментарии

Старый 14.10.2009 14:05 Котяра вне форума
Котяра
 
Аватар для Котяра
по поводу де//сериализации можно посмотреть в сторону google protobuf
Старый 14.10.2009 19:30 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Ага, спасибо, интересный вариант.
Только не думаю, что он так же быстро работает как и нативный флэшовый AMF энкодер/декодер.

Вчера попробовал прикрутить cPyAMF. Под 3-й питон он почему-то не компилится. Сейчас адаптирую свой модуль под 2.6, поэкспериментирую с ним, да и совместимость со 2-й веткой тоже не помешает.
Старый 15.10.2009 14:13 Котяра вне форума
Котяра
 
Аватар для Котяра
По поводу нативности AMF можно поспорить)))))) нативность и скрытость кода не одно и то же. Попробую если будет время протестировать по быстродействию и размеру AMF и protobuf. Результат сообщу всенепременно)
Старый 17.10.2009 00:41 alexcon314 вне форума
alexcon314
Skyggedans, простите, стало интересно, в NPSWF32.dll, то бишь в плугине, External API выдает/принимает данные в AMF? Тогда, что есть char* в аргументах flashp_call_in? Разве это не обычная строка с нулем в конце? Или сериализация идет где-то в теле метода Invoke()? Хотя, какая там может быть сериализация?
Или вы как-то "прикрутили" AMF сами? Как? byte array передаете или что? Не понятно..
Вобщем, в чем связь External API c AMF я не догоняю.. поясните?
Старый 17.10.2009 19:23 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
alexcon314
Я тут гриппую, поэтому голова сейчас особо не работает, но попробую объяснить, как это работает, точнее - как я это понимаю.

Каждый Netscape Plugin при загрузке хостом, помимо всего прочего, отдает ему структуру NPClass, которая описывает интерфейс взаимодействия со скрипт-объектом. В данной структуре имеется поле invoke, которое инициализируется указателем на соответвующую процедуру. Она используется для вызова методов скрипт-объекта, предоставляемого плагином.

В свою очередь хост отдает плагину структуру NPNetscapeFuncs, поля invoke и evaluate которой заполняет указателями на процедуры, реализованные разработчиком хоста.
evaluate используется для вызова методов хоста, а invoke - для вычисления результата обратных вызовов.

Имея опыт работы с ActiveX-флэшом, я надеялся, что при приеме и выполнении вызовов через ExternalInterface мне также придется иметь дело с обычным XML и в плагине.
Но не тут то было. Так как плагин нацелен на работу в первую очередь в браузере, и взаимодействие с хостом предполагается через JavaScript, оказалось, что при вызовах ИЗ флэша, в методе evaluate хоста, мы получаем не какой-то абстрактный XML, а строку в виде целой JavaScript-овой конструкции:
Код:
try {
    __flash__toXML(method_name("param"));
}
catch (e) {
    "<undefined/>";
}
Данная конструкция соответствует коду в ActionScript:
Код:
ExternalInterface.call(method_name, "param")
В ScreenWeaver-е из этого JS-кода тупо экстрактится две строки - "method_name" и ""param"", которые уже вполне можно обработать в любом своем скрипте.

При вызове же в обратную сторону - из хоста во флэш - все намного интереснее.
Сначала формируем массив параметров, совместимый с форматом вызова методов по NPAPI, потом вызываем метод invoke скриптабельного объекта, предоставляемого плагином(этот код присутствует в теле поста блога). А затем начинается самое интересное.
Плагин вызывает метод invoke нашего хоста, и передает ему такую строку:
Код:
__flash__request(method_name, param)
Этот код соответствует коду, если бы мы хотели вызвать из своего хоста метод во флеше, зарегенный как ExternalInterface.addCallback(method_name, "method_name"), где method_name это:
Код:
function method_name(param:String):void {}
Затем, этот JS-код парсится, из него извлекается method_name и param, из которых потом формируется стандартный XML (вида <invoke ...> и т.д.), который отдается плагину, который его выполняет и отдает результат через уже знакомый нам метод evaluate хоста в том же JS-формате.

Не спрашивайте меня, зачем такой изврат - и сам уже почти неделю в акуе.

Естественно, что никаким AMF там и не пахнет, там просто строка передается. Но мне большего и не надо - если в ByteArray можно сериализовать в принципе все что угодно, то потом его достаточно по Base64 закодировать - и можно перегнать куда угодно в виде строки.
Обновил(-а) Skyggedans 17.10.2009 в 19:33
Старый 17.10.2009 23:49 alexcon314 вне форума
alexcon314
Вах! точнее швах..
"Затем, этот JS-код парсится (кем - хостом же?), из него извлекается method_name и param, из которых потом формируется стандартный XML (вида <invoke ...> и т.д.), который отдается плагину (как отдается? хостером вызывается еще какой-то метод скрипт-объекта?), который его выполняет и отдает результат через уже знакомый нам метод evaluate хоста (в итоге: где синхронизация?)в том же JS-формате."
Тогда, может, там все ездит в нескольких потоках, которые как-то синхронизируются? Все это вобщем-то напоминает взаимодействие с ActiveX, но там все намного прозрачнее... Надо копать скриптовую машину браузеров, которые не ИЕ, раз плугин под это заточен.. копать только апи плугина мало. Что до сериализации АMF+Base64, я понял, это гуд.
Спасибо за разъяснения. Выздоравливайте поскорее!
Старый 18.10.2009 00:29 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
alexcon314
Парсится хостом, понятное дело, отдается в out-параметр result метода invoke хоста.
Я ж говорю, как-то добился адекватной работоспособности, сам до конца не поняв как Ни о какой синхронизации на уровне NPAPI речь и не идет - все операции должны выполняться в главном потоке. Я это решаю следующим образом:
Во время инициализации модуля создаю невидимое окно со своим message loop-ом, который крутится в главном потоке, а из коллбэка в это окно посылаю SendMessage() с определенным id (WM_SYNC_CALL) у которого wParam это некий Python callable (функция, грубо говоря), а lParam - кортеж с параметрами. В цикле эта мессага обрабатывается, а результат вызова возвращается как указатель на PyObject (который может быть чем угодно, в чем главная вкусность Питона).
В общем, если интересно, скачайте сорцы ScreenWeaver-a, а там по этой теме интерес представляют модули flash.c и np_host.c
А за пожелание выздоровления - отдельное спасибо
Обновил(-а) Skyggedans 18.10.2009 в 00:33
Старый 24.10.2009 18:37 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Я тут параллельно занялся вопросом добавления в интерфейс такого модного эффекта, как стекло в висте/семерке, но не могу допереть как правильно отображать на стекле прозрачный флэш. Через UpdateLayeredWindow почему-то не получается.
А если заполнять буфер предварительно цветом, который выбран в качестве прозрачного через LWA_COLORKEY в SetLayeredWindowAttributes(), то участки с частичной прозрачностью просто смешиваются с данным цветом и естественно не отсекаются.
Обновил(-а) Skyggedans 24.10.2009 в 18:41
Старый 25.10.2009 02:59 alexcon314 вне форума
alexcon314
Под вистой появился расширенный вин-апи для этих фич вроде..
Однозначно через UpdateLayeredWindow() . Это сложнее, но иначе никак.
ЗЫ. Эхх.. тоже этим интересовался. Жаль профукал исходнк один, на асме правда, но рисовал очень качественные полупрозрачные окна, причем, на основе то ли пнг, то ли джипега, не помню..да, с интеракивом еще... на русском форуме каком-то видел. Вот еще один исходник могу предложить. Там класс для отрисовки тени окна в стиле Виста Аэро. Посмотрите, должно помочь. думаю.
http://mdm-zinc.narod.ru/pub/Shadow.zip
Старый 25.10.2009 18:16 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Цитата:
alexcon314
Спасибо, но это не то. Прикол в том, что форма слоеного окна как раз определяется картинкой, которая на нем рисуется через UpdateLayeredWindow(). Пиксели, совпадающие по цвету с текущим колоркеем, и пиксели, имеющиее 255 в альфа-канале - отсекаются и не участвуют в формировании формы окна. Соответственно, от всего окна, с бордером, тайтлом и системными кнопками, остается, только, то не имеет полной прозрачности в SWF-ке.
Нутром чую - не катит тут UpdateLayeredWindow(), тут бы GDI+ Graphics вероятно помог, но не очень хочется с ним связываться.
Старый 30.10.2009 16:12 alexcon314 вне форума
alexcon314
Вобщем, вычитал я о компоненте ActivePython от ActiveState. Его наличие в системе позволяет запускать скрипты в MSScriptControl. Типа так
Код:
'testpy.vbs
Set ScrptCtrl = CreateObject("MSScriptControl.ScriptControl")
ScrptCtrl.Language = "python"
ScrptCtrl.AddCode "def test():" & vbCrLf & _
                  "    import win32api" & vbCrLf & _
                  "    win32api.Beep(440, 500)" & vbCrLf & _
                  "    return u'Привет, MSScriptControl & Python!'"
res = ScrptCtrl.Run("test")
WScript.Echo res
И меня осенило.
Занимаясь расширением флэш-проектора, я набрел на возможность создавать экземпляр MSScriptControl прямо в оболочке, ну и общаться с ним: генерить скрипты на лету в АС, отправлять на исполнение, получать результаты и т.д. Т.е. так же можно исполнять любой .ру скрипт, и не только .ру, но и перл, скажем, и даже подключать .net сборки. Кроме того, есть возможность закружать целые библиотеки классов в скрипт-машину, того же питона. Грузить можно из файла, из ресурсов длл, из сети и пр. Можно писать ком-серверы на скриптах, размещать их в сети, подключаться к ним и пр..
Конечно, нужен установленный ActivePython-компонент и т.п. (vbs в винде зашит по умолчанию).
Никак не соображу, нужно это вообще или все эти возможности малопригодны для практического использования. По идее, написав либу на питоне, сунуть ее в ресурсы той же оболочки и можно подрубать ее к флэш-гую в проекторе... Можно использовать .net-классы ... блин голова кругом идет, повторяюсь).
Вобщем, вот такая рабочая конструкция примерно получается в АС:
Код AS1/AS2:
PScript.initEngine("python");
var py:String =  "class MyClass:\r";
	 py +=  "    def test(self):\r";
	 py +=  "        import win32api\r";
	 py +=  "        win32api.Beep(440, 500)\r";
	 py +=  "        return u'Привет, MSScriptControl & Python!'\r";
	 py +=  "x = MyClass()\r";
out(PScript.add(py));
out(PScript.call("x.test()\r"));// out: Привет, MSScriptControl & Python!
Синхронизм обеспечивается оболочкой. PScript - это АС-класс, который дергает соответствующий класс в оболочке, тот, что хостит MSScriptControl. Ммда.. можно в принципе иметь несколько скрипт-машин в одном приложении..
Или так:
Код AS1/AS2:
PScript.initEngine("perlscript");
var pl:String ="use Win32;\r"
	pl += "sub MsgBox {\r";
	pl += "    my ($caption, $message, $icon_buttons) = @_;\r";
    	pl += "    my @return = qw/- Ok Cancel Abort Retry Ignore Yes No/;\r";
    	pl += "    my $result = Win32::MsgBox($message, $icon_buttons, $caption);\r";
    	pl += "    return $return[$result];\r";
  	pl += "}";
o1(PScript.add(pl));
o1(PScript.call("MsgBox(\"Test\", \"This is a test\", 48);\r"));
ЗЫ. Не знаю, что на меня нашло, просто хотелось с кем-то поделиться..
Обновил(-а) alexcon314 30.10.2009 в 16:41
Старый 03.11.2009 20:07 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Отладил наконец свой экшскриптовый аналог Unpickler-а, и попробовал его на предмет быстродействия.
На стороне питона делаю select ~27000 записей из SQLite базы, конверчу в список словарей, где каждый словарь это одна запись, пикляю, зиплю через zlib, кодирую по Base64 и отдаю флэшу. Каждая запись это 2 int(4)-а и один char(512).
На стороне флэша провожу обратные преобразования, анпиклю, получаю Array Object-ов и отдаю его DataGrid-y.
Времени это занимает ~7 секунд, причем без компрессии почему-то получается секунд 11. Размер массива ~4.5 метра без компрессии, ~800 кил с компрессией.
Не знаю, много это или мало, в старом проекторе, который работал нативно с XML-ем это занимало секунд 4-5. Но то, что это шустрее чем ненативный PyAMF это точно.
Обновил(-а) Skyggedans 03.11.2009 в 20:11
Старый 03.11.2009 22:49 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Хм, попробовал дома на трехлетнем Х2 3800+, отрабатывает за 3 секунды. Неплохо, в старом проекторе длительность этого процесса не зависела от платформы.
Старый 09.11.2009 11:36 alexcon314 вне форума
alexcon314
По поводу UpdateLayeredWindow хочу добавить.
Я тут поэкспериментировал со своим проектором на основе ActiveX-контрола с целью отобразить прозрачный контрол в прозрачном окне, ну типа транспарент моде, как в цинке. Получилось сделать примерно так:
-делаю layered окно;
-аттачу контрол, выставляю ему режим транспарент;
-гружу ролик;
-делаю мемори-контекст для окна через CreatreCompatibleDC;
-создаю в нем битмап через CreateDIBSection;
-отрисовываю флэш-контент через интерфейс контрола IViewObject->Draw() в этот контекст;
-делаю UpdateLayeredWindow(), отрисовываю мемори-контекст в конткест окна.
Типа того. Без GDI+. GDI+ можно заюзать (не обязательно через нее, просто с GD+ писанины много меньше), скажем для отрисовки статического/динамического фона под флэш-контентом (или над ним, гы)), скажем, пнг-стекло.
Результат - отображается прозрачное окно с прозрачным флэшем. Что не получилось: обновление содержимого (анимация, интерактив) идет криво, но, думаю это дожать можно, наверно надо копнуть метод SetAdvise IViewObject'а, который отвечает за рассылку события об обновлении контента. А может с контекстами помудрить надо... Вобщем, истина где-то рядом).
ЗЫ. идея основана на вот этом. смотреть исходник, тот что на VB в архиве выложен.
Обновил(-а) alexcon314 09.11.2009 в 11:46
Старый 09.11.2009 14:14 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Ага, точно так в Screenweaver-е и сделано, для transparent-режима. Ну, эту фичу я напотом отложил, все таки надо базовый функционал сначала сделать, а потом уже свистелки-перделки прикручивать, тем более что меня пока только fullscreen-прилады по работе интересуют.
У меня щас другая трабла, с потоками в питоне. Почему-то они останавливаются почти сразу после запуска. Если простой вызов print() отработать успевает, то, например, запрос к базе, уже нет. В старом проекторе я это победил сохранением ThreadState-а главного потока на на самом старте проги, но тут другая архитектура и этот подход не работает.
Старый 11.11.2009 11:20 Skyggedans вне форума
Skyggedans
 
Аватар для Skyggedans
Короче говоря, цирк да и только
Оказалось, чтоб отрисовать на стекле, достаточно просто рассчитать DC с альфаканалом (как при UpdateLayeredWindow делается), только вместо вызова UpdateLayeredWindow() сделать InvalidateRect(), а в WM_PAINT сделать BitBlt из этого DC в DC окна.
 
Последние записи от Skyggedans

 


Часовой пояс GMT +4, время: 21:06.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.