Giter VIP home page Giter VIP logo

Comments (4)

rodja avatar rodja commented on May 13, 2024 1

I renamed the issue from Improvements in JustPy to Callbacks vs Streamlit-Like "if"-statements for better clarification of the topic.

from nicegui.

falkoschindler avatar falkoschindler commented on May 13, 2024

Hi Eli,

Our problem with Streamlit was mainly due to the way it handles user interaction. Writing if st.button('Click'): st.text('Hey!') is super cool. But it hides the underlying event loop by implicitly re-evaluating the script. That works in simple cases, but it gets quickly rather complicated to achieve apparently "normal" behavior.

State

If the script starts with loading some non-constant initial state, e.g. from an external source, this state is reset whenever the script is processed again. The following script already shows inconsistent behavior. Initializing state with a constant works, but assigning a non-constant random value overwrites the user-selected state every time.

import streamlit as st
import random

state = random.randint(0, 5) # simulate external source
new_state = st.radio('State', ['A', 'B', 'C', 'D', 'E', 'F'], state)
st.text('State: ' + new_state)

It is hard to let Streamlit load the state once and let the user update it repeatedly. It gets worse when multiple widgets interact with each other, like a button resetting the radio selection to some value.

There are workarounds introducing session state. But they are not trivial and require quite some additional code. You can look into one of our projects to see how we mapped machine state onto session state to make it accessible for Streamlit:
https://github.com/zauberzeug/odrive-gui/blob/9d8a6d3b46a8e1f200670aa837bff476d98504a5/src/main.py#L21

Loops

Always reloading the script makes it also difficult to introduce own event loops, e.g. reading data and updating a plot repeatedly. In the above-mentioned project we found a workaround using threads that need to get stopped and restarted every time the script restarts due to user interaction:
https://github.com/zauberzeug/odrive-gui/blob/9d8a6d3b46a8e1f200670aa837bff476d98504a5/src/main.py#L40

Today (on the main branch) we avoid this struggle using NiceGUI:
https://github.com/zauberzeug/odrive-gui/blob/main/src/main.py

Handling user interaction without callbacks seems indeed intriguing. But we doubt it holds for slightly more demanding projects. It’s just not like the language works: A (non-blocking) if-statement evaluates the conditional expression to decide which path to take, before the user gets the chance to interact. Doing it otherwise requires Streamlit to perform a significant amount of "magic", causing trouble in other respects.

from nicegui.

elimintz avatar elimintz commented on May 13, 2024

Thank you very much for the detailed answer Falko. It does indeed look that Streamlit has a serious design flaw. They tried making things very simple, but that ended limiting the utility of their framework.

from nicegui.

falkoschindler avatar falkoschindler commented on May 13, 2024

@elimintz I was curious to see whether I could come up with Streamlit-like if-statements for NiceGUI (or JustPy) UI elements that postpone code execution until an event occurs, but don't require re-running the whole script. Although I don't think it's super reliable and it could break in more complicated scenarios, it's interesting to see what is possible.

#!/usr/bin/env python3
from nicegui import ui
import inspect
import ast

def magic_button(text):

    class NodeVisitor(ast.NodeVisitor):

        def __init__(self, lineno):
            self.lineno = lineno
            self.node = None

        def visit_If(self, node):
            if node.lineno == self.lineno:
                self.node = node
            self.generic_visit(node)

    frame = inspect.currentframe().f_back
    with open(inspect.getsourcefile(frame)) as f:
        code = f.read()

    tree = ast.parse(code)
    visitor = NodeVisitor(inspect.currentframe().f_back.f_lineno)
    visitor.visit(tree)

    block = code.splitlines()[visitor.node.lineno:visitor.node.end_lineno]
    indentation = min(len(line) - len(line.lstrip()) for line in block)
    block = '\n'.join(line[indentation:] for line in block)

    ui.button(text, on_click=lambda _: exec(block, frame.f_globals, frame.f_locals))

ui.label('This script demonstrates postponed code execution with streamlit-like if-statements.')

count_label = ui.label('count = 0')

count = 0
if magic_button('Click me!'):
    count += 1
    print(f'count = {count}')
    count_label.text = f'count = {count}'

When creating a "magic button", the source code is parsed into a tree and traversed looking for if-statements on the same line from which magic_button(...) has been called. The body of the if-statement is extracted and assigned to the click handler of a normal ui.button(...), together with global and local variables of the corresponding execution frame.

The question remains, how "nice" and pythonic it is to bend the normal flow of code execution. As Streamlit shows, it's easy to find examples where such an approach leads to unexpected behavior.

from nicegui.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.