Flash remoting for python

Yesterday I received an email form Vsevolod ILyushchenko announcing me the alpha version of Flash remoting for python!
http://simonf.com/amfpython/. There are also many examples here: http://simonf.com/amfpython/code.html

Second update: Serializer Class 2.1
There is a bug fixing with in special chars lenght, thanks to Rainer Becker. The new mxp file is available on the sourceforge site.

Last thing. I’ve just uploaded an example which Daniel Tavelli send me.
An example of Xpath usage in flash mx 2004 using the xfactorstudio xpath class for ActionScript

PEAR::SWF updating… read and modify swf with PHP

I’ve just begun to modify the old PHP class flashHeader. Now using the PEAR::File class for a better file I/O processes.
Basically now, other than returning all the main information from an swf file, such as the bg color, framerate, player version, compression, protection used, movie size.., it allows to modify some of those parameters.
For example it can change the framerate, background, protection… I’m also working in order to allows the class modify some other swf tags in runtime..
Working example

Here the source file

<?php
// include the PEAR package
require “File/File_SWF.php”;
$flash = new SWF(“source.swf”);
if(
$flash->is_valid()){
$stat = $flash->stat(); // this give all the info
// and also..
$fps = $flash->getFrameRate();
$size = $flash->getMovieSize();
$bg = $flash->getBackgroundColor();
$prot = $flash->getProtected();
$compr = $flash->getCompression();
$version = $flash->getVersion();

// they can be changed…
$flash->setFrameRate(60);
$flash->setProtected(1);
$flash->setBackgroundColor(51,51,204);
$flash->setCompression(1);

// and write a new file…
$flash->write(“./new_one.swf”,1);
}
?>

Ho to get bytesTotal from a dynamic generated content in PHP

Think you want to load from Flash some dynamically generated images from PHP (maybe using GD..), or also some else dynamic content..
Think at this simple code:

/**** MovieClip Loader listener ****/
loaderListener = {
	onLoadStart : function(){
		trace('load started!');
		trace(" ----------------------------------- ")
	},
	onLoadProgress : function(target, bLoaded, bTotal){
		trace(bLoaded + "/" + bTotal)
	},
	onLoadComplete : function(target){
		trace(" ----------------------------------- ")
		trace("complete: " + target.getBytesTotal());
		trace(" ----------------------------------- ")
	}
}

// I just add the rand variable to prevent caching...
movie_path = "http://local/ob_start.php?file=C:/DSCF0182.JPG&rand=" + new Date().getTime()
var mcLoader = new MovieClipLoader()
mcLoader.loadClip(movie_path, loader)
mcLoader.addListener( loaderListener )

Now, if your PHP code is written in this way (just a image copy and resize using the GD 2.x library):


<?php
// Get the required image from GET
$image = $_GET['file'];$size = getimagesize($image);
// create an empty image and copy the original
$dst_img = imagecreatetruecolor($size[0]/4, $size[1]/4);
$src_img = imagecreatefromjpeg($image);
// resize image
imagecopyresized($dst_img, $src_img, 0, 0, 0, 0, $size[0]/4, $size[1]/4, $size[0], $size[1]);
// start output content
header("Content-type: image/jpeg");
// display the image
imagejpeg($dst_img, "", 80);
?>

You will be noticed that the bytesTotal variable in flash is always ‘0’!
This could be a problem if you want to use any kind of graphic preloader for this such of things…
However with some little more code it’s always possible to get in flash (and not only from flash) the exact content bytes lenght.
Try now this:

<?php
// Turn on output buffering
ob_start();
// Get the required image from GET
$image = $_GET['file'];
$size = getimagesize($image);
// create an empty image and copy the original
$dst_img = imagecreatetruecolor($size[0]/4, $size[1]/4);
$src_img = imagecreatefromjpeg($image);
// resize image
imagecopyresized($dst_img, $src_img, 0, 0, 0, 0, $size[0]/4, $size[1]/4, $size[0], $size[1]);
// start output content
header("Content-type: image/jpeg");
// display the image
imagejpeg($dst_img, "", 80);
// send the content lenght to output
header("Content-Length: ". ob_get_length());
// flush the output
ob_end_flush();
?>

AMFPHP & NetConnection Call.BadVersion

If you used at least one time AMFPHP you probably had already encountered the NetConnection Call.BadVersion message.
This seems to be a very common issue for anyone who’s beginnig to develop with Flash Remoting in PHP. In fact I often read message from guys who had this this error while trying to use AMFPHP remoting.
The reasons of this error are often the same or at least similar, for this reason I thought to create a page on the wiki space about this problem.
This is a list of all the common situation where the Call.BadVersion appears. I get most of the poits reading people’s posts and I hope you will contribute to create the most comprehensive list about this common problem.

Other useful resources about AMFPHP:
AMFPHP installation guide
Flash-DB tutorial
Installation tips on amfphp.org

Link: http://www.sephiroth.it/phpwiki/index.php/Call.BadVersion

wxPython and Flash, first test


Today I make the first test integrating a Flash movie into a python application and using a communication between the two applications using FSCommand and the flashvars.
The result (surprendent easy) it’s a simple movie which enables text files saving in the local computer (through a prompt dialog window), but it has already opened my mind to future real cool applications đŸ˜‰

This is the python code, the core part it’s the “wx.lib.flashwin” import, which enable to use Flash ActiveX in a python frame

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx, sys, os
import string, codecs
from 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)
        
    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()

the flash part is really easy. Two text field, one button and one line of code!:

on (click) {	fscommand("saveFile", this._parent.fnome.text + "###" + this._parent.ftesto.text)}

P.S. Moreover today I read in wxPython mailing list a coming new version of wxPython. I hope this will begin to fix varoius problems with Mac installation of SE|PY

CellRenderer tutorial

Today I commit a new version of the Serializer Class.
What is Serializer Class?
This Class enable you to pass and receive complex data type from and to PHP using the native PHP functions serialize and unserialize (this means that you can receive an array directly from PHP without making any kind of split to obtain your original array).

After committing I also wrote a little tutorial on how to pass a PHP Class Object to Flash and how to bind the received value into a datagrid component using the cellRenderer api in order to customize its fields, adding checkboxes, numeric stepper and images to it…

You can find the tutorial here:
http://www.sephiroth.it/tutorials/flashPHP/cellRenderer/
More info about the cellRenderer APIs can be found here:
http://livedocs.macromedia.com/…/Documentation&file=16_cell4.htm

XML2Object

This class, that is just a translation of a simple set of function written for Flash MX, translates any kind of XML document into a readable Flash Object. This could be the core for projects XML oriented..
in fact i work often on xml based projects…

/**
* @class it.sephiroth.XML2Object
* @author Alessandro Crugnola
* @version 1.0
* @description return an object with the content of the XML translated
* NOTE: a node name with "-" will be replaced with "_" for flash compatibility.
* for example  will become FIRST_NAME
* If a node has more than 1 child with the same name, an array is created with the children contents
* The object created will have this structure:

* 	- obj {

*		nodeName : {

*			attributes : an object containing the node attributes

*			data : an object containing the node contents

*	}

* @usage data = new XML2Object().parseXML( anXML);
*/
//import utils.string

class it.sephiroth.XML2Object extends XML {
	private var oResult:Object = new Object ();
	private var oXML:XML;
	/**
	* @method get xml
	* @description return the xml passed in the parseXML method
	* @usage theXML = XML2Object.xml
	*/
	public function get xml():XML{
		return oXML
	}
	/**
	* @method public parseXML
	* @description return the parsed Object
	* @usage XML2Object.parseXML( theXMLtoParse );
	* @param sFile XML
	* @returns an Object with the contents of the passed XML
	*/
	public function parseXML (sFile:XML):Object {
		this.oResult = new Object ();
		this.oXML = sFile;
		this.oResult = this.translateXML();
		return this.oResult;
	}
	/**
	* @method private translateXML
	* @description core of the XML2Object class
	*/
	private function translateXML (from, path, name, position) {
		var xmlName:String;
		var nodes, node, old_path;
		if (path == undefined) {
			path = this;
			name = "oResult";
		}
		path = path[name];
		if (from == undefined) {
			from = new XML (this.xml);
			from.ignoreWhite = true;
		}
		if (from.hasChildNodes ()) {
			nodes = from.childNodes;
			if (position != undefined) {
				var old_path = path;
				path = path[position];
			}
			while (nodes.length > 0) {
				node = nodes.shift ();
				xmlName = node.nodeName.split("-").join("_");
				if (xmlName != undefined) {
					var __obj__ = new Object ();
					__obj__.attributes = node.attributes;
					__obj__.data = node.firstChild.nodeValue;
					if (position != undefined) {
						var old_path = path;
					}
					if (path[xmlName] != undefined) {
						if (path[xmlName].__proto__ == Array.prototype) {
							path[xmlName].push (__obj__);
							name = node.nodeName;
							position = path[xmlName].length - 1;
						} else {
							var copyObj = path[xmlName];
							path[xmlName] = new Array ();
							path[xmlName].push (copyObj);
							path[xmlName].push (__obj__);
							name = xmlName;
							position = path[xmlName].length - 1;
						}
					} else {
						path[xmlName] = __obj__;
						name = xmlName;
						position = undefined;
					}
				}
				if (node.hasChildNodes ()) {
					this.translateXML (node, path, name, position);
				}
			}
		}
		return this.oResult;
	}
}

An Example usage with this file:
http://www.sephiroth.it/tutorials/flashPHP/indice.xml

import it.sephiroth.XML2Object;
var oXML:XML = new XML ();
var sXML:String = "indice.xml";
oXML.ignoreWhite = true;
function xmlLoaded (success:Boolean) {
        if (success) {
                trace (success);
                _root.ObjFromXML = new XML2Object ().parseXML (this);
                delete this
        }
}
oXML.onLoad = xmlLoaded;
oXML.load (sXML);

undefined toString??

One of the things that it has always made me laugh on the new Flash AS2 is the translation of the undefined value (mmhh.. not a value, it’s undefined!).

a = undefined;trace(a.toString()) // undefined

b = a + " is not a string"trace(b) // "undefined is not a string"

???
Probably I don’t yet understand the strict data typing concept (quite sure), but undefined CAN’T be converted to "undefined".
It’s so Stupid for me!