import streamlit as st import cv2 import numpy as np from pyzbar.pyzbar import decode from PIL import Image from datetime import datetime import io # Configuration de la page - DOIT ÊTRE LA PREMIÈRE COMMANDE STREAMLIT st.set_page_config( page_title="Scanner QR de Produits", page_icon="🛒", layout="wide", initial_sidebar_state="collapsed" ) # Configuration pour éviter les erreurs CORS sur Hugging Face st.markdown(""" """, unsafe_allow_html=True) # Initialisation du session state if 'produits' not in st.session_state: st.session_state.produits = {} if 'total' not in st.session_state: st.session_state.total = 0.0 if 'derniere_image' not in st.session_state: st.session_state.derniere_image = None if 'compteur_scan' not in st.session_state: st.session_state.compteur_scan = 0 def ajouter_produit(nom, prix): """Ajoute un produit ou incrémente sa quantité""" if nom in st.session_state.produits: st.session_state.produits[nom]['quantite'] += 1 st.session_state.produits[nom]['total'] = ( st.session_state.produits[nom]['quantite'] * prix ) else: st.session_state.produits[nom] = { 'prix_unitaire': prix, 'quantite': 1, 'total': prix } # Recalculer le total st.session_state.total = sum( p['total'] for p in st.session_state.produits.values() ) def supprimer_produit(nom): """Supprime un produit de la liste""" if nom in st.session_state.produits: del st.session_state.produits[nom] st.session_state.total = sum( p['total'] for p in st.session_state.produits.values() ) def modifier_quantite(nom, nouvelle_quantite): """Modifie la quantité d'un produit""" if nom in st.session_state.produits and nouvelle_quantite > 0: st.session_state.produits[nom]['quantite'] = nouvelle_quantite st.session_state.produits[nom]['total'] = ( nouvelle_quantite * st.session_state.produits[nom]['prix_unitaire'] ) st.session_state.total = sum( p['total'] for p in st.session_state.produits.values() ) elif nouvelle_quantite <= 0: supprimer_produit(nom) def reinitialiser_caisse(): """Réinitialise la caisse""" st.session_state.produits = {} st.session_state.total = 0.0 st.session_state.derniere_image = None def scanner_qr_code(image): """Scanne les codes QR dans une image""" try: # Convertir PIL Image en array numpy img_array = np.array(image) # Convertir RGB en BGR pour OpenCV if len(img_array.shape) == 3 and img_array.shape[2] == 3: img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) elif len(img_array.shape) == 3 and img_array.shape[2] == 4: # Si RGBA, convertir en RGB d'abord img_array = cv2.cvtColor(img_array, cv2.COLOR_RGBA2RGB) img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) else: img_bgr = img_array # Décoder les codes QR codes = decode(img_bgr) resultats = [] image_annotee = img_bgr.copy() for code in codes: try: data = code.data.decode('utf-8') except: data = str(code.data) # Dessiner le contour du QR code points = code.polygon if len(points) == 4: pts = np.array(points, np.int32) pts = pts.reshape((-1, 1, 2)) cv2.polylines(image_annotee, [pts], True, (0, 255, 0), 3) # Afficher le texte x, y = points[0] cv2.putText(image_annotee, "QR Detecte", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) # Parser les données if '|' in data: try: nom, prix_str = data.split('|', 1) prix = float(prix_str) resultats.append({ 'nom': nom.strip(), 'prix': prix, 'data_brute': data }) except ValueError: resultats.append({ 'nom': None, 'prix': None, 'data_brute': data, 'erreur': 'Format invalide - Prix doit être un nombre' }) else: resultats.append({ 'nom': None, 'prix': None, 'data_brute': data, 'erreur': 'Format attendu: NomProduit|Prix' }) # Reconvertir BGR en RGB pour affichage image_annotee_rgb = cv2.cvtColor(image_annotee, cv2.COLOR_BGR2RGB) return resultats, image_annotee_rgb except Exception as e: st.error(f"Erreur lors du scan: {str(e)}") return [], np.array(image) # Interface Streamlit st.title("🛒 Scanner QR de Produits") st.markdown("### 📸 Uploadez une image contenant un code QR") # Colonnes principales col1, col2 = st.columns([2, 1]) with col1: st.subheader("📤 Zone d'Upload") # Upload d'image avec une clé unique uploaded_file = st.file_uploader( "Choisissez une image (PNG, JPG, JPEG)", type=['png', 'jpg', 'jpeg'], help="Uploadez une image contenant un ou plusieurs codes QR au format: NomProduit|Prix", key=f"uploader_{st.session_state.compteur_scan}" ) # Bouton de réinitialisation col_btn1, col_btn2 = st.columns(2) with col_btn1: if st.button("🔄 Réinitialiser la caisse", use_container_width=True): reinitialiser_caisse() st.session_state.compteur_scan += 1 st.rerun() with col_btn2: if st.button("🗑️ Effacer l'image", use_container_width=True): st.session_state.compteur_scan += 1 st.rerun() # Traitement de l'image uploadée if uploaded_file is not None: try: # Charger l'image image = Image.open(uploaded_file) # Convertir en RGB si nécessaire if image.mode != 'RGB': image = image.convert('RGB') # Scanner les codes QR with st.spinner("🔍 Scan en cours..."): resultats, image_annotee = scanner_qr_code(image) # Afficher l'image annotée st.image(image_annotee, caption="Image scannée", use_container_width=True) # Afficher les résultats if resultats: st.success(f"✅ {len(resultats)} code(s) QR détecté(s)") for i, resultat in enumerate(resultats, 1): if resultat['nom'] and resultat['prix'] is not None: col_res1, col_res2 = st.columns([3, 1]) with col_res1: st.write(f"**{i}. {resultat['nom']}** - {resultat['prix']:.2f} DH") with col_res2: # Utiliser une clé unique pour chaque bouton btn_key = f"add_{st.session_state.compteur_scan}_{i}_{resultat['nom']}" if st.button(f"➕ Ajouter", key=btn_key): ajouter_produit(resultat['nom'], resultat['prix']) st.success(f"✅ {resultat['nom']} ajouté au panier!") st.rerun() elif 'erreur' in resultat: st.warning(f"⚠️ {i}. {resultat['erreur']}: `{resultat['data_brute']}`") else: st.info(f"ℹ️ {i}. Code détecté: `{resultat['data_brute']}`") else: st.warning("⚠️ Aucun code QR détecté dans l'image") st.info("💡 **Format attendu:** Le code QR doit contenir: `NomProduit|Prix`\n\nExemple: `Pain|5.50`") except Exception as e: st.error(f"❌ Erreur lors du traitement de l'image: {str(e)}") else: st.info("📷 Uploadez une image pour commencer le scan") # Instructions with st.expander("ℹ️ Instructions d'utilisation"): st.markdown(""" **Comment utiliser le scanner:** 1. 📸 Prenez une photo d'un code QR avec votre téléphone 2. 📤 Uploadez l'image ci-dessus 3. ✅ Le système détectera automatiquement les codes QR 4. ➕ Cliquez sur "Ajouter" pour ajouter le produit au panier 5. 🛒 Gérez votre panier dans la colonne de droite **Format du code QR:** ``` NomProduit|Prix ``` **Exemples:** - `Pain|5.50` - `Lait|8.00` - `Coca Cola|7.50` - `Pommes|12.50` **Astuces:** - Vous pouvez scanner plusieurs produits en une seule image - Assurez-vous que l'image est claire et bien éclairée - Le code QR doit être bien visible et pas trop petit """) with col2: st.subheader("🧾 Panier") # Afficher les produits if st.session_state.produits: for nom, info in list(st.session_state.produits.items()): with st.container(): st.markdown(f"**{nom}**") col_qty, col_prix, col_del = st.columns([2, 2, 1]) with col_qty: # Utiliser une clé unique pour chaque input qty_key = f"qty_{nom}_{st.session_state.compteur_scan}" nouvelle_qty = st.number_input( "Qté", min_value=0, value=info['quantite'], key=qty_key, label_visibility="collapsed" ) if nouvelle_qty != info['quantite']: modifier_quantite(nom, nouvelle_qty) st.rerun() with col_prix: st.write(f"{info['prix_unitaire']:.2f} DH × {info['quantite']}") st.write(f"**{info['total']:.2f} DH**") with col_del: del_key = f"del_{nom}_{st.session_state.compteur_scan}" if st.button("🗑️", key=del_key): supprimer_produit(nom) st.rerun() st.divider() # Total st.markdown("---") st.markdown(f"### 💰 TOTAL: {st.session_state.total:.2f} DH") # Bouton de paiement if st.button("💳 Finaliser le paiement", type="primary", use_container_width=True): st.success(f"✅ Paiement de {st.session_state.total:.2f} DH effectué!") st.balloons() reinitialiser_caisse() st.session_state.compteur_scan += 1 st.rerun() else: st.info("🛒 Panier vide. Scannez des produits pour commencer.") # Statistiques du panier st.markdown("---") st.caption("📊 Statistiques:") st.caption(f"• Produits: 0") st.caption(f"• Total: 0.00 DH") # Pied de page st.markdown("---") col_footer1, col_footer2 = st.columns(2) with col_footer1: st.caption(f"🕒 Dernière mise à jour: {datetime.now().strftime('%H:%M:%S')}") with col_footer2: st.caption("💡 Astuce: Vous pouvez scanner plusieurs produits en une seule image!")