如何避免Streamlit扫码库存应用点击按钮时脚本重复刷新?
Streamlit扫码库存应用:解决交互时脚本重运行问题
我开发了一款基于Streamlit的扫码库存管理应用,核心功能是扫描条码并匹配MongoDB数据库中的记录,匹配成功后展示最低库存等信息。应用通过点击scanner按钮触发扫码功能,但目前存在问题:在扫码进入产品信息界面后,点击数字输入框的+-按钮或Valide按钮时,整个脚本会重新运行,导致扫码逻辑再次执行,影响使用体验。我尝试用session_state的布尔变量解决,但没有生效,相关代码如下:
原始代码
import pymongo import streamlit as st from pymongo import MongoClient import cv2 from pyzbar import pyzbar # Connexion à la base de données MongoDB client = pymongo.MongoClient("mongodb://localhost:27017/") # Sélection de la base de données et de la collection db = client["database"] PDR = db["PDR"] st.set_page_config( layout="wide") st.title('Magasine de les Pieces de Rechange') if st.button('scanner') : # Capture d'une seule image de la caméra cap = cv2.VideoCapture(0) ret, frame = cap.read() # Recherche des codes-barres dans l'image barcodes = pyzbar.decode(frame) # Boucle sur les codes-barres trouvés for barcode in barcodes: # Extraction des informations du code-barres barcode_data = barcode.data.decode("utf-8") barcode_type = barcode.type # Affichage des informations du code-barres print("Type de code-barres:", barcode_type) print("Données du code-barres:", barcode_data) # Libération des ressources cap.release() st.write(barcode_data) DataCodeBar = PDR.find_one({'CodeBar': str(barcode_data)}) if DataCodeBar: col1, col2, col3, col4, col5, col6, col7 = st.columns(7) col3.metric("MinStock", str(DataCodeBar['MinStock'])) col5.metric("Stockinitial", str(DataCodeBar['Stockinitail'])) number = col1.number_input('Insert a number', step = 1) if col2.button('Valide'): if DataCodeBar is not None: new_stock = int(DataCodeBar['stockactuel']) - number col4.metric("StockActuel", str(new_stock), delta=(-1)*number) PDR.update_one(DataCodeBar, {"$set" : {"stock-actuel" : new_stock}}) col6.metric("code KEN", str(DataCodeBar['CodeBar'])) col7.metric("place", str(DataCodeBar['place'])) else: st.write('Ce produit n\'existe pas dans la base de données.')
尝试修复的代码
import pymongo import streamlit as st from pymongo import MongoClient import cv2 from pyzbar import pyzbar # Connexion à la base de données MongoDB client = pymongo.MongoClient("mongodb://localhost:27017/") # Sélection de la base de données et de la collection db = client["database"] PDR = db["PDR"] st.set_page_config( layout="wide") st.title('Magasine de les Pieces de Rechange') if "boolean" not in st.session_state : st.session_state.boolean = False def callback() : st.session_state.boolean = True if st.button('scanner', on_click = callback or st.session_state.boolean) : # Capture d'une seule image de la caméra cap = cv2.VideoCapture(0) ret, frame = cap.read() # Recherche des codes-barres dans l'image barcodes = pyzbar.decode(frame) # Boucle sur les codes-barres trouvés for barcode in barcodes: # Extraction des informations du code-barres barcode_data = barcode.data.decode("utf-8") barcode_type = barcode.type # Affichage des informations du code-barres print("Type de code-barres:", barcode_type) print("Données du code-barres:", barcode_data) # Libération des ressources cap.release() st.write(barcode_data) DataCodeBar = PDR.find_one({'CodeBar': str(barcode_data)}) if DataCodeBar: col1, col2, col3, col4, col5, col6, col7 = st.columns(7) col3.metric("MinStock", str(DataCodeBar['MinStock'])) col5.metric("Stockinitial", str(DataCodeBar['Stockinitail'])) number = col1.number_input('Insert a number', step = 1, on_change= callback or st.session_state.boolean) if col2.button('Valide', on_click= callback or st.session_state.boolean): if DataCodeBar is not None: new_stock = int(DataCodeBar['stockactuel']) - number col4.metric("StockActuel", str(new_stock), delta=(-1)*number) PDR.update_one(DataCodeBar, {"$set" : {"stock-actuel" : new_stock}}) col6.metric("code KEN", str(DataCodeBar['CodeBar'])) col7.metric("place", str(DataCodeBar['place'])) else: st.write('Ce produit n\'existe pas dans la base de données.')
问题分析与修正方案
问题根源
on_click参数使用错误:callback or st.session_state.boolean不符合Streamlit API要求,该参数仅接受函数对象,而非逻辑表达式- 未将扫码结果、产品数据存入
session_state,导致每次脚本重运行都需重新执行扫码逻辑 - 交互元素(输入框、按钮)未与
session_state绑定,无法保留之前的操作状态
修正后代码
import pymongo import streamlit as st import cv2 from pyzbar import pyzbar # 初始化MongoDB连接 client = pymongo.MongoClient("mongodb://localhost:27017/") db = client["database"] PDR = db["PDR"] st.set_page_config(layout="wide") st.title('Magasine de les Pieces de Rechange') # 初始化session_state变量,用于保存状态 if "scanned_data" not in st.session_state: st.session_state.scanned_data = None if "current_product" not in st.session_state: st.session_state.current_product = None if "adjust_number" not in st.session_state: st.session_state.adjust_number = 0 def scan_barcode(): """执行扫码逻辑,将结果存入session_state""" cap = cv2.VideoCapture(0) ret, frame = cap.read() barcodes = pyzbar.decode(frame) cap.release() if barcodes: barcode_data = barcodes[0].data.decode("utf-8") st.session_state.scanned_data = barcode_data # 查询数据库并保存产品信息到session_state product = PDR.find_one({'CodeBar': str(barcode_data)}) st.session_state.current_product = product else: st.session_state.scanned_data = None st.session_state.current_product = None def update_stock(): """更新库存逻辑,仅在点击Valide时执行""" product = st.session_state.current_product if product and st.session_state.adjust_number > 0: new_stock = int(product['stockactuel']) - st.session_state.adjust_number # 更新数据库 PDR.update_one({'_id': product['_id']}, {"$set": {"stockactuel": new_stock}}) # 更新session_state中的产品库存,保证界面实时显示 product['stockactuel'] = new_stock st.session_state.current_product = product # 重置调整数量 st.session_state.adjust_number = 0 # 扫码按钮:仅触发扫码回调,不直接执行扫码逻辑 if st.button('scanner', on_click=scan_barcode): pass # 根据session_state状态渲染界面,避免重运行时重复扫码 if st.session_state.current_product: product = st.session_state.current_product col1, col2, col3, col4, col5, col6, col7 = st.columns(7) col3.metric("MinStock", str(product['MinStock'])) col5.metric("Stockinitial", str(product['Stockinitail'])) # 输入框绑定到session_state,保留输入值 st.session_state.adjust_number = col1.number_input( 'Insert a number', step=1, min_value=0, value=st.session_state.adjust_number ) # 库存更新按钮:触发更新回调 if col2.button('Valide', on_click=update_stock): pass # 实时显示当前库存 current_stock = int(product['stockactuel']) delta = -st.session_state.adjust_number if st.session_state.adjust_number != 0 else None col4.metric("StockActuel", str(current_stock), delta=delta) col6.metric("code KEN", str(product['CodeBar'])) col7.metric("place", str(product['place'])) elif st.session_state.scanned_data is not None: st.write('Ce produit n\'existe pas dans la base de données.')
关键改进点
- 将扫码、库存更新逻辑封装为独立函数,通过
on_click触发,避免脚本重运行时重复执行核心逻辑 - 所有状态(扫码结果、产品数据、调整数量)存入
session_state,保证页面重运行时保留之前的操作信息 - 输入框绑定到
session_state变量,确保调整数量不会在重运行时丢失 - 界面渲染完全基于
session_state的状态,而非按钮点击的临时分支,从根本上避免了交互触发的不必要逻辑执行
内容的提问来源于stack exchange,提问作者youssef1452hakam
相关产品推荐
相关产品推荐

