diff --git a/dashboard.py b/dashboard.py index f30c35f..36df071 100644 --- a/dashboard.py +++ b/dashboard.py @@ -3,6 +3,9 @@ import plotly.graph_objects as go import numpy as np import dash_bootstrap_components as dbc +import builtins +from dash.exceptions import PreventUpdate + import webbrowser from threading import Timer @@ -20,7 +23,16 @@ from GHA_triaxial.gha2_ES import gha2_ES from GHA_triaxial.gha2_approx import gha2_approx +# Prints von importierten Funktionen unterdücken +def _no_print(*args, **kwargs): + pass + +builtins.print = _no_print + + +# Bootstrap (CSS) einbindung app = Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP]) +# App-Name im Tab app.title = "Geodätische Hauptaufgaben" @@ -259,7 +271,7 @@ pane_gha1 = html.Div( method_row("Analytisch", "cb-ana-1", "input-ana-1", ""), method_row("Numerisch", "cb-num-1", "input-num-n-1", "2000", info="Anzahl Schritte"), - method_row("Stochastisch (ES)", "cb-stoch-1", "input-stoch-n-1", "1000", info="Info"), + method_row("Stochastisch (ES)", "cb-stoch-1", "input-stoch-n-1", "1000", info="Länge Streckensegment [m]"), method_row("Approximiert", "cb-approx-1", "input-approx-ds-1", "1000", info="Länge Streckensegment [m]"), @@ -301,7 +313,7 @@ pane_gha2 = html.Div( inputfeld("λ₁", "input-GHA2-lamb1", "°", min=-180, max=180), method_row("Numerisch", "cb-num-2", "input-num-n-2", "2000", info="Anzahl Schritte"), - method_row("Stochastisch", "cb-stoch-2", "input-stoch-n-2", "1000", info="Info"), + method_row("Stochastisch", "cb-stoch-2", "input-stoch-n-2", "1000", info="Länge Streckensegment [m]"), method_row("Approximiert", "cb-approx-2", "input-approx-ds-2", "1000", info="Länge Streckensegment [m]"), html.Div( @@ -426,11 +438,17 @@ app.layout = html.Div( ), ], ), + dcc.Store(id="calc-token-gha1", data=0), + dcc.Store(id="calc-token-gha2", data=0), #html.P("© 2026", style={"fontSize": "10px", "color": "gray", "textAlign": "center", "marginTop": "16px"}), ], + + ) + + # Funktion zur Wahl der Halbachsen @app.callback( Output("input-ax", "value"), @@ -523,7 +541,7 @@ def gha1_method_hint(n, a, nu, ap): @app.callback( Output("output-gha1-ana", "children"), Output("store-gha1-ana", "data"), - Input("button-calc-gha1", "n_clicks"), + Input("calc-token-gha1", "data"), State("cb-ana-1", "value"), State("input-GHA1-beta0", "value"), State("input-GHA1-lamb0", "value"), @@ -579,7 +597,7 @@ def compute_gha1_ana(n1, cb_ana, beta0, lamb0, s, a0, ax, ay, b): @app.callback( Output("output-gha1-num", "children"), Output("store-gha1-num", "data"), - Input("button-calc-gha1", "n_clicks"), + Input("calc-token-gha1", "data"), State("cb-num-1", "value"), State("input-num-n-1", "value"), State("input-GHA1-beta0", "value"), @@ -631,7 +649,7 @@ def compute_gha1_num(n1, cb_num, n_in, beta0, lamb0, s, a0, ax, ay, b): @app.callback( Output("output-gha1-stoch", "children"), Output("store-gha1-stoch", "data"), - Input("button-calc-gha1", "n_clicks"), + Input("calc-token-gha1", "data"), State("cb-stoch-1", "value"), State("input-stoch-n-1", "value"), State("input-GHA1-beta0", "value"), @@ -688,7 +706,7 @@ def compute_gha1_stoch(n1, cb_stoch, n_in, beta0, lamb0, s, a0, ax, ay, b, metho @app.callback( Output("output-gha1-approx", "children"), Output("store-gha1-approx", "data"), - Input("button-calc-gha1", "n_clicks"), + Input("calc-token-gha1", "data"), State("cb-approx-1", "value"), State("input-approx-ds-1", "value"), State("input-GHA1-beta0", "value"), @@ -739,7 +757,7 @@ def compute_gha1_approx(n1, cb_approx, ds_in, beta0, lamb0, s, a0, ax, ay, b): @app.callback( Output("output-gha2-num", "children"), Output("store-gha2-num", "data"), - Input("button-calc-gha2", "n_clicks"), + Input("calc-token-gha2", "data"), State("cb-num-2", "value"), State("input-num-n-2", "value"), State("input-GHA2-beta0", "value"), @@ -800,7 +818,7 @@ def compute_gha2_num(n2, cb_num, n_in, beta0, lamb0, beta1, lamb1, ax, ay, b): @app.callback( Output("output-gha2-stoch", "children"), Output("store-gha2-stoch", "data"), - Input("button-calc-gha2", "n_clicks"), + Input("calc-token-gha2", "data"), State("cb-stoch-2", "value"), State("input-stoch-n-2", "value"), State("input-GHA2-beta0", "value"), @@ -830,7 +848,7 @@ def compute_gha2_stoch(n2, cb_stoch, n_in, beta0, lamb0, beta1, lamb1, ax, ay, b P0 = ell.ell2cart(beta0_rad, lamb0_rad) P1 = ell.ell2cart(beta1_rad, lamb1_rad) - a0_stoch, a1_stoch, s_stoch, points = gha2_ES(ell, P0, P1, all_points=True) + a0_stoch, a1_stoch, s_stoch, points = gha2_ES(ell, P0, P1, maxIter=n_in, all_points=True) out = html.Div([ html.Strong("Stochastisch (ES): "), @@ -849,7 +867,7 @@ def compute_gha2_stoch(n2, cb_stoch, n_in, beta0, lamb0, beta1, lamb1, ax, ay, b @app.callback( Output("output-gha2-approx", "children"), Output("store-gha2-approx", "data"), - Input("button-calc-gha2", "n_clicks"), + Input("calc-token-gha2", "data"), State("cb-approx-2", "value"), State("input-approx-ds-2", "value"), State("input-GHA2-beta0", "value"), @@ -902,8 +920,9 @@ def compute_gha2_approx(n2, cb_approx, ds_in, beta0, lamb0, beta1, lamb1, ax, ay Input("input-ay", "value"), Input("input-b", "value"), Input("dropdown-coors-type", "value"), - Input("button-calc-gha1", "n_clicks"), - Input("button-calc-gha2", "n_clicks"), + Input("tabs-GHA", "value"), + Input("calc-token-gha1", "data"), + Input("calc-token-gha2", "data"), Input("store-gha1-ana", "data"), Input("store-gha1-num", "data"), Input("store-gha1-stoch", "data"), @@ -912,43 +931,98 @@ def compute_gha2_approx(n2, cb_approx, ds_in, beta0, lamb0, beta1, lamb1, ax, ay Input("store-gha2-stoch", "data"), Input("store-gha2-approx", "data"), ) -def render_all(ax, ay, b, coords_type, - n_clicks_gha1, n_clicks_gha2, - store_gha1_ana, store_gha1_num, store_gha1_stoch, store_gha1_approx, - store_gha2_num, store_gha2_stoch, store_gha2_approx): +def render_all(ax, ay, b, coords_type, tab, t1, t2, + s1a, s1n, s1s, s1p, s2n, s2s, s2p): if None in (ax, ay, b): return go.Figure() - n1 = n_clicks_gha1 or 0 - n2 = n_clicks_gha2 or 0 - ell = EllipsoidTriaxial(ax, ay, b) fig = ellipsoid_figure(ell, title="") fig = figure_constant_lines(fig, ell, coords_type) - def add_from_store(fig, store, expected_calc_id): + def add_from_store(store): + nonlocal fig if not store: - return fig - if store.get("calc_id") != expected_calc_id: - return fig + return pts = store.get("points") or [] if pts: fig = figure_points(fig, pts) line = store.get("polyline") - name = store.get("name", "") if line: - fig = figure_lines(fig, line, name, store.get("color", "#ff8c00")) - return fig + fig = figure_lines(fig, line, store.get("name", ""), store.get("color", "#ff8c00")) - for st in (store_gha1_ana, store_gha1_num, store_gha1_stoch, store_gha1_approx): - fig = add_from_store(fig, st, n1) - - for st in (store_gha2_num, store_gha2_stoch, store_gha2_approx): - fig = add_from_store(fig, st, n2) + if tab == "tab-GHA1": + for st in (s1a, s1n, s1s, s1p): + add_from_store(st) + else: + for st in (s2n, s2s, s2p): + add_from_store(st) return fig +# Funktion zum Leeren des Plots bei Änderung des Ellipsoids +@app.callback( + Output("store-gha1-ana", "data", allow_duplicate=True), + Output("store-gha1-num", "data", allow_duplicate=True), + Output("store-gha1-stoch", "data", allow_duplicate=True), + Output("store-gha1-approx", "data", allow_duplicate=True), + Output("store-gha2-num", "data", allow_duplicate=True), + Output("store-gha2-stoch", "data", allow_duplicate=True), + Output("store-gha2-approx", "data", allow_duplicate=True), + Input("input-ax", "value"), + Input("input-ay", "value"), + Input("input-b", "value"), + prevent_initial_call=True, +) +def clear_all_stores_on_ellipsoid_change(ax, ay, b): + if None in (ax, ay, b): + return (no_update,)*7 + + return (None, None, None, None, None, None, None) + + +# Funktionen zur separaten Darstellung der Tabs +@app.callback( + Output("calc-token-gha1", "data"), + Output("output-gha1-ana", "children", allow_duplicate=True), + Output("output-gha1-num", "children", allow_duplicate=True), + Output("output-gha1-stoch", "children", allow_duplicate=True), + Output("output-gha1-approx", "children", allow_duplicate=True), + Output("store-gha1-ana", "data", allow_duplicate=True), + Output("store-gha1-num", "data", allow_duplicate=True), + Output("store-gha1-stoch", "data", allow_duplicate=True), + Output("store-gha1-approx", "data", allow_duplicate=True), + Input("button-calc-gha1", "n_clicks"), + State("calc-token-gha1", "data"), + prevent_initial_call=True, +) +def start_calc_gha1(n, token): + if not n: + raise PreventUpdate + token = (token or 0) + 1 + return token, "", "", "", "", None, None, None, None + + +@app.callback( + Output("calc-token-gha2", "data"), + Output("output-gha2-num", "children", allow_duplicate=True), + Output("output-gha2-stoch", "children", allow_duplicate=True), + Output("output-gha2-approx", "children", allow_duplicate=True), + Output("store-gha2-num", "data", allow_duplicate=True), + Output("store-gha2-stoch", "data", allow_duplicate=True), + Output("store-gha2-approx", "data", allow_duplicate=True), + Input("button-calc-gha2", "n_clicks"), + State("calc-token-gha2", "data"), + prevent_initial_call=True, +) +def start_calc_gha2(n, token): + if not n: + raise PreventUpdate + token = (token or 0) + 1 + return token, "", "", "", None, None, None + + if __name__ == "__main__": # Automatisiertes Öffnen der Seite im Browser