Skip to content

Commit

Permalink
broadcast callback on non shared variables (#1441)
Browse files Browse the repository at this point in the history
* broadcast callback on non shared variables
resolves #1207

* update example

* remove shared from the broadcast example

* - remove broadcast_callback_on_shared
- add state to global ctx  to allow use of state in function call used as bound value

* remove broadcast_callback_on_shared

* some of Fab's comments

* some of Fab's comments

* Merci les tests

* Expose invoke_callback() and broadcast_callback() as Gui methods.

* Added Gui.broadcast_change() and gui.broadcast_changes()

* Fix Gui.broadcast_changes()

* Group shared/broadcast tests

* Fix tests

* Spelling

* Fix mypy

* Update doc examples

* Linter

* More linters

* Fix tests and make linters even more happy

* Fix test [2]

* Re-re-re-re-re-fix tests

* avoid impact of invoke callback on current state

---------

Co-authored-by: Fred Lefévère-Laoide <[email protected]>
Co-authored-by: Fabien Lelaquais <[email protected]>
  • Loading branch information
3 people authored and namnguyen20999 committed Jul 8, 2024
1 parent c22f256 commit c354e9b
Show file tree
Hide file tree
Showing 20 changed files with 425 additions and 128 deletions.
24 changes: 9 additions & 15 deletions doc/gui/examples/broadcast.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
# -----------------------------------------------------------------------------------------
# Demonstrate how to share variable values across multiple clients.
# This application creates a thread that increments a value every few seconds.
# The value is updated for every client using the state.broadcast() method.
# The text of the button that starts or stops the thread is updated on every client's browser
# using a direct assignment of the state property because the variable is declared 'shared'.
# The value is updated for every client in a function invoked by Gui.broadcast_change().
# The text of the button that starts or stops the thread is updated using the
# State.assign() method, and udateds on every client's browser because the variable was
# declared 'shared' by calling Gui.add_shared_variable().
# -----------------------------------------------------------------------------------------
from threading import Event, Thread
from time import sleep

from taipy.gui import Gui, State, broadcast_callback
from taipy.gui import Gui, State

counter = 0

Expand All @@ -35,22 +36,16 @@
button_text = button_texts[0]


# Invoked by the timer
def update_counter(state: State, c):
# Update all clients
state.broadcast("counter", c)


def count(event, gui):
while not event.is_set():
global counter
counter = counter + 1
broadcast_callback(gui, update_counter, [counter])
gui.broadcast_change("counter", counter)
sleep(2)


# Start or stop the timer when the button is pressed
def start_or_stop(state):
def start_or_stop(state: State):
global thread
if thread: # Timer is running
thread_event.set()
Expand All @@ -59,9 +54,8 @@ def start_or_stop(state):
thread_event.clear()
thread = Thread(target=count, args=[thread_event, state.get_gui()])
thread.start()
# Update button status.
# Because "button_text" is shared, all clients are updated
state.button_text = button_texts[1 if thread else 0]
# Update button status for all states.
state.assign("button_text", button_texts[1 if thread else 0])


page = """# Broadcasting values
Expand Down
57 changes: 57 additions & 0 deletions doc/gui/examples/broadcast_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2021-2024 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# -----------------------------------------------------------------------------------------
# To execute this script, make sure that the taipy-gui package is installed in your
# Python environment and run:
# python <script>
# -----------------------------------------------------------------------------------------
# Demonstrate how to update the value of a variable across multiple clients.
# This application creates a thread that sets a variable to the current time.
# The value is updated for every client when Gui.broadcast_change() is invoked.
# -----------------------------------------------------------------------------------------
from datetime import datetime
from threading import Thread
from time import sleep

from taipy.gui import Gui

current_time = datetime.now()
update = False


# Update the 'current_time' state variable if 'update' is True
def update_state(state, updated_time):
if state.update:
state.current_time = updated_time


# The function that executes in its own thread.
# Call 'update_state()` every second.
def update_time(gui):
while True:
gui.broadcast_callback(update_state, [datetime.now()])
sleep(1)


page = """
Current time is: <|{current_time}|format=HH:mm:ss|>
Update: <|{update}|toggle|>
"""

gui = Gui(page)

# Run thread that regularly updates the current time
thread = Thread(target=update_time, args=[gui], name="clock")
thread.daemon = True
thread.start()

gui.run()
48 changes: 48 additions & 0 deletions doc/gui/examples/broadcast_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copyright 2021-2024 Avaiga Private Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
# an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
# -----------------------------------------------------------------------------------------
# To execute this script, make sure that the taipy-gui package is installed in your
# Python environment and run:
# python <script>
# -----------------------------------------------------------------------------------------
# Demonstrate how to invoke a callback for different clients.
# This application creates a thread that, every second, invokes a callback for every client
# so the current time may be updated, under a state-dependant condition.
# -----------------------------------------------------------------------------------------
from datetime import datetime
from threading import Thread
from time import sleep

from taipy.gui import Gui

current_time = datetime.now()


# The function that executes in its own thread.
# Update the current time every second.
def update_time(gui):
while True:
gui.broadcast_change("current_time", datetime.now())
sleep(1)


page = """
Current time is: <|{current_time}|format=HH:mm:ss|>
"""

gui = Gui(page)

# Run thread that regularly updates the current time
thread = Thread(target=update_time, args=[gui], name="clock")
thread.daemon = True
thread.start()

gui.run()
18 changes: 9 additions & 9 deletions doc/gui/examples/charts/advanced-python-lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@
figure = go.Figure()

# Add trace for Normal Distribution
figure.add_trace(go.Violin(name="Normal",
y=np.random.normal(loc=0, scale=1, size=1000),
box_visible=True, meanline_visible=True))
figure.add_trace(
go.Violin(name="Normal", y=np.random.normal(loc=0, scale=1, size=1000), box_visible=True, meanline_visible=True)
)

# Add trace for Exponential Distribution
figure.add_trace(go.Violin(name="Exponential",
y=np.random.exponential(scale=1, size=1000),
box_visible=True, meanline_visible=True))
figure.add_trace(
go.Violin(name="Exponential", y=np.random.exponential(scale=1, size=1000), box_visible=True, meanline_visible=True)
)

# Add trace for Uniform Distribution
figure.add_trace(go.Violin(name="Uniform",
y=np.random.uniform(low=0, high=1, size=1000),
box_visible=True, meanline_visible=True))
figure.add_trace(
go.Violin(name="Uniform", y=np.random.uniform(low=0, high=1, size=1000), box_visible=True, meanline_visible=True)
)

# Updating layout for better visualization
figure.update_layout(title="Different Probability Distributions")
Expand Down
19 changes: 16 additions & 3 deletions doc/gui/examples/charts/heatmap-drawing-on-top.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ def spiral(th):

# Prepare the heatmap x and y cell sizes along the axes
golden_ratio = (1 + numpy.sqrt(5)) / 2.0 # Golden ratio
grid_x = [0, 1, 1 + (1 / (golden_ratio ** 4)), 1 + (1 / (golden_ratio ** 3)), golden_ratio]
grid_y = [0, 1 / (golden_ratio ** 3), 1 / golden_ratio ** 3 + 1 / golden_ratio ** 4, 1 / (golden_ratio ** 2), 1]
grid_x = [0, 1, 1 + (1 / (golden_ratio**4)), 1 + (1 / (golden_ratio**3)), golden_ratio]
grid_y = [
0,
1 / (golden_ratio**3),
1 / golden_ratio**3 + 1 / golden_ratio**4,
1 / (golden_ratio**2),
1,
]

# Main value is based on the Fibonacci sequence
z = [[13, 3, 3, 5], [13, 2, 1, 5], [13, 10, 11, 12], [13, 8, 8, 8]]
Expand All @@ -50,7 +56,14 @@ def spiral(th):
]

# Axis template: hide all ticks, lines and labels
axis = {"range": [0, 2.0], "showgrid": False, "zeroline": False, "showticklabels": False, "ticks": "", "title": ""}
axis = {
"range": [0, 2.0],
"showgrid": False,
"zeroline": False,
"showticklabels": False,
"ticks": "",
"title": "",
}

layout = {
# Use the axis template for both x and y axes
Expand Down
8 changes: 1 addition & 7 deletions doc/gui/examples/controls/metric-color-map.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,10 @@
# }

value = 50
color_map = {
20: "red",
40: None,
60: "blue",
80: None
}
color_map = {20: "red", 40: None, 60: "blue", 80: None}

page = """
<|{value}|metric|color_map={color_map}|>
"""

Gui(page).run()

1 change: 0 additions & 1 deletion doc/gui/examples/controls/metric-hide-value.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@


Gui(page).run()

1 change: 0 additions & 1 deletion doc/gui/examples/controls/metric-layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,3 @@
"""

Gui(page).run()

1 change: 0 additions & 1 deletion doc/gui/examples/controls/metric-range.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,3 @@


Gui(page).run()

1 change: 0 additions & 1 deletion doc/gui/examples/controls/metric-type.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,3 @@
"""

Gui(page).run()

1 change: 0 additions & 1 deletion doc/gui/examples/controls/metric-value-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,3 @@


Gui(page).run()

2 changes: 1 addition & 1 deletion frontend/taipy-gui/src/components/Taipy/PaginatedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const PaginatedTable = (props: TaipyPaginatedTableProps) => {
}
return [colsOrder, baseColumns, styTt.styles, styTt.tooltips, hNan, filter];
} catch (e) {
console.info("PTable.columns: " + ((e as Error).message || e));
console.info("PaginatedTable.columns: ", (e as Error).message || e);
}
}
return [
Expand Down
14 changes: 2 additions & 12 deletions frontend/taipy-gui/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,23 +170,13 @@ export const formatWSValue = (
if (value == "") {
return "";
}
try {
return getDateTimeString(value.toString(), dataFormat, formatConf);
} catch (e) {
console.error(`wrong dateformat "${dataFormat || formatConf.dateTime}"`, e);
}
return getDateTimeString(value.toString(), undefined, formatConf);
return getDateTimeString(value.toString(), dataFormat, formatConf);
case "datetime.date":
case "date":
if (value == "") {
return "";
}
try {
return getDateTimeString(value.toString(), dataFormat, formatConf, undefined, false);
} catch (e) {
console.error(`wrong dateformat "${dataFormat || formatConf.date}"`, e);
}
return getDateTimeString(value.toString(), undefined, formatConf, undefined, false);
return getDateTimeString(value.toString(), dataFormat, formatConf, undefined, false);
case "int":
case "float":
case "number":
Expand Down
Loading

0 comments on commit c354e9b

Please sign in to comment.