The Silverlight APIs

Experimenting with the IronPython Web IDE

The IronPython Web IDE

 

 

The IronPython Web IDE

In this article we will explore some of the APIs available to us in the Core-CLR that comes with Silverlight 2.0 Beta 1.

We will do this with the aid of the IronPython Web IDE:

www.voidspace.org.uk/ironpython/webide/webide.html

You can download the Web IDE as a project:

It comes with several pieces of example code, but also lets you explore the Silverlight API and load and save Python code.

It uses the Javscript EditArea syntax highlighting Python editor.

Standard out is diverted, so that print statements are sent to the 'debugging pane' at the bottom of the screen. Tracebacks are also printed there, to aid debugging.

The examples here are all shortened forms of the full code in the Web IDE, so I highly recommend checking it out. In all these examples, root is the root visual element. This has already been set using code that looks like:

from System.Windows import Application
from System.Windows.Controls import Canvas

The root element doesn't need to be a canvas. Some of the Microsoft examples use a UserControl or subclass. The programming model for Silverlight is based on WPF. It isn't identical, but in many cases some code and XAML could be shared between desktop and Silverlight applications. Those applications would only run on Windows though, there is currently no effort to port WPF to Mono (although it is possible that the Moonlight effort may change this of course).

The Video Player

One of the impressive features of Silverlight is the video player. From code this is called the MediaElement, and we can use it from IronPython.

from System.Windows.Controls import MediaElement
from System import TimeSpan, Uri, UriKind

m = MediaElement()
u = Uri('HelloWorld.wmv', UriKind.Relative)
t = TimeSpan(0)
m.Source = u
m.Position = t

The example in the Web IDE also shows setting events on the MediaElement to handle it being clicked and the video ending.

Accessing the Browser DOM

Changing HTML elements and setting style attributes. HtmlPage.Document gives us access to the browser DOM. We can access elements as attributes on the document (just like in Javascript - something you can't do from C#!), or use the GetElementById method. CSS attributes are set with the SetStyleAttribute method on elements.

from System.Windows.Browser import HtmlPage

html = '<strong>Set from IronPython</strong>'
HtmlPage.Document.experimental.innerHTML = html
e = HtmlPage.Document.GetElementById('experimental')
e.SetStyleAttribute('border', 'solid black 2px')


# Call a javascript function "writesomestuff"
HtmlPage.Window.CreateInstance("writesomestuff", 'One last thing...\n')

The last part of the code (HtmlPage.Window.CreateInstance) calls a Javascript function defined in the web-page.

We can also attach event handlers to Javascript events from Silverlight:

from System import EventHandler
from System.Windows.Browser.HtmlPage import Document
from System.Windows.Controls import TextBlock

root.Children.Clear()
t = TextBlock()
t.Text = 'Nothing yet...'
root.Children.Add(t)

def OnClick(sender, event):
    text = Document.input_field.value
    t.Text = text

handler = EventHandler(OnClick)
Document.OkButton.AttachEvent("onclick", handler)

Open File Dialog

from System.Windows.Controls import (
    OpenFileDialog, DialogResult
)

dialog = OpenFileDialog()
dialog.Filter = "Python files (*.py)|*.py|All files (*.*)|*.*"
if dialog.ShowDialog() == DialogResult.OK:
    print dialog.SelectedFile.OpenText().ReadToEnd()

Inexplicably in-between Silverlight 1.1a and 2.0b1 the OpenFileDialog lost the ability to set the Title. Presumably it was judged a security risk...

There is no save file dialog, but you can hack it up by bouncing data off a CGI script that sends the right headers back to the browser! (See this blog entry for details.)

IsolatedFileStorage

from System.IO.IsolatedStorage import (
    IsolatedStorageFile, IsolatedStorageFileStream
)
from System.IO import (
    FileMode, StreamReader, StreamWriter


)

store = IsolatedStorageFile.GetUserStoreForApplication()
isolated = IsolatedStorageFileStream(name, FileMode.OpenOrCreate, store)

writer = StreamWriter(isolated)
writer.Write(data)
writer.Close()
isolated.Close()

Silverlight provides 1 megabyte of local storage ('in the browser') per application, which presumably means per URL. Your application can request that this limit be raised, but it requires user confirmation.

You access it through the IsolatedStorageFile class. Isolated storage provides a filesystem. You can create subdirectories and read / list / change files in those directories.

Accessing Server Resources with the WebClient

from System import Uri, UriKind
from System.IO import StreamReader
from System.Net import WebClient

uri = Uri('/', UriKind.Relative)
web = WebClient()

def completed(s, e):
    print 'Completed'
    print 'Error?', e.Error
    print 'Cancelled?', e.Cancelled
    print e.Result

def changed(s, e):
    print 'Bytes Recieved', e.BytesReceived
    print 'Progress Percentage', e.ProgressPercentage

web.DownloadStringCompleted += completed
web.DownloadProgressChanged += changed
web.DownloadStringAsync(uri)

The changed and completed events are raised asynchronously. This means that if you need to know when they are completed then you will have to signal from the event handler.

Because they are asynchronous they are also raised on another thread. If you want to interact with the user interface (the WPF event loop) then we need to know a little bit about the WPF threading model.

Silverlight and Threading

In order to invoke code back onto the GUI thread (the main thread) we need to use the Dispatcher.

from System.Windows.Controls import TextBlock
from System.Threading import Thread, ThreadStart

text = TextBlock()
text.Text = "Nothing yet"
text.FontSize = 24
root.Children.Clear()
root.Children.Add(text)

def wait():
    Thread.Sleep(3000)
    def SetText():
        text.Text = 'Hello from another thread'
    text.Dispatcher.BeginInvoke(SetText)

t = Thread(ThreadStart(wait))
t.Start()

This model can also be used for keeping calculations on a background thread whilst leaving the user interface responsive.

Reading Files from the 'xap'

Silverlight doesn't give us access to the user's filesystem (except through the open file dialog). The file type is still present from IronPython though, and we can use it to read files contained in the 'xap'. This provides a convenient way of packaging data for your app:

from System.Windows.Controls import TextBlock

# Read from a file in the 'xap'
handle = file('app.py')
data = handle.read()
handle.close()

t = TextBlock()
t.Text = data

root.Children.Clear()
root.Children.Add(t)

There are several other examples in the Web IDE that we haven't covered here. These include setting the position of objects on a canvas, loading XAML and so on. Another potentially useful example is the Button class that is used to make the wonderful blue and yellow buttons in the Web IDE. This is contained in the file button.py and illustrates initialising a control from XAML (amongst other things).

The next article looks at the scriptable attributes, and how we can use them to communicate between C# and Javascript. Unfortunately this needs some C#, but we can compile this from the command line without needing to install Visual Studio.

For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store. If you're looking for a new techie job, try the Voidspace Tech Job Board. This is part of the Hidden Network of technology and programming jobs.

Hosted by Webfaction

Return to Top

Page rendered with rest2web the Site Builder

Last edited Sat Mar 22 00:53:32 2008.


Counter...


Voidspace: Cyberpunk, Technology, Fiction and More
Search this Site:
 
Web Site

IronPython in ActionIronPython in Action

Blogads

Follow me on:

Twitter

Pownce

Jaiku

Del.icio.us

Shared Feeds

Tech Jobs

Hidden Network

Tech Jobs Board

Hosting for an agile web