CPUG联盟::

CPUG::门户plone

BPUG

SPUG

ZPUG

SpreadPython Python宣传

将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.

   1 >>> import win32com.client
   2 >>> fl = win32com.client.Dispatch("ShockwaveFlash.ShockwaveFlash.9") #Flash 9 的ProgID是ShockwaveFlash.ShockwaveFlash.9,有很多工具可以查到机器内部注册的COM组件信息

这样就得到了Flash COM object,你可以让它LoadMovie,让它Play,但是你暂时还看不到它,你得传给它一个窗口,这样它才能显示在窗口。 所幸wxpython帮我们封装了这一切,你只需要调用wx.lib.flashwin.FlashWindow类就行了。

例:

   1 import wx
   2 from wx.lib.flashwin import FlashWindow
   3 
   4 class CGui(wx.Frame):
   5     def __init__(self):
   6         wx.Frame.__init__(self, None, 101, "map", size = (800, 600), style = wx.FRAME_SHAPED)        
   7         self.flash = FlashWindow(self, style=wx.SUNKEN_BORDER, size = (800, 600))                   #用wx.lib.flashwin.FlashWindow创建窗口
   8         self.flash.LoadMovie(0, 'C:\\drop_shadow_dog.swf')                                          #播放"C:\\drop_shadow_dog.swf"的Flash影片
   9         self.flash.SetSize((800, 600));
  10     
  11     def getText(self):
  12         returnValue = self.flash.GetVariable('FlashValue')                                          #从Flash端
  13         return returnValue
  14             
  15     def setText(self, text):
  16         self.flash.SetVariable("PythonValue", text)                                                 #传给Flash变量

这些传递变量在Flash AS端都处于_root层级下。

这儿有个例子:

http://www.sephiroth.it/weblog/archives/2004/05/wxpython_and_flash_first_test.php

   1 #!/usr/bin/env python
   2 # -*- coding: utf-8 -*-
   3 import wx, sys, os
   4 import string, codecsfrom wx.lib.flashwin 
   5 import FlashWindow
   6 from wx.lib.flashwin import EVT_FSCommand
   7 #----------------------------------------
   8 class TestPanel(wx.Panel):
   9     def __init__(self, parent, base, swf):
  10         wx.Panel.__init__(self, parent, -1)
  11         self.base = base
  12         sizer = wx.BoxSizer(wx.VERTICAL)
  13         self.flash = FlashWindow(self, style=wx.SUNKEN_BORDER)
  14         dlg = wx.MessageDialog(self, "This will work only under Windows!","Warning!",wx.OK | wx.ICON_INFORMATION)
  15         dlg.Center()
  16         dlg.ShowModal()
  17         wx.BeginBusyCursor()
  18         try:
  19             self.flash.LoadMovie(0, swf)
  20         except:
  21             wx.MessageDialog(self, "could not load the swf file","Error",wx.OK | wx.ICON_ERROR).ShowModal()
  22             sys.exit(2)
  23         wx.EndBusyCursor()
  24         self.flash.Stop()
  25         self.flash.SetSize((self.flash.GetSize()[0],self.flash.GetSize()[1]))
  26         # sizer
  27         sizer.Add(self.flash, 1, wx.EXPAND)
  28         self.SetSizer(sizer)
  29         self.SetAutoLayout(True)
  30         sizer.Fit(self)
  31         sizer.SetSizeHints(self)
  32         self.SetFlashOptions()
  33         self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
  34         self.Bind(EVT_FSCommand, self.CallMethod)  ##将Flash ocx的消息事件绑定到CallMethod函数上。
  35 
  36     def SetFlashOptions(self):
  37         self.flash.menu = False
  38         self.flash._set_FlashVars("data=Server started on " + sys.platform)
  39         self.flash.Play()
  40 
  41     def OnDestroy(self, evt):
  42         if self.flash:
  43             self.flash.Cleanup()
  44             self.flash = None
  45 
  46     # Called from Flash FSCommand
  47     def CallMethod(self, evt):
  48         try:
  49             arguments = string.split(evt.args,"###")
  50             filename = arguments[0]
  51             body = arguments[1]
  52             if filename == "" or body == "":
  53                 wx.MessageDialog(self, "Please check data inserted", "An Error occurred", wx.OK | wx.ICON_INFORMATION).ShowModal()
  54             else:
  55                 dlg = wx.FileDialog(self, "Save as..." , os.getcwd(), filename, "*.*", wx.SAVE | wx.OVERWRITE_PROMPT )
  56                 if dlg.ShowModal() == wx.ID_OK:
  57                     try:
  58                         f = codecs.open(os.path.normpath(dlg.GetPath()), "w", "utf-8", "ignore")
  59                         f.write(codecs.utf_8_decode(codecs.BOM_UTF8)[0])
  60                         f.write(body)
  61                         f.close()
  62                         self.flash._set_FlashVars("data=Succesfully saved text file")
  63                     except:
  64                         wx.MessageDialog(self, "%s %s %s" % sys.exc_info(), "An Error occurred", wx.OK | wx.ICON_ERROR).ShowModal()
  65                         self.flash._set_FlashVars("data=%s %s %s" % sys.exc_info())
  66         except:
  67             wx.MessageDialog(self, "Please check data inserted","An Error occurred",wx.OK | wx.ICON_INFORMATION).ShowModal()
  68             self.flash._set_FlashVars("data=%s %s %s" % sys.exc_info())
  69 
  70 #-------------------------------------------
  71 if __name__ == '__main__':
  72     class TestFrame(wx.Frame):
  73         def __init__(self):
  74             wx.Frame.__init__(self, None, -1, "ActiveX -- Flash", size=(640, 480), style=wx.DEFAULT_FRAME_STYLE )
  75             base = os.path.normpath(os.path.abspath(os.path.dirname(sys.argv[0])))
  76             swf = os.path.normpath(os.path.join(base, "movie.swf"))
  77             self.tp = TestPanel(self, base, swf)
  78     app = wx.PySimpleApp()
  79     frame = TestFrame()
  80     frame.Center()
  81     frame.Show(True)
  82     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之间的通迅。

   1 class EIDataSerializer:
   2     __xmlData=None
   3     def __packNumber(self,p,x):
   4         p.appendChild(self.__xmlData.createElement('number')).appendChild(self.__xmlData.createTextNode(str(x)))
   5         return
   6     def __packString(self,p,x):
   7         p.appendChild(self.__xmlData.createElement('string')).appendChild(self.__xmlData.createTextNode(x))
   8         return
   9     def __packNone(self,p):
  10         p.appendChild(self.__xmlData.createElement('null'))
  11         return
  12     def __packBool(self,p,x):
  13         if x:
  14             p.appendChild(self.__xmlData.createElement('true'))
  15         else:
  16             p.appendChild(self.__xmlData.createElement('false'))
  17         return
  18     def __packDict(self,p,x):
  19         p=p.appendChild(self.__xmlData.createElement('object'))
  20         for k,v in x.items():
  21             n=p.appendChild(self.__xmlData.createElement('property'))
  22             n.setAttribute('id',str(k))
  23             self.__packData(n,v)
  24         return
  25     def __packList(self,p,x):
  26         p=p.appendChild(self.__xmlData.createElement('array'))
  27         i=0
  28         for v in x:
  29             n=p.appendChild(self.__xmlData.createElement('property'))
  30             n.setAttribute('id',str(i))
  31             self.__packData(n,v)
  32             i+=1
  33         return
  34     def __packData(self,p,x):                   ##将Python的类型打包成XML
  35         t=type(x)
  36         if t in (int,long,float):               
  37             self.__packNumber(p,x)
  38         elif t in (str,unicode):                
  39             self.__packString(p,x)
  40         elif x==None:                           
  41             self.__packNone(p)
  42         elif t==bool:       
  43             self.__packBool(p,x)
  44         elif t in (list,tuple):
  45             self.__packList(p,x)
  46         elif t==dict:
  47             self.__packDict(p,x)
  48         return
  49     def __unpackNumber(self,p):
  50         try:
  51             return int(p.firstChild.nodeValue)
  52         except ValueError:
  53             try:
  54                 return float(p.firstChild.nodeValue)
  55             except ValueError:
  56                 return None
  57     def __unpackString(self,p):
  58         return p.firstChild.nodeValue
  59     def __unpackTrue(self):
  60         return True
  61     def __unpackFalse(self):
  62         return False
  63     def __unpackNull(self):
  64         return None
  65     def __unpackUndefined(self):
  66         return None
  67     def __unpackObject(self,p):
  68         d={}
  69         for n in p.childNodes:
  70             d[n.getAttribute('id')]=self.__unpackData(n.firstChild)
  71         return d
  72     def __unpackArray(self,p):
  73         a=[]
  74         for n in p.childNodes:
  75             a.append(self.__unpackData(n.firstChild))
  76         return a
  77     def __unpackData(self,p):                   ##将Flash传过来的XML解析成Python类型数值
  78         t=p.nodeName
  79         if t=='number':
  80             return self.__unpackNumber(p)
  81         elif t=='string':
  82             return self.__unpackString(p)
  83         elif t=='true':
  84             return self.__unpackTrue()
  85         elif t=='false':
  86             return self.__unpackFalse()
  87         elif t=='null':
  88             return self.__unpackNull()
  89         elif t=='undefined':
  90             return self.__unpackUndefined()
  91         elif t=='object':
  92             return self.__unpackObject(p)
  93         elif t=='array':
  94             return self.__unpackArray(p)
  95     def serializeReturn(self,v):
  96         self.__xmlData=minidom.Document()
  97         p=self.__xmlData
  98         self.__packData(p,v)
  99         return self.__xmlData.toxml()
 100     def serializeCall(self,name,args):
 101         self.__xmlData=minidom.Document()
 102         p=self.__xmlData.appendChild(self.__xmlData.createElement('invoke'))
 103         p.setAttribute('name',name)
 104         p.setAttribute('returntype','xml')
 105         p=p.appendChild(self.__xmlData.createElement('arguments'))
 106         for v in args:
 107             self.__packData(p,v)
 108         s=self.__xmlData.documentElement.toxml()
 109         return s
 110     def deserializeReturn(self,s):
 111         self.__xmlData=minidom.parseString(s)
 112         p=self.__xmlData.documentElement
 113         return self.__unpackData(p)
 114     def deserializeCall(self,s):
 115         self.__xmlData=minidom.parseString(s)
 116         p=self.__xmlData.documentElement#invoke
 117         name=p.getAttribute('name')
 118         args=[]
 119         p=p.firstChild#arguments
 120         for n in p.childNodes:
 121             args.append(self.__unpackData(n))
 122         return (name,args)

从Python调用Flash函数

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

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

Python端:

   1 def CallFlash(name,args):                  ## name是Flash ActionScript的函数名,args是传给Flash ActionScript的参数
   2     ds  = EIDataSerializer()
   3     s   = ds.serializeCall(name,args)      ## 将传递的内容打包成XML
   4     s   = flashWnd.ocx.CallFunction(s)     ## 调用Shockwave Flash ActiveX控件的CallFunction()方法
   5     s   = s.encode('utf-8')                ## 从ActionScript返回的任何值都被编码为XML格式字符串,并作为CallFunction()调用的返回值发送回来。
   6     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调用的函数

   1 def newMessage(self, message):
   2     print message
   3     return message

建立一个函数字典库

   1 def RegisterCallback(self,name,callback):                       ##将需要调用的函数注册
   2     if callable(callback):
   3         self.__callbackReg[name]=callback                       ##和Flash类似,name是Flash端调用的函数名。callback为Python端实际函数。
   4         return True
   5     else:
   6         return False

   1 self.RegisterCallback("newMessage", newMessage)                 ##将其注册到__callbackReg中

最后接收Flash消息,处理函数调用

   1 def OnFlashCall(self, receiveString):                           ##注册的Activex消息处理函数
   2     receiveString   = receiveString.encode('utf-8')             ##从Flash控件消息接收的XML字符串
   3     name,args       = self.__szr.deserializeCall(receiveString) ##解析成Python函数名和参数
   4     r               = self.__callbackReg[name](*args)           ##函数字典中注册的函数名
   5     ds              = EIDataSerializer()
   6     s               = ds.serializeReturn(r)                     ##返回值打包成XML
   7     self.SetReturnValue(s)
   8     return

这样就可在Flash端调用Python函数了。

以上方法在pythonwin和wxPython中均可使用。

反馈

Azureon Email: <[email protected]>

技术文档分类

UsageFlashInPyProj (last edited 2009-12-25 07:10:06 by localhost)