A l’ère du « drive », l’intérêt de cette Shopping List Machine peut laisser dubitatif mais une chose est sûre : au travers de la réalisation de ce projet, vous en apprendrez beaucoup sur des notions variées. Les concepts impliqués sont denses et ne pourront être que survolés mais l’essentiel nécessaire pour se lancer est ici synthétisé.

Mais avant d’entrer dans les détails techniques, formulons les exigences de la Shopping List Machine…
Spécifications de la Shopping List Machine

La Shopping List Machine doit rester simple et pratique. Elle doit permettre traquer les codes-barres des produits en explorant librement les placards de la cuisine : pas question de faire des aller-retour vers la machine pour chacun des produits. Parallèlement, un retour de la part du Raspberry quant à l’exécution des traitements est incontournable, tout particulièrement au moment du scan d’un article. En effet, comment savoir si l’article est reconnu par la machine ? Un écran encombrant le plan de travail de la cuisine pour afficher des messages n’est clairement pas adapté. Nous allons plutôt faire parler le Raspberry pour annoncer ces messages, audibles même à l’autre bout de la cuisine. Enfin, le scanner sera le seul périphérique à utiliser pour l’ensemble des actions : ajouter des articles à la liste courante, imprimer la liste de courses ou encore réinitialiser la liste. Cerise sur le gâteau : une grille de sudoku sous la liste d’articles pour patienter au cas où l’on choisirait encore, la caisse qui avance moins vite que les autres.
Le code-barres EAN13
Un code-barres est un symbole représentant une donnée numérique ou alphanumérique. Les différentes symbologies sont nombreuses et pour cette brève introduction, nous nous limiterons à une description succincte du code-barres le plus couramment utilisé sur les produits de la vie quotidienne : le code-barres unidimensionnel EAN13 (European Article Numbering). Le numéro EAN13 permet d’identifier de manière univoque un article. Représentation graphique du numéro qu’il surplombe, le code-barres EAN13 est destiné à la lecture automatisée par un lecteur optique facilitant le contrôle du flux des marchandises.
Le code barre EAN13 comportent 13 chiffres. Chaque chiffre est représenté par l’alternance des 4 barres verticales noires et blanches, d’épaisseur variable. Chaque chiffre comporte nécessairement 2 barres noires et 2 barres blanches. Cet ensemble de 4 barres est lui-même dessiné sur une surface divisée en 7 bandes verticales de même largeur appelées « modules ». Ainsi, les barres peuvent être plus ou moins épaisses (de 1 à 4 modules consécutifs) et un même chiffre peut être représenté de plusieurs façon différentes, ce sont les ensembles d’encodage au sein desquels chaque chiffre possède une représentation unique.
Prenons un exemple concret, avec le code-barres ci-dessous :

Comme indiqué plus haut, le symbole est la représentation de la séquence de chiffres 3175681851849, dont la signification est la suivante :
- Les deux premiers chiffres représentent le code pays (de 30 à 37 pour la France) ;
- Les cinq suivants représentent l’identifiant de la société ;
- Les cinq derniers chiffres représentent l’identifiant du produit ;
- Le dernier chiffre est le check digit, il permet de s’assurer de l’exactitude des chiffres précédents selon une formule définie.
A la lecture de l’image ci-dessus, on remarquera que le 13ième chiffre n’est pas représenté dans le symbole. En effet, il est déduit à partir des ensembles d’encodage utilisé pour les 6 chiffres de la partie gauche. Les zones de gardes permettent quant à elle de calibrer le lecteur optique.
Le lecteur de code-barres

Après avoir connecté le scanner USB, affichons les messages du noyau depuis le démarrage, voici un extrait du retour de la commande dmesg :
1 2 3 4 5 6 7 8 |
[ 619.754552] usb 1-1.3: new full-speed USB device number 6 using dwc_otg [ 619.857304] usb 1-1.3: New USB device found, idVendor=ffff, idProduct=0035 [ 619.857323] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [ 619.857336] usb 1-1.3: Product: Wireless [ 619.857348] usb 1-1.3: Manufacturer: Sycreader [ 619.857360] usb 1-1.3: SerialNumber: 08FF20150112 [ 619.861806] input: Sycreader Wireless as /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:FFFF:0035.0002/input/input1 [ 619.915116] hid-generic 0003:FFFF:0035.0002: input,hidraw0: USB HID v1.10 Keyboard [Sycreader Wireless] on usb-3f980000.usb-1.3/input0 |
Le lecteur code-barres est un périphérique USB. A l’aide des informations « new full-speed USB device number 6 » et « Keyboard [Sycreader Wireless]« , on comprend que le périphérique est relié au fichier 6 et qu’il est vu comme un clavier par le système. La commande lsusb permet d’obtenir des informations sur les bus USB du système et les matériels qui y sont connectés. Dans ce cas présent, davantage d’informations pourront être obtenus à l’aide de la commande :
1 |
sudo lsusb -D /dev/bus/usb/001/006 |
Comme l’indique le retour des commandes précédentes, le lecteur de code-barres est un périphérique d’entrée et son fonctionnement est comparable à celui d’un clavier : une fois scanné, le code-barres est décodé et la chaine de caractère correspondante est envoyée à l’ordinateur sur lequel il est connecté. Le scanner utilisé est un modèle sans fil, ce qui répond aux exigences de simplicité de la machine : on peut scanner les articles présents dans un rayon de 30m (couverture annoncée par la documentation du scanner).

Selon le modèle, il se peut qu’en scannant un paquet de céréales Trésor, la chaine de caractères envoyée ne soit pas 5050083458255 comme attendu mais « (à(àà_»’(_é((« .
En observant le clavier de son ordinateur, on comprend rapidement ce qui se produit :

Le scanner envoie le caractère bas de la touche du chiffre attendu. Pour contourner ce problème, nous allons utiliser une fonction de conversion. Cette fonction prendra en paramètre le caractère à convertir et retournera le caractère correspondant. Le mapping sera réalisé à l’aide d’un dictionnaire (structure équivalente à une liste de couple clé/valeur).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
def topdownchar(code): list_char_down = ["&", "é", "\"", "'", "(", "-", "è", "_", "ç", "à"] list_char_top = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] mapping = dict(zip(list_char_down, list_char_top)) codeEAN = str() # Chaine init. vide pour stockage code converti en chiffres for indice, valeur in enumerate(code): # Si un caractère du code barre est déjà un chiffre entre 0 et 9 if valeur in mapping.keys(): codeEAN = codeEAN + mapping[valeur] # Sinon, on convertit de down vers top else: try: codeEAN = codeEAN + valeur except KeyError: print("Erreur : code-barres invalide") return False return codeEAN |
Le référentiel d’articles en local
Bien entendu, une fois qu’un article sera scanné et que son identifiant sera envoyé au Raspberry, il sera question de « reconnaitre » l’article correspondant.
S’appuyer sur une API offrant le service est la première idée qui vient à l’esprit : on envoie un numéro EAN et on obtient le descriptif du produit associé en retour. Plusieurs API de ce type sont disponibles sur Internet mais je n’en ai trouvé aucune véritablement complète… à moins d’être payante. Dans ces conditions, je renonce donc à l’utilisation d’une API et je me résous à constituer mon propre référentiel pour gérer localement les données relatives aux produits.
S’agissant d’un faible volume de donnée (une centaine d’articles), on peut envisager d’embarquer les données dans l’application à l’aide d’un fichier texte. Toutes les données enregistrées sont accessibles de manière séquentielle et l’on peut effectuer des recherches sous réserve que les critères de recherche ne soient pas trop complexes. En effet, un simple fichier montrera très vite ses limites dès lors qu’on souhaitera :
- effectuer des sélections et des tris sur les données ;
- gérer des volumes de données importants ;
- relier les données entre elles par des relations hiérarchiques ;
- gérer les accès parallèles de plusieurs utilisateurs.
Néanmoins, avec ce type de BDD élémentaire (et non relationnelle), on sera très rapidement confronté à des problèmes de structuration de données et de performances.
Pour embarquer les données persistantes de manière relationnelle dans l’application Python, SQLite est la solution idéale.
SQLITE3 : MOTEUR DE BASE DE DONNEES RELATIONNELLES EMBARQUE
SQLite est un moteur de base de données relationnelles. Il ne fonctionne pas selon le modèle client/serveur, il est directement embarqué dans les applications qui l’utilisent. Les données ainsi que la structure des tables, les index et tout le nécessaire est stocké dans un fichier.
SQLite est libre, gratuit et fourni dans la bibliothèque standard de Python. Cela signifie qu’on peut développer une application en Python contenant son propre SGBDR intégré, sans aucune installation supplémentaire et que les performances seront au rendez-vous.
Voici les principales instructions Python nécessaires pour manipuler la base :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import sqlite3 from os import chdir # Ouverture de la BDD fileSQ3 ="shopping-bd.sq3" # Choix du fichier chdir("/home/pi/ESC_POS_PRINTER/SHOPPINGLIST") # Changement du répertoire courant # Objet-connexion assurant l'interface entre le script Python et la BDD SQ3 connexion = sqlite3.connect(fileSQ3) # correspond à ouvrir le fichier .sq3 # Curseur : Mémoire tampon mémorisant temporairement les données en cours de traitement et les opérations avant transfert définitif dans la base de données avec un commit() buffer = connexion.cursor() # Exécution d’une requête SQL buffer.execute("SELECT * FROM article") print(buffer.fetchall()) # fetchall() renvoie une liste de tuple # Transfert des opérations du tampon vers la BDD (annulation possible avant le commit) buffer.commit() # Fermeture de la BDD buffer.close() # Fermeture du curseur connexion.close() # Fermeture de la connexion à la base de données |
PEUPLEMENT DE LA BDD
Nous avons retenu la solution d’une BDD pour la persistance des données. Il est maintenant nécessaire de renseigner cette base. Pour cela, le workflow sera la suivant :

Le choix des champs est plus ou moins libre mais reste structurant pour la suite du développement. Il est préférable de faire les bons choix dès le départ. Par exemple, le champ PRIX a été prévu pour pouvoir anticiper la fonctionnalité « établir un devis ».
Le champ RAYON est destiné à caractériser chaque article selon leur rayon d’appartenance. Cette information permettra de créer des groupes cohérents d’articles au sein d’une même liste, selon le rayon dans lequel il se trouve, à l’intérieur du magasin. A l’édition, les groupes d’articles seront imprimés dans l’ordre selon lequel on parcourt les rayons. En effet, à moins d’être totalement désorganisé, on fait globalement toujours ses courses de manière cohérente en suivant le même circuit au sein du magasin. L’idée est de ne pas s’apercevoir que le dentifrice (situé à l’entrée du magasin) figure en fin de liste une fois arrivé au rayon animalerie (situé au fond du magasin) !
Le champ TTS permettra d’enregistrer un commentaire vocal potentiellement différent du nom du produit. Cela s’avère utile pour contourner la prononciation parfois perfectible du moteur TTS (exemple : la chaine Kinder est prononcé « quindé » et on pourra saisir la chaine « quinedeur » pour entendre une prononciation plus conventionnelle).
Le fichier .csv évoqué ci-dessus contient sur chaque ligne un enregistrement comportant l’ensemble des informations relatives à un produit. Pour la persistance en Base de Données, les informations seront stockées selon le modèle physique de données issu d’une méthode d’analyse type Merise. S’agissant d’un projet trivial, on devine intuitivement la mise en œuvre de 3 tables pour l’implémentation du système de stockage :

Côté Python, les requêtes SQL à exécuter sont les suivantes :
1 2 3 4 5 6 7 8 |
# REQUETES SQL POUR LA CREATION DES TABLES REQ_CREATE_ARTICLE = "CREATE TABLE article(code INTEGER UNIQUE NOT NULL, produit TEXT NOT NULL, marque TEXT DEFAULT 'Carrefour', description TEXT, prix REAL DEFAULT 1, texttospeech TEXT, rayon INTEGER, FOREIGN KEY(rayon) REFERENCES rayon(id))" REQ_CREATE_RAYON = "CREATE TABLE rayon(id INTEGER PRIMARY KEY NOT NULL, nom TEXT NOT NULL, priorite INTEGER UNIQUE NOT NULL)" REQ_CREATE_LISTE = "CREATE TABLE liste(code INTEGER UNIQUE NOT NULL, quantite INTEGER NOT NULL DEFAULT 1)" # REQUETES SQL POUR L’INSERTION DES DONNEES EN TABLES REQ_INSERT_ARTICLE = "INSERT INTO article (code, produit, marque, description, prix, texttospeech, rayon) VALUES (?, ?, ?, ?, ?, ?, ?)" REQ_INSERT_RAYON = "INSERT INTO rayon (nom, priorite) VALUES (?, ?)" |
La mise en variable des requêtes SQL permet d’améliorer la lisibilité du code, puisque l’instruction pour créer la table article s’écrit :
1 |
buffer.execute(REQ_CREATE_ARTICLE) |
A titre d’illustration, voici un focus sur la fonction populate_article() permettant de renseigner la table article à partir du fichier .csv.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
def populate_articles(): # OUVERTURE DU FICHIER EN MODE LECTURE file_src = open("articles.csv", "r") # READ() RENVOIE LE CONTENU DU FICHIER (\N COMPRIS) DANS UN STRING str_all_articles = file_src.read() # DECOUPAGE DU FICHIER EN ARTICLES list_articles = str_all_articles.split("\n") # INSERTION DES ARTICLES DANS LA TABLE ARTICLE for (i, infos) in enumerate(list_articles): try: # DECOUPAGE DE CHAQUE ARTICLE EN CHAMPS DESCRIPTIF var_code, var_produit, var_marque, var_description, var_prix, var_tts, var_rayon = infos.split(";") # LE FICHIER CSV EST PRODUIT SOUS WINDOWS : ON SUPPRIME LE \r DU EOF var_rayon = var_rayon.strip("\r") # TABLE RAYON DEJA RENSEIGNEE : ON RECUPERE L’ID DU RAYON PAR RAPPORT A SON NOM buffer.execute("SELECT id FROM rayon WHERE nom = '%s'" % var_rayon) try: # RECUPERE LE PREMIER ELEMENT PRESENT DANS LE BUFFER SUITE A LA REQUETE SQL var_rayon_id = buffer.fetchone()[0] except TypeError as ERROR: print("Erreur : %s. Rayon non reconnu (Avez-vous pensé à supprimer la ligne d'en-tête du fichier CSV ?)\n" % ERROR) exit() # INSERTION AVEC UNE REQUETE VARIABILISEE buffer.execute(REQ_INSERT_ARTICLE, (var_code, var_produit, var_marque, var_description, var_prix, var_tts, var_rayon_id)) except ValueError: # ERREUR DE TYPE ValeError POSSIBLE SI PLUSIEURS \n EN FIN DE FICHIER # DANS CE CAS, POURSUIVRE LE SCRIPT pass print("INSERTION TERMINEE AVEC SUCCES\n") file_src.close() # Fermeture du fichier source |
Pour améliorer l’expérience utilisateur lors de l’initialisation de la Base De Données, le script init-BDD-articles.py fournit un retour sémantique. Comme illustré dans les images ci-dessous, le script est enrichi d’instructions supplémentaires pour afficher les données effectivement renseignées dans les tables rayon et article.


Faire parler la Shopping List Machine à l’aide d’un moteur Text To Speech (TTS)
Concernant ces messages, il s’agira simplement du nom de l’article scanné ou bien d’un message de d’alerte notifiant le non référencement de l’article le cas échéant. Pour faire parler le Raspberry, nous allons utiliser un moteur Text To Speech (TTS). SVOX Pico TTS est l’équivalent de Google TTS mais en local. Pour l’installer, lancer la commande suivante :
1 |
sudo apt-get install libttspico-utils |
Une fois installé, on dispose de la commande pico2wave qui génère un fichier wave depuis le texte passé en paramètre. Pour entendre le texte parlé, il faudra évidemment lire le fichier wave généré, à l’aide de l’outil aplay. Nous allons utiliser un script pour simplifier la manipulation. Pour cela, taper la commande nano picoTTS et renseigner les lignes suivantes :
1 2 3 4 5 |
#!/bin/bash pico2wave -l fr-FR -w /tmp/test.wav "$1" aplay -q /tmp/test.wav rm /tmp/test.wav |
On ajoute les droits d’exécution avec la commande sudo chmod ugo+x picoTTS. Maintenant, nous pouvons tester la voix du Raspberry à l’aide de la commande :
1 |
picoTTS "Le Raspberry sait désormais parler !" |
Et voici le focus sur la fonction TTS :
1 2 3 4 5 6 7 8 9 10 11 12 |
import subprocess def tts(code): if code: buffer.execute("SELECT produit FROM article WHERE code = %d" % code) text = buffer.fetchone()[0] print("Le TTS est : {0}".format(text)) subprocess.call(['picoTTS', text]) else: text = "ATTENTION : produit non référencé, inventaire nécessaire." print("ATTENTION : produit non référencé, inventaire nécessaire.") subprocess.call(['picoTTS', text]) |
Impression de la Shopping List sur imprimante thermique USB
Pour ce projet, c’est une imprimante thermique USB qui sera utilisée. Il s’agit d’une imprimante à commandes ESC/POS[1], modèle NT-5890K. Le code Python permettant d’exploiter le potentiel de l’imprimante est concentré dans un script dédié (THERMALRECEIPTPRINTER.py) qui sera importé dans le script principal en tant que librairie. Pour développer cette librairie, un travail de recherche et d’analyse de librairies similaires (concernant les imprimantes à commandes ESC/POS) a été nécessaire pour recouper informations (parfois erronées) de la documentation technique fournie.
[1] ESC est le caractère qui introduit une commande, POS est l’acronyme de Point Of Sales

Sous Linux, tout est considéré comme fichier : lorsqu’on connecte un périphérique USB sur le Raspberry, un fichier associé est créé dans le répertoire /dev (ce répertoire contient des fichiers associés à des matériels, device en anglais).
Une fois l’imprimante connectée au Raspberry, la commande dmesg nous renseigne sur le matériel détecté. S’agissant d’une imprimante, on filtrera sur la chaine printer :
1 2 3 4 5 |
pi@milk:~ $ dmesg | grep -i printer [ 3.078129] usb 1-1.3: Product: USB Thermal Printer [ 3.085702] usb 1-1.3: Manufacturer: Printer [ 5.455133] usblp 1-1.3:1.0: usblp0: USB Bidirectional printer dev 4 if 0 alt 1 proto 2 vid 0x0416 pid 0x5011 pi@milk:~ $ |
L’imprimante est reconnue comme matériel USB et comme le suggère l’information « usblp0 » à la ligne n°4 dans le retour de la commande ci-dessus, l’imprimante est associée au fichier /dev/usb/lp0.
La commande ls –l /dev/usb nous indique qu’il s’agit en réalité d’un fichier spécial de type c désignant un character device. Ce type de fichier permet d’effectuer une opération sur périphérique à accès direct (sans buffer, caractère par caractère.
C’est le fichier /dev/usb/lp0 qu’il faudra utiliser pour piloter l’imprimante en y envoyant les caractères à imprimer et les commandes ESC (mise en forme, impression d’une image, sortie papier,…). La commande cat « Hello world ! » >/dev/usb/lp0 aura pour effet d’imprimer la chaine « Hello world ! » sur l’imprimante. Bien entendu, nous n’allons pas rédiger un script Shell, mais nous allons plutôt utiliser le langage Python.
Comme le montre l’extrait de la librairie THERMALRECEIPTPRINTER.py ci-dessous, la notion d’objet est utilisée :
1 2 3 4 |
class ThermalPrinter(object): def __init__(self, RTP): self.printer = open(RTP, "w") |
La méthode __init__ est une méthode particulière : c’est le constructeur permettant d’instancier des objets de la classe ThermalPrinter. En plus du paramètre self, le constructeur attend un second argument : le fichier associé à l’imprimante. L’invocation de la classe permet de créer automatiquement un objet de type ThermalPrinter. L’instruction présente dans la définition du constructeur (self.printer = open(RTP, « w »)) déclare un attribut nommé printer qui est un pointeur vers un fichier (passé en paramètre) ouvert en écriture. Dans le script principal shopping.py, p est un objet de la classe ThermalPrinter, instancié à l’aide de l’instruction suivante :
1 |
p = ThermalPrinter("/dev/usb/lp0") |
L’objet p ainsi créé dispose donc d’un attribut printer qui est un pointeur vers le fichier /dev/usb/lp0. Par ailleurs, un certain nombre de méthodes sont définies dans la classe ThermalPrinter. Chacune de ces méthodes est destinée à écrire dans l’attribut printer (désignant le fichier spécial /dev /usb/lp0), ce qui permettra au final, d’imprimer des caractères sur l’imprimante ou de lui envoyer des commandes ESC/POS.
A titre d’exemple, voici la méthode permettant d’imprimer une chaine de caractère sur l’imprimante :
1 2 |
def print_text(self, msg): self.printer.write(msg) |
Pour fixer les idées, la méthode ci-dessous permet d’activer/désactiver le mode souligné :
1 2 3 4 |
def underline(self, on=2): self.printer.write(self.ESC) self.printer.write(chr(45)) self.printer.write(chr(on)) |
La séquence (ESC 45 n) composant la commande est décrite dans la documentation de l’imprimante :

Dans le script principal, il est désormais possible d’activer/désactiver le mode souligné en appelant la méthode underline() sur l’objet p de la manière suivante :
1 2 |
p.underline() # Active le mode souligné = p.underline(True) p.underline(False) # Désactive le mode souligné |
Une grille de Sudoku sur la Shopping List
Comme évoqué dans les spécifications, nous allons ajouter en fin de liste, une grille de sudoku à résoudre. Pour cela, il existe un module Python proposé par Adafruit pour générer de tels puzzles. Suite à des erreurs rencontrées à l’impression, des modifications ont été apportées : le script sudoku.py se « contente » de générer une image .bmp (l’algorithme de backtracking mis en œuvre est assez complexe) et de l’enregistrer localement. Sans entrer les détails algorithmiques, le script sudoku.py part d’un modèle de grille vierge pour générer un puzzle.

L’image ainsi générée sera ensuite imprimée à l’aide de la librairie THERMALRECEIPTPRINTER.py dédiée à l’impression.
Autres précisions
Comme évoqué dans le chapitre formulant les exigences, l’utilisation la machine doit être la plus simple possible. Dans cette perspective, le scanner de code-barres doit être le seul périphérique nécessaire pour interagir avec la machine. La Shopping List Machine doit donc être opérationnelle dès le démarrage du Raspberry et pour cela, il sera nécessaire :
- d’activer l’auto-login de l’utilisateur pi au démarrage à l’aide de l’utilitaire de configuration raspi-config ;
- lancer le script au démarrage du Raspberry : voir l’article sur le sujet.
Mais la machine doit également permettre des actions élémentaires telles que l’impression de la liste ou encore sa réinitialisation. Le seul périphérique de saisie étant le scanner de code-barres, nous allons utiliser des codes-barres spécialement générés (http://barcode.tec-it.com/fr) à cet effet :




Ces code-barres représentent la chaine de caractères afférente et le code Python prévoit évidemment un traitement spécifique associé.
Pour conclure
Et pour finir, comment scanner un article en rupture de stock dans les placards de la cuisine ? Pour répondre à ce problème, on pourra coller les code-barres sur l’intérieur de la porte de chaque placard, pour chacun des articles présents dans le placard en question. S’il n’y a que des tiroirs dans la cuisine, on pourra également centraliser les code-barres dans un catalogue d’article. En plus d’être pertinent, ce projet ludique offre des perspectives d’apprentissage considérables. Vous n’avez décidemment aucune bonne raison pour de ne pas vous y mettre !
Je vous livre l’ensemble du code source.
Salut,
Merci pour ce travail.
Je rencontre pas mal de difficultés en appliquant ce projet, et c’est ça qui est génial.
C’est très formateur.
Un exemple, j’ai bossé au début sur Python 2.7.13 et rien ne fonctionnait, saisie du code barre renvoyait des erreurs, saisie d’une chaine vide plantait, j’ai mis dès heures à m’apercevoir que les scripts ont été développés en Python3. Ca va mieux mais j’ai encore des erreurs. Pas de demande d’ajout d’article, aucune lecture des codebarres print,flush etc. Je m’éclate et en apprend beaucoup.
A+
Florian
Salut Florian,
Merci pour l’intérêt que vous portez à ce projet !
Résoudre de problèmes techniques pour en apprendre toujours un peu plus : c’est exactement ce qui me plait également. C’est très formateur de faire aboutir les projets par soi-même, à force de travail de recherche et d’analyse. J’ai également rencontré des problèmes à mes débuts avec Python entre les versions 2 et 3 et cela arrive encore aujourd’hui quand j’utilise certaines librairies. Je réalise désormais tout mes scripts en Python 3.
N’hésitez pas à me solliciter pour toutes informations complémentaires si besoin.
Bonne continuation.
Chucky.
Bonjour,
je viens de prendre connaissance de votre projet et je suis très intéressé par ce dernier.
Comme Florian je suis novice dans python, et je prends ce projet pour essayer de comprendre la mécanique de Python pour des projets futur.
Mais lorsque que je lance le projet, ce dernier refuse de me remplir la base sql.
Avez-vous des pistes à me donner.
Merci d’avance
Cyrille
Bonjour,
Quel script Python utilisez-vous pour l’initialisation de la base ? Quel est le retour de l’exécution du script ? Une erreur est renvoyée ?