## page was renamed from 将Flash应用于Python项目 ##language:zh #format wiki <<TableOfContents>> ## 默许导航,请保留 <<Include(CPUGnav)>> = 将Flash应用于Python项目 = == 基于本地的Python应用程序 == === 写在之前 === {{{ 这篇所说的是关于建立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) ## 返回值解包}}} Flash端: 要从Python调用ActionScript函数,必须向ExternalInterface类注册函数,然后再用Shockwave Flash ActiveX控件的CallFunction()方法调用它。 Python只能调用ExternalInterface类注册函数中的ActionScript代码,而不能调用任何其它ActionScript代码。 向ExternalInterface类注册ActionScript函数的方法,如下所示: {{{ function callMe(name:String):String { return "busy signal"; } ExternalInterface.addCallback("myFunction", callMe);}}} ExternalInterface.addCallback()方法采用两个参数。第一个参数为 String 类型的函数名,这是告诉Python端调用的函数名。第二个参数为Flash端实际ActionScript函数。 由于这些名称是截然不同的,因此可以指定将由Python使用的函数名与实际的ActionScript函数具有不同的名称。这在函数名未知的情况下特别有用,例如:指定了匿名函数或需要在运行时确定要调用的函数。 ===== 从Flash调用Python函数 ===== 从Flash调用Python函数实际上是Shockwave Flash ActiveX控件发送了一个控件消息FlashCall,并附带包含有关函数调用信息的XML 格式的字符串。Python将其解析成函数名和参数,并调用相应函数。 我们继续从消息流程上解析这个过程,首先Flash端示例: {{{ public function sendMessage(message:String):void { ExternalInterface.call("newMessage", message); //调用了Python端的newMessage的方法,message是newMessage方法的参数 }}}} Python端示例: 先建立一个供Flash调用的函数 {{{#!python def newMessage(self, message): print message return message}}} 建立一个函数字典库 {{{#!python def RegisterCallback(self,name,callback): ##将需要调用的函数注册 if callable(callback): self.__callbackReg[name]=callback ##和Flash类似,name是Flash端调用的函数名。callback为Python端实际函数。 return True else: return False}}} {{{#!python self.RegisterCallback("newMessage", newMessage) ##将其注册到__callbackReg中}}} 最后接收Flash消息,处理函数调用 {{{#!python def OnFlashCall(self, receiveString): ##注册的Activex消息处理函数 receiveString = receiveString.encode('utf-8') ##从Flash控件消息接收的XML字符串 name,args = self.__szr.deserializeCall(receiveString) ##解析成Python函数名和参数 r = self.__callbackReg[name](*args) ##函数字典中注册的函数名 ds = EIDataSerializer() s = ds.serializeReturn(r) ##返回值打包成XML self.SetReturnValue(s) return}}} 这样就可在Flash端调用Python函数了。 以上方法在pythonwin和wxPython中均可使用。 = 反馈 = Azureon Email: <<MailTo(Azureon@hotmail.com)>> [[技术文档分类]]