vendredi 22 août 2014

Début de la programmation : test d'URWID

Je n'ai toujours pas tout mon matériel et mon Raspberry se sent bien seul.
Mais que cela n'empêche pas de commencer à travailler.

Pour pouvoir faire mes tests, je vais avoir besoin d'un ensemble de fonctions Python. Comme je n'ai pas envie de les lancer à la main, je vais faire un launcher, utilisable en ligne de commande.
J'ai tourné mon attention vers urwid, une librairie Python permettant de créer des interfaces graphiques en mode console.
Je m'en vais donc tester ça.

La chance, c'est que cette librairie existe en package ! Donc aptitude, mon ami, j'ai besoin d'urwid ...
sudo aptitude install python-urwid
... et quelques tests plus tard, la version sur aptitude est trop ancienne pour faire marcher ne serait-ce que les tutoriaux. Bref, je repars et me voici à installer la dernière version. Récupération de l'archive ici, décompression et ensuite :
sudo python setup.py install
Cette fois cela fonctionne correctement et je peux lancer les tutoriaux.

Quelques tutoriaux plus tard ... cela commence à ressemble à quelque chose !
Cette librairie est vraiment sympa.
Voici le résultat :




Le programme commence à s'organiser.
En gros deux classes : une classe menu qui gère que ce que vous voyez, une classe "PiFire" qui s'occupera du tir.
De cette façon si j'ai besoin de changer le menu pour un autre système je n'aurais pas tout à réécrire ...

Un peu de code :

import urwid
import os

class PiFire:
 'Classe de gestion du tir'
 seqFile = '' # Fichier de sequence charge
 seqFolder = '/home/pi/tir_sequences' # Dossier ou chercher les fichiers de sequence
 
 
 def __init__(self):
  self.reboot_relays() # On ferme tous les relais au lancement
 
 # Getter dossiers des fichiers de sequence
 def get_seqFolder(self):
 
 # Setter du fichier de sequence
 def set_seqFile(self,file=''):
  
 # Getter du fichier de sequence
 def get_seqFile(self):
 
 # Methode de parsing du fichier de sequence
 def parse_seqFile(self,file):
  
 # Methodes de base pour controler les relais
 def on_relay(self,relayNum):

 def off_relay(self,relayNum):
 
 # Methode pour passer tous les relais a OFF
 def reboot_relays(self):
  
 # Methode pour charger un fichier de sequence
 def load_seq(self):
  
 # Methode pour lancer le fichier de sequence
 def launch_seq(self):

class Menus:
 'Classe de gestion de la navigation dans les menus'
 
 TitleLvl1 = 'PiFire' # Nom de l'application

 # Initialisation du menu principal et de l'overlay
 def __init__(self,firesys):
 
 # renvoie l'overlay pour la premiere execution
 def initialize(self):
 
 # Creation d'un bouton
 def create_button(self,text,function):
  button = urwid.Button(text)
  urwid.connect_signal(button, 'click', eval('self.'+function), text)
  return urwid.AttrMap(button, None, focus_map='reversed')
   
 # Construit le menu de premier niveau (lance fois au debut et a chaque changement de fichier de sequence)
 def menu(self):
  body = [urwid.Text(Menus.TitleLvl1), urwid.Divider()]
  body.append(urwid.Text(('banner', u"Maintenance"), align='center'))
  body.append(self.create_button('Reset Relais','item_chosen'))
  body.append(self.create_button('Test de ligne','item_chosen'))
  body.append(self.create_button('Choix Fichier Sequence','file_choose'))
  body.append(urwid.Text(self.fireSystem.get_seqFile()))
  body.append(urwid.Divider())
  body.append(urwid.Text(('banner', u"Tir"), align='center'))
  body.append(self.create_button('Lancement de ligne','item_chosen'))
  body.append(self.create_button('Lancement pas a pas','item_chosen'))
  body.append(self.create_button('Lancement Sequence','item_chosen'))
  body.append(urwid.Divider())
  body.append(self.create_button('Sortie','exit_program'))
  self.menuLvl1 = urwid.ListBox(urwid.SimpleFocusListWalker(body))
 
 # Methode pour selectionner un fichier de tir
 def file_choose(self,button,choice):
 
 # Methode utilisee pour positionner le fichier de tir
 def file_set(self,button,choice):
  
 # Methode standard (do nothing) appelee par un choix sur un menu de premier niveau
 def item_chosen(self,button, choice):
 
 # Renvoi au menu de premier niveau
 def re_build_menu(self,button,choice=1):
 
 # Sors du programme
 def exit_program(self,button,choice):

firesystem = PiFire()
menu = Menus(firesystem)
urwid.MainLoop(menu.initialize(), palette=[('reversed', 'standout', '')]).run()

J'ai enlevé toutes les lignes, sauf la création du menu principal et le squelette.
Quelques commentaires sur le squelette. J'ai essayé de bien diviser la partie affichage de la partie "mécanique". Par exemple quand on sélectionne le fichier qui contient les ordres de tir, toute l'intelligence (parsing du fichier, vérification de son format, sauvegarde des variables) se fait dans l'objet PiFire avec des getters / setters. Naturellement le contrôle des relais se fera là bas aussi.

Ensuite pour la partie menu, le bout de code que j'ai laissé illustre bien la facilité de mise en oeuvre : on créé un "cadre" et on ajoute des éléments dedans. La méthode "button" gérant comme une grande la navigation avec les flèches du clavier et la mise en place de listeners.

Il me reste à faire le parsing des fichiers de tir et ensuite il me faudra attendre la réception du matériel électronique, j'ai hâte !

Si vous voulez la source complète voici le lien vers le commit sur Google Code :
https://code.google.com/p/pifire/source/browse/main.py?r=8d425676c597b3d32168a2ef163ce2d0dd24b315

PS : pour les développeurs qui regarderaient ce code, ce n'est forcément très propre, en particulier le "eval" dont j'aurais pu me passer, mais comme ça c'est simple de rajouter un élément au menu !

Aucun commentaire:

Enregistrer un commentaire