Il y a maintenant quelques semaines, je montrais comment récupérer des données de manière asynchrone (à l'aide de la méthode HttpWebRequest.BeginGetResponse) et mettre à jour l'interface dans Silverlight avec IronPython. Prochainement, je traiterai du databinding dans Silverlight avec IronPython, mais avant cela, je vais vous présenter une autre manière de récupérer des données de manière asynchrone.
Pour ce faire, je vais utiliser la méthode WebClient.DownloadStringAsync.
Pour les personnes les ayant utilisées en c#, la classe WebClient et sa méthode DownloadStringAsync sont facilement utilisables en IronPython (contrairement à mon exemple précédent qui nécessitait l'appel à IronPython.Runtime.Calls.CallTarget(0, 1, ..., n), le wrapper permettant de créer des délégués).
C'est d'ailleurs peu de chose de le dire, car elles s'utilisent exactement de la même façon (syntaxe mise à part).
Une fois votre object WebClient créé, il vous faut créer un délégué DownloadStringCompletedEventHandler. ce délégué identifiera la méthode qui s'occupera de gérer l'événement DownloadStringCompleted.
Sans plus tarder, voici le code qui vous montre la marche à suivre:
from System.Windows import Application
from System.Windows.Controls import UserControl
class App:
def __init__(self):
self.scene = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
def start(self):
url = ... #url de votre source de données
self.getDatas(url)
def getDatas(self, url):
from System.Net import WebClient, DownloadStringCompletedEventHandler
wc = WebClient()
wc.DownloadStringCompleted += DownloadStringCompletedEventHandler(self.downloadDatasCompleted)
from System import Uri
endPoint = Uri(url)
wc.DownloadStringAsync(endPoint)
def downloadDatasCompleted(self, sender, e):
if e.Error == None:
try:
datas = e.Result
... #traitement
finally:
... #traitement
App().start()
A bientôt
EDIT : j'ai lu dans un post de Michael Foord (blog qui répertorie les ressources que l'on peut trouver sur le net touchant de près ou de loin à IronPython) qu'il n'était pas nécessaire d'utiliser explicitement un délégué DownloadStringCompletedEventHandler. Il suffit en fait d'écrire wc.DownloadStringCompleted += self.downloadDatasCompleted et IronPython se charge du reste :).
Dans le présent billet, je vais aborder une manière de récupérer un flux dans Silverlight de manière asynchrone et de mettre à jour l'UI (grâce à IronPython, mais ça vous l'aviez déjà compris ;)).
1 - Récupération du flux:
Pour ce faire, vous avez à votre disposition la méthode HttpWebRequest.BeginGetResponse.
signature --> public IAsyncresult BeginGetResponse(AsyncCallback, Object)
Voici le code qui la met en oeuvre :
import clr
clr.AddReference("IronPython")
from System.Windows import Application
from System.Windows.Controls import UserControl
class App:
def __init__(self):
self.scene = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
def start(self):
self.getDatas()
def getDatas(self):
from System import Uri, AsyncCallback
endPoint = Uri("url")
from System.Net import WebRequest
request = WebRequest.Create(endPoint) #création de la requête
request.BeginGetResponse(AsyncCallback(self.readCallback), request)
def readCallback(self, result):
request = result.AsyncState #récupération de la requête
response = request.EndGetResponse(result) #récupération de la réponse
from System.Net import HttpStatusCode
if response.StatusCode == HttpStatusCode.OK: #test du statut
stream = response.GetResponseStream() #récupération du flux
#Mise à jour de l'UI
...
App().start()
2 - Mise à jour de l'UI
A ce moment, nous avons récupéré le flux depuis notre url (webservice ou autre...), mais nous ne pouvons pas modifier l'interface. En effet, nous ne sommes plus dans le 'Main Thread' et toute tentative de mise à jour directe sera bloquée.
Pour ce faire il nous faut utiliser un Dispatcher et sa méthode BeginInvoke comme suit:
self.scene.Dispatcher.BeginInvoke(...)
La méthode BeginInvoke attend un delegate et un object[] en paramètre. comment y arriver avec IronPython?
3 - delegate
Si vous avez introspecté la dll IronPython à l'aide de reflector, vous êtes certainement tombé sur ce qui nous sera très utile dans le cas qui nous intéresse ici. il s'agit du wrapper CallTarget1 (namespace IronPython.Runtime.Calls) qui nous retourne un delegate :)
Nous avons donc :
def readCallback(self, result):
request = result.AsyncState #récupération de la requête
response = request.EndGetResponse(result) #récupération de la réponse
from System.Net import HttpStatusCode
if response.StatusCode == HttpStatusCode.OK: #test du statut
stream = response.GetResponseStream() #récupération du flux from IronPython.Runtime import Calls
self.scene.Dispatcher.BeginInvoke(Calls.CallTarget1(self.update), stream)
def update(self, stream):
from System.IO import StreamReader
reader = StreamReader(stream)
#... mise à jour
reader.Close()
Pour les modifications de l'interface, à vous de jouer ;)
il est également possible d'utilser la méthode DownloadStringAsync.
A plus.