Skip to content

Commit

Permalink
Add examples for the chat control (#1631)
Browse files Browse the repository at this point in the history
- Examples for the chat control
- Example for the table control
- Remove fallback hover text in Chat component
- Hide JsonAdapter class
- Minor fixes in reference and elements docs
  • Loading branch information
FabienLelaquais authored Aug 5, 2024
1 parent bd686a4 commit 1ad66d6
Show file tree
Hide file tree
Showing 15 changed files with 230 additions and 67 deletions.
3 changes: 0 additions & 3 deletions doc/gui/examples/charts/advanced-python-lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@
figure.update_layout(title="Different Probability Distributions")

page = """
# Plotly Python
<|toggle|theme|>
<|chart|figure={figure}|>
"""

Expand Down
Binary file added doc/gui/examples/controls/alice-avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui/examples/controls/beatrix-avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/gui/examples/controls/charles-avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions doc/gui/examples/controls/chat-calculator.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>
# -----------------------------------------------------------------------------------------
# Human-computer dialog UI based on the chat control.
# -----------------------------------------------------------------------------------------
from math import cos, pi, sin, sqrt, tan # noqa: F401

from taipy.gui import Gui

# The user interacts with the Python interpreter
users = ["human", "Result"]
messages: list[tuple[str, str, str]] = []


def evaluate(state, var_name: str, payload: dict):
# Retrieve the callback parameters
(_, _, expression, sender_id) = payload.get("args", [])
# Add the input content as a sent message
messages.append((f"{len(messages)}", expression, sender_id))
# Default message used if evaluation fails
result = "Invalid expression"
try:
# Evaluate the expression and store the result
result = f"= {eval(expression)}"
except Exception:
pass
# Add the result as an incoming message
messages.append((f"{len(messages)}", result, users[1]))
state.messages = messages


page = """
<|{messages}|chat|users={users}|sender_id={users[0]}|on_action=evaluate|>
"""

Gui(page).run()
84 changes: 84 additions & 0 deletions doc/gui/examples/controls/chat-discuss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 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>
# -----------------------------------------------------------------------------------------
# A chatting application based on the chat control.
# In order to see the users' avatars, the image files must be stored next to this script.
# If you want to test this application locally, you need to use several browsers and/or
# incognito windows so a given user's context is not reused.
# -----------------------------------------------------------------------------------------
from os import path

from taipy.gui import Gui, Icon
from taipy.gui.gui_actions import navigate, notify

username = ""
users: list[str|Icon] = []
messages: list[tuple[str, str, str]] = []

Gui.add_shared_variables("messages", "users")


def on_init(state):
# Copy the global variables users and messages to this user's state
state.users = users
state.messages = messages


def on_navigate(state, path: str):
# Navigate to the 'register' page if the user is not registered
if path == "discuss" and state.username == "":
return "register"
return path


def register(state):
# Check that the user is not already registered
for user in users:
if state.username == user or (isinstance(user, (list, tuple)) and state.username == user[0]):
notify(state, "error", "User already registered.")
return
# Use the avatar image if we can find it
avatar_image_file = f"{state.username.lower()}-avatar.png"
if path.isfile(avatar_image_file):
users.append((state.username, Icon(avatar_image_file, state.username)))
else:
users.append(state.username)
# Because users is a shared variable, this propagates to every client
state.users = users
navigate(state, "discuss")


def send(state, _: str, payload: dict):
(_, _, message, sender_id) = payload.get("args", [])
messages.append((f"{len(messages)}", message, sender_id))
state.messages = messages


register_page = """
Please enter your user name:
<|{username}|input|>
<|Submit|button|on_action=register|>
"""

discuss_page = """
<|### Let's discuss, {username}|text|mode=markdown|>
<|{messages}|chat|users={users}|sender_id={username}|on_action=send|>
"""

pages = {"register": register_page, "discuss": discuss_page}
gui = Gui(pages=pages).run()
40 changes: 40 additions & 0 deletions doc/gui/examples/controls/table-formatting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 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>
# -----------------------------------------------------------------------------------------
import datetime

from taipy.gui import Gui

stock = {
"date": [datetime.datetime(year=2000, month=12, day=d) for d in range(20, 30)],
"price": [119.88, 112.657, 164.5, 105.42, 188.36, 103.9, 143.97, 160.11, 136.3, 174.06],
"change": [7.814, -5.952, 0.01, 8.781, 7.335, 6.623, -6.635, -6.9, 0.327, -0.089],
"volume": [773, 2622, 2751, 1108, 7400, 3772, 9398, 4444, 9264, 1108],
}

columns = {
"date" : {"title": "Data", "format": "MMM d"},
"price" : {"title": "Price", "format": "$%.02f"},
"change" : {"title": "% change", "format": "%.01f"},
"volume" : {"title": "Volume"}
}

page = """
# Formatting cells in a table
<|{stock}|table|columns={columns}|>
"""

Gui(page).run()
3 changes: 0 additions & 3 deletions doc/gui/examples/controls/text-format.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
pi = 3.14159265358979

page = """
# Text - Formatting
<|toggle|theme|>
π≈<|{pi}|text|format=%.3f|>
"""

Expand Down
3 changes: 0 additions & 3 deletions doc/gui/examples/controls/text-md.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@
""" # noqa W291

page = """
# Text - Markdown
<|toggle|theme|>
<|{markdown}|text|mode=markdown|>
"""

Expand Down
3 changes: 0 additions & 3 deletions doc/gui/examples/controls/text-pre.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ def say_hello(name: str):
"""

page = """
# Text - pre
<|toggle|theme|>
<|{code}|text|mode=pre|>
"""

Expand Down
3 changes: 0 additions & 3 deletions doc/gui/examples/controls/text-simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@
name = "Taipy"

page = """
# Text - simple
<|toggle|theme|>
<|Hello {name}!|>
"""

Expand Down
4 changes: 1 addition & 3 deletions frontend/taipy-gui/src/components/Taipy/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ const Chat = (props: ChatProps) => {

const [rows, setRows] = useState<RowType[]>([]);
const page = useRef<key2Rows>({ key: defaultKey });
const [rowCount, setRowCount] = useState(0);
const [columns, setColumns] = useState<Array<string>>([]);
const scrollDivRef = useRef<HTMLDivElement>(null);
const anchorDivRef = useRef<HTMLElement>(null);
Expand Down Expand Up @@ -291,7 +290,6 @@ const Chat = (props: ChatProps) => {
useEffect(() => {
if (!refresh && props.messages && page.current.key && props.messages[page.current.key] !== undefined) {
const newValue = props.messages[page.current.key];
setRowCount(newValue.rowcount);
const nr = newValue.data as RowType[];
if (Array.isArray(nr) && nr.length > newValue.start && nr[newValue.start]) {
setRows((old) => {
Expand Down Expand Up @@ -341,7 +339,7 @@ const Chat = (props: ChatProps) => {
);

return (
<Tooltip title={hover || "" || `rowCount: ${rowCount}`}>
<Tooltip title={hover || ""}>
<Paper className={className} sx={boxSx} id={id}>
<Grid container rowSpacing={2} sx={gridSx} ref={scrollDivRef}>
{rows.length && !rows[0] ? (
Expand Down
1 change: 1 addition & 0 deletions taipy/gui/_renderers/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@


class JsonAdapter(ABC):
"""NOT DOCUMENTED"""
def register(self):
_TaipyJsonAdapter().register(self)

Expand Down
13 changes: 8 additions & 5 deletions taipy/gui/gui_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def download(
name: File name for the content on the client browser (defaults to content name).
on_action: Callback function (or callback name) to call when the download ends. See below.
## Notes:
<h4>Notes:</h4>
- *content*: this parameter can hold several values depending on your use case:
- a string: the value must be an existing path name to the file that gets downloaded or
Expand Down Expand Up @@ -122,9 +122,12 @@ def hold_control(
chooses to cancel.<br/>
If empty or None, no cancel action is provided to the user.<br/>
The signature of this function is:
- state (State^): The user state;
- state (`State^`): The user state;
- id (str): the id of the button that triggered the callback. That will always be
"UIBlocker" since it is created and managed internally;
If this parameter is None, no "Cancel" button is displayed.
message: The message to show. The default value is the string "Work in Progress...".
"""
if state and isinstance(state._gui, Gui):
Expand Down Expand Up @@ -212,8 +215,8 @@ def get_state_id(state: State) -> t.Optional[str]:
state (State^): The current user state as received in any callback.
Returns:
A string that uniquely identifies the state.<br/>
If None, then **state** was not handled by a `Gui^` instance.
A string that uniquely identifies the state. If this value None, it indicates that *state* is not
handled by a `Gui^` instance.
"""
if state and isinstance(state._gui, Gui):
return state._gui._get_client_id()
Expand Down Expand Up @@ -273,7 +276,7 @@ def invoke_callback(
"""Invoke a user callback for a given state.
Calling this function is equivalent to calling
*gui*.[Gui.]invoke_callback(state_id, callback, args, module_context)^`.
*gui*.`(Gui.)invoke_callback(state_id, callback, args, module_context)^`.
Arguments:
gui (Gui^): The current Gui instance.
Expand Down
Loading

0 comments on commit 1ad66d6

Please sign in to comment.