Updated the create_pace_bars_plot design and uploaded a new version of the Dashboard preview image.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
@@ -34,7 +34,9 @@ from matplotlib.colors import LinearSegmentedColormap, Normalize
|
||||
from xml.etree.ElementTree import Element, SubElement, tostring
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
# === Helper Functions ===
|
||||
# =============================================================================
|
||||
# Helper Functions
|
||||
# =============================================================================
|
||||
def list_files():
|
||||
"""
|
||||
Listet alle .fit Files aus fit_files/ und .gpx Files aus gpx_files/ auf
|
||||
@@ -148,9 +150,9 @@ def haversine(lon1, lat1, lon2, lat2):
|
||||
return 2 * R * asin(sqrt(a))
|
||||
|
||||
|
||||
########
|
||||
# FIT
|
||||
########
|
||||
# -----------------------------------------------------------------------------
|
||||
# FIT-FILE-FUNCTION
|
||||
# -----------------------------------------------------------------------------
|
||||
def process_fit(file_path):
|
||||
"""
|
||||
Verarbeitet eine FIT-Datei und erstellt einen DataFrame
|
||||
@@ -313,9 +315,9 @@ def process_fit(file_path):
|
||||
|
||||
|
||||
|
||||
########
|
||||
# GPX
|
||||
########
|
||||
# -----------------------------------------------------------------------------
|
||||
# GPX-FILE-FUNCTION
|
||||
# -----------------------------------------------------------------------------
|
||||
def process_gpx(file_path):
|
||||
"""
|
||||
Verarbeitet GPX-Dateien und gibt DataFrame im gleichen Format wie process_fit() zurück
|
||||
@@ -520,9 +522,9 @@ def calculate_calories_burned(df):
|
||||
return int(round(cumulative))
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# -----------------------------------------------------------------------------
|
||||
# INFO BANNER
|
||||
# =============================================================================
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_info_banner(df):
|
||||
# Total distance in km
|
||||
total_distance_km = df['cum_dist_km'].iloc[-1]
|
||||
@@ -590,9 +592,9 @@ def create_info_banner(df):
|
||||
return info_banner
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# -----------------------------------------------------------------------------
|
||||
# EXPORT SUMMARY IMAGE (SVG)
|
||||
# =============================================================================
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_strava_style_svg(df, total_distance_km=None, total_time=None, avg_pace=None,
|
||||
width=800, height=600, padding=50):
|
||||
"""
|
||||
@@ -753,9 +755,10 @@ def calculate_pace(distance_km, total_seconds):
|
||||
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# -----------------------------------------------------------------------------
|
||||
# START OF THE PLOTS
|
||||
# =============================================================================
|
||||
# MAP-PLOT:
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_map_plot(df):
|
||||
fig = px.line_map(
|
||||
df,
|
||||
@@ -830,9 +833,9 @@ def create_map_plot(df):
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# =============================================================================
|
||||
# HILFSFUNKTION: GPS → Pixel-Koordinaten (Mercator-Projektion)
|
||||
# -----------------------------------------------------------------------------
|
||||
# =============================================================================
|
||||
def _gps_to_pixel(lats, lons, img_width=800, img_height=600,
|
||||
padding=None,
|
||||
pad_top=40, pad_bottom=60, pad_left=10, pad_right=10):
|
||||
@@ -879,9 +882,9 @@ def _gps_to_pixel(lats, lons, img_width=800, img_height=600,
|
||||
return xs, ys, meta
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# =============================================================================
|
||||
# HILFSFUNKTION: Matplotlib-Figure → base64-PNG (für Dash dcc.Graph)
|
||||
# -----------------------------------------------------------------------------
|
||||
# =============================================================================
|
||||
def _fig_to_base64(fig):
|
||||
"""
|
||||
Konvertiert eine Matplotlib-Figure zu einem base64-PNG.
|
||||
@@ -985,7 +988,7 @@ def load_runs_for_city(city_code, all_file_options):
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PLOT 1: HEATMAP (count) – mehrere Läufe, Linienstärke = Häufigkeit
|
||||
# PIXEL-PLOT 1: HEATMAP (count) – mehrere Läufe, Linienstärke = Häufigkeit
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_pixel_heatmap(dataframes,
|
||||
img_width=900, img_height=900,
|
||||
@@ -995,7 +998,7 @@ def create_pixel_heatmap(dataframes,
|
||||
n_city_runs=0,
|
||||
highlight_df=None):
|
||||
"""
|
||||
Sam-Style Heatmap: Zeichnet einen oder mehrere Läufe pixelweise.
|
||||
Heatmap: Zeichnet einen oder mehrere Läufe pixelweise.
|
||||
Je öfter ein Pixel überquert wurde, desto heller/wärmer die Farbe.
|
||||
|
||||
Args:
|
||||
@@ -1167,7 +1170,7 @@ def create_pixel_heatmap(dataframes,
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PLOT 2: ELEVATION-MAP – Farbe zeigt Steigung/Gefälle je Segment
|
||||
# PIXEL-PLOT 2: ELEVATION-MAP – Farbe zeigt Steigung/Gefälle je Segment
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_pixel_elevation_map(df, img_width=900, img_height=900,
|
||||
line_width=3, bg_color='#0d0d0d'):
|
||||
@@ -1309,12 +1312,12 @@ def create_pixel_elevation_map(df, img_width=900, img_height=900,
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PLOT 3: PACE-MAP – Farbe zeigt Tempo je Segment (schnell = blau, langsam = rot)
|
||||
# PIXEL-PLOT 3: PACE-MAP – Farbe zeigt Tempo je Segment (schnell = blau, langsam = rot)
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_pixel_pace_map(df, img_width=900, img_height=900,
|
||||
line_width=3, bg_color='#0d0d0d'):
|
||||
"""
|
||||
Sam-Style Pace-Map: Die Route wird pixelweise gezeichnet, wobei die
|
||||
Pace-Map: Die Route wird pixelweise gezeichnet, wobei die
|
||||
Farbe anzeigt, wie schnell du in diesem Bereich gelaufen bist.
|
||||
Blau = schnell (niedriger min/km-Wert), Rot = langsam (hoher min/km-Wert).
|
||||
|
||||
@@ -1447,7 +1450,7 @@ def create_pixel_pace_map(df, img_width=900, img_height=900,
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PLOT 4: HEART-RATE-MAP
|
||||
# PIXEL-PLOT 4: HEART-RATE-MAP
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_pixel_hr_map(df, img_width=900, img_height=900,
|
||||
line_width=3, bg_color='#0d0d0d'):
|
||||
@@ -1626,7 +1629,9 @@ def create_pixel_hr_map(df, img_width=900, img_height=900,
|
||||
|
||||
|
||||
|
||||
# #####################
|
||||
# -----------------------------------------------------------------------------
|
||||
# ELEVATION-PLOT:
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_elevation_plot(df, smooth_points=500):
|
||||
# Originale Daten
|
||||
x = df['time']
|
||||
@@ -1846,8 +1851,10 @@ def create_elevation_plot(df, smooth_points=500):
|
||||
|
||||
|
||||
|
||||
|
||||
def create_deviation_plot(df): #Distanz-Zeit-Diagramm
|
||||
# -----------------------------------------------------------------------------
|
||||
# DEVIATION-PLOT: Distanz-Zeit-Diagramm
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_deviation_plot(df):
|
||||
# Compute mean velocity in km/s
|
||||
vel_kmps_mean = df['cum_dist_km'].iloc[-1] / df['time_diff_sec'].iloc[-1]
|
||||
# Expected cumulative distance assuming constant mean velocity
|
||||
@@ -1889,6 +1896,9 @@ def create_deviation_plot(df): #Distanz-Zeit-Diagramm
|
||||
return fig
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# SPEED-PLOT:
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_speed_plot(df):
|
||||
mask = df['speed_kmh_smooth'].isna()
|
||||
mean_speed_kmh = df['speed_kmh'].mean()
|
||||
@@ -1926,10 +1936,9 @@ def create_speed_plot(df):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# heart_rate Plot NEW !!!
|
||||
# -----------------------------------------------------------------------------
|
||||
# HEART-RATE-PLOT:
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_heart_rate_plot(df):
|
||||
# Maske für gültige Heart Rate Daten
|
||||
mask = df['hr_smooth'].isna()
|
||||
@@ -2079,30 +2088,33 @@ def create_heart_rate_plot(df):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# PACE-BAR-PLOT:
|
||||
# -----------------------------------------------------------------------------
|
||||
def create_pace_bars_plot(df, formatted_pace=None):
|
||||
# Ensure time column is datetime
|
||||
"""
|
||||
Strava-Style Pace-Histogram: Horizontale Balken, ein Balken pro km-Segment.
|
||||
Links: km-Label + Pace-Text. Mitte: Balken (Breite = Pace-Wert).
|
||||
Rechts: Elevation-Delta und Heart Rate je Segment.
|
||||
Vertikale gestrichelte Linie = Durchschnittspace.
|
||||
"""
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import plotly.graph_objects as go
|
||||
|
||||
# Sicherstellen dass time eine datetime-Spalte ist
|
||||
if not pd.api.types.is_datetime64_any_dtype(df['time']):
|
||||
df = df.copy()
|
||||
df['time'] = pd.to_datetime(df['time'], errors='coerce')
|
||||
|
||||
# Assign km segments
|
||||
df = df.copy()
|
||||
df['km'] = df['cum_dist_km'].astype(int)
|
||||
|
||||
# Time in seconds from start
|
||||
df['time_sec'] = (df['time'] - df['time'].iloc[0]).dt.total_seconds()
|
||||
|
||||
# Step 3: Compute pace manually per km group
|
||||
df['km_start'] = np.nan
|
||||
df['segment_len'] = np.nan
|
||||
df['pace_min_per_km'] = np.nan
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Pace, Elevation-Delta, HR je km-Segment berechnen
|
||||
# -------------------------------------------------------------------------
|
||||
segments = []
|
||||
for km_val, group in df.groupby('km'):
|
||||
dist_start = group['cum_dist_km'].iloc[0]
|
||||
dist_end = group['cum_dist_km'].iloc[-1]
|
||||
@@ -2112,91 +2124,225 @@ def create_pace_bars_plot(df, formatted_pace=None):
|
||||
time_end = group['time_sec'].iloc[-1]
|
||||
elapsed_time_sec = time_end - time_start
|
||||
|
||||
if segment_len > 0:
|
||||
pace_min_per_km = (elapsed_time_sec / 60) / segment_len
|
||||
if segment_len > 0 and elapsed_time_sec > 0:
|
||||
pace_min = (elapsed_time_sec / 60) / segment_len
|
||||
else:
|
||||
pace_min_per_km = np.nan
|
||||
pace_min = np.nan
|
||||
|
||||
df.loc[group.index, 'km_start'] = km_val
|
||||
df.loc[group.index, 'segment_len'] = segment_len
|
||||
df.loc[group.index, 'pace_min_per_km'] = pace_min_per_km
|
||||
# Elevation-Delta für dieses Segment
|
||||
elev_delta = np.nan
|
||||
if 'elev' in group.columns and group['elev'].notna().sum() >= 2:
|
||||
elev_delta = group['elev'].iloc[-1] - group['elev'].iloc[0]
|
||||
elif 'rel_elev' in group.columns and group['rel_elev'].notna().sum() >= 2:
|
||||
elev_delta = group['rel_elev'].iloc[-1] - group['rel_elev'].iloc[0]
|
||||
|
||||
# Clean types
|
||||
df['km_start'] = df['km_start'].astype(int)
|
||||
df['segment_len'] = df['segment_len'].astype(float)
|
||||
df['pace_min_per_km'] = pd.to_numeric(df['pace_min_per_km'], errors='coerce')
|
||||
# Durchschnittliche HR für dieses Segment
|
||||
hr_mean = np.nan
|
||||
if 'heart_rate' in group.columns:
|
||||
valid = group['heart_rate'].dropna()
|
||||
if len(valid) > 0:
|
||||
hr_mean = valid.mean()
|
||||
|
||||
# Step 4: Create Plotly bar chart
|
||||
# Km-Label: letzter Km erhält tatsächliche Distanz als Label
|
||||
is_last = (km_val == df['km'].max())
|
||||
km_label = f"{dist_end:.1f}" if is_last else str(km_val + 1)
|
||||
# Ersten Km explizit auf "1" setzen auch wenn km_val=0
|
||||
if km_val == 0 and not is_last:
|
||||
km_label = "1"
|
||||
|
||||
segments.append({
|
||||
'km_val': km_val,
|
||||
'km_label': km_label,
|
||||
'segment_len': segment_len,
|
||||
'pace_min': pace_min,
|
||||
'elev_delta': elev_delta,
|
||||
'hr_mean': hr_mean,
|
||||
})
|
||||
|
||||
seg_df = pd.DataFrame(segments)
|
||||
seg_df = seg_df[seg_df['pace_min'] < 20] # Pausen/Ausreißer raus
|
||||
seg_df = seg_df.dropna(subset=['pace_min'])
|
||||
seg_df = seg_df.sort_values('km_val').reset_index(drop=True)
|
||||
|
||||
if seg_df.empty:
|
||||
fig = go.Figure()
|
||||
fig.add_trace(go.Bar(
|
||||
x=df['km_start'], # Mittig unter jeder Bar
|
||||
y=df['pace_min_per_km'],
|
||||
width=df['segment_len'],
|
||||
text=[f"{v:.1f} min/km" if pd.notnull(v) else "" for v in df['pace_min_per_km']],
|
||||
#textposition='outside',
|
||||
textposition='inside',
|
||||
marker_color='#125595',
|
||||
opacity=0.9, # Transparenz
|
||||
name='Pace pro km',
|
||||
offset=0
|
||||
))
|
||||
fig.update_layout(paper_bgcolor='#1e1e1e',
|
||||
title=dict(text='Keine Pace-Daten', font=dict(color='white')))
|
||||
return fig
|
||||
|
||||
|
||||
|
||||
|
||||
# #########
|
||||
# Calculate average pace if not provided from Info-Banner function
|
||||
# -------------------------------------------------------------------------
|
||||
# Durchschnittspace berechnen
|
||||
# -------------------------------------------------------------------------
|
||||
total_distance_km = df['cum_dist_km'].iloc[-1]
|
||||
total_seconds = df['time_diff_sec'].iloc[-1]
|
||||
|
||||
# Average pace (min/km) - KORRIGIERT
|
||||
if total_distance_km > 0:
|
||||
pace_sec_per_km = total_seconds / total_distance_km
|
||||
pace_min = int(pace_sec_per_km // 60)
|
||||
pace_sec = int(pace_sec_per_km % 60)
|
||||
formatted_pace = f"{pace_min}:{pace_sec:02d} min/km"
|
||||
# Numerischen Wert für die dash'ed Linie berechnen
|
||||
avg_pace_numeric = pace_sec_per_km / 60
|
||||
pace_min_i = int(pace_sec_per_km // 60)
|
||||
pace_sec_i = int(pace_sec_per_km % 60)
|
||||
formatted_pace = f"{pace_min_i}:{pace_sec_i:02d} min/km"
|
||||
else:
|
||||
avg_pace_numeric = seg_df['pace_min'].mean()
|
||||
formatted_pace = "N/A"
|
||||
avg_pace_numeric = 0
|
||||
|
||||
# Add horizontal dash'ed reference line (avg_pace_numeric)
|
||||
# -------------------------------------------------------------------------
|
||||
# Pace → Formatierung als "M:SS"
|
||||
# -------------------------------------------------------------------------
|
||||
def fmt_pace(p):
|
||||
if pd.isna(p):
|
||||
return ""
|
||||
m = int(p)
|
||||
s = int(round((p - m) * 60))
|
||||
return f"{m}:{s:02d}"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Y-Achse: Segment-Labels (von oben = km 1 nach unten = letztes km)
|
||||
# Strava zeigt älteste Km oben, letzte unten → umgekehrte Reihenfolge
|
||||
# -------------------------------------------------------------------------
|
||||
# Alle Listen aus demselben sortierten Index ziehen
|
||||
y_labels = seg_df['km_label'].tolist() # ["1","2",...,"11","0.2"]
|
||||
pace_vals = seg_df['pace_min'].tolist()
|
||||
elev_vals = seg_df['elev_delta'].tolist()
|
||||
hr_vals = seg_df['hr_mean'].tolist()
|
||||
|
||||
# Maximale Pace für X-Achse (leicht über Max für optischen Puffer)
|
||||
x_max = max(pace_vals) * 1.18
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Farbe der Balken: blau wie Strava, schneller = etwas heller
|
||||
# -------------------------------------------------------------------------
|
||||
pace_min_val = min(pace_vals)
|
||||
pace_max_val = max(pace_vals)
|
||||
pace_range = max(pace_max_val - pace_min_val, 0.01)
|
||||
|
||||
bar_colors = []
|
||||
for p in pace_vals:
|
||||
# Schnellster Km = hellstes Blau, langsamster = dunkelstes Blau
|
||||
norm = (p - pace_min_val) / pace_range # 0 = schnell, 1 = langsam
|
||||
r = int(18 + norm * 10)
|
||||
g = int(85 + norm * 20)
|
||||
b = int(149 + norm * 30)
|
||||
bar_colors.append(f'rgb({r},{g},{b})')
|
||||
|
||||
fig = go.Figure()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Balken (horizontal)
|
||||
# -------------------------------------------------------------------------
|
||||
fig.add_trace(go.Bar(
|
||||
x=pace_vals,
|
||||
y=y_labels,
|
||||
orientation='h',
|
||||
marker=dict(
|
||||
color=bar_colors,
|
||||
line=dict(width=0),
|
||||
),
|
||||
opacity=0.92,
|
||||
width=0.72,
|
||||
text=[fmt_pace(p) for p in pace_vals],
|
||||
textposition='inside',
|
||||
textfont=dict(color='white', size=11),
|
||||
hovertemplate=(
|
||||
'km %{y}<br>'
|
||||
'Pace: %{text}<br>'
|
||||
'<extra></extra>'
|
||||
),
|
||||
name='',
|
||||
showlegend=False,
|
||||
))
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Durchschnitts-Linie (vertikal, gestrichelt)
|
||||
# -------------------------------------------------------------------------
|
||||
fig.add_shape(
|
||||
type='line',
|
||||
x0=0, # Start bei 0
|
||||
x1=total_distance_km, # Ende bei maximaler Distanz
|
||||
y0=avg_pace_numeric,
|
||||
y1=avg_pace_numeric,
|
||||
line=dict(color='gray', width=1, dash='dash'),
|
||||
x0=avg_pace_numeric, x1=avg_pace_numeric,
|
||||
y0=-0.5, y1=len(y_labels) - 0.5,
|
||||
line=dict(color='rgba(180,180,180,0.7)', width=1.5, dash='dash'),
|
||||
layer='above',
|
||||
)
|
||||
fig.add_annotation(
|
||||
x=avg_pace_numeric,
|
||||
y=len(y_labels) - 0.5,
|
||||
text=f"Ø {formatted_pace}",
|
||||
showarrow=False,
|
||||
yanchor='bottom',
|
||||
font=dict(color='rgba(180,180,180,0.9)', size=10),
|
||||
bgcolor='rgba(0,0,0,0)',
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Elevation-Delta als Annotation rechts vom Balken
|
||||
# -------------------------------------------------------------------------
|
||||
has_elev = any(not np.isnan(e) for e in elev_vals)
|
||||
has_hr = any(not np.isnan(h) for h in hr_vals)
|
||||
|
||||
title_text = f'Tempo je Kilometer'
|
||||
title_text += f' - Ø {formatted_pace}'
|
||||
for i in range(len(seg_df)):
|
||||
km_lbl = y_labels[i]
|
||||
elev = elev_vals[i]
|
||||
hr = hr_vals[i]
|
||||
|
||||
right_text = ''
|
||||
if has_elev and not np.isnan(elev):
|
||||
arrow = '↑' if elev > 0.5 else ('↓' if elev < -0.5 else '—')
|
||||
right_text += f"<span style='color:#aaaaaa'>{arrow}{abs(elev):.0f}m</span>"
|
||||
|
||||
if has_hr and not np.isnan(hr):
|
||||
if right_text:
|
||||
right_text += ' '
|
||||
right_text += f"<span style='color:#ff6b6b'>♥ {hr:.0f}</span>"
|
||||
|
||||
if right_text:
|
||||
fig.add_annotation(
|
||||
x=x_max,
|
||||
y=km_lbl,
|
||||
text=right_text,
|
||||
showarrow=False,
|
||||
xanchor='right',
|
||||
font=dict(size=12),
|
||||
bgcolor='rgba(0,0,0,0)',
|
||||
)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Layout
|
||||
# -------------------------------------------------------------------------
|
||||
# Höhe dynamisch: ~32px pro Balken, mindestens 300px
|
||||
plot_height = max(300, len(y_labels) * 36 + 80)
|
||||
|
||||
fig.update_layout(
|
||||
title=dict(text=title_text, font=dict(size=16, color='white')),
|
||||
xaxis_title='Distanz (km)',
|
||||
yaxis_title='Minuten pro km',
|
||||
barmode='overlay',
|
||||
bargap=0,
|
||||
bargroupgap=0,
|
||||
title=dict(
|
||||
text=f'Tempo je Kilometer · Ø {formatted_pace}',
|
||||
font=dict(size=15, color='white')
|
||||
),
|
||||
xaxis=dict(
|
||||
type='linear',
|
||||
range=[0, df['cum_dist_km'].iloc[-1]],
|
||||
tickmode='linear',
|
||||
dtick=1,
|
||||
showgrid=True
|
||||
title='Pace (min/km)',
|
||||
range=[0, x_max],
|
||||
tickmode='array',
|
||||
tickvals=[i * 0.5 for i in range(int(x_max / 0.5) + 2)],
|
||||
ticktext=[fmt_pace(i * 0.5) for i in range(int(x_max / 0.5) + 2)],
|
||||
showgrid=True,
|
||||
gridcolor='rgba(255,255,255,0.07)',
|
||||
zeroline=False,
|
||||
color='white',
|
||||
),
|
||||
yaxis=dict(
|
||||
title='',
|
||||
autorange='reversed', # km 1 oben, letzter km unten (wie Strava)
|
||||
tickfont=dict(size=11, color='white'),
|
||||
showgrid=False,
|
||||
zeroline=False,
|
||||
),
|
||||
template='plotly_dark',
|
||||
height=400,
|
||||
margin=dict(l=40, r=40, t=30, b=40),
|
||||
height=plot_height,
|
||||
margin=dict(l=50, r=80, t=45, b=45),
|
||||
plot_bgcolor='#111111',
|
||||
paper_bgcolor='#1e1e1e',
|
||||
font=dict(color='white'),
|
||||
uirevision='constant', # Avoiding not needed Re-renderings
|
||||
bargap=0.15,
|
||||
uirevision='constant',
|
||||
)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user