Skip to content

Commit

Permalink
Refactored to allow tooltip template update on existing graph
Browse files Browse the repository at this point in the history
Updated tests
  • Loading branch information
kb- authored May 5, 2024
1 parent ab4ed40 commit f3eaf4e
Show file tree
Hide file tree
Showing 15 changed files with 663 additions and 245 deletions.
72 changes: 36 additions & 36 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
repos:
- repo: local
hooks:
- id: ruff
name: ruff
description: "Run 'ruff' for extremely fast Python linting"
entry: ruff check --force-exclude
language: python
types_or: [python, pyi]
args: []
require_serial: true
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"

- id: ruff-format
name: ruff-format
description: "Run 'ruff format' for extremely fast Python formatting"
entry: ruff format --force-exclude
language: python
types_or: [python, pyi]
args: []
require_serial: true
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"

- id: black-fix
name: black (auto format)
entry: ./env/Scripts/black.exe
Expand All @@ -13,20 +35,6 @@ repos:
language: system
types: [python]

- id: pytest-selenium
name: pytest (selenium tests)
entry: ./env/Scripts/pytest.exe
args: ['tests/', '-k', 'selenium', '--webdriver', 'Firefox']
language: system
pass_filenames: false

- id: pytest-non-selenium
name: pytest (non-selenium tests)
entry: ./env/Scripts/pytest.exe
args: ['tests/', '-k', 'not selenium']
language: system
pass_filenames: false

- id: black
name: black
entry: ./env/Scripts/black.exe
Expand All @@ -39,30 +47,22 @@ repos:
language: system
types: [python]

- id: ruff
name: ruff
description: "Run 'ruff' for extremely fast Python linting"
entry: ruff check --force-exclude
language: python
types_or: [python, pyi]
args: []
require_serial: true
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"

- id: ruff-format
name: ruff-format
description: "Run 'ruff format' for extremely fast Python formatting"
entry: ruff format --force-exclude
language: python
types_or: [python, pyi]
args: []
require_serial: true
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"

- id: mypy
name: mypy
entry: ./env/Scripts/mypy.exe
language: system
types: [python]

- id: pytest-selenium
name: pytest (selenium tests)
entry: ./env/Scripts/pytest.exe
args: ['tests/', '-k', 'selenium', '--webdriver', 'Firefox', '--testmon']
language: system
pass_filenames: false

- id: pytest-non-selenium
name: pytest (non-selenium tests)
entry: ./env/Scripts/pytest.exe
args: ['tests/', '-k', 'not selenium', '--testmon']
language: system
pass_filenames: false
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.12.0
3.12.3
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,117 @@ tooltip(app10, style=custom_style, graph_ids=["graph-id"], template=template, de
For more examples, refer to the provided `dash_tooltip_demo.py` and check out [Plotly’s Text and Annotations documentation](https://plotly.com/python/text-and-annotations/#styling-and-coloring-annotations), which provides a wealth of information on customizing the appearance of annotations.
Refer to the [Plotly Annotation Reference](https://plotly.com/python/reference/layout/annotations/) for a comprehensive guide on available styling attributes and how to apply them.

## Template updating

Tooltip content can be updated to match with selected data in a dynamic Dash app:
```python
GRAPH_ID = "scatter-plot16a"

# Sample DataFrame with DatetimeIndex
date_range = pd.date_range(start="2025-01-01", periods=5)
df = pd.DataFrame(
{
"x": [1, 2, 3, 4, 5],
"y": [2, 4, 6, 8, 10],
"z": [3, 6, 9, 12, 15],
"a": [4, 8, 12, 16, 20],
"b": [5, 10, 15, 20, 25],
},
index=date_range,
)

# Initialize the Dash app
app16 = dash.Dash(__name__)

# Define the layout
app16.layout = html.Div(
[
html.Label("Select X and Y columns:"),
dcc.Dropdown(
id="x-column",
options=[{"label": col, "value": col} for col in df.columns],
placeholder="Select X axis data",
),
dcc.Dropdown(
id="y-column",
options=[{"label": col, "value": col} for col in df.columns],
placeholder="Select Y axis data",
),
dcc.Graph(
id=GRAPH_ID,
style={"width": "600px", "height": "600px"},
config={
"editable": True,
"edits": {"shapePosition": True, "annotationPosition": True},
},
),
]
)

# Create a tooltip instance
tooltip_instance16 = tooltip(app16, graph_ids=[GRAPH_ID])

# Define callback to update the scatter plot
@app16.callback(
Output(GRAPH_ID, "figure", allow_duplicate=True),
[Input("x-column", "value"), Input("y-column", "value")],
prevent_initial_call=True,
)
def update_scatter_plot(x_column, y_column):
if not x_column or not y_column:
raise PreventUpdate # Prevent update if either dropdown is not selected

non_selected_columns = [
col for col in df.columns if col not in [x_column, y_column]
]
customdata = df[non_selected_columns].apply(
lambda row: "<br>".join(
f"{col}: {val}" for col, val in zip(non_selected_columns, row)
),
axis=1,
)
# gives (depending on selected entries):
# 2022-01-01 x: 1<br>z: 3<br>b: 5
# 2022-01-02 x: 2<br>z: 6<br>b: 10
# ...

# New template, to match selected data entries
template = (
"<b>Date</b>: %{customdata}<br>"
+ f"<b>{x_column}: %{{x}}<br>"
+ f"{y_column}: %{{y}}</b><br>"
)
# gives (depending on selected entries):
# <b>Date</b>: %{customdata}<br><b>x: %{x}<br><b>a</b>: %{y}<br>

# Update template for new tooltips
tooltip_instance16.update_template(graph_id=GRAPH_ID, template=template)

trace = go.Scatter(
x=df[x_column],
y=df[y_column],
mode="markers",
marker=dict(color="blue"),
customdata=df.index.strftime("%Y-%m-%d %H:%M:%S") + "<br>" + customdata,
# Include date and time with other data
hovertemplate=template,
)
layout = go.Layout(
title="Scatter Plot",
xaxis=dict(title=x_column),
yaxis=dict(title=y_column),
hovermode="closest",
height=800,
width=800,
)
return {"data": [trace], "layout": layout}


# Run the app
if __name__ == "__main__":
app16.run_server(debug=False, port=8196)
```

## Handling Log Axes

Due to a long-standing bug in Plotly (see [Plotly Issue #2580](https://github.com/plotly/plotly.py/issues/2580)), annotations (`fig.add_annotation`) may not be placed correctly on log-scaled axes. The `dash_tooltip` module provides an option to automatically correct the tooltip placement on log-scaled axes via the `apply_log_fix` argument in the `tooltip` function. By default, `apply_log_fix` is set to `True` to enable the fix.
Expand Down
16 changes: 10 additions & 6 deletions dash_qt_demo2.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import http.client
import threading
import os
import sys
import threading
import time

import dash
import numpy as np
import plotly.graph_objects as go
from dash import dcc, html
from PyQt6.QtCore import QUrl
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWidgets import QApplication, QFileDialog, QMainWindow
import plotly.graph_objects as go

from dash_tooltip import tooltip

dash_port = 8050
Expand Down Expand Up @@ -89,8 +90,8 @@ def create_dash_app():
layout_margin = {"l": 25, "r": 25, "t": 25, "b": 25}

# Creating a grid of x and y values
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
x = np.linspace(0, 10, 400)
y = np.linspace(0, 10, 400)
X, Y = np.meshgrid(x, y)

# Calculate Z as a function of X and Y
Expand All @@ -102,7 +103,7 @@ def create_dash_app():
z=Z,
x=x,
y=y,
colorscale="Viridis"
colorscale="Viridis",
# You can change the colorscale as needed
),
layout=go.Layout(margin=layout_margin),
Expand Down Expand Up @@ -151,7 +152,10 @@ def create_dash_app():
template1 = "x: %{x:.2f},<br>y: %{y:.2f}"
tooltip(app, template=template1, graph_ids=["example-graph1"])
template2 = "x: %{x:.2f},<br>y: %{y:.2f},<br>z: %{z:.3f}"
tooltip(app, template=template2, graph_ids=["example-graph2"])
tooltip_style = {
"bgcolor": "rgba(255, 255, 255, 0.2)",
}
tooltip(app, style=tooltip_style, template=template2, graph_ids=["example-graph2"])
return app


Expand Down
Loading

0 comments on commit f3eaf4e

Please sign in to comment.