The Silverlight APIs
Experimenting with the IronPython Web IDE
Silverlight & IronPython
In this article we will explore some of the APIs available to us in the CoreCLR that is the heart of Silverlight.
We will do this with the aid of the IronPython Web IDE:
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.Controls import Canvas
root = Canvas()
Application.Current.RootVisual = root
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).
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 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 restarting the video when it ends (causing it to loop).
html = '<strong>Set from IronPython</strong>'
HtmlPage.Document.experimental.innerHTML = html
e = HtmlPage.Document.GetElementById('experimental')
e.SetStyleAttribute('border', 'solid black 2px')
HtmlPage.Window.CreateInstance("writesomestuff", 'One last thing...\n')
from System.Windows.Browser.HtmlPage import Document
from System.Windows.Controls import TextBlock
t = TextBlock()
t.Text = 'Nothing yet...'
def OnClick(sender, event):
text = Document.input_field.value
t.Text = text
handler = EventHandler(OnClick)
The only way you can access files on the local filesystem is by presenting the user with an OpenFileDialog. If the user selects a file(s) then you can read from them without having access to their names (their location on the filesystem).
dialog = OpenFileDialog()
dialog.Filter = "Python files (*.py)|*.py|All files (*.*)|*.*"
dialog.Multiselect = True
if dialog.ShowDialog() == True:
data = dialog.File.OpenText().ReadToEnd()
print 'The user cancelled'
Inexplicably in-between the move from Silverlight 1.1 to 2.0 we lost the ability to set the Title on the dialog. Presumably it was judged a security risk...
There is no SaveFileDialog, 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 an example.)
from System.IO import (
FileMode, StreamReader, StreamWriter
store = IsolatedStorageFile.GetUserStoreForApplication()
isolated = IsolatedStorageFileStream(name, FileMode.OpenOrCreate, store)
writer = StreamWriter(isolated)
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 stored in the browser cache. You can create subdirectories and read / list / change files in those directories.
The APIs for accessing server resources from Silverlight are asynchronous. This means that we need to configure the request and provide callbacks for events that are raised as the request is executed. This means a slightly different style of programming than you may be used to, even for straightforward operations. On the other hand it does avoid blocking the user interface and so it can be a good change. One of the core classes for accessing web services and server resources is the WebClient.
from System.IO import StreamReader
from System.Net import WebClient
uri = Uri('/', UriKind.Relative)
web = WebClient()
def completed(s, e):
print 'Error?', e.Error
print 'Cancelled?', e.Cancelled
def changed(s, e):
print 'Bytes Recieved', e.BytesReceived
print 'Progress Percentage', e.ProgressPercentage
web.DownloadStringCompleted += completed
web.DownloadProgressChanged += changed
The example in the web IDE also shows the APIs for working with a stream instead of strings - useful if you want to download binary data (like an image).
Because events 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.
In order to invoke code back onto the GUI thread (the main thread) we need to use the Dispatcher.
from System.Threading import Thread, ThreadStart
text = TextBlock()
text.Text = "Nothing yet"
text.FontSize = 24
text.Text = 'Hello from another thread'
t = Thread(ThreadStart(wait))
This model can also be used for keeping calculations on a background thread whilst leaving the user interface responsive.
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:
# Read from a file in the 'xap'
handle = file('app.py')
data = handle.read()
t = TextBlock()
t.Text = data
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).
For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store.
Last edited Sat Aug 14 14:30:50 2010.