Dynamiczne języki programowania i Silverlight  

Udostępnij na: Facebook

Autor: Bartosz Kierun

Opublikowano: 2011-02-18

W niniejszym artykule zajmiemy się integracją platformy Silverlight oraz dynamicznych języków programowania IronPython oraz IronRuby.

Po przeczytaniu artykułu dowiesz się:

  • jak wygląda integracja technologii Silverlight z językami IronPython oraz IronRuby,
  • jak stworzyć za pomocą tego tandemu swoją pierwszą aplikację.

Wstęp

W ostatnich latach można było zauważyć niesamowity rozwój technologii webowych, w tym aplikacji typu RIA (Rich Internet Application). Okazało się, że jej zalety – takie jak: wieloplatformowość, łatwiejsza dystrybucja i wdrażanie nowych wersji, możliwość umieszczenia dużej części logiki biznesowej po stronie serwera lub klienta, czy wreszcie możliwość tworzenia zaawansowanych interfejsów użytkownika (a często i całych aplikacji) bez konieczności używania niezbyt łatwego w programowaniu języka JavaScript i technologii AJAX – pozwalają łatwiej tworzyć nawet zaawansowane rozwiązania. Wśród aplikacji typu RIA można obecnie spotkać zarówno proste gry, jak i zaawansowane aplikacje do komunikacji audio/wideo.

Najbardziej znaną technologią do tworzenia aplikacji typu RIA jest bez wątpienia technologia Flex firmy Adobe, ale i firma Microsoft próbuje od jakiegoś czasu zdobyć nieco większą część tego rynku, intensywnie rozwijając swoją technologię o nazwie Silverlight. W czasie pisania tego artykułu bieżąca wersja tej technologii to Silverlight 4, ale już pojawiają się zapowiedzi nowych funkcjonalności w wersji piątej.

Ze względu na mniejszej wielkości aplikacje, budowane zazwyczaj przy użyciu technologii RIA, dynamiczne języki programowania zdają się dosyć dobrze do nich pasować, oferując programistom łatwość programowania i dużą elastyczność, bez nadmiernej ilości „formalizmów” narzucanych zazwyczaj przez „dojrzałe”, statycznie typowane języki programowania, takie jak C# czy Visual Basic (w aplikacjach serwerowych) lub znacznie mniej formalny i często kłopotliwy w użyciu JavaScript (w programach uruchamianych po stronie klienta).

Co to jest Silverlight?

Silverlight to stworzona przez firmę Microsoft technologia do tworzenia aplikacji internetowych z bogatym interfejsem użytkownika, uruchamianych za pomocą specjalnego środowiska uruchomieniowego po stronie klienta, zazwyczaj za pomocą odpowiedniego dodatku doinstalowywanego do przeglądarki internetowej (choć w przypadku technologii Silverlight istnieje również możliwość uruchamiania takich aplikacji poza przeglądarką). W przeciwieństwie do technologii .NET Framework, który to pakiet w przypadku instalacji zajmuje nawet kilkaset megabajtów, paczka instalacyjna z platformą Silverlight to najwyżej kilka megabajtów. Technologia Silverlight rozwija się również dużo dynamiczniej od technologii .NET Framework, z wersji na wersję zyskując nowe możliwości i funkcjonalności. Liczba bibliotek standardowych dostarczanych wraz z technologią Silverlight jest oczywiście dużo uboższa od tego, co znamy z platformy .NET, ale w kontekście aplikacji internetowych naprawdę dużo nam nie brakuje. Do niewątpliwych zalet należy zaliczyć natomiast to, że model obiektowy tychże bibliotek standardowych w obu technologiach jest do siebie bardzo podobny.

Warto również nadmienić, że sposób tworzenia interfejsów użytkownika wzorowany jest na technologii WPF (Windows Presentation Foundation), co daje programistom naprawdę spore możliwości w kontekście grafiki 2D i 3D oraz animacji i możliwości budowania formularzy. Do unikalnych zalet technologii Silverlight należą również interfejsy programistyczne, pozwalające na strumieniowe odtwarzanie muzyki i obrazów wideo (nawet w rozdzielczości HD) czy obsługa kamer internetowych.

Podsumujmy zatem najważniejsze cechy platformy Silverlight:

  • Wieloplatformowość (jeżeli tymi platformami są Windows lub MAC) oraz wsparcie dla większości popularnych przeglądarek internetowych.
  • Interfejs użytkownika oparty na modelu programistycznym technologii WPF.
  • Bogaty zestaw bibliotek standardowych, ze wsparciem między innymi dla wielowątkowości, operacji na gniazdach sieciowych (oraz bardziej wysokopoziomowych technologii do komunikacji, takich jak usługi sieciowe XML Web Services czy Windows Communication Foundation), technologii XML czy JSON.
  • Zaawansowany odtwarzacz wideo.
  • Technologia Deep Zoom pozwalająca na dynamiczne ładowanie nawet bardzo dużej ilości grafiki i obrazów oraz adaptacyjną obsługę strumieni wideo.
  • Możliwość przechowywania danych na komputerze klienta.
  • Możliwość interakcji z modelem obiektowym DOM, czyli ustandaryzowaną reprezentacją strony w języku HTML lub dokumentu XML oraz językiem JavaScript.

Osoby zainteresowane pogłębieniem swojej wiedzy o platformie Silverlight odsyłam na stronę: https://www.silverlight.net/.

Po krótkim wstępie przejdźmy zatem do tematów omawiających integrację technologii Silverlight oraz dynamicznych języków programowania, takich jak IronPython czy IronRuby.

Projekt „Gestalt”

Do chyba najbardziej kompletnych i dopracowanych projektów pozwalających na tworzenie aplikacji na platformie Silverlight za pomocą dynamicznych języków programowania należy rozwijany już od jakiegoś czasu projekt „Gestalt”. Jest to rozwiązanie umożliwiające łączenie języka Python, Ruby oraz XAML (eXtensible Application Markup Language) bezpośrednio na stronie webowej.

Rys.1. Logo projektu.

Do najważniejszych cech rozwiązania należą:

  • Możliwość użycia kodu w języku Ruby oraz Python pomiędzy znacznikami <script> bezpośrednio na stronach (X)HTML. Z punktu widzenia programisty można bez problemu zauważyć analogię do sposobu, w jaki używa się języka JavaScript. Wymienionych języków programowania można użyć zarówno w bardzo prostych przypadkach – takich jak manipulacja modelem obiektowym strony, jak i do budowy zaawansowanych interfejsów użytkownika w języku XAML czy kontrolowania złożonych animacji. Projekt posiada również wsparcie dla języka HTML 5.
  • Możliwość użycia języka XAML do budowy zaawansowanych interfejsów użytkownika. Choć aplikacje, takie jak galeria obrazów, formularze do wprowadzania danych, czy komunikator internetowy da się stworzyć tylko i wyłącznie polegając na języku (X)HTML i JavaScript oraz ewentualnie technologii AJAX, to analogiczne rozwiązania tworzy się jednak zdecydowanie szybciej i prościej za pomocą języka XAML, wspomagając się znacznie bardziej zaawansowanymi językami programowania takimi jak Python czy Ruby. Dzięki niezależności od platformy czy przeglądarki internetowej nie musimy się martwić o wiele wynikających z tego problemów, znanych bez wątpienia wielu programistom aplikacji webowych.
  • Kompilacja kodu napisanego w językach Ruby czy Python jest przezroczysta dla programisty, a dzięki sprytnym metodom dynamicznej kompilacji taki kod jest również wyjątkowo szybki (w przeciwieństwie do często interpretowanego za każdym razem kodu w języku JavaScript). Projekt „Gestalt” wykorzystuje tutaj intensywnie możliwości oferowane przez platformę DLR (Dynamic Language Runtime).
  • Dzięki swojej prostocie tworzone rozwiązania oparte na projekcie „Gestalt” nie wymagają stosowania zaawansowanych środowisk programistycznych takich jak np. Visual Studio. Do stworzenia nawet większej aplikacji możemy użyć edytora tekstu typu Notepad++ na platformie Windows czy edytora TextMate, jeżeli używamy komputerów typu Mac.
  • Wieloplatformowość została tutaj osiągnięta dzięki wykorzystaniu platformy Silverlight i jej środowiska uruchomieniowego. Innymi słowy, wspierane są te systemy operacyjne oraz te przeglądarki internetowe, które wspiera technologia Silverlight.
  • Dzięki swojej względnej popularności projekt ten doczekał się wielu dodatków i rozszerzeń, które możemy wykorzystać, budując własne aplikacje. Dostępne są między innymi komponenty do wyświetlania wideo, dźwięku czy grafiki.

Witryny związane z projektem:

Budowa prostej aplikacji

Użycie wymienionych wcześniej technologii jest wyjątkowo proste, niemniej jednak podamy kilka niezbędnych informacji. Do działania tworzonych przez nas aplikacji potrzebnych będzie kilka dodatkowych plików, które będą musiały stać się częścią tworzonych przez nas aplikacji webowych. Te pliki to:

  • dlr.js – skrypt umożliwiający stosowanie kodu języków Ruby lub Python na stronach (X)HTML,
  • Microsoft.Scripting.slvx, dlr.xap – rozszerzenia platformy Silverlight będące implementacją wsparcia dla efektywnego wykonywania dynamicznych języków programowania,
  • IronPython.slvx, IronRuby.slvx – implementacje języków IronPython oraz IronRuby pod platformę Silverlight.

Teraz przejdźmy do analizy kodu najprostszej aplikacji. Na załączonym przykładzie widzimy, że poza referencją do pliku dlr.js nie ma tutaj żadnych szczególnych wymagań. Efektem wykorzystania projektu „Gestalt” jest natomiast możliwość wykorzystania, jako głównego języka programowania po stronie klienta, języka IronPython.

<html>
<head>
    <script src="/dlr/dlr.js" type="text/javascript"></script>
</head>
<body>

<input id="say_hello" type="button" value="Say, Hello!" />

<script type="text/python">
    import System
    def OnClick(s,e):
      window.Alert("Hello, World!")
    document.say_hello.AttachEvent('onclick', System.EventHandler[System.Windows.Browser.HtmlEventArgs](OnClick))
  </script>

</body>
</html>

Przykład dla języka IronRuby będzie wyglądał bardzo podobnie. Jedyna różnica to inna wartość atrybutu w znaczniku <script> oraz nieco inna składnia samego języka.

<html>
<head>
    <script src="/dlr/dlr.js" type="text/javascript"></script>
</head>
<body>

<input id="say_hello" type="button" value="Say, Hello!" />

<script type="text/ruby">
  def onclick(s,e)
    window.alert "Hello, World!"
  end
  document.say_hello.attach_event(
    'onclick', 
    System::EventHandler[System::Windows::Browser::HtmlEventArgs].new(method(:onclick))
  )
</script>

</body>
</html>

Wynik uruchomienia powyższych aplikacji pokazano na poniższym zrzucie ekranu:

Rys.2. Prosta aplikacja.

Po najprostszych aplikacjach warto przejść do przykładu wykorzystującego język XAML. W tym przypadku znowu praktycznie cała filozofia sprowadza się do użycia w znacznikach <script> kodu w języku XAML, co ilustruje poniższy przykład:

<script type="application/xml+xaml" id="mushroom" width="200" height="280">
  <UserControl x:Name="blinking_mushroom" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" Width="200" Height="280" Background="Black" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
    <UserControl.Resources>
      <Storyboard x:Name="left_eye_blink" RepeatBehavior="Forever">
        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Path_3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
          <SplineDoubleKeyFrame KeyTime="00:00:00.1000000" Value="0.025"></SplineDoubleKeyFrame>
          <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="1"></SplineDoubleKeyFrame>
          <SplineDoubleKeyFrame KeyTime="00:00:03" Value="1"></SplineDoubleKeyFrame>
        </DoubleAnimationUsingKeyFrames>
. . .
</script>

Rys.3. Przykład aplikacji wykorzystującej język XAML.

Integracja – typowe konstrukcje

Dla utrwalenia i uporządkowania tematu pokażmy kilka przykładowych konstrukcji używanych podczas tworzenia aplikacji opartych na projekcie „Gestalt”.

Przygotowanie strony

Jedynym wymaganym wpisem na stronie .html lub .aspx jest referencja do pliku dlr.js.

<script src="http://gestalt.ironpython.net/dlr-latest.js"
        type="text/javascript"></script>

Jeżeli chcemy hostować ten plik na własnym serwerze, można oczywiście użyć jednej z następujących konstrukcji:

<script type="text/javascript">

    window.DLR = {path: 'path/to/gestalt.latest'}

</script>

<script src="path/to/gestalt.latest/dlr.js" type="text/javascript">

</script>

Użycie kodu w języku Python lub Ruby

Poniżej przedstawiono dwa sposoby osadzenia kodu w języku Python w znacznikach <script>:

<script type="application/python">

    window.Alert("Python inline script-tag")

</script>

<script type="text/python">

    window.Alert("Also a Python inline script-tag")

</script>

W wielu przypadkach istnieje również możliwość użycia opcjonalnego atrybutu defer. Jeżeli posiada on wartość ustawioną na ‘true’ lub w ogóle jest obecny (to dziwne, jak czasami zachowują się nowoczesne przeglądarki), to zawarty pomiędzy znacznikami <script> kod będzie mógł być wykonany później:

<script type="application/python" defer="true" id="for_later">

     print 6 + 1

</script>

<script type="application/python">

     eval(document.for_later.innerHTML)

</script>

Niektórzy Czytelnicy zauważyli z pewnością, że w większości przykładów przedstawionych do tej pory używana była niezbyt elegancka technika umieszczania w jednym pliku kodu różnych technologii (HTML + Python lub Ruby). Jest to raczej niepożądane zjawisko, określane mianem spaghetti code. Aby go uniknąć, na naszych stronach możemy używać, oczywiście, referencji do plików z większą ilością kodu w języku Python lub Ruby. Najczęściej używane rozszerzenia takich plików to analogicznie „.py” oraz „.rb”:

# foo.py
window.Alert("Hello from a python file")

<!-- foo.html -->
<script type="application/python" src="foo.py"></script>

Użycie kodu w języku XAML

Użycie kodu w tym języku nie różni się zbytnio od sposobu użycia kodu w języku Python lub Ruby. Kod XAML może być osadzony pomiędzy znacznikami <script> zarówno bezpośrednio, jak i poprzez referencję:

<!-- Osadzony kod XAML -->
<script type="application/xml+xaml" id="inlineXAML" width="200" height="75">
  <Canvas Background="Wheat">
    <TextBlock Canvas.Left="20" FontSize="24" />
  </Canvas>
</script>

<!-- referencja do pliku XAML -->
<script type="application/xml+xaml" id="externalXAML" src="foo.xaml">
</script>

Programiści zajmujący się wcześniej technologią WPF lub Silverlight wiedzą, że praktycznie ten sam efekt można osiągnąć zarówno w kodzie deklaratywnym przy użyciu języka XAML, jak i przy użyciu kodu imperatywnego (np. w C# lub Pythonie).

Poniższy kod:

<script type="application/xml+xaml" id="xamlContent">
  <Canvas Background="Wheat">
    <TextBlock Canvas.Left="20" FontSize="24" />
  </Canvas>
</script>

posiada więc swój ekwiwalent w postaci:

from System.Windows import Application

from System.Windows.Media import SolidColorBrush, Colors

from System.Windows.Controls import Canvas, TextBlock

c = Canvas(Background = SolidColorBrush(Colors.Wheat))

t = TextBlock(FontSize = 24)

c.Children.Add(t)

Canvas.SetLeft(t, 20)

Application.Curren.RootVisual = c

Interakcja z modelem obiektowym strony DOM

Interakcja tego typu jest bardzo prosta i sprowadza się do poznania kilku faktów. Dostęp do znaczników kodu HTML lub XHTML wygląda bardzo podobnie to tego, co oferuje język JavaScript, a mianowicie każdy element reprezentowany jest jako zmienna (przy czym proces ten odbywa się w sposób całkowicie automatyczny):

document.a_div_id
# to samo co ...
document.GetElementById("a_div_id")

document.doesnotexist # None

Kolejny przykład (odwoływanie się do wartości atrybutów):

document.a_div_id.innerHTML

# to samo co...

document.a_div_id.GetProperty("innerHTML")

document.a_div_id.innerHTML = "Witam!"

# to samo co...

document.a_div_id.SetProperty("innerHTML", "Witam!")

Sytuacja wygląda bardzo podobnie, gdy chcemy zapewnić interakcję z elementami zdefiniowanymi w kodzie XAML. Przykład:

root_visual.Message.Text = "Dostałeś wiadomość"

Nie zapomniano też oczywiście o obsłudze zdarzeń:

<!--standardowy element ‘input’ -->
<a id="cm">Click Me</a>
<!—i obsługa zdarzenia ‘click’ w kodzie języka Python -->
<script type="application/python">
  def do_c(link):
    link.innerHTML = "Clicked!"
  document.cm.events.onclick += do_c
</script>

Analogicznie wygląda sytuacja dla obsługi zdarzeń elementów z kodu XAML:

<script type="application/xml+xaml">

  ...

<TextBox x:Name="xcm" Text="Click Me" />

  ...

</script>

<script type="application/python">

  def click(s, e):

      s.text = "Clicked!"

  root_visual.xcm.MouseLeftButtonDown += click

</script>

Operacje na systemie plików

Jedną z ciekawostek, którą warto przytoczyć, jest specyficzny i różniący się nieco od tego co oferuje platforma Silverlight sposób obsługi systemu wejścia/wyjścia, a w szczególności operacji na plikach. Jak wiemy, kod wykonywany pod kontrolą środowiska uruchomieniowego Silverlight uruchamiany jest w tzw. ograniczonym kontekście bezpieczeństwa (sandbox), chroniąc komputer klienta przed możliwością uzyskania nieuprawnionego dostępu do jego systemu plików lub możliwości wykonania niebezpiecznego kodu.

Użycie języka Python daje nam jednakże możliwość dostępu do plików wskazanych w zewnętrznych znacznikach <script>, tak jak do zawartości systemu plików (w trybie tylko do odczytu). Daje to bardzo ciekawe możliwości, szczególnie w kombinacji np. z archiwami typu ZIP, których obsługę wspiera język Python.

Poniższy przykład ilustruje dostęp do jednego z plików znajdujących się w archiwum ZIP:

<script type="application/x-zip-compressed" src=" moje_archiwum.zip "></script>
<script type="application/python">
  import clr
  clr.AddReferenceToFile("moje_archiwum.zip/biblioteka.dll")
  txt = open("moje_archiwum.zip/plik.txt").read()
</script>

Widgety

Widgety to małe, reużywalne aplikacje, których możemy użyć we własnych rozwiązaniach. Dostępny jest widget do odtwarzania obrazu wideo oraz dźwięku, a także widget do prezentacji galerii zdjęć.

Rys.4. Widgety w akcji.

Debugowanie i interaktywna konsola

W debugowaniu oraz eksperymentach z technologią Silverlight i językiem IronPython lub IronRuby może pomóc nam interaktywna konsola dostępna wprost z poziomu przeglądarki. W przypadku używania środowiska uruchomieniowego Visual Studio, debugowanie można wykonywać w standardowy sposób.

Rys.5. Interaktywna konsola IronPython.

Dynamiczne języki, Silverlight i Visual Studio

Dla osób używających już do codziennej pracy środowiska programistycznego Visual Studio warto odnotować, iż pakiet ten (po zainstalowaniu odpowiedniego dodatku) posiada spore wsparcie dla tworzenia aplikacji przy użyciu technologii Silverlight oraz języka IronPython lub IronRuby.

Projekt typu Silverlight w języku IronPython w akcji:

Rys.6. Silverlight i IronPython.

Debugowanie kodu w Visual Studio:

Rys.7.Debugowanie.

Podsumowanie

Po przeczytaniu niniejszego artykułu dowiedzieliśmy się, jak wygląda integracja dynamicznych języków z platformą Silverlight oraz poznaliśmy kilka typowych przypadków jej użycia.

Osoby zainteresowane dalszą eksploracją tego tematu zachęcam do zapoznania się z przykładami dostępnymi na stronie projektu „Gestalt” lub witrynie Codeplex.