[Python] How to set reactive value from callback in non-reactive module?

Discussão em 'Python' iniciado por Stack, Junho 11, 2025.

  1. Stack

    Stack Membro Participativo

    I'm building a app using Shiny for Python and Tinkerforge hardware modules. I separated all hardware-related code into a class and create an instance of it when starting the app. Tinkerforge offers callback registration for button state change events. So I can execute a function whenever the button changes state.

    I want to trigger a reactive chain in the app when the button is depressed, I also want to know how long the button was pressed. However it seems, that while I can make use of global variables within the callback, the same is not true of reactive values. There is "no current reactive context". So how can I make use of this callback to trigger events in the Shiny app?

    Here is a MWE akin to what I have, I'm unsure of how to build one without Tinkerforge:

    # TinkerforgeModule.py
    from tinkerforge.ip_connection import IPConnection
    from tinkerforge.bricklet_rgb_led_button import BrickletRGBLEDButton

    class TinkerforgeModule (object):
    def __init__(self):
    ipcon = IPConnection() # Create IP connection to tinkerforge
    self.rgb_button = BrickletRGBLEDButton('24fu', ipcon)
    ipcon.connect('127.0.0.1', 4223)

    def register_hw_button_cb(self, func):
    self.rgb_button.register_callback(
    BrickletRGBLEDButton.CALLBACK_BUTTON_STATE_CHANGED,
    func
    )


    # app.py
    from shiny import App, ui, render, reactive
    from datetime import datetime
    import TinkerforgeModule

    app_ui = ui.page_auto(
    ui.output_text("result_output")
    )

    def server(input, output, session):

    hw_button_last_press = None
    triggerMe = reactive.value(None)

    def hw_button_callback(state):
    global hw_button_last_press
    if state == 1: # BrickletRGBLEDButton.BUTTON_STATE_RELEASED = 1
    press_time = datetime.now() - hw_button_last_press
    print(f"Button released after {press_time} seconds.")

    # The line below does not work
    # triggerMe.set(f"Button released after {press_time} seconds.")

    else: # BrickletRGBLEDButton.BUTTON_STATE_PRESSED = 0
    hw_button_last_press = datetime.now()
    print("Button pressed.")

    # The line below does not work
    # triggerMe.set("Button pressed.")

    tfm = TinkerforgeModule.TinkerforgeModule()
    tfm.register_hw_button_cb(hw_button_callback)

    @output
    @render.text
    def result_output():
    return(f"Message: {triggerMe()}")

    app = App(app_ui, server)


    Registering the callback works, the printing happens when the button is pressed and released. However when it comes to setting the reactive value the app crashes. Placing a breakpoint and executing step by step I get a RuntimeError: No current reactive context.

    Initially I just polled the state of the button every 50ms using reactive.invalidate_later. But the callback would be a much cleaner way of doing it.

    Continue reading...

Compartilhe esta Página