How to stop a MQTT subscription with a button while keeping my data?

Hi,

I would like to create an application that connects to a MQTT server, fetch some data, and display it in a chart.
I’m able to subscribe to a MQTT topic from a button and refresh in real-time my chart but it goes wrong when i’m adding a button to stop the stream.

I’ve tried two approaches, one with loop_forever:

import json
import streamlit as st
from paho.mqtt import client as mqtt



# Create a line chart
my_chart = st.line_chart([0.])

# Initialize MQTT client
mqtt_client = mqtt.Client()

# Connect to MQTT broker
mqtt_client.connect("broker.hivemq.com", 1883)

# Define a function to handle incoming MQTT messages
def on_message(client, userdata, msg):
    my_chart.add_rows([float(json.loads(msg.payload)["temp"])])

        
# Set the function to handle incoming messages
mqtt_client.on_message = on_message


# Create a "Start subscription" button
if st.button("Start subscription"):
    mqtt_client.subscribe("xxx")
    mqtt_client.loop_forever()

# Create a "Stop subscription" button
if st.button("Stop subscription"):
    mqtt_client.disconnect()
	# Tried with loop_stop() as well, same issue
	#mqtt_client.loop_stop()
	
    
if st.button("dummy"):
    pass

But when I’m clicking the “Stop subscription” or even the “dummy” button, the whole webpage refreshes and i lose my data.

I’ve tried with loop_start with another thread and a queue to update the chart, but this time I’m unable to stop the stream:

import json
import streamlit as st
from paho.mqtt import client as mqtt
import threading
import queue

# Initialize empty list to store data for the line chart
data = []

# Initialize MQTT client
mqtt_client = mqtt.Client()

# Connect to MQTT broker
mqtt_client.connect("broker.hivemq.com", 1883)

# create queue
q = queue.Queue()

# Define a function to handle incoming MQTT messages
def on_message(client, userdata, msg):
    q.put(float(json.loads(msg.payload)["temp"]))

# Set the function to handle incoming messages
mqtt_client.on_message = on_message

# Define a function to run in a separate thread
def mqtt_thread():
    mqtt_client.subscribe("xxx")
    mqtt_client.loop_start()

# Create a line chart
chart = st.line_chart(data)

thread = None
running = True

# Create a "Start subscription" button
if st.button("Start subscription"):
    thread = threading.Thread(target=mqtt_thread)
    thread.start()
    while running:
        if not q.empty():
            chart.add_rows([q.get()])
   

# Create a "Stop subscription" button
if st.button("Stop subscription"):
    running = False
    mqtt_client.loop_stop()
    #thread.join()
    mqtt_client.disconnect

Does someone see what’s wrong in my code ? Is it even possible to achieve such a thing on Streamlit?
Thanks in advance!

Hey @Mehdi,

Thanks for sharing your question. It’s expected behavior that interacting with a widget will cause your script to rerun – check out our docs on the execution model here.

“every time a user interacts with a widget, Streamlit simply reruns your script from top to bottom, assigning the current state of the widget to your variable in the process.”

To prevent your app from rerunning every time you interact with a widget, you can use one or more of the following: