Skip to content

Commit

Permalink
Merge branch 'develop' into multi_threading_tip
Browse files Browse the repository at this point in the history
# Conflicts:
#	docs/knowledge_base/index.md
#	docs/knowledge_base/tips/index.md
#	mkdocs.yml_template
  • Loading branch information
jrobinAV committed Oct 19, 2023
2 parents aa5f645 + 42e28b2 commit cbf5285
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 0 deletions.
12 changes: 12 additions & 0 deletions docs/knowledge_base/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,18 @@ Learn how to use Taipy and improve your skills.
</div>
</a>
</li>
<li>
<a class="tp-content-card tp-content-card--horizontal tp-content-card--small" href="tips/the_on_change_callback">
<header class="tp-content-card-header">
<img class="tp-content-card-icon" src="tips/images/icon-code.svg">
</header>
<div class="tp-content-card-body">
<h4> On Change Callback </h4>
<p> Make your multi-user graphical interface fully interactive using the on-change callback.
</p>
</div>
</a>
</li>
<li>
<a class="tp-content-card tp-content-card--horizontal tp-content-card--small" href="tips/multithreading">
<header class="tp-content-card-header">
Expand Down
14 changes: 14 additions & 0 deletions docs/knowledge_base/tips/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,20 @@ Tips and Tricks!
</div>
</a>
</li>

<li class="tp-col-12 tp-col-md-6 d-flex">
<a class="tp-content-card tp-content-card--horizontal tp-content-card--small" href="the_on_change_callback">
<header class="tp-content-card-header">
<img class="tp-content-card-image" src="taipy_cloud_deploy/logo_artwork.png">
</header>
<div class="tp-content-card-body">
<h4> On Change Callback </h4>
<span class="tp-tag">Front-end </span>
<p> Make your multi-user graphical interface fully interactive using the on-change callback.
</p>
</div>
</a>
</li>
<li class="tp-col-12 tp-col-md-6 d-flex">
<a class="tp-content-card tp-content-card--horizontal tp-content-card--small" href="multithreading">
<header class="tp-content-card-header">
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
209 changes: 209 additions & 0 deletions docs/knowledge_base/tips/the_on_change_callback/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@

In Taipy, an on_change callback is a Python function which is executed when some application variable is modified.
This callback is used for implementing some behavior after the user performs an action,
such as dragging a slider to define the value of some parameter or typing some text into an input box.

![Callback](callbacks_demo.gif){width=50%}

Note that Taipy supports various types of callbacks which serve different purposes
although this tip focuses on just one.

These callbacks are:

- *on_change*, the topic of this tip
- *on_action*
- *on_init*
- *on_navigate*
- *on_exception*

When referring to callbacks in this article, we are referring to *on_change callbacks only*,
and these alone are sufficient to build simple to complex web-apps!

That being said, let’s go through the two variations of *on_change* callbacks:

- Local (or control-bound) *on_change* callbacks; and
- Global *on_change* callbacks

# Example 1: Fahrenheit to Celsius (Local Callback)

Local callbacks are functions that are bound to a specific [Taipy control](../../../manuals/gui/controls/) (a type of visual element).
This function then gets called when the user interacts with that control.
For instance, in Taipy, this may happen when a user:

1. Drags a [slider](../../../manuals/gui/viselements/slider/) control to select some number;
2. Selects a date using the [date](../../../manuals/gui/viselements/date/) control; or
3. Selects an item from the [selector](../../../manuals/gui/viselements/selector/) control

![Example 1](callbacks_demo_fahrenheit_to_celsius_cropped-1.gif){width=50%}

Let’s demonstrate local callbacks with a small example.
This simple app allows a user to select a temperature in degrees Fahrenheit
and automatically convert it to degrees Celsius:

```python linenums="1"
from taipy.gui import Gui, Markdown

def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5 / 9

fahrenheit = 100
celsius = fahrenheit_to_celsius(fahrenheit)

md = Markdown("""
# Local Callbacks
## Fahrenheit:
<|{fahrenheit}|number|on_change=update_celsius|>
## Celsius:
<|{celsius}|number|active=False|>
""")

def update_celsius(state):
state.celsius = fahrenheit_to_celsius(state.fahrenheit)

Gui(page=md).run()
```

The relevant line here is line 12, where we defined a number control using the Taipy construct syntax.
We will use this to select the temperature in degrees Fahrenheit
which we wish to be automatically converted to degrees Celsius.

The aforementioned construct consists of 3 components (bordered by the pipes):

- *{fahrenheit}*: The variable attached to this number control;
- *number*: The name of the Taipy control; and
- *on_change=update_celsius*: Sets this control’s on_change local callback to the update_celsius function

The update_celsius local callback function defined on line 18 receives one parameter,
which we conventionally name state.

We can use this state object within our function to access and modify the runtime variables
used in our application, which we also call state variables. Accordingly, we update celsius on line 19.

Now, when the user interacts with the number control, the update_celsius local callback computes
and updates the state variable of celsius, displaying its new value.

# Example 2: Celsius to Kelvin (Global Callback)

The next improvement to our app is yet another simple one:
add a new number control to display the temperature in kelvin.

![Example 2](callbacks_demo_fahrenheit_to_celsius.gif){width=50%}

Take a look at the updated code:

```python linenums="1"
from taipy.gui import Gui, Markdown

def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5 / 9

def celsius_to_kelvin(celsius):
return celsius + 273.15

fahrenheit = 100
celsius = fahrenheit_to_celsius(fahrenheit)
kelvin = celsius_to_kelvin(celsius)

md = Markdown("""
# Local and Global Callbacks
## Fahrenheit:
<|{fahrenheit}|number|on_change=update_celsius|>
## Celsius:
<|{celsius}|number|active=False|>
## Kelvin:
<|{kelvin}|number|active=False|>
""")

def update_celsius(state):
state.celsius = fahrenheit_to_celsius(state.fahrenheit)

def on_change(state, var_name, var_value):
if var_name == "celsius":
state.kelvin = celsius_to_kelvin(state.celsius)

Gui(page=md).run(dark_mode=False)
```

On line 22, we added a new number control to our app, which is bound to the kelvin variable.
The existing code we implemented in the previous section — which updates celsius when fahrenheit is modified — is maintained.

Now the behavior we wish to implement here is to update the value of kelvin whenever the value of celsius is modified.

This is a perfect use case for the global on_change callback function.
Take a look at this handy flowchart which determines the type of on_change function that would be called:

![Example 2](callbacks_flowchart-1.png){width=100%}

This flowchart visualizes the process in which Taipy determines which on_change function would be called

In this example, our update_celsius function executed the code:

```python
state.celsius = fahrenheit_to_celsius(state.fahrenheit)
```

We call this programmatically modifying the celsius variable.
Looking at the flowchart above, we know that the global on_change function would be called, if it exists.

The global callback function should have the exact name "on_change" so that Taipy automatically recognizes it.
The parameters for the global on_change function are conventionally named as follows:

1. *state*: The State object with which we can access and modify our runtime variables;
2. *var_name*: The name of the variable that was modified; and
3. *var_value*: The value of the variable that was modified

Notice that on line 33, we preceded our updates to kelvin with an if var_name == "celsius" block. Within the on_change function,
we almost always want to operate within an if block, to avoid unintentionally recursing infinitely through on_change.
Remember that programmatically modifying kelvin or any other variables will also call the on_change function,
though that execution would make no changes because of our if block.

You might also have noticed that the functionality in this section could also have been accomplished
by updating kelvin using the existing update_celsius local callback — and indeed, adding a global callback
was not necessary for this particular situation.
However, you may encounter some situations where you may not be able to use local callbacks alone,
so using the global callback may be the right choice.

# Example 3: No Callbacks

Side-tracking a little from the focus of this article,
it’s worth noting that this app never actually needed callbacks!
We can update the code as follows:

```python
from taipy.gui import Gui, Markdown

def fahrenheit_to_celsius(fahrenheit):
return (fahrenheit - 32) * 5 / 9

def celsius_to_kelvin(celsius):
return celsius + 273.15

fahrenheit = 100
celsius = fahrenheit_to_celsius(fahrenheit)
kelvin = celsius_to_kelvin(celsius)

md = Markdown("""
# Global Callbacks
## Fahrenheit:
<|{fahrenheit}|number|>
## Celsius:
<|{fahrenheit_to_celsius(fahrenheit)}|number|active=False|>
## Kelvin:
<|{celsius_to_kelvin(fahrenheit_to_celsius(fahrenheit))}|number|active=False|>
""")

Gui(page=md).run(dark_mode=False)
```

Without using any callbacks, we instead simply interpolate the expression to be evaluated
into the curly braces for both the celsius and kelvin controls — much like an f-string! Since
the fahrenheit state variable is present in the expression, Taipy knows that the expression should be reevaluated whenever fahrenheit is modified.

A drawback of this approach however is that the function fahrenheit_to_celsius is executed twice.
For a function as simple as this one, this drawback is insignificant.
However, if this was a heavy and uncacheable function, we would certainly want to avoid executing it unnecessarily.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions mkdocs.yml_template
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ nav:
- "Skippable Tasks": knowledge_base/tips/skippable_tasks/index.md
- "Callbacks in Taipy": knowledge_base/tips/the_on_change_callback/index.md
- "Taipy Cloud": knowledge_base/tips/taipy_cloud_deploy/index.md
- "On Change Callback": knowledge_base/tips/the_on_change_callback/index.md
- "Real-time data visualization": knowledge_base/tips/multithreading/index.md
- "Manuals":
- "About Taipy's Manuals": manuals/index.md
Expand Down

0 comments on commit cbf5285

Please sign in to comment.