Création d’interfaces graphiques
Introduction
1. Pourquoi une interface graphique ?
Quand on apprend un langage de scripting, 99 % du temps, c’est pour automatiser un grand nombre de tâches fastidieuses et répétitives. Ces tâches n’apportent généralement aucune plus-value à notre travail et à nos compétences. Mais alors, pourquoi créer des interfaces graphiques ? Les scripts que l’on crée au fil du temps deviennent des outils. Ces outils, on les utilise de façon quotidienne. Quand d’autres personnes observent le travail que l’on réalise avec ces outils, ça leur donne envie de les utiliser. Mais toutes ne sont pas encore aptes à sauter le pas et utiliser une ligne de commande pourrait les dérouter. On crée donc une sorte de masque séduisant qui vient habiller le script de cases à cocher et de boutons très simples d’utilisation. Un utilisateur lambda peut alors exécuter le script de manière simpliste. On limite également les erreurs de frappe débouchant sur un appel au support.
Un autre point est appréciable avec les interfaces graphiques sous PowerShell. Les scripts n’étant pas compilés (ce qui n’est pas le cas avec C#), les interfaces sont facilement modifiables. On est ainsi libre d’apporter sa griffe, ou de compléter un élément...
Windows Forms
1. Présentation des Windows Forms
Les Windows Forms (ou WinForms) sont apparues depuis la version 2.0 du framework .NET. Elles sont regroupées dans un ensemble de classes dans l’assembly System.Windows.Forms. Attention toutefois, car cette dernière n’est pas chargée par défaut dans PowerShell. Il est donc nécessaire de le faire manuellement avant toute manipulation de WinForms. Pour cela, on utilise la commande Add-Type :
PS > Add-Type -AssemblyName System.Windows.Forms
Si les Windows Forms sont contenues dans des classes, cela signifie que leur utilisation passe par l’instanciation d’objets à travers celles-ci (voir le chapitre Classes), ce que l’on fait tout le temps sous PowerShell. Il est donc possible de passer en revue le contenu de chaque objet à l’aide de la commande Get-Member.
Dans une interface graphique, chaque élément est génériquement nommé contrôle. Cela va du simple bouton à la forme complète de la fenêtre. Chaque contrôle est représenté via une des classes WinForms. La caractéristique des contrôles est qu’ils peuvent être greffés les uns aux autres, par exemple une case à cocher ou un menu que l’on souhaite positionner sur une fenêtre (Form).
Une des particularités des Forms est qu’elles obtiennent les mêmes caractéristiques graphiques que celles sur lesquelles se fonde le système d’exploitation. L’exemple le plus parlant est la transparence des fenêtres sous Windows Vista et Windows 7.
Les contrôles disposent d’une grande palette d’événements. Ces derniers sont en fait des interactions sur ces formes, comme un clic de souris ou une frappe sur le clavier. Et sur chacun de ses événements, on peut déclarer des actions à réaliser. Par exemple, la création d’une nouvelle Form pour afficher un message. Ou encore, lancer un traitement en tâche de fond sur plusieurs machines distantes. Les possibilités sont vastes. Tout contrôle peut avoir un ou plusieurs événements configurés, et chaque événement peut avoir à son tour une ou plusieurs actions paramétrées.
Les interfaces graphiques n’étant...
Windows Presentation Foundation
WPF est un choix d’avenir en termes de conception graphique. Microsoft a mis au point cette technologie en 2006 et n’a cessé d’y incorporer des fonctionnalités. Au contraire de Windows Forms, qui a été abandonné. WPF se base sur un aspect vectoriel qui se décorrèle de la résolution d’écran. De plus, il permet de bénéficier de l’accélération matérielle des GPU, libérant ainsi la charge CPU.
Sa définition s’effectue à travers un langage en XAML, séparant ainsi la définition de l’interface graphique du reste du traitement. Le code rédigé en XAML peut être stocké de deux manières en PowerShell, soit dans un fichier à part avec une extension XAML, soit directement à l’intérieur du script, contenu dans une Here-String. Les deux cas seront présentés. L’avantage de la Here-String, c’est la réduction des dépendances dans des fichiers extérieurs au script. L’avantage du fichier XAML séparé est que la mise à jour de la GUI ne nécessite pas de modification du fichier script, le XAML seul peut être modifié.
Une notion à bien prendre en compte lorsque l’on fait du WPF est l’apartment. Pour faire simple, il s’agit du cloisonnent subi par chaque thread. Deux modes sont acceptables. En mode STA (Single-Threaded Apartment), chaque thread est complètement cloisonné. Les objets créés à l’intérieur ne sont consultables que par lui. En mode MTA (Multi-Threaded Apartment), plusieurs threads peuvent partager le même cloisonnement, et donc les objets qu’ils contiennent.
Pour la génération graphique en WPF, le mode STA doit être utilisé. D’ailleurs, depuis la version 3.0 de PowerShell, la console est exécutée par défaut dans ce mode. Et PowerShell ISE est une application WPF exécutée en STA.
Pour Windows Forms, il est nécessaire de charger un assembly contenant les classes d’objets....
Mise à jour d’une GUI via des runspaces
Lorsqu’on utilise WPF, l’interface graphique n’est pas mise à jour tant que les actions affectées à l’événement d’un contrôle ne sont pas terminées. Cela provoque alors un freeze de l’interface graphique. On a pu le constater précédemment avec la barre de progression qui se remplit seulement lorsque tous les pings sont réalisés.
Il existe toutefois un moyen de contourner ce problème. Mais attention, les notions qui vont suivre sont ardues.
Le problème qui se pose ici est l’incapacité d’un runspace en mode STA à effectuer plusieurs traitements. Or, il s’agit d’un prérequis lorsque l’on fait du WPF. Pour résoudre cette contrainte, on doit créer plusieurs runspaces. Le tout premier contient la définition de l’interface graphique. Le ou les suivants se décomposent en fonction des besoins de la GUI. Il est recommandé d’en faire un par traitement relativement long. Pour donner un exemple, le traitement de la case à cocher ajoutée précédemment est court. Il ne nécessite pas de runspace. Cependant, le traitement mis en place sur l’événement Click du bouton est lui assez long. Un runspace doit donc lui être attribué.
Pour éviter de trop surcharger les exemples de code, nous repartirons sur un exemple plus simple.
La création d’un runspace passé par l’utilisation de la classe [runspacefactory] et de sa méthode statique CreateRunspace(). Cette dernière crée un objet de type [runspace].
Exemple
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
Dans ce cas présent, le runspace est de type STA. La première instruction crée un nouveau thread qui sera réutilisé par la suite. On ouvre ensuite le runspace avec la méthode Open().
Les runspaces sont d’ordinaire hermétiques et sont donc incapables de communiquer entre eux. Il existe toutefois un moyen de contourner ce problème en créant une hashtable qui sera synchronisée entre les deux runspaces.
Exemple de création...