Affichage des articles dont le libellé est Python. Afficher tous les articles
Affichage des articles dont le libellé est Python. Afficher tous les articles

dimanche 19 janvier 2020

Imprimante Zebra USB en réseau avec CUPS sur Raspberry-Pi

Ce n'est pas le premier article du blog que nous écrivons sur les imprimantes Zebra utilisée en ZPL. Il y a même une bibliothèque PythonPcl publiée sur GitHub.

Avant propos
Nous avons quelques imprimantes Zebra exploitées quotidiennement pour notre étiquetage. Le hic, c'est qu'elles sont toutes branchées en USB sur un PC... du coup pas facile à utiliser en réseau :-/
Zebra ZD420

Cups et Raspberry-Pi à la rescousse
Les carte Raspberry-Pi sont devenue tellement puissantes qu'elles peuvent facilement être utilisées en serveur d'impression.
François Mocq à publié son article "Transformer une imprimante USB en imprimante WiFi" à l'aide de CUPS et d'un Raspberry-Pi.

Nous avons utilisé son tutoriel à la différence que nous avons exploité une connexion Ethernet filaire et une adresse IP fixe.

Ensuite, nous avons raccordé notre (nos) imprimante(s) Zebra USB sur le Raspberry-Pi et suivi la procédure suivante.

Rappel PythonPcl
PythonPcl utilise les imprimantes en mode RAW (voir article Imprimante Zebra USB, ZPL et CUPS - Damned CUPS!!!) afin de pouvoir envoyer directement du ZPL sur l'imprimante.

Configurer l'imprimante sur le serveur CUPS
Pour mettre l'imprimante en réseau, il faut d'abord configurer cette imprimante sur le serveur CUPS (fonctionnant sur le Raspberry-Pi).
Cela se fait par l'intermédiaire de l'interface d'administration CUPS et d'un navigateur internet.

http://adresse-ip:631/

Ajouter une imprimante

Sélectionner l'imprimante à configurer : imprimante locale ZD420 (locale puisqu'elle est branchée en USB)

Viens ensuite le moment de définir le nom de l'imprimante (celui qui sera exposé sur le réseau). Ne surtout pas oublier de partager l'imprimante!
C'est le moment d'indiquer le type de pilote... il faut sélectionner une imprimante de marque Raw (terme anglais signifiant "brute")... car notre intention est d'envoyer du ZPL brute directement sur l'imprimante... un pilote Zebra essayerait d'interpréter le contenu et imprimerait le ZPL comme si cela était du texte.
Et comme c'est une imprimante Raw, le modèle d'imprimante sera inévitablement Raw Queue (queue d'impression brute).


La dernière option permet de sélectionner les bannières d'impressions... laissés à None (rien).

Voila, notre imprimante est configurée sur le serveur CUPS et partagée sur le réseau.

Installer l'imprimante réseau sur Linux
Nous utilisons Linux Mint (dérivé d'Ubuntu, dérivé de Debian), installer l'imprimante Zebra-ticket se fait par l'intermédiaire du gestionnaire d'imprimante du système d'exploitation.

Dans le gestionnaire d'imprimante, cliquer sur le bouton "Ajouter".
Cela affiche la liste des imprimantes disponibles, on y retrouve l'imprimante Zebra (ZD420) partagée sur le serveur CUPS.
Ne reste plus qu'a sélectionner l'imprimante et presser sur le nouton "Suivant".

Tous comme sur le serveur CUPS, il nous faut une imprimante qui n'interprète pas le contenu imprimé... donc qui fait un transfert brute.
Il faut donc choisir la catégorie Generic

Puis sélectionner le pilote Raw Queue (exactement comme sur le serveur cups.
Voila, l'imprimante est ajoutée dans le gestionnaire...


il ne reste plus qu'à la renommer pour pouvoir l'utiliser plus facilement en ligne de commande (menu contextuel, option "renommer"). Le nom Zebra_Ticket sera plus simple aussi bien pour Python ou en ligne de commande.

Utiliser l'imprimante

Il suffit de suivre les instructions de l'article "Imprimante Zebra USB, ZPL et CUPS - Damned CUPS!!!" en utilisant le nom d'imprimante Zebra_Ticket permet de vérifier le bon fonctionnement de l'ensemble.

Cette configuration CUPS avec Raspberry-Pi fonctionne parfaitement. Elle est utilisée avec la bibliothèque PythonPcl.

x
x
x
x
xx

dimanche 15 avril 2018

Python threads synchronization: Locks, RLocks, Semaphores, Conditions, Events and Queues

Un excellent article concernant les outils de synchronisation sous Python.

Aborde également les principes de producer/consumer... à lire !!

samedi 5 août 2017

wxPython pour réaliser des interfaces graphique sous Python

Il n'y a pas si longtemps, je suis tombé sur cet article qui à retenu toute mon attention


Pour en revenir à l'article, il propose de réaliser:
  1. Une interface graphique à l'aide de wxFormBuilder
  2. Exporter l'interface dans un fichier python (directement depuis wxFormBuilder)
  3. Créer le code (vraiment très rudimentaire) pour coder l'exemple en python
Exemple wxFormBuilder et Python

Je me suis donc lancé dans l'expérimentation en installant wxFormBuilder sur mon Linux Mint (Ubuntu).

Petite surprise
En suivant les instructions mentionnées dans l'article pour l'installation de wxFormBuilder, j'ai vite remarqué que le code Python généré par wxFormBuilder (compilé depuis GitHub) n'était pas compatible avec wxPython  (installé depuis les packages pythons avec "pip install -U wxPython").

Ce n'est pas grand chose... juste un appel de fonction à modifier dans le fichier Python généré par wxFormBuilder.
Ce n'est quand même pas très "propre", sans savoir ce que cela cache comme autre surprise.

Cause et solution
Je me doutais que la version de wxPython (des dépots Python) n'était pas en concordance avec la version wxFormBuilder disponible sur GitHub.
La solution:
  • re-compiler wxPython 
  • compiler wxFormBuilder
Recette pour recompiler wxPython et wxFormBuilder
Recompiler wxPython ne fut pas de tout repos et il n'aura fallut pas loin d'une journée pour y arriver.
Voici donc la recette magique:

# === Installer les essentiels ===
# essentiels de compilation
sudo apt-get install build-essential g++

# Installer GTK 2.0
sudo apt-get install gtk2.0

# === Install wxPython ===
# comme mentionné sur https://www.wxpython.org/pages/downloads/
# l intruction suivante ne compatible pas bien:
#
# pip install -U wxPython

# Comme indiqué dans l'article, l'instruction suivante 
# ne fournit pas une compatibilité optimale entre le code 
# Python généré par wxFormBuilder et le package Python 
#
# sudo apt-get install python-wxgtk3.0

# === Compilation de wxPython ===
# Installer GStreamer requis pour compiler wxPython
sudo apt-get install libgstreamer0.10
sudo apt-get install libgstreamer0.10-dev
sudo apt-get install libgstreamer-plugins-base0.10
sudo apt-get install libgstreamer-plugins-base0.10-dev
sudo apt-get install gir1.2-gst-plugins-base-0.10
sudo apt-get install gir1.2-gstreamer-0.10

# Installer WebKit requis pour compiler wxPython 
# NB: identifier les module WebKit avec "sudo apt-cache search libwebkit"
# NB: pas suffisant "sudo apt-get install libwebkit2gtk-3.0-dev"
sudo apt-get install libwebkitgtk-3.0-0 libwebkitgtk-dev 

# Installer python3-dev, requis pour avoir python3-config.
# python3-config utilisé par la compilation de wxPython
sudo apt-get install python3-dev

# Installer wxPython 4.0 pour python3
# La compilation peut prendre plus d'une heure
sudo pip3 install -U \
    -f https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-16.04 \
    wxPython

# === Compilation de wxFormBuilder ===
# Conformément au projet GitHub
# https://github.com/wxFormBuilder/wxFormBuilder

sudo apt-get install libwxgtk3.0-dev libwxgtk-media3.0-dev libboost-dev

cd /tmp
git clone --recursive --depth=1 https://github.com/wxFormBuilder/wxFormBuilder
cd wxFormBuilder
./create_build_files4.sh
cd build/3.0/gmake
make config=release

=== Exécuter la version compilée ===
cd ../../../output/bin/
./wxformbuilder

== Installer wxFormBuilder (Ubuntu/Mint) ==
# copy software to /usr/bin/
sudo mkdir /usr/share/wxformbuilder

# COPIER TOUS les fichiers de 
#   /tmp/wxFormBuilder/output/
# vers
#   /usr/share/wxformbuilder

# Reconstruire le lien symbolique de share/wxformbuilder 
# vers le répertoire "share" de notre installatin
sudo rm /usr/share/wxformbuilder/share/wxformbuilder
cd /usr/share/wxformbuilder/share
sudo ln -s /usr/share/wxformbuilder wxformbuilder

# Créer un lien symbolique dans /usr/bin
# pour pouvoir appeler wxformbuilder depuis la ligne 
# de commande
cd /usr/bin
ln -s /usr/share/wxformbuilder/bin/wxformbuilder wxformbuilder

Ressources

vendredi 16 septembre 2016

Python: Découverte des possibilités vidéo de GStreamer et FFmpeg grâce à FWomaj

En consultant les news de Linuxfr.org, je tombe sur l'article "Fwomaj 0.3 : Vidéos à la coupe au rayon frais".
Bien que je ne m'intéresse pas spécialement à la vidéo, le fait de Fwomaj utilise Python 3 m'incite à faire une petite lecture... fructueuse puisque je découvre de nouveaux outils pour mon environnement favoris.
Si l'article décrit le travail de l'auteur dans son projet, il énumère surtout certaines dépendances et donne des liens vraiment utiles.
Couteaux Suisse de la vidéo, je découvre donc les possibilités de GStreamer et FFmpeg qu'il est possible commander depuis Python et d'inclure dans une application GTK.

FFmpeg - codage/décodage/transcodage
FFmpeg est un Framework Multimedia très populaire capable de décoder, encoder, transcoder mixer (mux), démixer (demux), streamer, filtrer et jouer (play) presque tout ce que l'homme et la machine ont créés. Il support la plupart des anciens formats obscur jusqu'au tout dernier standard hi-tech.
Très portable, FFmpeg compile, s'exécute et passe avec succès l'infrastructure de test FATE sous Linux, Mac OS X, Microsoft Windows, les BSDs, Solaris, etc. Sous une large variété d'environnement de compilation, architecture logiciel et configuration.
GStreamer - rendu vidéo
Source: Fwomaj 0.3 : Vidéos à la coupe au rayon frais

Pour reprendre la note de l'article d'origine:
On peut faire plein de choses avec GStreamer. C'est un langage de pipes vidéo assez fascinant, qu'on prototype à l'aide de l'utilitaire gst-launch, pour construire des "tuyaux" d'images qui bougent:

gst-launch-1.0 videotestsrc pattern=1 ! video/x-raw,format=AYUV,framerate=\(fraction\)10/1,width=100,height=100 ! videobox border-alpha=0 top=-70 bottom=-70 right=-220 ! videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.5 ! videoconvert ! xvimagesink videotestsrc ! video/x-raw,format=AYUV,framerate=\(fraction\)5/1,width=320,height=240 ! mix.

Comme rien ne vaut un beau dessin pour comprendre rapidement

Source: GStreamer Cheat Sheet

Source: GStreamer Cheat Sheet

Ressources:
  • GStreamer Cheat Sheet
    Une excellente ressource à propos de GStreamer incluant de nombreux exemples (capture webcam, network stream, generator, etc)

vendredi 12 août 2016

Python Logging & Logger - CE QU'IL NE FAUT PAS RATER

Petit détour sur les méthodes de logging Python et une erreur assez répandue (je pense).

Dans le blog victorlin, j'ai trouvé l'exemple suivant à propos du logging, exemple qui recèle une erreur dans la façon d'utiliser le logger :

import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

logger.info('Start reading database')
# read database here

records = {'john': 55, 'tom': 66}
logger.debug('Records: %s', records)
logger.info('Updating records ...')
# update records here

logger.info('Finish updating records')

Il y a comme un bug!
Si ce code est parfaitement fonctionnel, il y a quelque-chose qui me choque.
  1. Il faut créer un "logger" pour pouvoir logger
  2. Ou passer le logger d'objet en objet si on ne veut pas en créer à tour de bras.
Si vous avez une grosse application, vous allez vous retrouver avec des logger dans tous les coins (classe, fichier, etc) pour enregistrer des messages... alors que tout ce qui vous intéresse c'est faire du logging d'information (info ou degug) pour les utiliser quand cela sera utile.

Découpler logging et logger
Le plus approprié, il me semble, est de faire du logging agnostique, sans se préoccuper de la question "si cela intéresse un logger ou non"! En gros, découpler le logger et les opérations de logging dans le code.
Cela se fait très simplement en écrivant les traces directement sur "logging".

import logging

logging.debug(' %r _fire_select(): %r ' % (self, selection) ) 
logging.debug('%r _fire_cancel()' % self )

Utiliser un logger pour garder une trace
Ensuite, au besoin, il suffit de créer un logger pour garder une trace des messages qui passent comme ceci:

logger = logging.getLogger()
handler = logging.StreamHandler() # Logging vers console
formatter = logging.Formatter(
    '%(asctime)s [%(levelname)s] -> %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.INFO) # ou DEBUG 

Et toutes les opérations loggin.info, logging.debug, etc tomberons dans logger :-)
Cela va singulièrement simplifier la codage des opérations de logging dans vis classes :-)

PythonMegaWidget (Pmw) - Ressources utiles

Voici une liste de ressources collectées durant mes recherches et apprentissage sur TkInter et PythonMegaWidget.
Cette liste est destinée à être régulièrement agrémentée.
  • Comment construire des Pmw MegaWidget (pmw.sourceforge.net, anglais)
    Détaille et commente la création d'un MegaWidget. Incontournable dès que l'on veut spécialiser un composant.
  • Origine de la MultiListBox, une sorte de TableView (mypythonadventure.com, anglais)
  • SimpleTable (stackoverflow.com, anglais)
    Créer une table avec des libellés et un Grid geometry manager. Une approche simple qui peut se révéler utile.
  • Calculating the pixel size of a string with Python (stackoverflow.com, anglais)
    Ressource très intéressante pour estimer la hauteur d'un texte dans un Widget
  • Listbox.Autowidth (http://stackoverflow.com, anglais)
    Permet d'évaluer la largeur en Pixel des entrées d'une Listbox et d'ajuster la largeur du composant en conséquence. Font et taille de texte. Excellente ressource!
  • x
  • DataQ/DataTools.py (github.com/pythonadventurer, anglais)
    Un ensemble d'outils pour ouvrir et manipuler les données d'un fichier csv
  • x
  • Creating a simple Dialog (effbot.org, anglais)
    Quelques lignes de code... mais également l'utilité de destroy() et root.wait_window().
    Simple et très pertinent.
  • Tk Named Color (wiki.tcl.tk)
    Toutes les couleurs dispo dans Tk (et TkInter). Un Must!.
  • xxx

vendredi 5 août 2016

PyhtonMegaWidget (Pmw) StrictComboBox

Dans l'article "PythonMegaWidget (Pmw) ComboBox restreind à une liste de valeur" j'expliquais comment avoir configuré une combobox pour n'accepter qu'une liste finie de valeur.

Ce hack est maintenant transformé en classe StrictComboBox .

In the previous article "PythonMegaWidget (Pmw) ComboBox restreind à une liste de valeur" I did explain how to restrict the selected value to a given list of values.
That hack has been converted to the StrictComboBox  class.

class StrictComboBox( Pmw.ComboBox ):
    """ Pmw.ComboBox that strictly restrict the selection to the list of items stored in the "items" keyword
        
        Thank to RL_ScrolledText class for their wonderfull example
        http://www.reportlab.com/examples/frml/old/rlextra/src/rlextra/graphics/guiedit/guidialogs.py                
    """
    _items = None # List of combobox items

    def __init__( self, *args, **kw ):
        # Take a local copy of some property
        for a in ['items']:
            print( a )
            if a in list(kw.keys()):
                print( ' --removed')
                v=kw[a]
                del kw[a]
            else:
                v=None
            setattr(self,a,v)

        # add the items to combobox + validator
        kw['scrolledlist_items'] = self.items
        kw['entryfield_extravalidators'] = { 'strictselect' : (self.strictselect_validate, self.strictselect_stringtovalue) }
        kw['entryfield_validate'] = { 'validator' : 'strictselect' }

        Pmw.ComboBox.__init__( *(self,)+args, **kw )
        w = self._entryfield = self.component('entryfield')

        self.initialiseoptions(StrictComboBox)
    
    def destroy(self):
        Pmw.ComboBox.destroy( self )
        

    def strictselect_validate(self, avalue):
        """ called by ComboBox entry field to validate the keyIn value """
        print( 'validate: %s' % avalue )
        if avalue in self.items:
            return Pmw.OK
        elif any( [avalue in v for v in self.items] ):
            return Pmw.PARTIAL
        else:
            return Pmw.ERROR

    def strictselect_stringtovalue(self, avalue):
        """ Not called since we did not defined min and max values """
        return None

Qui s'utilise comme ceci:
And you can use it like that:

    # StrictComboBox
    f = Frame( fbody  )
    l = Label( f, text="Prefered Parent", anchor=W, width=15 ).pack( side=LEFT, expand=N )
    combobox4 = StrictComboBox( f, items=['Dady','Mom','Sister','Grandpa'],
                        labelpos='wn', listbox_width=24, dropdown=1, # selectioncommand=choseEntry,
                        entry_state = 'disabled', entry_bg='white', entry_fg='black' )
    combobox4.pack( side=LEFT, expand=N )
    fields.append( (combobox4, None) ) # the Field and the string variable
    f.pack( side=TOP, expand=N, fill=X )

PythonMegaWidget (Pmw) ComboBox restreind à une liste de valeur

Bonjour a tous, j'essaye de restreindre la sélection de valeur d'un Pmw.ComboBox aux seules valeurs disponibles dans la liste. Voici comment j'ai utilisé extravalidators pour y arriver.
Code en beta mais fonctionnant correctement

Hello there, I'm trying to restrict the selected value of a Pmw.Combox to the only possible values in the list. Here how I did use the extravalidators to reach that goal.
Roughly beta code but worked fine

from tkinter import *
import Pmw

root = Tk()
root.option_readfile('optionDB')

...
...

    combovalues = ['v1','v2','v3'] 
    f = Frame( root  )
    l = Label( f, text="Combo", anchor=W, width=15 ).pack( side=LEFT, expand=N )
    def strictselect_validate(avalue):
        print( 'validate: %s' % avalue )
        if avalue in combovalues:
            return Pmw.OK
        elif any( [avalue in v for v in combovalues] ):
            return Pmw.PARTIAL
        else:
            return Pmw.ERROR
    def strictselect_stringtovalue(avalue): # Not used in this case
        print( 'stringtovalue: %s' % avalue )
        return None
    combobox2 = Pmw.ComboBox( f, labelpos='wn', listbox_width=24, dropdown=1, 
                        entry_bg='white', entry_fg='black',
                        scrolledlist_items=combovalues,
                        entryfield_extravalidators = { 'strictselect' : (strictselect_validate, strictselect_stringtovalue) }, 
                        entryfield_validate = { 'validator' : 'strictselect' } )
    combobox2.pack( side=LEFT, expand=N )
    fields.append( (combobox2, None) ) # the Field and the string variable
    f.pack( side=TOP, expand=N, fill=X )


Le truc: le Pmw.ComboBox utilise un Pmw.EntryField, il est donc possible d'utiliser les fonctionnalités de Pmw.EntryField

The Tip: the Pmw.ComboBox use a Pmw.EntryField, it is then possible to use the features of Pmw.EntryField

jeudi 4 août 2016

Python: faire un breakpoint en mode interactif pour faire du débogage

Dans les IDE évolués nous disposons généralement d'une fonctionnalité "Breakpoint" pour arrêter le programme et pouvoir inspecter le contenu des variables locales.
Il est possible de réaliser quelque-chose de similaire en mode console (python interactif) qui fonctionne aussi bien sous Windows que sous Linux. Très utile si l'on se trouve sur un environnement sans facilité.

Je bosse sur un projet TkInter et j'aurais trouvé intéressant de pouvoir inspecter le contenu d'un événement en mode interactif... bref, sauter dans un débogueur mais "sans débogueur" à disposition.

J'ai trouvé ce petit bout de code qui fonctionne à merveille et qui lance un python interactif là où j'en ai besoin.
La réponse se trouve dans code.interact()
 
>>> import code
>>> def foo():
...   a = 10
...   code.interact(local=locals())
...   return a
...
>>> foo()
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> a
10

  • Presser Ctrl+Z pour terminer la session interactive. 
  • Il n'est pas possible de modifier les variables locales (cela n'a pas d'implication sur le code appelant). 
  • Vous pouvez lister inspecter les variables locales en utilisant dir() 
Voici un autre exemple tel qu'utilisé depuis un code tkInter.
N'oubliez pas de démarrez votre programme avec python.exe et non pythonw.exe pour disposer d'une console ;-)

import code
...
...
def focus_next_window(event):
    """ Move focus to next item, Bind it for  """
    print( type(event.widget) )
    code.interact(local=locals())
    event.widget.tk_focusNext().focus()
    return("break")

Souce: http://stackoverflow.com/questions/13432717/enter-interactive-mode-in-python

mardi 26 juillet 2016

Utiliser Curses avec Python sous Windows

Curse pour créer une interface console
J'ai déjà touché un peu à Curse avec python sous Linux.
Je trouvais cette bibliothèque d'interface rudimentaire mais suffisante pour réaliser des applications en mode console (ce que nous avons déjà avec de vielle applications clipper qu'il faudra un jour ré-écrire).
Le Hic c'est qu'il n'existait pas de portage Python's curses sous Windows... jusqu'à assez récemment.

Donc, curses sous Python était uniquement cantonné au machine Linux... mais maintenant accessible sous Windows

Des binaires non-officiel pour Python sous Windows
En grattant une nouvelle fois sur le Net, j'ai trouvé
Où il propose justement des packages curses pour Windows.

Curses, an extension based on the PDCurses library.
Unicode characters are not supported.
Comme j'utilise Python 3.4 sous Windows 7, j'ai opté pour le téléchargement du fichier "curses-2.2-cp34-none-win32.whl" (cp34 signifie CPython 3.4... donc le compilateur officiel de la fondation).

whl est un fichier wheel (pythonwheels.com) est le nouveau standard de distribution des bibliothèque python qui remplacera à terme les fichiers .egg

Vous pouvez installer un fichier whl avec votre utilitaire pip.
Dans mon cas, j'ai utilisé la signe de commande suivante sous Windows:
c:\Users\Stock\Downloads>pip3 install curses-2.2-cp34-none-win32.whl
Processing c:\users\stock\downloads\curses-2.2-cp34-none-win32.whl
Installing collected packages: curses
Successfully installed curses-2.2

Et j'ai pu utiliser du code curses suivant sous Windows

from curses import wrapper

def main(stdscr):
    # Clear screen
    stdscr.clear()

    for i in range(0, 10):
        v = i-10
        stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10/v))

    stdscr.refresh()
    stdscr.getkey()

wrapper(main)


Une chouette avancée pour le portage des applications d'une plateforme à l'autre... à voir si cela tient vraiment ses promesses.

Basé sur PDCurses
Le bibliothèque curse installée (celle mentionnée ci-avant) est basé sur PDCurses.
http://www.projectpluto.com/win32a.htm


Support des accentués sous Windows
Je me suis littéralement cassé les dents dès l'impression du premier texte contenant des accentués.
Voyez ce billet sur IndexError (http://indexerror.net/4170/python-3-4-curses-sous-windows-7-erreur-dencodage).
L'utilisation de différents code pages dans le système d'exploitation (voir le billet) ne simplifie rien... et même en passant ma console en cp1252 (comme supporté par curse puisque la doc mentionne qu'il utilise du local.preferredencoding() - soit cp1252 -  pour l'encodage des chaine unicode)... curse détruit toujours l'affichage de mes accentués.



Au détour de différentes lectures (a confirmer), j'ai appris que cette bibliothèque curses utilise PDCurses s'appuie elle-même sur la SDL. La SDL qui, elle, n'intègre le support Unicode que depuis peu. C'est peut être normal que cela cafouille un peu.... et c'est franchement regrettable.

samedi 20 février 2016

Python : Lecture de carte eID Belge avec Python

Bien que cet article concerne la lecture de eID Belge sur Raspberry-Pi, la bibliothèque et le code doivent pouvoir être utilisé avec d'autres systèmes.
Bonne lecture
Dominique
 
En Belgique, les carte d'identités sont équipées de puce Smart Card. Cela permet d'emporter:
  • Un certificat CitizenBE utilisé pour la signature et authentification numérique du citoyen (protégé par une mot de passe).
  • D'autres informations librement accessible comme le nom, prénom, ....
Toutes ces informations étant décrites dans des spécifications ouvertes publiées sur le Net par l'eGouvernement Belge il y a une décennie.

Des spécifications ouvertes... c'est génial, du coup, nous avons une connaissance qui s'est lancé dans le projet fou de faire lire des cartes d'identités beid (carte d'identité Belge) par un Raspberry-Pi sous Jessie et en Python 3 s'il vous plaît! C'est trop de la balle.

from beid.beid import scan_readers, read_infos, triggered_decorator
from pprint import pprint
from time import sleep

# retrieve a list of available readers
r = scan_readers()[0]

# declare a function that will be executed automatically when a card is removed/insterted
# funcion arguments should be :
# - action : which will be "inserted" or "removed" when the function will be called
# - card : which will be the card if inserted
# - reader : which will hold  the name of the reader to use 

@triggered_decorator
def basic_read(action, card, reader=r.name):
    if action=="inserted":
        i = read_infos(card)
        pprint(i)

sleep(5)

infos = read_infos(r, read_photo=True)
with open("photo.jpg", "wb") as f:
    f.write(infos['photo'])

Vous trouverez pythonbeid sur le GitHub de Lapin Blanc.

Un grand merci pour ce partage avec la communauté.
Dominique

dimanche 20 décembre 2015

Python: PrettyTable pour afficher une table en mode texte

Petit outil à se garder sous le coude pour se simplifier l'écriture d'outil en mode texte (dans la console).
Il n'est pas toujours utile de se prendre la tête...



from prettytable import PrettyTable

x = PrettyTable(["City name", "Area", "Population", "Annual Rainfall"])
x.align["City name"] = "l" # Left align city names
x.padding_width = 1 # One space between column edges and contents (default)
x.add_row(["Adelaide",1295, 1158259, 600.5])
x.add_row(["Brisbane",5905, 1857594, 1146.4])
x.add_row(["Darwin", 112, 120900, 1714.7])
x.add_row(["Hobart", 1357, 205556, 619.5])
x.add_row(["Sydney", 2058, 4336374, 1214.8])
x.add_row(["Melbourne", 1566, 3806092, 646.9])
x.add_row(["Perth", 5386, 1554769, 869.4])
print x

Ressources

lundi 14 décembre 2015

RpnCalc: Install failure + Doc failure = projet d'exception qui tombe dans l'oubli

Pré-préambule:
Coup de gueule! Il faut développer les idées jusqu'au bout! Installation (ou doc d'installation) comprise!

Préambule
Une calculatrice RPN en Python, est-ce possible?

J'ai une calculatrice HP48 que j'utilise énormément, et de façon plus ou moins avancé, au jour le jour. Ce qui m'ennuie, c'est de passer mon temps à la sortie et la rentrer dans mon tiroir (je manque cruellement de place sur mon bureau).

Du coup, je me suis dit: mais pourquoi ne pas garder une calculatrice sur ton bureau Linux Mint? J'ai deux moniteurs... ce serait certainement très pratique.

Je fouille donc sur les dépôts et trouve un outil offrant des fonctionnalités plutôt avancé sous la forme de CalcRpnPy et d'un dépôt Python rpncalc 2.7.

La doc est détaillée, cela fonctionne depuis un interpréteur Python. bref, de la balle pour qui préfère utiliser un clavier pour des questions de performance humaine!
Faut dire que je suis addict à Linux Mint (donc de la chaîne de parenté Mint - Ubuntu - Debian).

Mon premier reflexe est: Génial, je vais pouvoir l'installer avec un pip install ;-)

Sauf que...
rpncalc s'appuie sur clnum (CLN ou encore Class Library for Number) et que l'installation part totalement en couille.
L'installation de clnum en python 2.7 (pip install clnum) inclus un problème d'encoding ASCII!!!

Et en python3, je me retrouve avec une belle erreur de compilation GCC.

En fouillant, j'arrive jusqu'au support et night-build des paquet Debian. 

Conclusion
Je voulais gagner du temps avec un outil sympa et je me retrouve à zoner dans de la doc, des problème d'install & compilation et même des paquets Debian!

Comme je suis à la bourre, je vais ressortir ma calculette Hp48 et rpncalc à toutes les chances de tomber dans l'oubli.

Vous ne trouvez pas cela dommage? Il manque presque rien pour l'exploiter... mais ce "presque rien" est le frein final qui va stopper net la découverte du projet rpncalc.

Moralité
Ne vous arrêtez pas à un mètre de l'arrivé.
C'est trop bête de ne pas faire profiter les autres de votre victoire!

mardi 6 octobre 2015

Inférence entre dictionnaire et paramètres nommés

Cas pratique
J'ai un cas pratique où je veux recharger des données depuis un fichier json (une fois chargé, j'ai une liste de dictionnaire) PUIS les injecter dans une DB à l'aide de sqlAlchemy.
In [57]: d[0]
Out[57]: 
{'actif': 'Y',
 'libe': 'Le carbone lorraine',
 'name': '0200',
 'pnet': 50.0,
 'sync_id': 1.0}

In [58]: d[1]
Out[58]: 
{'actif': 'Y',
 'libe': 'BUSELURES',
 'name': '0201',
 'pnet': 50.0,
 'sync_id': 2.0}

Dans sqlAlchemy, j'ai définit un modèle pour mes tables.
En utilisant ce modèle (ici family_table), l'insertion est relativement simple:

q = family_table.insert()
q.execute( actif='Y', libe= 'Le carbone lorraine', name= '0200', pnet= 50.0, sync_id= 1.0 )

Mais ce qui serait vraiment cool, c'est appeler execute() directement avec le dictionnaire d[0], d[1], ...et se passer de la tache fastidieuse décrite ci-dessus. Comme sqlAlchemy utilise **kwargs, nous pouvons faire de l'inférence :-)

q = family_table.insert()
dic = d[0] # récuperer un dictionnaire
# La ligne suivante dit que dic doit être passé directement
# en tant que paramètre **kwargs de la fonction 
q.execute( **dic ) 

**KWargs - comment ça marche?
Définissons la fonction printkw() pour afficher les valeurs disponibles dans le paramètre **kwargs.
En fait, kwargs est un dictionnaire qui contient les paramètres nommés sous forme clé = valeur. Comme kwargs n'est pas un mot clé (mais une convention de nommage), on utilise le signe ** pour identifier kwargs (et ses propriétés extraordinaires) dans la déclaration de la fonction printkw()
Vous pourriez fort bien appeler ce paramètre **dico_des_parametre_nommes , mais il est préférable de respecter les conventions.

def printkw( **kwargs ):
   for k,v in kwargs.items():
      print( '%s = %r' % (k,v) )

Maintenant, si nous appelons la fonction printkw() avec des paramètres nommés, nous avons:

>>>printkw( name='hello', value=12, value2='test me' )
value2 = 'test me'
name = 'hello'
value = 12

Dans notre fonction printkw() nous pourrions facilement récupérer une valeur donnée avec l'instruction le_nom = kwargs['name']
Cela nous fournirait la valeur du paramètre "name".

Mais le plus intéressant, selon moi, réside au passage des paramètres par l'intermédiaire d'un dictionnaire. Il devient alors très facile de:
  • Passer de nombreuses valeurs d'une fonction à l'autre
  • De présenter une interface très flexible (surtout avec des DB) pour transmettre de nombreuses données
  • De faire une persistance des paramètres nommés dans une DB,Fichier, Json, service en ligne, etc
>>> d = { 'name':'hello', 'value':12, 'value2':'test me' }
>>> printkw( **d )
value2 = 'test me'
name = 'hello'
value = 12

La première ligne crée un dictionnaire avec les paramètre nommés (qui auraient pu provenir d'un fichier json comme dans l'intro de l'article).
La deuxième ligne appel printkw() en passant le dictionnaire d en tant que paramètres nommés (c'est à cela que sert ** )

Recharger un fichier json
Juste pour le fun, voici comment j'ai rechargé le fichier json dans la liste d.

import json
# retourne le nom de fichier pour les données famille
#    'c:\\python\\catflask-alpha\\toolbox\\family.json'
filename = json_filename( 'family' )
with open( filename ) as json_data:
    d = json.load( json_data )
    json_data.close()

vendredi 18 septembre 2015

Inspection des propriétés - petite technique pratique

Voici une petite technique intéressante que vous pouvez utiliser dans un python Interactif (comme IEP par exemple).

Cela fonctionne avec tous les objets... mais dans mon cas, c'est avec un module que j'ai utilisé cette technique. Dans l'article précédent, je voulais recharger le module model.py ... mais je voulais aussi m'assurer que mes objets user_table, group_table, permission_table, etc étaient maintenant disponible!

Les petits trucs à retenir de cet article seront
  [ attr for attr in dir(m) if '_table' in attr]
  [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]

Mais pour les plus curieux, je vous propose de lire les différentes étapes.

1) Récupérer la référence vers module
import sys
m = sys.modules['model']

2) Faire un dir() --> inefficace
Faire un dir() sur un module (ou un objet) peut retourner un contenu fastidieux à parcourir suivant la taille de l'objet. Dans le cas ci-présent, il n'y a pas moins de 193 entrées! Gloups!

dir( m )
Out[67]: 
['AliasOption',
 'AttributeExtension',
 'BIGINT',
 'BINARY',
 'BLOB',
 ...
 'with_parent',
 'with_polymorphic']

In [68]: len( dir(m) )
Out[68]: 193

3) Utilisons une compréhension list
Avec une simple compréhension liste basé sur dir(), nous allons pouvoir filtrer le contenu retourné à l'aide de l'instruction if.
Comme dir() liste les attribut, il suffit de tester si la chaine de caractère '_table' apparaît dans le nom de l'attribut.
C'est magique :-)

[ attr for attr in dir(m) if '_table' in attr]
Out[69]: 
['group_permission_table',
 'group_table',
 'permission_table',
 'user_group_table',
 'user_table']

4) Avec les valeurs svp
Avoir les noms des attributs c'est bien, avec leur valeur c'est encore mieux!
Cette fois-ci, nous utilisons getattr() pour obtenir la valeur et nous justifions avec ljust() pour le confort de lecture.

In [72]: [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[72]: 
['group_permission_table    : tf_group_permission',
 'group_table               : tf_group',
 'permission_table          : tf_permission',
 'user_group_table          : tf_user_group',
 'user_table                : tf_user']

Attention, c'est uniquement pour faire de l'investigation en live!!!

5) Avec le représentation des valeurs
Si vous jouez avec python depuis un moment, vous avez certainement déjà rencontré la fonction __repr()__. Cette dernière est utilisée par l'interpréteur pour afficher plus de détails  sur les objets (et pas uniquement leur adresse).
Si cela est pratique, cela peut aussi fournir du contenu très... touffu selon les circonstances.

Cette version du script remplace uniquement un des %s par %r (pour demander l'affichage de la "représentation").

In [73]: [ '%s : %r' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[73]: 
["group_permission_table    : Table('tf_group_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id_permission', Integer(), ForeignKey('tf_permission.id'), table=), Column('id_group', Integer(), ForeignKey('tf_group.id'), table=), schema=None)",
 "group_table               : Table('tf_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('group_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "permission_table          : Table('tf_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('permission_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "user_group_table          : Table('tf_user_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('user_id', Integer(), ForeignKey('tf_user.id'), table=, primary_key=True, nullable=False), Column('group_id', Integer(), ForeignKey('tf_group.id'), table=, primary_key=True, nullable=False), schema=None)",
 "user_table                : Table('tf_user', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('user_name', Unicode(length=16), table=, nullable=False), Column('password', Unicode(length=40), table=, nullable=False), Column('display_name', Unicode(length=255), table=, default=ColumnDefault('')), Column('created', DateTime(), table=, default=ColumnDefault(. at 0x04560B70>)), schema=None)"]
Comme vous pouvez le constaté, c'est nettement plus chargé... et avec le retour à la ligne, c'est franchement illisible)
Comme quoi "le mieux est parfois l'ennemi du bien".

Voila, Happy Python Hacking ;-)

Recharger un module Python à la volée

Il y a peu, je vous parlais d'IEP dans ce billet "IEP: un éditeur Python interactif convivial (Windows, Mac, Linux)".

Environnement bien pratique pour faire des tests et je découvre justement SqlAlchemy.
J'étais justement entrain de coder un petit module de test model.py lorsque je me suis demandé comment recharger mon module!
IEP - édition d'un module "model.py"

J'avais déjà fait un import model ... mais ayant modifié le code, je voulais savoir comment le recharger.
Voici donc comment faire...

Option 1 - à la dure
import sys
del( sys.modules['model'] ) # virer le module "model.py"
from model import *         # ré-importer le module "model.py"

Option 2 - importlib.reload()
import importlib
import sys
a = sys.modules['model'] # Référence vers le module
importlib.reload( a )    # Demander à Python de recharge le module

jeudi 17 septembre 2015

Entête pour fichier Python

Voici une proposition d'entête de fichier Python à partir d'information collectées ici et là.

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Foobar.py: Description of what foobar does."""

__author__ = "Rob xxxx, Gavin xxxx, and Peter xxxx"
__copyright__ = "Copyright 2007, The xxxx Project"
__credits__ = ["Rob xxx", "Peter xxx", "Gavin xxxx",
                    "Matthew xxxx"]
__license__ = "GPL"
__version__ = "1.0.1"
__maintainer__ = "Rob xxx"
__email__ = "rob@spot.xxx.edu"
__status__ = "Production"


from sqlalchemy import *
from sqlalchemy.orm import *
from datetime import datetime
....

vendredi 4 septembre 2015

IEP: un éditeur Python interactif convivial (Windows, Mac, Linux)

IEP est un outil vraiment très pratique et agréable permettant de faire du prototypage en Python dans un milieu ergonomique.
Nous ne parlons pas de développement (qui est un autre domaine) mais de Python Interactif (et même IPython).
IEP - Un python interactif vraiment convivial.
J'aime employer Python en mode interactif... c'est vraiment pratique pour faire du prototypage ou essayer des nouvelles fonctions. Il y a aussi son grand frère IPython que je ne connais pas encore très bien.

Limite de Python Interactif en mode console
Par contre, si ces environnements consoles sont plus que pratique du fait de leur mode console, ils deviennent également pénibles lorsqu'il faut faire un "fix" dans une fonction mal définie.
Il faut recommencer la saisie ligne a ligne... ce n'est pas gênant si c'est une fois mais autrement plus ennuyant en usage intensif.

Je voulais charger dynamiquement le contenu d'une table postgresql dans un objet SqlAlchemy (via l'ORM)... puis sauver les objets dans un fichier JSON. Le but est d'exporter les données sur un serveur FTP pour rechargement dans une DB en ligne. Alors oui, vu le nombre de manipulation, j'étais un peu à l'étroit dans la version console.

IEP... KASEKO?!?

J'ai donc cherché un environnement Python Interactif plus convivial - aussi sous Windows - et je suis tombé sur Interactive Editor for Python (IEP)

C'est toujours un Python interactif (IPython) auquel est associé une gestion de fichier, quelques outils, un logger et des fonctionnalités vraiment efficaces.
Une combinaison idéale pour une utilisation en mode Interactif:
  • Exécution du texte sélectionné à la volée (dans l'interpréteur)
  • Exécution du script ou du contenu du fichier
  • Explorateur de fichier
  • Explorateur de classe
  • Auto complétion
  • Python Interactif fonctionnant dans un thread séparé
    Autorise donc la poursuite du coding pendant une phase d'éxécution.
  • Excellent support de raccourcis clavier!
    Capital pour allez vite.
  • etc
L'installer Windows ne fait que 10Mb... tout à fait raisonnable pour les services rendus.

Ressources

samedi 29 août 2015

Pillow - le fork de PIL

PIL Python Image Library est une puissante bibliothèque de traitement d'image.

PIL est mondialement reconnu pour sa puissance... mais aussi tristement connu pour être parfois vraiment pénible à installer.

En travaillant sur la documentation du Sense Hat pour Raspberry-Pi, j'ai découvert que la fondation Pi proposait d'installation Pillow en même temps que la bibliothèque du Sense-Hat.
Piqué à vif, j'ai jeté un petit coup d'oeil sur Pillow.

Pillow? Kaseko?!?!
Pillow est un Fork de PIL, créé par Alex Clark and Contributeurs. PIL est la bibliothèque Python Imaging Library par Fredrik Lundh et Contributeurs.


Pillow Logo
source: python-pillow.github.io

Si la fondation Pi propose d'installer Pillow plutôt de Pil c'est probablement parce que Pillow peut s'installer via l'utilitaire pip et que la bibliotèque est suffisamment légère que pour pouvoir tourner sur un Pi.
Cela n'enlève pourtant rien à l'intérêt de Pillow (cfr la doc).

De la doc
Autre point crucial, c'est la documentation... une belle doc comme je les aiment bien.
Installer Pillow

sudo pip-3.2 install pillow

Plus d'information

mercredi 26 août 2015

Bibliothèque & outils pour Python

Python-Editor
En m'intéressant à Alembic et SqlAlchemy, je suis tombé sur un Package portant le nom de Pyhton-Editor.

Et bien, que voila un petit outil intéressant... Python-Editor ouvre un éditeur (par programmation) et se débrouille pour capturer le résultat.
Outil simple mais efficace, il permet surtout de s'affranchir des problèmes liée à la plateforme utilisée (Windows, Mac, Linux) :-)

https://pypi.python.org/pypi/python-editor/0.3