01ivier – Sam & Max http://sametmax.com Du code, du cul Tue, 10 Sep 2019 09:14:50 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 La course du bus de l’innovation sur le chemin de la croissance http://sametmax.com/la-course-du-bus-de-linnovation-sur-le-chemin-de-la-croissance/ http://sametmax.com/la-course-du-bus-de-linnovation-sur-le-chemin-de-la-croissance/#comments Sat, 22 Jul 2017 20:10:46 +0000 http://sametmax.com/?p=18765 Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Bonsjours à toutes et à tous,

Dans un sursaut de patriotisme, je me suis dit qu’il me fallait, moi aussi, participer à l’effort national et promouvoir le savoir-faire technologique français. Partant du constat que, de nos jours, proposer quelque chose de nouveau semble suffisant pour être innovant, j’ai fait comme tout bon startupeur, je ne me suis pas embarrassé à concevoir quelque chose d’utile. À vrai dire, ce n’est même pas nouveau. Mais je suis français et j’ai un compte Twitter, je crois que c’est amplement suffisant pour prétendre au label #CitizenOfTheFrenchTechNation.

 
frenchtech

 

Où le bus l’innovation va tout droit.

Parce que je suis bien conscient de l’influence que cet article va avoir dans le monde du jeu vidéo, je vais détailler les différentes étapes de la réalisation de ce projet bouleversant.

Tout d’abord, j’ai fait en sorte que le bus de l’innovation aille tout droit sur un chemin de la croissance qui est tout droit.

#-*- coding: utf-8 -*-

# On importe de quoi dormir
from time import sleep

# On définit le graphisme en accord avec l'équipe de designers
chemin = " " 
arbre = "A"
bus = "B"

# On définit la qualité de l'image après avoir consulté les experts en GPU du Digital-Agile-Open-Tech-Lab-Responsive.IO
largeur_ecran = 70

# On définit l'état initial conjointement avec la team level design.
# (elle-même ayant sollicité au préalable le narrative designer, bien entendu)
largeur_chemin = 15
position_chemin = 28
position_bus = 35
 
while 1:
 
    # On place la première portion d'arbres dans le paysage
    paysage = arbre * position_chemin
    # On place le chemin
    paysage += chemin * largeur_chemin
    # On remplit le reste du paysage par la deuxième portion d'arbres
    paysage += arbre * (largeur_ecran - len(paysage))
    # On place le bus sur notre paysage
    paysage = paysage[:position_bus] + bus + paysage[position_bus:]

    # On affiche le tout
    print(paysage)

    # On dort un peu
    sleep(0.2)

bus_innovation_droit

Oui. Il est effectivement possible d’arriver plus simplement au même résultat, mais, mes qualifications ne m’offrent pas le loisir d’être incompétent. J’anticipe donc les futurs mouvements du bus de l’innovation ainsi que les inéluctables virages du chemin de la croissance.

Ce doit être un peu déroutant pour vous, j’en conviens, mais c’est l’expérience qui parle.

Faites-moi confiance.

 

Où le bus l’innovation va tout droit mais en bien plus beau.

Parce que l’on n’est jamais assez exigent sur l’apparence d’un produit de luxe, j’ai voulu changer le rendu des arbres en remplaçant le “A” par un “█” bien plus raffiné. Voici le résultat que j’ai obtenu :

bus_innovation_droit_arbre

Diable comme le succès est semé d’embûches !

En effet, un petit…

>>> len("█")
3

… m’a appris que le “█” comptait pour trois caractères, faussant mon ingénieuse gestion des positions.

Mon talent m’avait permis d’anticiper les mouvements et autres virages mais pas ça. Damned. Qu’allais-je faire ?

Un petit replace() bien placé et le tour était joué. Sans compter que je pouvais désormais me permettre de mettre de la couleur.

La classe internationale.

#-*- coding: utf-8 -*-

from time import sleep

chemin = " " 
arbre = "A"
#On définit un arbre classe et vert en utilisant les codes ANSI
arbre_classe_et_vert = "\33[32m█\033[0m"
bus = "B"

largeur_ecran = 70
largeur_chemin = 15
position_chemin = 28
position_bus = 35
 
while 1:
 
    paysage = arbre * position_chemin
    paysage += chemin* largeur_chemin
    paysage += arbre * (largeur_ecran - len(paysage))
    paysage = paysage[:position_bus] + bus + paysage[position_bus:]
    # PAF ! On remplace l'arbre ridicule par un arbre classe et vert après avoir géré la position des différents éléments.
    paysage = paysage.replace(arbre, arbre_classe_et_vert)

    print(paysage)

    sleep(0.5)

BIM !

bus_innovation_droit_arbre_vert

 

Où le bus de l’innovation va tout droit mais sur un chemin de la croissance qui tourne.

Pour faire tourner le chemin de la croissance je fais un petit +2/-2 sur sa position avec un randint(), et zou.

Par contre, pour forcer le chemin de la croissance à rester dans l’écran, j’ai été surpris de ne pas trouver de fonction prête à l’emploi pour contraindre un nombre dans un intervalle donné. Je suis donc passé par une “astuce” pécho sur Stack Overflow que j’ai trouvée élégante. On classe par ordre croissant le nombre donné avec les bornes de l’interval et on récupère le deuxième élément.

position = sorted(position_min, position, position_max)[1]

Si vous avez mieux, ça m’intéresse.

#-*- coding: utf-8 -*-

# On importe de quoi choisir des nombres au hasard
from random import randint

from time import sleep

chemin = " " 
arbre = "A"
arbre_classe_et_vert = "\33[32m█\033[0m"
bus = "B"

largeur_ecran = 70
largeur_chemin = 15
position_chemin = 28
position_bus = 35

# On définit une position maximale pour le chemin de la croissance
position_max_chemin = largeur_ecran - largeur_chemin
 
while 1:

    # On calcule la nouvelle position du chemin de la croissance
    # Un peu plus à droite, un peu plus à gauche ou un peu plus tout droit...
    position_chemin += randint(-2, 2)

    # En s'assurant qu'il ne déborde pas de la largeur de l'écran
    position_chemin = sorted([1, position_chemin, position_max_chemin])[1]
 
    paysage = arbre * position_chemin
    paysage += chemin * largeur_chemin
    paysage += arbre * (largeur_ecran - len(paysage))
    paysage = paysage[:position_bus] + bus + paysage[position_bus:]
    paysage = paysage.replace(arbre, arbre_classe_et_vert)

    print(paysage)

    sleep(0.5)

bus_innovation_chemin_virage

 

Où le bus de l’innovation crache des flammes.

Avant de me pencher sur les mouvements du bus de l’innovation, j’ai pris le temps de le tuner un peu.

Déjà, direct, je l’ai peint en rouge, rapport au fait que le bus de l’innovation, c’est un peu la Ferrari de l’entrepreneuriat.

Et puis, je me suis dit qu’il fallait qu’il n’y ai vraiment absolument aucun doute sur le fait que c’était bel et bien le bus de l’innovation. Je lui ai donc fait écrire “LE BUS DE L’INNOVATION” sur la route. Je m’en remets à vous pour me dire s’il reste une ambiguïté.

Accessoirement, le “A” utilisé pour placer les arbres est devenu un “a“, pour ne pas être confondus avec le “A” présent dans “INNOVATION”. C’est un détail, mais les générations d’ingénieurs qui liront ça dans deux cent ans seront bien contents de trouver cette explication.

#-*- coding: utf-8 -*-

from random import randint
from time import sleep

chemin = " " 
arbre = "a"
arbre_classe_et_vert = "\33[32m█\033[0m"

largeur_ecran = 70
largeur_chemin = 15
position_chemin = 28
position_bus = 35

# On définit le texte écrit par le bus de l'innovation
texte_du_bus = "LE BUS DE L'INNOVATION "
# On récupère le nombre de caractères dans le texte écrit par le bus de l'innovation
nb_caractere = len(texte_du_bus)
# On initialise un compteur pour gérer la succession des caractères
compteur = 0

position_max_chemin = largeur_ecran - largeur_chemin
 
while 1:

    position_chemin += randint(-2, 2)
    position_chemin = sorted([1, position_chemin, position_max_chemin])[1]
 
    paysage = arbre * position_chemin
    paysage += chemin * largeur_chemin
    paysage += arbre * (largeur_ecran - len(paysage))

    # Dans le texte écrit par le bus de l'innovation, on prend le caractère 
    # indiqué par le compteur modulo le nombre de caractères possibles
    caractere = texte_du_bus[compteur%nb_caractere]
    # On peint le caractère en rouge Ferrari
    bus = "\33[31m{0}\033[0m".format(caractere)
    # On incrémente le compteur pour avoir le caractère suivant au prochain tour
    compteur += 1

    paysage = paysage[:position_bus] + bus + paysage[position_bus:]
    paysage = paysage.replace(arbre, arbre_classe_et_vert)

    print(paysage)

    sleep(0.5)

Magnifique.

bus_innovation_flamme

 

Où l’on cherche à faire tourner le bus de l’innovation.

Je ne vais pas vous mentir, la course du bus de l’innovation sur le chemin de la croissance n’est pas vraiment une nouveauté vidéoludique. Nous, c’est à dire La Labomedia, l’avons déjà présentée à Toulouse lors du THSF 2014 ainsi qu’au PSESHSF 2016 de Choisy-le-Roi dont il existe même une vidéo de la Master Class.

Sur cette photo spectaculaire, vous pouvez découvrir la pertinence de notre ingénieuse interface biologique imputrescible nourrie aux anti-biotiques NF.

busdelinnovationsurlechemindelacroissance

Mais, non seulement j’avais perdu totalement le code initial mais en plus l’installation s’appuyait sur l’excessivement chère MakeyMakey qui, par ailleurs, telle qu’elle est vendue, impose à l’utilisateur d’être relié à une masse.

Pour sa résurrection, je lui ai donc préférée la Capacitive Touch HAT pour RaspberryPi conçue par Adafruit et qui fonctionne direct au simple touché.

(Il va de soit que les natural chicken flavor interfaces n’ont, elles, pas été remises en question.)

Voici le code minimal pour utiliser le Capacitive Touch HAT une fois la librairie d’Adafruit installée :

import Adafruit_MPR121.MPR121
from time import sleep

interface = Adafruit_MPR121.MPR121.MPR121()
interface.begin()

while 1:
 
  # Si la patine numéro 0 est touchée
  if interface.is_touched(0):
      print("GAUCHE !")

  # Si la patine numéro 10 est touchée
  if interface.is_touched(10):
      print("DROITE !")

  sleep(0.1)

Qui devient ceci quand on instaure un seuil de détection pour tenir compte de la conductivité des pattes de poulet.

import Adafruit_MPR121.MPR121
from time import sleep

interface = Adafruit_MPR121.MPR121.MPR121()
interface.begin()

seuil = 100

while 1:
 
  # Si la patte de poulet reliée à la patine numéro 0 est touchée
  if interface.filtered_data(0) < seuil:
      print("GAUCHE !")

  # Si la patte de poulet reliée à la patine numéro 0 est touchée
  if interface.filtered_data(10) < seuil:
      print("DROITE !")

  sleep(0.1)

On ne peut pas dire que ce soit très compliqué. À noter tout de même la nécessité d'activer l'I2C et de lancer le script en root. Une formalité pour celles et ceux qui ont de l'ambition dans la Vie.

Une fois intégré dans notre formidable simulation de sport mécanique extrême, le script ressemble alors à ça :

#-*- coding: utf-8 -*-
 
# On importe la librairie d'Adafruit
import Adafruit_MPR121.MPR121 as MPR121

from random import randint
from time import sleep

# On instancie l'interface...
interface = MPR121.MPR121()
# ... et on la démarre.
interface.begin()

chemin = " " 
arbre = "a"
arbre_classe_et_vert = "\33[32m█\033[0m"
 
largeur_ecran = 70
largeur_chemin = 15
position_chemin = 28
position_bus = 35
 
texte_du_bus = "LE BUS DE L'INNOVATION "
nb_caractere = len(texte_du_bus)
compteur = 0
 
position_max_chemin = largeur_ecran - largeur_chemin

seuil = 100
 
while 1:

    # En fonction des patines touchées,
    # on déplace le bus de l'innovation vers la droite...
    if interface.filtered_data(0) < seuil:
        position_bus += 1

    # ... ou vers la gauche.
    if interface.filtered_data(10) < seuil:
        position_bus -= 1
 
    position_chemin += randint(-2, 2)
    position_chemin = sorted([1, position_chemin, position_max_chemin])[1]

    paysage = arbre * position_chemin
    paysage += chemin * largeur_chemin
    paysage += arbre * (largeur_ecran - len(paysage))

    caractere = texte_du_bus[compteur%nb_caractere]
    bus = "\33[31m{0}\033[0m".format(caractere)
    compteur += 1
 
    paysage = paysage[:position_bus] + bus + paysage[position_bus:]
    paysage = paysage.replace(arbre, arbre_classe_et_vert)
 
    print(paysage)
 
    sleep(0.5)

bus_innovation_tourne

 

Où le bus de l'innovation est tenu de rester sur le chemin de la croissance.

Écoutez, que les choses soient bien claires, moi aussi je trouve cet article beaucoup trop long, mais c'est du rayonnement de la France dont il est question. Dois-je vous le rappeler ?

Voici donc comment j'ai tenu compte des sorties du chemin de la croissance par notre bus de l'innovation :

#-*- coding: utf-8 -*-
 
import Adafruit_MPR121.MPR121 as MPR121
 
from random import randint
from time import sleep

interface = MPR121.MPR121()
interface.begin()
 
chemin = " " 
arbre = "a"
arbre_classe_et_vert = "\33[32m█\033[0m"
 
largeur_ecran = 70
largeur_chemin = 15
position_chemin = 28
position_bus = 35
 
texte_du_bus = "LE BUS DE L'INNOVATION "
nb_caractere = len(texte_du_bus)
compteur = 0
 
position_max_chemin = largeur_ecran - largeur_chemin

# On définit un booléen qui rendra compte de l'état du bus de l'innovation
le_bus_roule = True

seuil = 100
 
while 1:

    if interface.filtered_data(0) < seuil:
        position_bus += 1
 
    if interface.filtered_data(10) < seuil:
        position_bus -= 1

    # Si le bus roule...
    if le_bus_roule:
 
        position_chemin += randint(-2, 2)
        position_chemin = sorted([1, position_chemin, position_max_chemin])[1]
     
        paysage = arbre * position_chemin
        paysage += chemin * largeur_chemin
        paysage += arbre * (largeur_ecran - len(paysage))     

        # Si le bus sort de la route, à gauche ou à droite...
        if position_bus <= position_chemin or position_bus >= position_chemin + largeur_chemin:

            # On change l'apparence du bus (qui devient une croix verte)
            bus = "\33[32mX\033[0m"
            # On change l'état du bus de l'innovation
            le_bus_roule = False

        # Sinon, on affiche le bus comme précédement défini
        else:
            caractere = texte_du_bus[compteur%nb_caractere]
            bus = "\33[31m{0}\033[0m".format(caractere)
            compteur += 1
     
        paysage = paysage[:position_bus] + bus + paysage[position_bus:]
        paysage = paysage.replace(arbre, arbre_classe_et_vert)
     
        print(paysage)

        # Si, entre temps, le bus de l'innovation s'est arrêté de rouler
        if not le_bus_roule:
 
            # On affiche un message sympathique après avoir sauté une ligne
            print("\nIl n'est pas exclu que le bus de l'innovation se soit pris un arbre...")
     
        sleep(0.5)

bus_innovation_crash

 

Où la course du bus de l'innovation sur le chemin de la croissance va pouvoir enfin commencer.

Afin que le public intègre bien le défi collectif que représente la relance de l'économie par le financement et le développement d'innovants nouveaux projets novateurs avec de la technologie à la pointe de la technologie, j'ai fait en sorte d'afficher la croissance totale que l'ensemble des joueurs auront fait parcourir au bus de l'innovation.

Ainsi, n'y a-t-il jamais un individu qui perd, mais toujours un collectif qui gagne.

Quelque chose entre la victoire éternelle et le succès permanent.

Une véritable leçon de Vie.

#-*- coding: utf-8 -*-
 
import Adafruit_MPR121.MPR121 as MPR121
 
from random import randint
from time import sleep

interface = MPR121.MPR121()
interface.begin()
 
chemin = " " 
arbre = "a"
arbre_classe_et_vert = "\33[32m█\033[0m"
 
largeur_ecran = 70
largeur_chemin = 15

# On met en place un système d'initialisation des positions
# du chemin de l'innovation et du bus de la croissance
init_position_chemin = 28
init_position_bus = 35
position_chemin = init_position_chemin
position_bus = init_position_bus
 
texte_du_bus = "LE BUS DE L'INNOVATION "
nb_caractere = len(texte_du_bus)
compteur = 0
 
position_max_chemin = largeur_ecran - largeur_chemin

seuil = 100

le_bus_roule = True

# On déclare des variables qui vont nous permettre de rendre compte
# des quantités de croissance parcourue par le bus de l'innovation
croissance_parcourue_totale = 0
croissance_parcourue = 0

# On définit un petit texte à formater pour présenter les quantités en question
texte_crash ='''
Bravo !
Tu t'es pris un arbre mais tu as parcouru {0} mètres de croissance !
 
Pour ton information, le Bus de l'Innovation a parcouru {1} mètres
de croissance depuis son départ.
 
Pour le faire redémarrer, appuie sur la cuisse de poulet du milieu.
La France compte sur toi !
'''
 
while 1:

    if interface.filtered_data(0) < seuil:
        position_bus += 1
 
    if interface.filtered_data(10) < seuil:
        position_bus -= 1

    # On met en place un moyen de reprendre le chemin de la croissance
    # si le bus de l'innovation s'est pris un arbre
    if if interface.filtered_data(5) < seuil and not le_bus_roule:
        le_bus_roule = True
        croissance_parcourue = 0
        position_chemin = init_position_chemin
        position_bus = init_position_bus
        compteur = 0

    if le_bus_roule:
 
        position_chemin += randint(-2, 2)
        position_chemin = sorted([1, position_chemin, position_max_chemin])[1]
     
        paysage = arbre * position_chemin
        paysage += chemin * largeur_chemin
        paysage += arbre * (largeur_ecran - len(paysage))     

        if position_bus <= position_chemin or position_bus >= position_chemin + largeur_chemin:

            bus = "\33[32mX\033[0m"
            le_bus_roule = False

        else:
            caractere = texte_du_bus[compteur%nb_caractere]
            bus = "\33[31m{0}\033[0m".format(caractere)
            compteur += 1
            # On incrémente la croissance parcourue à chaque tour
            croissance_parcourue += 5
     
        paysage = paysage[:position_bus] + bus + paysage[position_bus:]
        paysage = paysage.replace(arbre, arbre_classe_et_vert)
     
        print(paysage)

        if not le_bus_roule:
 
            # On calcule la croissance totale parcourue
            croissance_parcourue_totale += croissance_parcourue
            # On formate notre petit texte pour informer le joueur de sa 
            # performance individuelle et collective.
            print(texte_crash.format(croissance_parcourue, croissance_parcourue_totale))
     
        sleep(0.5)

bus_innovation_arcade

]]>
http://sametmax.com/la-course-du-bus-de-linnovation-sur-le-chemin-de-la-croissance/feed/ 6 18765
Enfin un moyen d’afficher une vidéo avec des cases à cocher (PyQt4 et OpenCV inside) http://sametmax.com/enfin-un-moyen-dafficher-une-video-avec-des-cases-a-cocher-pyqt4-et-opencv-inside/ http://sametmax.com/enfin-un-moyen-dafficher-une-video-avec-des-cases-a-cocher-pyqt4-et-opencv-inside/#comments Sun, 24 Jan 2016 14:11:29 +0000 http://sametmax.com/?p=17836 Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Bonsjours les amis,

J’espère que vous allez bien et que vous n’avez pas trop pensé que j’étais mort rapport au fait que ça faisait longtemps que je n’avais pas posté d’article.

 

Où je mentionne plusieurs fois le mot JavaScript.

Le fait est que j’étais trop occupé à faire du JavaScript.

Bon, ce n’est pas exactement vrai, mais, je voulais vérifier une théorie selon laquelle dès qu’on parle de JavaScript sur Sam&Max, le nombre des commentaires explosent (je n’ai pas de théorie sur le fait qu’il faille l’écrire en gras, mais je trouve ça joli (sauf quand il y en a partout (parce qu’après ça fait trop))).

Mais, dire que j’étais trop occupé à faire du JavaScript n’est pas précisément faux non plus dans la mesure où je me suis mis à p5.js, qui est au JavaScript ce que Processing est au Java, à savoir un environnement de développement simplifié pour les pucelles et autres puceaux de la programmation.

p5.js qui m’a permis d’assouvir un de mes fantasmes : afficher un cheval qui court avec des cases à cocher dans une page web..

Un cheval qui court en cases à cocher d'après les célèbres photographies d'Easweard Muybridge, désormais dans le domaine publique.

Un cheval qui court en cases à cocher d’après les photos d’E.Muybridge, désormais dans le domaine publique.

Je vous ai mis un GIF animé, pour faire de l’animation, mais vous pouvez le voir en vrai ici avec vos cases à cocher à vous.

(Sauf pour celles et ceux pour qui ça ne marchera pas, bien évidemment.)

(Voilà pour la partie JavaScript. Je ne vais plus en parler. Les personnes concernées peuvent donc se rendre dès à présent à la section des commentaires. Merci.)

 

Où je m’attelle à une tâche bien plus sérieuse.

J’étais content, mais, passé ces enfantillages, il me fallait désormais m’atteler à une tâche bien plus sérieuse : afficher un cheval qui court avec des cases à cocher en Python.

Pour cela, j’ai choisi d’utiliser PyQt4 parce que c’est la meilleure librairie pour créer des interfaces graphiques en Python.

Ah Ah Ah…
Nan, mais, comme si j’en savais quelque chose…
C’est juste qu’un ami avait déjà réussi à afficher un bouton avec…
Donc, je me suis dit que c’était pour moi…
Bon, en fait, ce n’est pas pour moi…
Mais, que les choses soient claires, c’est l’informatique en général et la programmation en particulier qui ne sont pas pour moi…
Je veux dire, quand je vais lire Stack Overflow, j’ai de la peine pour moi…
C’est à dire que je me fais vraiment pitié tant je comprends que dalle…
Il m’est déjà arrivé de mettre une dizaine de secondes pour savoir si je lisais bien du Python (et s’en était)…
Nan, c’est moche…
Mais bon, je ne sais faire que de l’informatique (on me souffle à l’oreille que je sais aussi faire du Chirashi et c’est vrai)
Donc, je fais de l’informatique… (et du Chirashi)
Et j’en chie…
Mais à la fin je gagne…
Et je me paye même le luxe d’en faire un article sur S&M…
Pourquoi je vous raconte ça ?
Aucune idée…
Toujours est-il que j’ai choisi PyQt4
Que ça n’est pas pour moi…
Mais que [SPOILER ALERT] ça a fini par marcher…

 

Où j’affiche une fenêtre. UNE FE-NÊ-TRE. Oui messieurs, dames.

D’abord, il faut installer PyQt4

sudo apt-get install python-qt4

…et on ne peut pas dire que ce soit trop compliqué.

Et, voici le code minimal pour afficher une fenêtre :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui

# On crée une appli Qt
application = QtGui.QApplication([])
# On fout quelque chose dedans
fond = QtGui.QWidget()
# On l'affiche
fond.show()
# On lance l'appli
application.exec_()

Les connaisseurs apprécieront mon désormais célèbre style procédural de champion du monde.
Mais bon. Ça marche où ça marche pas ?

Une fenêtre

Une fenêtre

Ça marche.

 

Où j’affiche un bouton dans la fenêtre.

Toujours en procédural, voici comment on ajoute un bouton à notre fenêtre (que l’on customize un peu au passage).
(J’insiste pour le procédural car, comme PyQt4 est conçu pour être utilisé en Programmation Orientée Objet, vous ne verrez pas ça tous les jours)

#-*- coding: utf-8 -*-
from PyQt4 import QtGui

app = QtGui.QApplication([])

# On crée un fond et lui donne une taille, une position sur l'écran et un titre
fond = QtGui.QWidget()  
fond.resize(300, 200)
fond.move(100, 100) 
fond.setWindowTitle("C'est formidable") 

# On crée un bouton sur notre fond et on lui donne une position sur celui-ci
bouton = QtGui.QPushButton('Mon petit bouton',fond)
bouton.move(80, 80)

fond.show()
app.exec_() 

Et hop…

Un petit bouton dans une fenêtre

Un petit bouton dans une fenêtre

La première fois que ça a marché, j’ai cliqué 25 fois sur le bouton et j’en garde un très bon souvenir.

 

Où j’affiche plein de cases à cocher dans la fenêtre.

Bon, on peut afficher tout un tas de bordel dans une fenêtre, vous vous en doutez. Des images, du texte, des champs texte, des sliders, des menus déroulants, des labels, des séparations…
Mais, ça n’est pas le propos ici.
Nous ce qu’on veut, ce sont des cases à cocher. Plein de cases à cocher.

J’ai commencé par les placer dans une grille, mais, je me suis rendu compte qu’en leur donnant des positions absolues en pixels, il était possible de les faire se toucher, voire se chevaucher. Ce qui, quand on veut afficher un cheval, apporte un petit plus indéniable.
(chevaucher/cheval/humour)

Allons y donc pour nos cases à cocher.
(Je vous recolle tout à chaque fois parce que je sais qu’il y a des goulus parmi vous.)

#-*- coding: utf-8 -*-
from PyQt4 import QtGui

# On définie les dimensions de notre bazar
largeur = 40
hauteur = 30
tailleCase =14

app = QtGui.QApplication([])

fond = QtGui.QWidget()  
fond.resize(largeur*tailleCase+6, hauteur*tailleCase+6) # le +6 c'est pour tenir compte des marges
fond.setWindowTitle(u"Plein de cases à cocher") 

# On parcourt les ordonnées
for j in range(hauteur):

    # On parcourt les abscisses
    for i in range(largeur):

        # On crée une case à cocher sur notre fond
        check_box = QtGui.QCheckBox(fond)
        # Et on la positionne
        check_box.move(i*tailleCase, j*tailleCase)

fond.show()
app.exec_()
Le mot

Le mot “Joie” écrit à la main avec des cases à cocher.

 

Où j’affiche plein de cases à cocher dans la fenêtre mais en utilisant cette fois la Programmation Orientée Objet.

On arrive au bout. Mais, ce qu’il faut comprendre avec pyQt c’est que quand le programme arrive à l’instruction app.exec_() il démarre une boucle. On est alors un peu coincé pour parler avec l’interface.
Il est possible de cliquer sur les cases à la main, soit, mais, nous ce qu’on veut, c’est que ce soit un bout de code qui le fasse à notre place (car ayant bien mis 2 minutes pour composer une seule image affichant le mot “Joie”, j’ai des doutes pour le 25 fps).

Dans tous les cas, il n’est plus possible de rester en procédural. Voici donc la version POO du précédent script :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui
import sys

largeur = 40
hauteur = 30
tailleCase = 14

class CheckBoxVideo(QtGui.QWidget):
    
    # On initialise la class, j'imagine...
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
    
    # On initialise l'interface    
    def interface(self):
        
        self.resize(largeur*tailleCase+6, hauteur*tailleCase+6)
        self.setWindowTitle(u"Plein de cases à cocher, mais en POO.")
        
        for j in range(hauteur):

            for i in range(largeur):

                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)     

        self.show()


if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())
Le mot "Bonheur" écrit en cases à cocher mais en utilisant cette fois -ci la programmation orientée objet.

Le mot “Bonheur” écrit en cases à cocher mais en utilisant cette fois la programmation orientée objet.

J’ai ajouté un petit if __name__ == "__main__" histoire de laisser croire que je comprends ce que je fais.
Et puis j’ai intégré la librairie sys pour être conforme avec tous les exemples de tous les tutos que j’ai trouvé. Mais c’est vraiment par respect pour les devs qui se sont donné du mal à les écrire. Parce que, vous pouvez essayer, ça marche très bien sans.
Bref.

 

Où, en faisant ce qu’il ne faut pas faire, je constate qu’il ne faut pas le faire.

Je vous épargnerai la liste des trucs ridicules que j’ai essayé pour entrer dans cette boucle et modifier l’état des cases à cocher et n’évoquerai que les solutions que j’ai trouvé pour faire marcher mon bazar… pendant 20 secondes.

En effet, à force de chercher, je suis tombé sur ce fil de Stack Overflow où la réponse la mieux notée donne trois moyens différents de créer des threads avec Qt.

J’ai méticuleusement tout recopié à l’arrache et testé chacune des solutions.
Toutes m’ont permis de voir ma silhouette en cases à cocher avant de me retourner un délicat segfault au bout de 20 secondes.

Vraisemblablement, il y a quelque chose que je faisais mal.
Chose qui a été confirmée par un article intitulé : “You’re doing it wrong…”

C’est à ce moment que j’ai décidé de me coucher, vu que le Soleil se levait.

 

Où j’ai la confirmation que dormir peut s’avérer parfois utile pour se reposer.

Si j’étais mort dans mon sommeil ce jour là, mes derniers mots auraient été : “Putain de boucle de merde”.
Mais je me suis réveillé et je me suis dis que, tout de même, il devait y avoir un moyen simple de faire exécuter du code dans cette boucle principale.

Et là, avec un cerveau frais, je me suis souvenu de cet exemple expliquant comment animer une barre de progression.
Passer des valeurs à un objet pour en changer son état, c’est un peu ce que je cherchais à faire, non ?
Non.
C’était exactement ce que je cherchais à faire.

Le principe est de créer un timer et d’écouter ses tours d’horloge pour déclencher des événements.
À noter la nécessité d’importer QtCore pour cela.

#-*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
import sys
 
largeurOut = 40
hauteurOut = 30
tailleCase = 14
 

class CheckBoxVideo(QtGui.QWidget):
 
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
 
    def interface(self):
 
        self.resize(largeurOut*tailleCase+6, hauteurOut*tailleCase+6)
        self.setWindowTitle("OMG ! Mais que se passe-t-il dans la console ?")
 
        for j in range(hauteurOut):
 
            for i in range(largeurOut):
 
                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)

        # On crée le timer.
        self.timer = QtCore.QBasicTimer()
        # Et on le lance en renseignant un délais pour chaque tour d'horloge.
        # Ici, 40 millisecondes, pour tenter (naïvement) d'obtenir du 25 images par seconde.
        self.timer.start(40, self)

        self.show()
         
    # Et voici la méthode qui écoute le timer.
    def timerEvent(self, e):
 
        print("Ce message va s'écrire en console toutes les 40 ms.")
 
 
if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())
Des cases à cocher dans une fenêtre et des messages dans une console.

Des cases à cocher dans une fenêtre et des messages dans une console.

 

Où j’évoque ma jeunesse.

Il s’agissait maintenant de trouver un moyen de traiter du flux vidéo.
Mais comme je suis un ouf et que non seulement j’avais déjà réussi à afficher de la vidéo 3-bit dans mon terminal , mais qu’en plus je vous en avais fait part ici-même, et bien je suis allé copier/coller mon pâté. Tout simplement.

Pour celles et ceux qui n’ont pas eu la force psychologique de cliquer sur le lien, sachez que la solution que j’avais retenue à l’époque pour lire un fichier vidéo afin d’en traiter les données était OpenCV, et que c’est tout aussi pertinent maintenant.
(Enfin, ça marche. Ce qui, à mon niveau, est la définition même de la pertinence.)

 

Où il est de nouveau affiché un cheval qui court en cases à cocher, mais en Python cette fois.

Et voilà…
C’est terminé…

Voici le script après l’intégration du code exploitant OpenCV :

#-*- coding: utf-8 -*-
from PyQt4 import QtGui, QtCore
import sys
import cv2
 
largeur = 50
hauteur = 34
tailleCase = 14
 
# On crée une liste qui va stocker les QCheckBox
listeCB = []

# On définit un seuil pour pour cocher ou non les cases
seuil = 150

# On définit le fichier à lire.
video = cv2.VideoCapture('cheval_qui_court.mjpg')
 
class CheckBoxVideo(QtGui.QWidget):
 
    def __init__(self):
        super(CheckBoxVideo, self).__init__()
        self.interface()
 
    def interface(self):
 
        self.resize(largeur*tailleCase+6, hauteur*tailleCase+6)
        self.setWindowTitle(u"Un cheval qui court en cases à cocher")
 
        for j in range(hauteur): 
            for i in range(largeur): 
                check_box = QtGui.QCheckBox(self)
                check_box.move(i*tailleCase, j*tailleCase)
                # On ajoute chaque QCheckBox dans notre liste
                listeCB.append(check_box)   
 
        self.timer = QtCore.QBasicTimer()
        self.timer.start(40, self)
        self.show()
 
    def timerEvent(self, e):

        # On lit une frame de la vidéo
        ret,img = video.read()
        # On la passe en niveau de gris
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # On la retaille pour que le nombre de pixel corresponde au nombre de cases à cocher
        gray = cv2.resize(gray, (largeur, hauteur)) 

        # On parcourt notre liste
        for index, checkBox in enumerate(listeCB):

            # On transforme les index en coordonnées            
            x = index%largeur
            y = index/largeur
            # On récupère le niveau de gris du pixel concerné
            valeur = gray.item(y, x)
            
            # En fonction de la valeur on coche ou non la case à cocher
            if (valeur > seuil):
              checkBox.setCheckState(0)
            else:
              checkBox.setCheckState(2)

            # Les plus attentifs auront remarqué que l'état de la case à cocher ne dépend pas d'un booléen.
            # En effet, il existe un état intermédiaire, le (1), que j'ai appelé l'état de Schrödinger, où 
            # la case à cocher est partiellement cochée. Un peu comme si elle était à la fois cochée et non cochée.
            # (Et j'interdis à quiconque d'évoquer, qu'en fait, elle n'est ni l'une, ni l'autre.)

            
if __name__ == "__main__":
      appli = QtGui.QApplication(sys.argv)
      ckbx = CheckBoxVideo()
      sys.exit(appli.exec_())

Et voici le cheval qui court en cases à cocher :

Je concède qu’arrivé là, il n’aurait pas été totalement farfelu d’ajouter un slider pour gérer le niveau du seuil.
Mais j’avais un peu peur de rendre le truc utile, vous voyez.
Donc j’ai laissé en l’état.

 

Où je conclue.

Je pense qu’il est assez clair désormais qu’il est possible de sauver l’Humanité en programmant en Python.
Je vous encourage donc à le faire.

À bientôt ou tard.

 

Où j’ajoute tout de même un bonus.

Et voici un cheval qui court avec des cases à cocher qui se chevauchent.
Ne me remerciez pas, ça me fait plaisir.

cheval

]]>
http://sametmax.com/enfin-un-moyen-dafficher-une-video-avec-des-cases-a-cocher-pyqt4-et-opencv-inside/feed/ 20 17836
Un bot qui tweete 4992 Super-Héros à raison de un toutes les 3,14 heures peut-il sauver Internet ? http://sametmax.com/un-bot-qui-tweete-4992-super-heros-a-raison-de-un-toutes-les-314-heures-peut-il-sauver-internet/ http://sametmax.com/un-bot-qui-tweete-4992-super-heros-a-raison-de-un-toutes-les-314-heures-peut-il-sauver-internet/#comments Wed, 24 Sep 2014 06:43:55 +0000 http://sametmax.com/?p=11783 Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Depuis l’Antiquité, en moyenne, environ une seule personne s’est posée cette question, dont moi.

Je me propose donc de partager avec vous mon expérience sur le sujet en vous expliquant ce que j’ai mis en œuvre pour tenter d’obtenir une réponse.

Bonsjours, je voudrais tweeter en Python, s’il vous plaît.

Il existe plusieurs bibliothèques en Python qui papotent avec l’API de Twitter. Neuf sont actuellement référencées par Twitter.

Parce qu’il y avait écrit “Actively maintained”, j’ai tenté twython .

sudo pip install twython

Il se trouve qu’elle fait très bien ce que j’attendais d’elle, donc je ne suis pas allé chercher plus loin.

Par ailleurs, le code pour envoyer un tweet est flippant de simplicité :

from twython import Twython

APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"

twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)

texte = 'Oh mon Dieu ! Mais cette librairie crache des flammes ! #Twython'

twitter.update_status(status=texte)

Et paf :

 

Pour obtenir ses petites clefs-clefs, il est nécessaire de se créer un compte sur la plate-forme de Twitter réservée aux développeurs.

Je vous la fait courte :

> My applications dans le menu de votre icône en haut à droite > Create New Apps
> Remplir le formulaire > accepter les conditions d’utilisation après les avoir lues
> Permissions > Read and Write
> API Keys > Generate my acces token

La partie la plus compliquée pour moi fut de passer les permissions en “Read and Write“.

En effet, il faut associer un numéro de téléphone au compte twitter utilisé et la manipulation doit être faite à partir du téléphone en question.
Or, je n’ai pas de téléphone portable.

Heureusement, la femme de ma Vie a eu la bonne idée de faire l’Amour il y a 17 ans et sa fille, la fille de ma Vie, possède ce genre d’appareil.
Sauvé.

Si vous n’avez pas de fille de votre Vie, ni même de femme de votre Vie, faites en sorte d’avoir un téléphone portable.

(J’ai mis Amour et Vie en gras pour gagner des points de Karma. Je ne sais pas si ça marche, mais je me dis que ce serait tout de même bien malheureux que ça ait un effet négatif.)

Bonsjours, je voudrais ajouter une image au tweet que je tweete en Python, s’il vous plaît.

En partant du principe que vous avez une image.png à côté de votre script :

from twython import Twython

APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"

twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)

texte = 'Mais quelle folie ! #Twython tweete aussi des images ! '
image = open("image.png", 'rb')

twitter.update_status_with_media(status=texte, media=image)

Et hop :

 

J’ai du mal à imaginer qu’on puisse faire plus simple.

Maintenant, j’aimerais tweeter toutes les 3,14 heures. C’est possible ?

Pour tweeter toutes les 3 heures, une petite crontab réglée de la sorte aurait fait l’affaire :

0 */3 * * * python chemin/vers/mon/script.py

Mais pour 3,14 heures, il faut faire dans le délicat.
Je fais donc patienter le script 11 304 secondes entre chaque envoi :

from time import sleep

while True:

	envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()
	sleep(11304)

Tout à fait cochon.
Tout à fait parfait.

Et pour les 4992 Super-Héros.
Comment fait-on ?

Alors là, vous n’allez pas être déçus.

Afin de d’obtenir 4992 Super-Héros, je me suis dit qu’il me suffisait de trouver 24 têtes de Super-Héros, 13 torses de Super-Héros et 16 jambes de Super-Héros puis de réaliser toutes les combinaisons possibles de ces trois éléments.

24 * 13 * 16 = 4992

Hasard de folie, il se trouve qu’afin de récolter des fonds pour mener à bien leurs actions, La Quadrature Du Net propose une petite appli de personnalisation de Super-Héros pour celles et ceux qui font un don.

Et bien, aussi incroyable que cela puisse paraître, elle propose de choisir parmi 24 têtes de Super-Héros du Net, 13 torses de Super-Héros du Net, 16 jambes de Super-Héros du Net.

L’émotion est un peu retombée, mais, sur le moment, j’ai eu la chaire de poule face à une telle coïncidence, surtout quand on sait que La Quadrature défend les droits et les libertés des citoyens sur Internet depuis 2008 et que c’est pile-poil dans la thématique de la question qui fait l’objet de cet article.

Me voilà donc parti pour trouver un moyen de récupérer tous ces morceaux de Super-Héros du Net et de reconstituer les 4992 Super-Héros entiers possibles.

Pour retrouver l’adresse de chaque morceau, un simple coup d’œil au code-source de la page a suffi.

Ensuite, en terme de n’importe quoi je vous propose un morceau de choix.

Car pour faire ma petite popote, j’ai utilisé Processing.

_ Processing ?!?!
_ Oui. Processing.
_ Mais… ça utilise une syntaxe Java, ça. Non ?
_ Oui. Mais pour Sam&Max j’ai utilisé le mode Python qui vient tout juste de sortir.
_ Utiliser un “framework Java” en Python. Excuse-moi, mais c’est un peu n’importe quoi.
_ Tout à fait.

Pour celles et ceux qui ne le connaissent pas, Processing est un logiciel/langage conçu pour que les artistes, les graphistes, les designers et les grosses bubuses comme moi puissent facilement et rapidement produire du code créatif (image, son, vidéo).

Je ne vais pas vous faire un cours. Il y a un FLOSS Manual en français pour ça.

Utilisant Processing depuis plusieurs années, je vous avoue que le mode Python m’a clairement donné l’impression de faire du skate avec des rollers, mais comme outils pédagogique, ça doit être pas mal.
Et puis ça marche !

Voici le code que j’ai pondu pour l’occasion :

# La fonction setup() est propre à Processing.
# Elle s’exécute en premier et une seule fois.

def setup():

    # On crée une image transparente de 1 pixel de côté qui sera utilisée
    # pour représenter l’absence de choix de tête, de torse et de jambes
    img_vide = createImage(1, 1, ARGB)
    img_vide.pixels[0] = color(0, 0)

    # On crée des listes qui contiendront les images des têtes, torses et jambes
    # et on place l'image vide crée juste avant comme premier élément.
    global liste_torses, liste_tetes, liste_jambes
    liste_tetes = [img_vide]
    liste_torses = [img_vide]
    liste_jambes = [img_vide]

    # On lance le téléchargement de toutes les images nécessaires.
    telechargement_images()

    # On fabrique les 4992 Super-Héros avec les images téléchargées.
    usine_super_heros()

def telechargement_images():

    # Sachant que chaque image est atteignable par une URL du type
    # http://soutien.laquadrature.net/images/bonus/head_01.png

    # On définit l'URL du dossier où sont situées les images.
    url = "http://soutien.laquadrature.net/images/bonus/"

    # On télécharge l'image du Super-Héros tout nu qui est placé au fond
    # de chaque image et qui sert de base à tous les autres.
    global base
    base = loadImage(url + "base.png")

    # On télécharge les 24 têtes, les 13 torses et les 16 jambes.
    choix_image(url, "head_", liste_tetes, 24)
    choix_image(url, "torso_", liste_torses, 13)
    choix_image(url, "legs_", liste_jambes, 16)

def choix_image(url, nom, liste, nb_image):

    # Pour chaque nombre entre 1 et nb_image (0 correspondant à img_vide),
    for i in range(1, nb_image):

        # on définit un numéro toujours composé de 2 chiffres. Ex: 05 pour 5,
        numero = str(i).zfill(2)
        # on récupère l'image en recomposant l'URL,
        image_recuperee = loadImage(url + nom + numero + ".png")
        # et on ajoute cette image dans la liste correspondante.
        liste.append(image_recuperee)

def usine_super_heros():

    # Sachant que toutes images récupérées ont la même taille (214x383)
    # et qu'elles sont des .png transparents dont le contenu trouve sa place
    # quand les images sont parfaitement superposées .

    # On définit une variable qui va compter les 4992 Super-Héros.
    compteur = 0

    # Pour toutes les images de têtes,
    for tete in liste_tetes:

        # pour toutes les images de torses,
        for torse in liste_torses:

            # et pour toutes les images de jambes,
            for jambe in liste_jambes:

                # on crée un élément graphique de 214x383,
                super_hero = createGraphics(214, 383)
                # on démarre le dessin de l'élément graphique,
                super_hero.beginDraw()
                # on place un fond blanc,
                super_hero.background(255)
                # on place le Super-Héros tout nu aux coordonnées 0, 0
                super_hero.image(base, 0, 0)
                # on place l'image des jambes en 0, 0
                super_hero.image(jambe, 0, 0)
                # on place l'image du torse en 0, 0
                super_hero.image(torse, 0, 0)
                # on place l'image de la tête en 0, 0
                super_hero.image(tete, 0, 0)
                # on clos le dessin de l'élément graphique,
                super_hero.endDraw()
                # on sauvegarde notre Super-Héros du Net dans le dossier data,
                super_hero.save("data/SuperHeros-{0}.png".format(compteur))
                # et on incrémente le compteur de 1 avant de passer
                # au Super-Héros suivant
                compteur += 1

Et hop !

Et sinon, c’est encore loin Grand Schtroumpf ?

On arrive au bout.

Pour ne pas tweeter les Super-Héros du Net dans l’ordre et pour ne pas non plus poster deux fois le même en cas de croutage du serveur, je me suis créé un petit ordre.txt comme cela :

from random import shuffle

tous_les_numeros = range(4992)
shuffle(tous_les_numeros)

with open ("ordre.txt", 'w') as base:

    for numero in tous_les_numeros:

        base.write("{0},".format(numero))

Et dont le contenu est juste :

1798,915,1402,1032,507,112,1098,600,323,787,1435,200,774,33,1419,106,1836... etc

Enfin, voici le script final, qui tweete donc toutes les 3,14 heures un des 4992 Super-Héros du Net.

# -*- coding: utf8 -*-

# Ce script permet d'envoyer un tweet avec une image toutes les 3,14 heures
# Il récupère dans une liste le numéro de l'image a envoyer,
# et enregistre le nombre de tweets déjà postés
# pour pouvoir changer d'image à la prochaine exécution

import os
from twython import Twython
from time import sleep

# Clef d'authentification à obtenir sur https://dev.twitter.com
APP_KEY = "votre petite clef-clef à vous"
APP_SECRET = "une autre petite clef-clef"
OAUTH_TOKEN = "encore une petite clef-clef"
OAUTH_TOKEN_SECRET = "enfin, une dernière petite clef-clef"

twitter = Twython(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)

ordre = []
combien_deja_poste = 0

# Récupère le répertoire courant
chemin_dossier = os.path.dirname(os.path.realpath(__file__))+"/"

def envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net():

    # Récupère l'ordre des images à poster
    with open (chemin_dossier+"ordre.txt", 'r') as contenu:

            ordre = contenu.readline().rstrip('\n').split(",")

    # Récupère le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'r') as contenu:

        combien_deja_poste = int(contenu.readline().rstrip('\n\r'))

    # Récupère le numéro de l'image
    numero = int(ordre[combien_deja_poste])

    # Construit le chemin vers cette image
    # Sachant que le dossier AllStar contient les 4992 Super-Héros du Net
    chemin_image = chemin_dossier+"AllStar/SuperHeros-{0}.png".format(numero)

    # Charge l'image
    image = open(chemin_image, 'rb')

    # Forme le texte du tweet
    texte = ("Voici le Super-Héros du Net n°{0}\n"
             "Si ce n'est pas déjà fait, aidez-le, lui et ses 4992 amis\n"
             "soutien.laquadrature.net").format(numero+1)

    # Envoie le tweet composé d'un texte et d'une image
    twitter.update_status_with_media(status=texte, media=image)

    # Met à jour le nombre d'images déjà postées
    with open (chemin_dossier+"deja_poste.txt", 'w') as contenu:

        contenu.write(str(combien_deja_poste + 1))

while True:

    envoie_moi_donc_un_tweet_avec_une_image_de_Super_Heros_du_Net()

    # Patiente pendant les 3,14 heures, soit  les 11 304 secondes
    sleep(11304)

    # Le script plantera dans 2 ans quand les 4992 Super-Héros du Net
    # aurons tous été postés, et c'est très bien ainsi...

Et voilà, il ne reste plus qu’à choper 10 Millions de followers sur ce compte et à espérer que 3,14% d’entre eux filent 10€ à LQDN pour que l’on puisse augmenter les chances de répondre positivement à la question posée dans le titre.
Une simple formalité…

Je précise que La Quadrature n’a absolument rien à voir avec ce compte Twitter, c’était juste l’occasion pour moi de pondre un article de plus ici tout en soutenant une cause qui me tient hackeur (humour).

À la prochaine…

Information complémentaire :
Le Monde en date du 12 août – Près d’un compte Twitter sur 10 est alimenté automatiquement

]]>
http://sametmax.com/un-bot-qui-tweete-4992-super-heros-a-raison-de-un-toutes-les-314-heures-peut-il-sauver-internet/feed/ 10 11783
Où il est présenté une méthode en Python pour afficher de la vidéo 3-bit dans son terminal. http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/ http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/#comments Thu, 19 Jun 2014 04:36:29 +0000 http://sametmax.com/?p=10535 Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Il est des choses inutiles plus passionnantes que d’autres.
Il semblerait que j’ai un petit faible pour les choses inutiles que je fais moi-même.

Car, afficher le contenu de sa webcam dans une console, ça ne sert clairement pas à grand chose mais j’ai pourtant crié très fort en serrant les poings quand j’y suis arrivé.

Capturer de la vidéo en Python.

Sous Nunux, il existe un joli petit paquet tout beau tout chaud qui permet de gérer du flux vidéo en Python : python-opencv.

Alors, zou :

sudo apt-get install python-opencv

Et pour afficher sa webcam, seules quelques lignes suffisent :

#-*- coding: utf-8 -*-

import cv2

# Choix du périphérique de capture. 0 pour /dev/video0.
cap = cv2.VideoCapture(0)
 
while True:

    # On capture l'image.
    ret,im = cap.read()

    # On l'affiche.
    cv2.imshow('Ma Webcam à moi',im)

    # Et on attend 40 millisecondes pour avoir du 25 images par seconde.
    key = cv2.waitKey(40)

GO !!

Je vais bien me garder de vous faire un tuto sur OpenCV tant cette librairie est puissante (comprendre : j’y panne que dalle). Si vous êtes curieux, vous pouvez aller faire un tour ici.

Et, parce que j’avais bien d’autres choses plus intéressantes à faire que d’aller visiter le lien ci-dessus, j’ai fait un petit print du im précédent et découvert une liste toute mignonne dont voici la structure:

# Avec des valeurs pour les niveaux comprises entre 0 et 255.
im[n° de ligne][n° de colonne][niveau de bleu, niveau de vert, niveau de rouge]

J’étais content car j’allais pouvoir récupérer les valeurs de chaque pixel de cette façon:

for ligne in range(hauteur):

    for colonne in range(largeur):
            
        niv_bleu = im[ligne][colonne][0]
        niv_vert = im[ligne][colonne][1]
        niv_rouge = im[ligne][colonne][2]

Si, pour un autre projet formidable, je n’avais pas eu à travailler avec une RasberryPi et ses petites cuisses de bébé, je pense que j’utiliserai encore cette méthode de bourrin.

Mais voilà, c’est juste ridicule quand on sait que la liste en question est en fait un array numpy et qu’il est 10, 100 fois plus rapide de faire:

for ligne in range(hauteur):

    for colonne in range(largeur):
            
        niv_bleu = im.item(ligne, colonne, 0)
        niv_vert = im.item(ligne, colonne, 1)
        niv_rouge = im.item(ligne, colonne, 2)

Je n’en suis pas à me dire que la prochaine fois que j’achèterai un grille-pain, je lirai la notice avant de l’utiliser, mais presque…

Petite remarque en passant : les valeurs de rouge, de vert et de bleu étant comprise entre 0 et 255, cela nous donne 256 valeurs pour chaque couleur.
Soit 256 x 256 x 256. Soit 2⁸ x 2⁸ x 2⁸. Soit 2²⁴ ==> les couleurs de notre flux sont codées par défaut sur 24 bits.

À noter qu’il est tout à fait possible d’analyser une image sans avoir à l’afficher, ce qui permet d’utiliser OpenCV dans un environnement sans gestionnaire de fenêtre.

Afficher de la couleur dans la console.

Bon. J’avais mes niveaux RVB pour chaque pixel. Il me fallait désormais afficher de la couleur dans la console.

Quelques requêtes Duck Duck Go plus tard, je découvre termcolor qui fait très bien le job mais dont on peut se passer en regardant les codes ANSI de plus près.

Bien entendu, la grande majorité des consoles étant limitées à 8 couleurs, soit 2³, soit 3-bit je me suis restreint à cette qualité.

Démonstration :

Il est possible d’obtenir beaucoup plus de couleurs en combinant les fonds, les caractères et les intensités comme le fait la libcaca, mais on ne joue pas vraiment dans la même cour.

Perso, quand ça s’est affiché en rouge pour la première fois, j’ai eu des frissons partout. Parce qu’il faut bien comprendre que je n’ai toujours aucune idée de ce que “\033[” et autres “m” veulent dire. J’ai copié/collé, c’est tout. Et dans ces cas là, quand ça marche, c’est toujours la fête.

Ce qui pourrait être un problème, on le voit à l’image, c’est qu’une fois que j’ai écrit TATA YOYO en rouge, le prompt devient lui aussi rouge, et ainsi de suite à chaque changement de couleur. Pour remédier à ça, il faut ajouter \033[0m à la fin du texte à afficher pour que le reste soit écrit avec la couleur par défaut du terminal.

Démonstration :

C’est d’ailleurs ce que fait termcolor, sauf que, dans notre cas, nous n’avons pas besoin de revenir à cette valeur par défaut à chaque affichage de pixel vu que le suivant sera lui aussi coloré.

Je vous en parle seulement parce que vous avez l’air sympa.

J’ajouterai qu’après avoir effectué un benchmark de folie exploitant brillamment les deux points qui clignotent à chaque seconde sur mon radio-réveil, il s’est avéré que la solution “maison” était plus performante que termcolor : mon choix était fait.

Du pixel au █

J’ai renoué contact avec le █ il n’y a pas si longtemps. Aussi étonnant que cela puisse paraître, alors que je baigne quasi quotidiennement dans l’informatique depuis 30 ans, il n’est pas impossible que notre dernière rencontre remonte à 1986 sur le Commodore 64 familial.

Pour vous donner une idée de l’émotion qui m’a traversé quand j’ai revu le █, vous pourriez très clairement user de l’expression “le █ d’Olivier” en lieu et place de “la madeleine de Proust” dans vos discussions. Mais, à l’oral, le █ passe mal, et c’est bien dommage.

Pour info, le pseudo unicode de █ c’est \u2588.

Et, pour afficher un █ en couleur, il suffit de faire comme vu au dessus.

Reste à trouver un moyen de passer des millions de couleurs potentielles de notre vidéo aux huit de notre console.

C’est là que vous allez comprendre pourquoi je me suis acharné avec mes captures d’écrans. C’était pour bien vous faire intégrer l’association entre les couleurs et la valeur qui les code. À savoir :

1 : rouge
2 : vert
3 : jaune
4 : bleu
5 : violet
6 : turquoise
7 : blanc

Et là qu’est qu’on remarque ?
Que cela respecte la synthèse additive si on attribue 1 au rouge, 2 au vert et 4 au bleu, bien entendu !

1 + 2 = 3 et en synthèse additive rouge + vert = jaune
1 + 4 = 5 et en synthèse additive rouge + bleu = violet
2 + 4 = 6 et en synthèse additive vert + bleu = turquoise
1 + 2 + 4 = 7 et en synthèse additive rouge + vert + bleu = blanc

Mettez-vous à ma place: je venais de découvrir l’Amérique !

Bon, rétrospectivement, cela ne constitue vraiment rien d’extraordinaire en soi dans la mesure où c’est ce qui découle logiquement d’un codage sur 3 bits mis en place par un être humain qui a juste envie de faire simple plutôt que de faire compliqué.

Mais tout de même, sur le moment…
… L’AMÉRIQUE BORDEL ! L’AMÉRIQUE !

Il devenait alors facile d’évaluer le degré de présence de chaque composante RVB d’un pixel puis de déterminer laquelle des 8 couleurs lui correspondait le plus.

Voilà comment je m’y suis pris :

# On initialise à 0 l'indice du pixel analysé 
indice_couleur = 0

# On analyse le niveau de Bleu du pixel.
# S'il est au dessus du seuil...
if img.item(ligne, colonne, 0) > seuil :

    #...on ajoute 4 à l'indice.
    indice_couleur += 4

# On analyse le niveau de Vert du pixel.
# S'il est au dessus du seuil...
if img.item(ligne, colonne, 1) > seuil :

    #...on ajoute 2 à l'indice.
    indice_couleur += 2

# On analyse le niveau de Rouge du pixel.
# S'il est au dessus du seuil...
if img.item(ligne, colonne, 2) > seuil :

    # ...on ajoute 1 à l'indice.
    indice_couleur += 1

L’indice obtenu correspond alors au code couleur ANSI à utiliser !!

Je veux dire.

Tout de même.

C’est super, non ?

Hum…

Bien, bien…
C’est bientôt fini, il me reste juste…

Quelques remarques supplémentaires.

1) Le noir ANSI est en fait du gris, et c’est bien moche, j’ai donc préféré partir du principe que la console aurait un fond noir et afficher un “espace” pour chaque pixel noir.

2) print(“\033[H\033[2J”) permet d’effacer la console comme le fait os.system(‘clear’).
Mais, j’imagine que ça devait faire trop de 033 dans le script pour moi, parce que, psychologiquement, ça ne passait pas.
J’ai un peu discuté avec moi-même et on a fini par décider d’utiliser le clear.

3) J’ai commencé par utiliser la concaténation pour ajouter mes █ colorés à mon texte_image final :

texte_image += u"\033[3{0}m█".format(indice_couleur))

Mais, Stack Overflow a tapé à la fenêtre et il m’a dit qu’il était beaucoup plus rapide de créer une liste puis d’en joindre les éléments.
J’ai benchmarké avec mon radio-réveil.
Stack Overflow avait raison.

4) Par contre, ce que Stack Overflow s’était bien gardé de me dire, c’est que l’affichage en console avait ses propres limites internes.
Bilan, après avoir optimisé mon code du mieux que je le pouvais, j’ai constaté que le script calculait de toutes façon les texte_image plus vite que la console ne pouvait les afficher.

Ce qui relève un peu du FAIL quand on y pense.

Donc, si vous avez une idée pour que ça ne scintille plus au delà de 25 lignes de hauteur, je suis preneur, sachant que je suis tout à fait à même d’entendre que j’ai fait n’importe quoi dès le début.

ÉDIT: Dans les commentaires, Tmonjalo a proposé une solution qui résout le problème du scintillement en faisant revenir le curseur en haut à gauche plutôt que d’effacer la console. J’ai donc édité le code en conséquence. Merci à lui.

La totale.

Voici le script final. Il est diffusé sous les termes de la très sérieuse WTFPL.

Les variables à modifier pour faire des tests sont le seuil, la largeurOut et la hauteurOut.

À noter aussi que si vous faite un petit…

cap = cv2.VideoCapture("VotreFilm.avi")

… au lieu d’ouvrir la webcam en /dev/video0, et bien vous allez voir VotreFilm.avi dans la console. Super génial !

#-*- coding: utf-8 -*-

import cv2
import os

# Définition du flux capturé.
# Comme elle sera, de toutes façons, retaillée à la baisse,
# elle est fixée à la valeur la plus petite supportée par la webcam.
# À noter que cette valeur minimale peut varier en fonction de votre cam.
largeurIn = 160
hauteurIn = 120

# Définition du flux qui s'affichera en console.
# À savoir le nombre de caractères en largeur et en hauteur.
largeurOut = 60
hauteurOut = 20

# Seuil de présence des couleurs rouge, vert, bleu dans un pixel. 
# Entre 0 et 255.
seuil = 120

# Choix du périphérique de capture.
# Ici /dev/video0
cap = cv2.VideoCapture(0)

# Configuration de la définition du flux.
cap.set(3, largeurIn)
cap.set(4, hauteurIn)

# On efface la console.
os.system('clear')

# On définit une position de référence pour le curseur.
# En haut à gauche, donc, puisqu'on vient juste d'effacer la console.
print ('\033[s')

# Pendant... tout le temps...
while True:

    # On capture une image.
    ret, img = cap.read()

    # On retaille l'image capturée.
    img = cv2.resize(img,(largeurOut, hauteurOut))

    # On initialise une liste qui contiendra tous les éléments de l'image
    liste_image = []

    # Pour chaque ligne de l'image.
    for ligne in range(hauteurOut):

        # Pour chaque colonne de chaque ligne.
        for colonne in range(largeurOut):
            
            # On initialise à 0 l'indice du pixel analysé 
            indice_couleur = 0

            # On analyse le niveau de bleu du pixel.
            # S'il est au dessus du seuil...
            if img.item(ligne, colonne, 0) > seuil :

                #...on ajoute 4 à l'indice.
                indice_couleur += 4

            # On analyse le niveau de bleu du pixel.
            # S'il est au dessus du seuil...
            if img.item(ligne, colonne, 1) > seuil :

                #...on ajoute 2 à l'indice.
                indice_couleur += 2

            # On analyse le niveau de bleu du pixel.
            # S'il est au dessus du seuil...
            if img.item(ligne, colonne, 2) > seuil :

                # ...on ajoute 1 à l'indice.
                indice_couleur += 1

            # Si l'indice obtenu est différent de 0...
            if indice_couleur:

                # ...on ajoute un █ coloré à la liste.
                liste_image.append(u"\033[3{0}m█".format(indice_couleur))

            # Si l'indice est égal à 0...
            if not indice_couleur:

                # ...on ajoute un espace (noir ?) à la liste.
                liste_image.append(" ")
        
        # On fait en sorte que le terminal retrouve sa couleur initiale
        liste_image.append("\n\033[00m")
    
    # On produit un string en mettant bout à bout tous les éléments de la liste
    texte_image = ''.join(liste_image)

    # On affiche l'image.
    print(texte_image)

    # On replace le curseur à la position de référence.
    print ('\033[u')

    # On attend 40 millisecondes pour obtenir du 25 images par seconde.
    key = cv2.waitKey(40)

Des vidéos ! Des vidéos !

Voici une petite vidéo réalisée pour promouvoir un événement à nous. À partir de la 44ème seconde, on peut m’y voir coiffé d’un masque de soudeur en train de faire tenir au plafond un donut géant au moyen d’une batte de base-ball en aluminium :

Pour plus d’information sur cet événement vous pouvez allez voir ici et admirer, par la même occasion, notre magnifique affiche réalisée en pur Python.

Enfin, compte tenu des tauliers du site, je ne pouvais passer à côté de la figure imposée.
Je vous propose donc un extrait de Deep 3-bit, hommage appuyé à Vuk Cosic et son légendaire Deep ASCII.

]]>
http://sametmax.com/ou-il-est-presente-une-methode-en-python-pour-afficher-de-la-video-3-bit-dans-son-terminal/feed/ 16 10535
Un Tic-Tac-Toe en HTML écrit en Python, sans framework, mais avec les pieds. http://sametmax.com/un-tic-tac-toe-en-html-ecrit-en-python-sans-framework-mais-avec-les-pieds/ http://sametmax.com/un-tic-tac-toe-en-html-ecrit-en-python-sans-framework-mais-avec-les-pieds/#comments Sun, 27 Apr 2014 08:28:21 +0000 http://sametmax.com/?p=9851 Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Mea Sam&Maxima Culpa

Tout d’abord, je me dois de vous présenter mes excuses.

Je vous avais laissés miroiter, lors de ma première intervention, des articles avec du code bien dégueulasse afin de décomplexer les plus nuls d’entre vous, mais, quand je me suis penché sur la rédaction de mon troisième article ainsi que sur le script que je vais vous présenter aujourd’hui, j’étais tellement embarrassé de ne pas comprendre moi-même ce que j’avais fait à l’époque que j’ai repoussé cette publication à un jour où j’aurais le temps de décortiquer mon propre sale travail.

Bien entendu, ce jour n’est jamais venu et vous n’avez plus entendu parler de moi.

Quelle misère.
Alors que j’avais sous les yeux un code illisible, d’une inutilité profonde et surtout particulièrement long pour ce qu’il avait à faire, je faisais la fine bouche et vous privais d’une véritable leçon de vie qui pourrait se résumer par “Comment faire n’importe quoi n’importe comment.”

Me voilà donc devant vous aujourd’hui pour tenter de me rattraper.

L’idée géniale.

En 1998, poussé par une intuition qui ne survient sur Terre qu’une fois tous les 69 ans, mon ami Frédéric s’est lancé dans l’écriture informatique d’un Tic-Tac-Toe afin de rendre un hommage appuyé à l’accadémie des neufs. Il a donc pondu, à la main, en HTML, toutes les combinaisons possibles pour pouvoir jouer au jeu.

Chez moi, qui découvrais à peine les homepages sur fond gris et les GIF animés “Work in progress…”, je peux vous assurer que cette initiative eut l’effet d’une bombe. Mon ami était un génie.

Une dizaine d’années plus tard, pour son anniversaire, je me suis dit que j’allais faire la même chose, mais en Python. Compte-tenu de mon niveau et de l’intérêt de la démarche, j’étais convaincu d’arriver à un résultat tout aussi absurde. Je n’ai pas été déçu.

Le résultat.

Avant d’aborder la manière dont je m’y suis pris et d’essuyer, de fait, les ricanements des plus véloces d’entre vous, je vous propose d’admirer le résultat. Car, c’est n’importe quoi, c’est fait n’importe comment, mais ça marche :

Cliquez ici pour être certain de ne pas gagner.

La mise en Œuvre.

Le script s’appuie sur la méthode .format() qui permet de faire des choses émouvantes comme :

>>> toto = "J'ha{1} Orleans pour son cor{0} artis{3} suc{2}ent."
>>> toto.format("bite", "nichon", "anal", "cul")
"J'hanichon Orleans pour son corbite artiscul sucanalent."

À noter que si vous faites…

>>> toto = "J'ha{1} Orleans pour son cor{0} artis{3} suc{2}ent."
>>> titi = ("bite", "nichon", "anal", "cul")
>>> toto.format(titi)

…vous allez récolter un IndexError parce que, même si titi contient 4 éléments, vous ne filez qu’un seul argument à .format() alors qu’il en a besoin de 4. Il faut donc faire…

>>> toto.format(*titi)

… où l’étoile vous dépackagera votre tuple bien comme il faut (mais ne me demandez pas pourquoi).

Armé de cette subtilité, j’ai créé une page HTML avec un petit tableau de 3×3 puis j’ai écrit dans chacune des cellules {0}, {1}, {2}… pour pouvoir les remplacer par le texte

si j’avais besoin d’y placer un rond, ou par

si j’avais besoin d’y mettre une croix.

Ici, par exemple, je vois bien que j’ai numéroté les cellules en faisant un petit escargot et non pas ligne par ligne, mais je n’ai aucune idée de pourquoi j’ai fait ce choix. J’ai dû vouloir faire une blague. Si ça vous fait rire, c’est qu’elle est réussie. Moi, je suis resté de marbre.

{0} {1} {2}
{7} {8} {3}
{6} {5} {4}

Puis, pour exporter chaque page HTML, j’ai écrit cette petite fonction…

  
  def ecriturePage (namePage, contenuPage):
    with open ('{0}.html'.format(namePage), 'w') as new_file:
        new_file.write (contenuPage)

…qui récupère le nom de la page, son contenu et l’enregistre dans le dossier courant.

Ensuite…

Ensuite, c’est plus compliqué.
De ce que je comprends, j’initialise une liste que je réécris partiellement en fonction des combinaisons recherchées.

C’est un peu flou, mais, ce qui est sûr c’est que je n’ai pas automatisé grand chose, surtout sur la fin…
J’ai rempli des tas de feuilles de brouillon pour trouver les bonnes positions pour les croix et les ronds à chaque étape… et je les ai retranscrites avec des for des if et des else

Pour la dernière étape, je suis sûr qu’il eut été plus rapide pour moi d’écrire les pages HTML directement à la main, mais, j’aurais alors lamentablement échoué dans ma mission et mon ami m’aurait sans nul doute frappé violemment au visage si je lui avais présenté ce résultat médiocre comme cadeau pour son anniversaire.

J’ai donc persévéré.

La récompense.

Vous l’aurez compris, cette décision absurde s’est révélée extrêmement judicieuse.

En effet, concentré que j’étais dans l’obtention de toutes les pages nécessaires pour jouer, je n’ai absolument pas prêté attention à la non-obtention des pages inutiles…

Et je me suis retrouvé avec ce joyau.

J’avais fait écrire une combinaison qui n’était pas possible.
J’avais créé un futur pour le jeu qui, bien qu’existant, ne lui était pas accessible.

MON DIEU QUEL PUR MOMENT DE BONHEUR !!!

Le code

L’archive avec l’index.html, les images, le CSS… est disponible ici.

Pour créer toutes les pages (dont ces bijoux de pages inutiles) il suffit de lancer le fichier GO.py.

Mais pour les plus impatients, voici l’intégralité du code :

# -*- coding: utf-8 -*-
# Ce programme écrit toutes les pages html nécessaires pour jouer au Tic-Tac-Toe 
# En partant du principe que l'ordinateur ne doit pas perdre, donc commence...

c = range (10)
rond = ''
croix = ''
croixR = ''
back = """ Pour recommencer, c'est ici... """

def page (grille):
    return('''



 
  
  
  
  Y a matière à... l académie des neuf
   

 

{0} {1} {2}
{7} {8} {3}
{6} {5} {4}
{9}
'''.format(*grille)) def lien (link): return (''.format(link)) def ecriturePage (namePage, contenuPage): with open ('{0}.html'.format(namePage), 'w') as new_file: new_file.write (contenuPage) def grilleVide (num): for i in range (8): c[i] = lien ("{0}{1}".format(num, i)) c[8] = croix c[9] = "" def grilleFin (): for i in range (8): c[i] = '' c[8] = croix c[9] = back ####### 1er clic for i in range (8): grilleVide(i) c[i] = rond if i !=1 and i !=5: c[1] = croix else: c[7] = croix ecriturePage(i, page(c)) ####### 2eme clic for i in range (8): if i == 0: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[6] = croix ecriturePage("{0}{1}".format(i, j), page(c)) if i == 2: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[4] = croix ecriturePage("{0}{1}".format(i, j), page(c)) if i == 7: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[2] = croix ecriturePage("{0}{1}".format(i, j), page(c)) if i == 3: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[0] = croix ecriturePage("{0}{1}".format(i, j), page(c)) elif i == 1: for j in range (8): if j !=3: grilleFin() c[7] = croixR c[i] = rond c[j] = rond c[3] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[7] = croix c[i] = rond c[j] = rond c[6] = croix ecriturePage("{0}{1}".format(i, j), page(c)) elif i == 5: for j in range (8): if j !=3: grilleFin() c[7] = croixR c[i] = rond c[j] = rond c[3] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[7] = croix c[i] = rond c[j] = rond c[6] = croix ecriturePage("{0}{1}".format(i, j), page(c)) elif i == 6: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[4] = croix ecriturePage("{0}{1}".format(i, j), page(c)) elif i == 4: for j in range (8): if j !=5: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[5] = croixR c[8] = croixR else: grilleVide("{0}{1}".format(i, j)) c[1] = croix c[i] = rond c[j] = rond c[6] = croix ecriturePage("{0}{1}".format(i, j), page(c)) #######3eme clic for i in range (8): if i in (0, 4): j = 5 for k in range (8): if k !=2: grilleFin() c[1] = croix c[i] = rond c[j] = rond c[6] = croixR c[k] = rond c[2] = croixR c[8] = croixR else: grilleVide("{0}{1}{2}".format(i, j, k)) c[1] = croix c[i] = rond c[j] = rond c[6] = croix c[k] = rond c[3] = croix ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) if i in (2, 6): j = 5 for k in range (8): if k !=0: grilleFin() c[1] = croix c[i] = rond c[j] = rond c[4] = croixR c[k] = rond c[0] = croixR c[8] = croixR else: grilleVide("{0}{1}{2}".format(i, j, k)) c[1] = croix c[i] = rond c[j] = rond c[4] = croix c[k] = rond c[7] = croix ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) if i == 7: j = 5 for k in range (8): if k in (0, 3, 4): grilleFin() c[1] = croix c[i] = rond c[j] = rond c[2] = croixR c[k] = rond c[6] = croixR c[8] = croixR elif k == 6: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[2] = croixR c[k] = rond c[0] = croixR ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) if i == 3: j = 5 for k in range (8): if k in (2, 6, 7): grilleFin() c[1] = croix c[i] = rond c[j] = rond c[0] = croixR c[k] = rond c[4] = croixR c[8] = croixR elif k == 4: grilleFin() c[1] = croixR c[i] = rond c[j] = rond c[0] = croixR c[k] = rond c[2] = croixR ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) if i == 1: j = 3 for k in range (8): if k in (0, 4, 5): grilleFin() c[7] = croix c[i] = rond c[j] = rond c[6] = croixR c[k] = rond c[2] = croixR c[8] = croixR elif k == 2: grilleFin() c[7] = croixR c[i] = rond c[j] = rond c[6] = croixR c[k] = rond c[0] = croixR ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) if i == 5: j = 3 for k in range (8): if k in (0, 1, 4): grilleFin() c[7] = croix c[i] = rond c[j] = rond c[6] = croixR c[k] = rond c[2] = croixR c[8] = croixR elif k == 2: grilleFin() c[7] = croixR c[i] = rond c[j] = rond c[6] = croixR c[k] = rond c[0] = croixR ecriturePage("{0}{1}{2}".format(i, j, k), page(c)) ######4eme clic for i in range (8): if i == 0: grilleFin() j = 5 k = 2 l = 4 c[1] = croix c[i] = rond c[j] = rond c[6] = croix c[k] = rond c[3] = croixR c[l] = rond c[7] = croixR c[8] = croixR ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) l = 7 c[3] = croix c[l] = rond c[7] = croix c[8] = croix c[l] = rond c[4] = croix ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) if i == 2: grilleFin() j = 5 k = 0 l = 3 c[1] = croix c[i] = rond c[j] = rond c[4] = croix c[k] = rond c[7] = croix c[l] = rond c[6] = croix ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) l = 6 c[l] = rond c[3] = croixR c[7] = croixR c[8] = croixR ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) if i == 4: grilleFin() j = 5 k = 2 l = 7 c[1] = croix c[i] = rond c[j] = rond c[6] = croix c[k] = rond c[3] = croix c[l] = rond c[0] = croix ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) l = 0 c[l] = rond c[7] = croixR c[3] = croixR c[8] = croixR ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) if i == 6: grilleFin() j = 5 k = 0 l = 3 c[1] = croix c[i] = rond c[j] = rond c[4] = croix c[k] = rond c[7] = croix c[l] = rond c[2] = croix ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c)) l = 2 c[l] = rond c[3] = croixR c[7] = croixR c[8] = croixR ecriturePage("{0}{1}{2}{3}".format(i, j, k, l), page(c))

Je ne doute pas un seul instant que ces lignes vont vous ouvrir les yeux sur le sens de la Vie et que tout le reste n’aura plus trop d’importance, mais retenez tout de même que la première personne à être concernée par les commentaires dans vos codes, c’est vous. Et que si vous ne voulez pas vous retrouver, comme moi, avec des scripts imbitables, prenez le temps de les commenter (avec des commentaires à jour, bien entendu, sinon, autant écrire en JavaScript :-p )

À bientôt…

]]>
http://sametmax.com/un-tic-tac-toe-en-html-ecrit-en-python-sans-framework-mais-avec-les-pieds/feed/ 10 9851
Floodsport… http://sametmax.com/floodsport/ http://sametmax.com/floodsport/#comments Thu, 21 Nov 2013 07:50:13 +0000 http://sametmax.com/?p=7745 flooder une boite mail... Pas très sympathique à première vue, mais c'est sans compter le fait que j'ai pris ma carte au club des gentils depuis plusieurs années... Je floode donc dans la joie et la bonne humeur.]]> Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

L’un des premiers “vrais” scripts que j’ai écrit en Python avait pour fonction de flooder une boite mail…

Pas très sympathique à première vue, mais c’est sans compter le fait que j’ai pris ma carte au club des gentils depuis plusieurs années…

Je floode donc dans la joie et la bonne humeur.

Tout d’abord, il m’a fallu identifier un moyen pas trop compliqué d’envoyer un mail.
J’ai donc récupéré un bout de code je ne sais où et j’ai viré tout ce qui dépassait :

# -*- coding: utf-8 -*-

import smtplib
from email.MIMEText import MIMEText

de_qui = 'ton@adresse.mail'
a_qui = 'son@adresse.mail' 
ton_server_smtp = 'smtp.ton.FAI'

message = MIMEText('Ceci est le corps du mail')      
message['Subject'] = 'Ceci est le titre du mail'                  
                                                        
server = smtplib.SMTP(ton_server_smtp)                 
server.sendmail(de_qui, a_qui, message.as_string())   
server.quit()
print ("Le message a été envoyé avec un succès phénoménal !")

On reconnaît tout de suite le style qui caractérise les champions: pas de fonction, tout en ligne droite !
J’aimerai pouvoir vous dire ce qu’est MIMEText, mais ayant pu me débrouiller jusqu’ici sans le savoir… et bien je ne le sais pas. (Et ne comptez pas sur moi pour aller le chercher juste pour vous. J’ai une posture de mauvais élève à assumer.)

À ce niveau, sur ma Linux Mint, il me suffit de renseigner de_qui, a_qui et ton_server_smtp puis d’ouvrir un terminal là où se trouve mon script et de taper :

python go.py

(Oui, j’appelle souvent mes fichiers go.py. Comme ça j’ai l’impression qu’ils vont partir plus vite. Essayez, vous verrez. On y croit vraiment. Il faut juste s’habituer à appuyer un peu plus fort que d’habitude sur “entrer” tout en hurlant “GO !”. Mais l’effet est garanti.)

Si le message a bien “été envoyé avec un succès phénoménal” alors, quelque part dans l’univers, une boite mail a reçu un petit courrier ayant pour titre “Ceci est le titre du mail” et pour contenu “Ceci est le corps du mail”.
Je vous assure que je ne suis jamais aussi heureux de m’envoyer un mail que de cette façon.
Parfois je change l’objet pour me faire une surprise et je suis content.
Ça marche à chaque fois.

C’est à ce moment que l’on parle de petits chiens.

Maintenant que je savais envoyer un mail, il me fallait trouver un moyen de convertir la photo d’un petit chien en ASCII-art (rapport à la joie et la bonne humeur du flood, souvenez-vous).
J’ai trouvé mon bonheur avec le projet AA.

sudo apt-get install python-aalib

Paf !

J’ai récupéré ici un bout de code dont j’ai compris juste l’essentiel (où changer l’image, la largeur et la hauteur) et mon style tout en finesse a fait le reste :

import aalib
import Image

pix = 'toutou.jpg'
nb_lettre_largeur = 80
nb_lettre_hauteur = 40

screen = aalib.AsciiScreen(width = nb_lettre_largeur, height = nb_lettre_hauteur)
image = Image.open(pix).convert('L').resize(screen.virtual_size)
screen.put_image((0, 0), image)
print (screen.render())

“GO ! GO !”

python gogo.py

Ici aussi, une joie authentique est au rendez vous à chaque tentative.
C’est merveilleux.

Où l’on mélange les torchons et les torchons…

Je savais comment envoyer un mail.
Je savais comment convertir une photo de petit chien en ASCII.

La suite découlait de source.
Il me fallait désormais unifier le tout pour pouvoir envoyer des dizaines de mails à la même adresse de manière à ce que la photo du petit chien apparaissent dans le client de messagerie de mon destinataire grâce à l’empilement des titres des messages en partant du principe qu’il utiliserai une police à chasse fixe pour l’affichage.
Tout ce qu’il y a de plus classique.

À l’époque, sur gMail, le thème “Terminal” proposait une police monospaced verte sur fond noir du plus bel effet.
Mais ce n’est plus le cas.
Heureusement, en plagiant l’interface de gMail, Yahoo a aussi plagié le thème “Terminal” avec la police qui nous intéresse.
Voici donc le petit chien que je me suis envoyé sur un compte créé pour l’occasion.

Tootoo for yahoo

Ne me dites pas que ça ne vous donne pas envie de chialer.

Voici le script auquel j’ai (empiriquement) fini par arriver pour obtenir ce résultat émouvant :

# -*- coding: utf-8 -*-
 
# la lib smtp qui nous permet de dialoguer avec un serveur de mail
import smtplib
 
# pour simuler une irrégularité dans l'envoi
from time import sleep
from random import random
 
# nécessaire pour l'ASCII-Art
import aalib
import Image
 
import sys
from email.MIMEText import MIMEText
 
de_qui = 'À RENSEIGNER'        
a_qui = 'À RENSEIGNER'         
serveur_smtp = 'À RENSEIGNER'  
 
 
i = j = 25  # nombre de mail a envoyer donc de ligne,
nb = 81     # nombre de caractères par ligne, correspond au nombre de caractères
            # affichés par Yahoo pour un titre avec le skin "terminal"  


###############
## FONCTIONS ##
###############
 
 
def gotoascii(pix):    
                                    
    screen = aalib.AsciiScreen(width = nb-1, height = j)
    image = Image.open(pix).convert('L').resize(screen.virtual_size)
    screen.put_image((0, 0), image)

    # les replace() concernent des caractères qui sont gérés différemment 
    # des autres quand ils sont utilisés dans le tite d'un mail.
    return (screen.render().replace(' ', '.').replace(',', '.').replace(';','.'))
 
def essai(pix):

    # permet éventuellement de tester le rendu de l'image avant de l'envoyer
    print (gotoascii(pix))
 
def ligne(num_l, pix):

    # Comme il y a 'nb' élément par ligne, on compte de 'nb' en 'nb'
    # en fonction du numéro de la ligne renseigné par 'num_l' 
    return (gotoascii(pix)[nb*num_l:nb*(num_l+1)])
 
def send(num, pix):

    email = MIMEText("Qu'est-ce qu'on s'amuse !")

    # L'objet du mail est la ligne donnée en premier argument
    # de l'image ASCII donnée en deuxième argument
    email['Subject'] = ligne(num, pix)
                                                   
    server = smtplib.SMTP(serveur_smtp)                 
    server.sendmail(de_qui, a_qui, email.as_string())   
    server.quit()                                       
 
 
#########################
## PROGRAMME PRINCIPAL ##
#########################


# Écoute le flag -test pour faire... des tests
# --> python go.py -test monImage.jpg
if sys.argv[1] == "-test": 
 
    essai(sys.argv[2])
 
else:
 
    while i > 0:
 
        # Augmente le délais et brise la régularité des envois de mail
        # afin d'éviter, peut-être, d'être black-listé par Yahoo :-p
        # Utilité purement hypothétique.
        sleep(20*random()+10)
        
        # Envoi le mail          
        send(i-1, sys.argv[1])                 
        if i == 1:
            print ("Mail n°{0} envoyé. Il n'en reste plus.".format(j+1-i))
        else:
            print ("Mail n°{0} envoyé. Il en reste encore {1}.".format(j+1-i, i-1))
        i -= 1
 
    print ("Bravo ! Cet envoi a été un succès !")

Pour l’utiliser, j’ai fait en sorte de pouvoir passer l’image à envoyer en argument.

python go.py monImage.jpg

J’ai écrit ce script il y a un peu plus de 2 ans, et, même si je progresse lentement, je me rends bien compte désormais, que je recalcule l’image à chaque mail .
Mais je vous l’ai laissé en l’état, car c’est tout aussi attendrissant qu’un chiot, je trouve.
Surtout qu’à part ça, le reste frôle la perfection (la gestion des arguments doit en laisser plus d’un rêveur, j’imagine).

Enfin, grâce à Sam & Max, j’ai découvert qu’il était possible d’utiliser d’autres photos que celle du petit chien.
Merci à eux.

]]>
http://sametmax.com/floodsport/feed/ 8 7745
Apprendre le python en 10 ans… http://sametmax.com/apprendre-le-python-en-10-ans/ http://sametmax.com/apprendre-le-python-en-10-ans/#comments Fri, 08 Nov 2013 06:31:12 +0000 http://sametmax.com/?p=7660 "Comment devenir un hacker ?" dans un monopolisant moteur de recherche... Comme ça... Pour déconner...]]> Ceci est un post invité de 01ivier posté sous licence creative common 3.0 unported.

Il y a quelques années, esseulé devant 1 millions de pixels noctures, j’ai tapé “Comment devenir un hacker ?” dans un monopolisant moteur de recherche…
Comme ça…
Pour déconner…

Je suis alors tombé sur le texte “Comment devenir un hacker ?” d’Eric Steven Raymond.

Après quelques instants dubitatifs où j’ai pris conscience que si j’avais écrit “Comment planter des choux ?” ou autres “Comment dessiner un poulet en contre-plongée ?” je serai certainement tombé sur un document du même nom, j’ai commencé à lire le texte en question.
Mais ce n’est que plusieurs mois après que j’en suis venu à bout.

En effet, arrivé à la partie “Apprenez à programmer”, j’ai non seulement réalisé que je ne savais pas vraiment programmer mais surtout que je pouvais dès à présent mettre à l’épreuve ma théorie sur les choux et les poulets.
J’ai donc cherché “Comment apprendre à programmer ?” et j’ai fini par tomber sur l’article “Apprendre à programmer en 10 ans ?

Celui-là, je l’ai lu en entier et d’une seule traite.
Mais le titre seulement avait suffit à me décomplexer pour deux ou trois vies.

Comment acquérir une compétence en 10 ans?

Quelle évidence !
Tout me paraissait bien plus envisageable en me donnant 10 ans pour y arriver…
Je veux apprendre l’espagnol ? Et bien je ferai le bilan dans 10 ans…
Pas besoin de culpabiliser parce que je n’ai pas fait la page du jour de la méthode à Mimile…

Il devait être 1h du matin et je me suis dit :
“Et si j’apprenais le Python…”
J’avais 10 ans devant moi, mais ce n’était pas une raison pour perdre du temps.

À 6h, j’avais donc bouclé la première partie du tuto “Apprendre le Python” du site du zéro et entamé la seconde…

Maintenant, vous vous demandez peut-être, si, trois an plus tard, je connais le Python…
Et bien je vous dirais ça dans sept ans… :-)

Hello world !

En attendant, j’ai proposé à Sam et Max de poster quelques articles de temps en temps…
Je ne suis pas du tout developpeur, mais je n’ai aucun scrupule à écrire des lignes de code… encore moins à publier ma prose de cochon…

Sachant qu’il est tout à fait possible d’obtenir des résultats enthousiasmants avec du code bancal, je me propose donc de décomplexer les débutants en publiant des articles de mauvais élève…
Nulle doute que les lecteurs avertis de S&M sauront, par leurs commentaires avisés, réhausser le niveau technique de mes posts afin de les rendre aussi instructifs que ceux déjà disponibles sur ce blog…

À bientôt…

]]>
http://sametmax.com/apprendre-le-python-en-10-ans/feed/ 29 7660