You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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.')

问题分析与修正方案

问题根源

  1. on_click参数使用错误:callback or st.session_state.boolean不符合Streamlit API要求,该参数仅接受函数对象,而非逻辑表达式
  2. 未将扫码结果、产品数据存入session_state,导致每次脚本重运行都需重新执行扫码逻辑
  3. 交互元素(输入框、按钮)未与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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:45:02