Sam & Max » 01ivier http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Un bot qui tweete 4992 Super-Héros à raison de un toutes les 3,14 heures peut-il sauver Internet ? 10 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
Où il est présenté une méthode en Python pour afficher de la vidéo 3-bit dans son terminal. 15 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/ 15
Un Tic-Tac-Toe en HTML écrit en Python, sans framework, mais avec les pieds. 10 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

<image src="rond.png" />

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

<image src="croix.png" />

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.

<table >
  <tr>
    <td>{0}</td>
    <td>{1}</td>
    <td>{2}</td>
  </tr>
  <tr>
    <td>{7}</td>
    <td>{8}</td>
    <td>{3}</td>
  </tr>
  <tr>
    <td>{6}</td>
    <td>{5}</td>
    <td>{4}</td>
  </tr>
</table>

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 = '<image src="rond.png" />'
croix = '<image src="croix.png" />'
croixR = '<image src="croixR.png" />'
back = """<a href="index.html"> Pour recommencer, c'est ici... </a>"""
 
def page (grille):
    return('''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link rel="stylesheet" type="text/css" href="style.css" />
  <link rel="icon" type="image/png" href="favicon.png" />
  <title>Y a matière à... l académie des neuf</title>
 </head>  
 
<body> 
 
<div class="grille">
<table >
<tr>
<td>{0}</td>
<td>{1}</td>
<td>{2}</td>
</tr>
<tr>
<td>{7}</td>
<td>{8}</td>
<td>{3}</td>
</tr>
<tr>
<td>{6}</td>
<td>{5}</td>
<td>{4}</td>
</tr>
</table>
</div>
 
<div class="redo">
{9}
</div>
 
 
</body>
 
</html>
'''.format(*grille))
 
def lien (link):
    return ('<a href="{0}.html"><image src="vide.png" /></a>'.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] = '<image src="vide.png" />'
        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
Floodsport… 8 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
Apprendre le python en 10 ans… 29 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