Differences between revisions 25 and 35 (spanning 10 versions)
Revision 25 as of 2007-09-12 08:28:30
Size: 14839
Editor: Azureon
Comment:
Revision 35 as of 2008-01-14 13:13:26
Size: 122
Editor: Azureon
Comment:
Deletions are marked like this. Additions are marked like this.
Line 4: Line 4:
[[TableOfContents]]
## 默许导航,请保留
[[Include(CPUGnav)]]
Line 8: Line 5:
= 将Flash应用于Python项目 =
== 基于本地的Python应用程序 ==
["Django 与 Flash 的结合!"]
Line 11: Line 7:

=== 写在之前 ===
{{{
这篇所说的是关于建立python调用Flash的本地应用,不同于Adobe的Apollo。

没有用到浏览器嵌入flash网页的方法,直接在pythonwin或者wxpython建立的窗口中插入Flash ocx。

因为是操作Activex控件的方式因此大概只适用于windows平台。抱歉我并未在其它平台上试过这种方法,不过linux中应该也有类似的技术。}}}

=== Flash ocx介绍 ===
{{{
Flash ocx实际上是一种COM组件开发模型(Microsoft Component Object Model),它原先是从Windows 3.x中的OLE发展过来的。现在又被改名叫做Activex。Activex是COM的一种,一般是指带有UI界面的COM。

Flash ocx的本名是叫Shockwave Flash Object,是一个Activex控件。Activex控件文件名的后缀是ocx。

原先的Shockwave包括了很多东西。被Adobe收购的MicroMedia公司的另一个产品Director的web应用就叫shockwave,它集合了视频流、Flash、shockwave 3D于一身。

对于Director我还是挺有感情的,只不过Director到了8.5以后的版本就基本不再发展了,我也渐渐不用它了。(听说Adobe收购MicroMedia以后,还会推出Director 11)}}}


=== Flash ocx与外界通迅的方法 ===

==== 调用ocx标准COM接口IDispatch ====

这种方法最简单,也比较通用。

它又叫COM对象的自动化接口。使用自动化,对象就可以提供一个简单的自动化接口,这样脚本语言作者只需掌握IDispatch和几个COM应用程序接口就可以了。

pythonwin的作者 Mark Hammond 的一本书(Python Programming on Win32)就讲到了怎样用python直接操作COM对象(操作的函义包括使用和发布)。如果想深入细节的话,可以参考这本书。

Python 程序使用 win32com.client.Dispatch() 方法来创建 COM objects。
如创建一个 Flash COM object.

{{{#!python
>>> import win32com.client
>>> fl = win32com.client.Dispatch("ShockwaveFlash.ShockwaveFlash.9") #Flash 9 的ProgID是ShockwaveFlash.ShockwaveFlash.9,有很多工具可以查到机器内部注册的COM组件信息
}}}
这样就得到了Flash COM object,你可以让它LoadMovie,让它Play,但是你暂时还看不到它,你得传给它一个窗口,这样它才能显示在窗口。
所幸wxpython帮我们封装了这一切,你只需要调用wx.lib.flashwin.FlashWindow类就行了。

例:
{{{#!python
import wx
from wx.lib.flashwin import FlashWindow

class CGui(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, 101, "map", size = (800, 600), style = wx.FRAME_SHAPED)
        self.flash = FlashWindow(self, style=wx.SUNKEN_BORDER, size = (800, 600)) #用wx.lib.flashwin.FlashWindow创建窗口
        self.flash.LoadMovie(0, 'C:\\drop_shadow_dog.swf') #播放"C:\\drop_shadow_dog.swf"的Flash影片
        self.flash.SetSize((800, 600));
    
    def getText(self):
        returnValue = self.flash.GetVariable('FlashValue') #从Flash端
        return returnValue
            
    def setText(self, text):
        self.flash.SetVariable("PythonValue", text) #传给Flash变量
        
}}}
这些传递变量在Flash AS端都处于_root层级下。

这儿有个例子:

http://www.sephiroth.it/weblog/archives/2004/05/wxpython_and_flash_first_test.php
{{{#!python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx, sys, os
import string, codecsfrom wx.lib.flashwin
import FlashWindow
from wx.lib.flashwin import EVT_FSCommand
#----------------------------------------
class TestPanel(wx.Panel):
    def __init__(self, parent, base, swf):
        wx.Panel.__init__(self, parent, -1)
        self.base = base
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.flash = FlashWindow(self, style=wx.SUNKEN_BORDER)
        dlg = wx.MessageDialog(self, "This will work only under Windows!","Warning!",wx.OK | wx.ICON_INFORMATION)
        dlg.Center()
        dlg.ShowModal()
        wx.BeginBusyCursor()
        try:
            self.flash.LoadMovie(0, swf)
        except:
            wx.MessageDialog(self, "could not load the swf file","Error",wx.OK | wx.ICON_ERROR).ShowModal()
            sys.exit(2)
        wx.EndBusyCursor()
        self.flash.Stop()
        self.flash.SetSize((self.flash.GetSize()[0],self.flash.GetSize()[1]))
        # sizer
        sizer.Add(self.flash, 1, wx.EXPAND)
        self.SetSizer(sizer)
        self.SetAutoLayout(True)
        sizer.Fit(self)
        sizer.SetSizeHints(self)
        self.SetFlashOptions()
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
        self.Bind(EVT_FSCommand, self.CallMethod) ##将Flash ocx的消息事件绑定到CallMethod函数上。

    def SetFlashOptions(self):
        self.flash.menu = False
        self.flash._set_FlashVars("data=Server started on " + sys.platform)
        self.flash.Play()

    def OnDestroy(self, evt):
        if self.flash:
            self.flash.Cleanup()
            self.flash = None

    # Called from Flash FSCommand
    def CallMethod(self, evt):
        try:
            arguments = string.split(evt.args,"###")
            filename = arguments[0]
            body = arguments[1]
            if filename == "" or body == "":
                wx.MessageDialog(self, "Please check data inserted", "An Error occurred", wx.OK | wx.ICON_INFORMATION).ShowModal()
            else:
                dlg = wx.FileDialog(self, "Save as..." , os.getcwd(), filename, "*.*", wx.SAVE | wx.OVERWRITE_PROMPT )
                if dlg.ShowModal() == wx.ID_OK:
                    try:
                        f = codecs.open(os.path.normpath(dlg.GetPath()), "w", "utf-8", "ignore")
                        f.write(codecs.utf_8_decode(codecs.BOM_UTF8)[0])
                        f.write(body)
                        f.close()
                        self.flash._set_FlashVars("data=Succesfully saved text file")
                    except:
                        wx.MessageDialog(self, "%s %s %s" % sys.exc_info(), "An Error occurred", wx.OK | wx.ICON_ERROR).ShowModal()
                        self.flash._set_FlashVars("data=%s %s %s" % sys.exc_info())
        except:
            wx.MessageDialog(self, "Please check data inserted","An Error occurred",wx.OK | wx.ICON_INFORMATION).ShowModal()
            self.flash._set_FlashVars("data=%s %s %s" % sys.exc_info())

#-------------------------------------------
if __name__ == '__main__':
    class TestFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, -1, "ActiveX -- Flash", size=(640, 480), style=wx.DEFAULT_FRAME_STYLE )
            base = os.path.normpath(os.path.abspath(os.path.dirname(sys.argv[0])))
            swf = os.path.normpath(os.path.join(base, "movie.swf"))
            self.tp = TestPanel(self, base, swf)
    app = wx.PySimpleApp()
    frame = TestFrame()
    frame.Center()
    frame.Show(True)
    app.MainLoop()
}}}

Flash端很简单,两句话就搞定了。
{{{
on (click) {
 fscommand("saveFile", this._parent.fnome.text + "###" + this._parent.ftesto.text)
}}}}

这里用到了Flash的fscommand。

在Flash端点击了以后,它就会发送一个fscommand消息事件。

python端接收到了以后,由CallMethod处理。

==== 使用Flash ExternalInterface ====

ExternalInterface 类是一个子系统,通过它可以轻松地实现从 ActionScript 和 Flash Player 到 HTML 页中的 JavaScript 或任何包含 Flash Player 实例的台式机应用程序的通信。
ExternalInterface 可以提供以下功能:
{{{
■ 可以调用注册过的 python 函数。 从python端也可以调用注册过的Flash ActionScript函数。
■ 可以传递任意数量的、具有任意名称的参数;而不是仅限于传递一个命令和一个字符串参数。
■ 可以传递各种数据类型(例如 Boolean 、Number 和 String);不再仅限于 String 参数。
■ 可以接收调用值,该值将立即返回到 ActionScript(作为进行的调用的返回值)。}}}

Flash利用ExternalInterface与Python之间的通信使用特定的XML格式对函数调用和值进行编码。Flash端自动处理XML格式,Python则需要将接收到的XML数据解析和发送前打包成XML格式。

使用ExternalInterface与Python进行通信时,Flash以特定的XML格式向应用程序发送消息(函数调用和返回值),并要求来自Python的函数调用和返回值使用相同的 XML格式。

下面的 XML 片断说明了一个 XML 格式的函数调用示例:

{{{
<invoke name="functionName" returntype="xml">
   <arguments>
     ... (individual argument values)
   </arguments>
</invoke> }}}

通过XML格式,ExternalInterface与Python之间可以传递多种类型的参数,包括Python的list和dic类型。

我们可以建立一个数据转换类来专门将翻译Python与Flash之间的通迅。

{{{#!python
class EIDataSerializer:
    __xmlData=None
    def __packNumber(self,p,x):
        p.appendChild(self.__xmlData.createElement('number')).appendChild(self.__xmlData.createTextNode(str(x)))
        return
    def __packString(self,p,x):
        p.appendChild(self.__xmlData.createElement('string')).appendChild(self.__xmlData.createTextNode(x))
        return
    def __packNone(self,p):
        p.appendChild(self.__xmlData.createElement('null'))
        return
    def __packBool(self,p,x):
        if x:
            p.appendChild(self.__xmlData.createElement('true'))
        else:
            p.appendChild(self.__xmlData.createElement('false'))
        return
    def __packDict(self,p,x):
        p=p.appendChild(self.__xmlData.createElement('object'))
        for k,v in x.items():
            n=p.appendChild(self.__xmlData.createElement('property'))
            n.setAttribute('id',str(k))
            self.__packData(n,v)
        return
    def __packList(self,p,x):
        p=p.appendChild(self.__xmlData.createElement('array'))
        i=0
        for v in x:
            n=p.appendChild(self.__xmlData.createElement('property'))
            n.setAttribute('id',str(i))
            self.__packData(n,v)
            i+=1
        return
    def __packData(self,p,x): ##将Python的类型打包成XML
        t=type(x)
        if t in (int,long,float):
            self.__packNumber(p,x)
        elif t in (str,unicode):
            self.__packString(p,x)
        elif x==None:
            self.__packNone(p)
        elif t==bool:
            self.__packBool(p,x)
        elif t in (list,tuple):
            self.__packList(p,x)
        elif t==dict:
            self.__packDict(p,x)
        return
    def __unpackNumber(self,p):
        try:
            return int(p.firstChild.nodeValue)
        except ValueError:
            try:
                return float(p.firstChild.nodeValue)
            except ValueError:
                return None
    def __unpackString(self,p):
        return p.firstChild.nodeValue
    def __unpackTrue(self):
        return True
    def __unpackFalse(self):
        return False
    def __unpackNull(self):
        return None
    def __unpackUndefined(self):
        return None
    def __unpackObject(self,p):
        d={}
        for n in p.childNodes:
            d[n.getAttribute('id')]=self.__unpackData(n.firstChild)
        return d
    def __unpackArray(self,p):
        a=[]
        for n in p.childNodes:
            a.append(self.__unpackData(n.firstChild))
        return a
    def __unpackData(self,p): ##将Flash传过来的XML解析成Python类型数值
        t=p.nodeName
        if t=='number':
            return self.__unpackNumber(p)
        elif t=='string':
            return self.__unpackString(p)
        elif t=='true':
            return self.__unpackTrue()
        elif t=='false':
            return self.__unpackFalse()
        elif t=='null':
            return self.__unpackNull()
        elif t=='undefined':
            return self.__unpackUndefined()
        elif t=='object':
            return self.__unpackObject(p)
        elif t=='array':
            return self.__unpackArray(p)
    def serializeReturn(self,v):
        self.__xmlData=minidom.Document()
        p=self.__xmlData
        self.__packData(p,v)
        return self.__xmlData.toxml()
    def serializeCall(self,name,args):
        self.__xmlData=minidom.Document()
        p=self.__xmlData.appendChild(self.__xmlData.createElement('invoke'))
        p.setAttribute('name',name)
        p.setAttribute('returntype','xml')
        p=p.appendChild(self.__xmlData.createElement('arguments'))
        for v in args:
            self.__packData(p,v)
        s=self.__xmlData.documentElement.toxml()
        return s
    def deserializeReturn(self,s):
        self.__xmlData=minidom.parseString(s)
        p=self.__xmlData.documentElement
        return self.__unpackData(p)
    def deserializeCall(self,s):
        self.__xmlData=minidom.parseString(s)
        p=self.__xmlData.documentElement#invoke
        name=p.getAttribute('name')
        args=[]
        p=p.firstChild#arguments
        for n in p.childNodes:
            args.append(self.__unpackData(n))
        return (name,args)}}}


== 从Python调用Flash函数 ==

从Python端调用Flash端函数实际上是Python调用Shockwave Flash ActiveX控件的CallFunction()方法,通过ExternalInterface从Flash调用ActionScript函数。

以下示范了从Python调用Flash函数的用法:

Python端:

{{{#!python
def CallFlash(name,args): ## name是Flash ActionScript的函数名,args是传给Flash ActionScript的参数
    ds = EIDataSerializer()
    s = ds.serializeCall(name,args) ## 将传递的内容打包成XML
    s = flashWnd.ocx.CallFunction(s) ## 调用Shockwave Flash ActiveX控件的CallFunction()方法
    s = s.encode('utf-8') ## 从ActionScript返回的任何值都被编码为XML格式字符串,并作为CallFunction()调用的返回值发送回来。
    return ds.deserializeReturn(s) ## 返回值解包}}}


------ 未完待续 ------






= 反馈 =
[[PageComment2]]

Azureon Email: [[MailTo([email protected])]]

["技术文档分类"]
["将Flash应用于Python项目"]

["Django 与 Flash 的结合!"]

["将Flash应用于Python项目"]

Azureon (last edited 2009-12-25 07:10:01 by localhost)