Add collaborative counter example
https://github.com/pynecone-io/pynecone-examples/assets/12568287/135ecee5-b9d6-42a9-99d3-e0f52980aeb5
This counter demonstrates a simple Collaborative web app. And it can also support IoT applications too if you need.
- Tick: Use the Tick pattern to update UI whenever the state is changing
- State Manager: Use state manager to update every state for every client
- API Routes: use API Routes to allow changing state from another place.
For this collaborative web app, how does MQTT Support? For example, you may use an MQTT subscriber to receive messages from IoT devices and send requests to the API route.
I show one pattern to implement a collaborative counter-example in this PR Someone may know how to improve it in a better way.
That's better to use squash merge because I have a messy repo. 😝
I try to improve the code in to real time without calling sleep by using await event.wait(), event.set(), event.clear()
But the code cannot work because the on_click will not invoke the function directly.
But I can use localhost:8000/state_count/1122 to invoke the set_state_count function.
For more observation,
If I click count up button 10 time, the count_up function will not be called.
Then I can send request on localhost:8000/state_count/11
You can find out count become 11 first and there will be 10 time count up function happened.
I don't know why, but it look like we cannot use await tickevent.wait() to optimized our code.
import pynecone as pc
import asyncio
from .styles import base_style
# create an instance of an event
tickevent = asyncio.Event()
tickevent.clear()
async def set_state_count(count_val:int):
print("set_state_count with count_val = " + str(count_val))
keys = list(app.state_manager.states) #convert the keys of dict into the list
ret_count_val: int = count_val
for token in keys: # change state.count for all current running frontend
state = app.state_manager.get_state(token)
state.count = count_val
app.state_manager.set_state(token, state)
print("start to call tickevent.set()")
tickevent.set()
print("end of call tickevent.set()")
return {"state_count":ret_count_val}
class State(pc.State):
count: int = 0
is_run_tick:bool = False
async def count_up(self):
print("count_up start")
self.count = self.count + 1
await set_state_count(self.count)
print("count_up end")
async def count_down(self):
print("count_up start")
self.count = self.count - 1
await set_state_count(self.count)
print("count_down end")
async def tick(self):
print("tick")
# run tick for update frontend ui for updating count
if self.is_run_tick:
# wait for the event to be set
await tickevent.wait()
tickevent.clear()
return self.tick
async def onload(self):
self.is_run_tick = True
return self.tick
def index():
return pc.center(
pc.vstack(
pc.heading("Counter"),
pc.text("Collaborative Count"),
pc.hstack(
pc.button("+", on_click=State.count_up),
pc.button("-", on_click=State.count_down),
),
pc.link(State.count),
spacing="1.5em",
font_size="2em",
),
padding_top="10%",
)
print("Hint:You can open http://localhost:8000/state_count/33 to set count value as 33")
# Add state and page to the app.
app = pc.App(state=State, style=base_style)
app.api.add_api_route("/state_count/{count_val}", set_state_count)
app.add_page(
index,
title = "Collaborative Counter App",
description = "A collaborative counter app",
meta = [
{"name": "theme_color", "content": "#FFFFFF"},
{"char_set": "UTF-8"},
{"property": "og:url", "content": "url"},
],
on_load = State.onload,
)
app.compile()
If you still want this example merged, you can upgrade it to use reflex==0.2.0, then we'll review it.
If you still want this example merged, you can upgrade it to use reflex==0.2.0, then we'll review it. Thanks,
I have updated it to support reflex 0.2.0 now. We can start to review it.
need fix
Initial value when opening a new tab does not synchronize to other already existing tabs.
improvement
I think it is working for now, but we should consider adding a built in way to synchronize some variable between many states (or use database Model to keep synchronization working)
@Lendemor Thanks you so much ! Thanks for your nice testing and the nice suggestion.