diff --git a/.gitignore b/.gitignore index 9af1340..e42318d 100644 --- a/.gitignore +++ b/.gitignore @@ -158,4 +158,5 @@ tech_tests.ipynb /pyquac/very_good.csv /pyquac/research.ipynb /examples/style.css - +/pyquac/random_example.py +/pyquac/example_data.csv diff --git a/examples/random_test.ipynb b/examples/random_test.ipynb index 0aa0464..6b1df1b 100644 --- a/examples/random_test.ipynb +++ b/examples/random_test.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -47,7 +47,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -65,7 +65,7 @@ " " ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -88,7 +88,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -577,7 +577,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.9.13 | packaged by conda-forge | (main, May 27 2022, 16:58:50) \n[GCC 10.3.0]" }, "vscode": { "interpreter": { diff --git a/pyproject.toml b/pyproject.toml index 0e4b807..d7f3977 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ PeakUtils = ">=1.3.3" matplotlib = "^3.5.2" dash-bootstrap-components = "^1.2.1" dash-bootstrap-templates = "^1.0.7" +dash-daq = "^0.5.0" [tool.poetry.dev-dependencies] pytest = "^7.1.2" diff --git a/pyquac/components/heatmap.py b/pyquac/components/heatmap.py index 3d2e58c..a397085 100644 --- a/pyquac/components/heatmap.py +++ b/pyquac/components/heatmap.py @@ -3,22 +3,22 @@ """ from dash.dependencies import Input, Output -import plotly.graph_objects as go from dash import dcc, callback - +import plotly.graph_objects as go +from pyquac.settings import settings AXIS_SIZE = 13 GRAPH_STYLE = { "position": "fixed", - "top": "100px", + "top": "110px", "left": "16rem", "bottom": 0, "width": "45rem", "height": "37rem", # "z-index": 1, "overflow-x": "hidden", - "transition": "all 0.5s", + "transition": settings.transition_time, # "transition-delay": "width 500ms", # "transition-property": "margin-right", # "padding": "0.5rem 1rem", @@ -26,63 +26,142 @@ GRAPH_HIDEN = { "position": "fixed", - "top": "100px", + "top": "110px", "left": 0, "bottom": 0, + "right": 0, "width": "45rem", # "width": "70rem", "height": "37rem", # "z-index": 1, - "overflow-x": "hidden", - "transition": "all 0.5s", + # "overflow-x": "hidden", + "transition": settings.transition_time, # "transition-delay": "500ms", # "transition-property": "margin-right", # "padding": "0.5rem 1rem", } +def define_figure( + z, + x, + y, + x_axis_title: str, + y_axis_title: str, + cmap: str, +): + """sets figure layout + + Args: + data (Spectroscopy): spectroscopy-like object + + Returns: + go.gigure: plotly figure + """ + fig = go.Figure(data=go.Heatmap(z=z, x=x, y=y, colorscale=cmap)) + + fig.update_layout( + xaxis_title=x_axis_title, + yaxis_title=y_axis_title, + autosize=False, + separators=".", + ) + + fig.update_yaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) + fig.update_xaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) + fig.update_layout(yaxis=dict(showexponent="none", exponentformat="e")) + fig.update_traces(zhoverformat=".2f") + fig.update_layout(width=650, height=550) + return fig + + +def define_figure_extend( + z, + x, + y, + x_axis_title: str, + y_axis_title: str, + cmap: str, +): + """sets figure layout + + Args: + data (Spectroscopy): spectroscopy-like object + + Returns: + go.gigure: plotly figure + """ + fig = go.Figure(data=go.Heatmap(z=z, x=x, y=y, colorscale=cmap)) + + fig.add_trace(go.Scatter(x=None, y=None, mode="lines", xaxis="x2")) + + fig.update_layout( + xaxis_title=x_axis_title, + yaxis_title=y_axis_title, + autosize=False, + separators=".", + ) + + fig.update_layout( + autosize=False, + xaxis=dict(zeroline=False, domain=[0, 0.60], showgrid=False), + # yaxis=dict(zeroline=False, domain=[0, 0.85], showgrid=False), + xaxis2=dict(zeroline=False, domain=[0.60, 1], showgrid=False), + # yaxis2=dict(zeroline=False, domain=[0.85, 1], showgrid=False), + bargap=0, + hovermode="closest", + ) + + fig.update_yaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) + fig.update_xaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) + fig.update_layout(yaxis=dict(showexponent="none", exponentformat="e")) + # fig.update_traces(zhoverformat=".2f") + fig.update_layout(width=850, height=550) + return fig + + def figure_layout( data, - x_axis_title: str = "Voltages, V", - y_axis_title: str = "Frequencies, GHz", - cmap: str = "rdylbu", + x_axis_title: str, + y_axis_title: str, + cmap: str, ): """constructor for heatmap layout Args: data (pandas DataFrame): DataFrame with the columns in the following order: [x, y, z] """ - - def define_figure(data): - fig = go.Figure( - data=go.Heatmap( - z=data.iloc[:, 2], x=data.iloc[:, 0], y=data.iloc[:, 1], colorscale=cmap - ) - ) - fig.update_layout( - xaxis_title=x_axis_title, - yaxis_title=y_axis_title, - autosize=False, - separators=".", - ) - - fig.update_yaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) - fig.update_xaxes(title_font={"size": AXIS_SIZE}, tickfont_size=AXIS_SIZE) - fig.update_layout(yaxis=dict(showexponent="none", exponentformat="e")) - fig.update_traces(zhoverformat=".2f") - fig.update_layout(width=650, height=550) - return fig - - figure = dcc.Graph(id="heatmap", figure=define_figure(data), style=GRAPH_STYLE) + z = data.njit_result + x = data.x_1d + y = data.y_1d + figure = dcc.Graph( + id="heatmap", + figure=define_figure( + z=z, + x=x, + y=y, + x_axis_title=x_axis_title, + y_axis_title=y_axis_title, + cmap=cmap, + ), + style=GRAPH_STYLE, + ) return figure @callback( Output("heatmap", "style"), + Output("heatmap", "figure"), Input("btn_sidebar", "n_clicks"), Input("side_click", "data"), + Input("z_store", "data"), + Input("x_store", "data"), + Input("y_store", "data"), + Input("x_label", "data"), + Input("y_label", "data"), + Input("cmap", "data"), ) -def toggle_graph(n, nclick): +def toggle_graph(n, nclick, z, x, y, x_label, y_label, cmap): """function to hide and reveal sidebar Args: @@ -92,12 +171,49 @@ def toggle_graph(n, nclick): Returns: dict: style objects """ + fig = define_figure( + z, + x, + y, + x_label, + y_label, + cmap, + ) if n: if nclick == "SHOW": graph_style = GRAPH_STYLE + fig = define_figure( + z, + x, + y, + x_label, + y_label, + cmap, + ) else: graph_style = GRAPH_HIDEN + fig = define_figure_extend( + z, + x, + y, + x_label, + y_label, + cmap, + ) else: graph_style = GRAPH_STYLE - return graph_style + return graph_style, fig + + +# @callback( +# Output("heatmap", "figure"), +# Input("interval-graph-update", "n_intervals"), +# Input("z_store", "data"), +# State("heatmap", "figure"), +# ) +# def update_graph(i, z, fig): + +# if i == 0: +# raise PreventUpdate +# return go.Figure(fig).update_traces(z=z) diff --git a/pyquac/components/property_nav.py b/pyquac/components/property_nav.py index ca50aa5..3a9ddeb 100644 --- a/pyquac/components/property_nav.py +++ b/pyquac/components/property_nav.py @@ -7,7 +7,10 @@ from dash.dependencies import Input, Output from dash import callback +from dash.exceptions import PreventUpdate import dash_bootstrap_components as dbc +import dash_daq as daq +from pyquac.settings import settings PROPERTY_NAV_STYLE = { "position": "fixed", @@ -18,7 +21,7 @@ # "height": "100%", "z-index": 1, "overflow-x": "hidden", - "transition": "all 0.5s", + "transition": settings.transition_time, "padding": "0.5rem 1rem", } @@ -31,32 +34,39 @@ # "height": "100%", "z-index": 1, "overflow-x": "hidden", - "transition": "all 0.5s", + "transition": settings.transition_time, "padding": "0.5rem 1rem", } property_nav = dbc.Nav( [ dbc.NavItem(dbc.Label("Status")), - dbc.NavItem( - dbc.Spinner(color="danger", type="grow", size="sm"), - style={"margin-left": "0.5rem"}, - ), + # dbc.NavItem( + # dbc.Spinner(color="danger", type="grow", size="sm"), + # style={"margin-left": "0.5rem"}, + # ), dbc.NavItem( dbc.Label("Heatmap updating"), style={"margin-left": "2rem"}, ), dbc.NavItem( - dbc.Checklist( - options=[ - {"label": "Pause", "value": 1}, - ], - value=[], - id="switches-inline-input", - inline=True, - switch=True, - ), + [ + daq.BooleanSwitch(id="interval-switches", on=True, color="#3459e6"), + ], style={"margin-left": "0.5rem"}, ), + dbc.NavItem( + dbc.Input( + id="update-interval-value", + type="number", + min=800, + step=1, + placeholder="Update graph in... ms", + disabled=False, + html_size=16, + size="sm", + ), + style={"margin-left": "1rem"}, + ), ], id="property-nav", style=PROPERTY_NAV_STYLE, @@ -87,3 +97,38 @@ def toggle_nav(n, nclick): nav_style = PROPERTY_NAV_STYLE return nav_style + + +@callback( + Output("interval-graph-update", "max_intervals"), Input("interval-switches", "on") +) +def toggle_checklist(switch_state): + """function to change max interval property + + Args: + n (_type_): _description_ + max_interval (_type_): _description_ + + Returns: + _type_: _description_ + """ + + if switch_state is True: + new_max_interval = -1 + print("I will update") + else: + new_max_interval = 0 + print("I stop updating") + return new_max_interval + + +@callback( + Output("interval-graph-update", "interval"), Input("update-interval-value", "value") +) +def change_interval_update(new_interval): + + if new_interval is not None: + print(f"HEY! now I update in {new_interval} ms") + return new_interval + else: + raise PreventUpdate diff --git a/pyquac/components/sidebar.py b/pyquac/components/sidebar.py index c311ff2..06d7a42 100644 --- a/pyquac/components/sidebar.py +++ b/pyquac/components/sidebar.py @@ -8,6 +8,7 @@ from dash import html from dash import callback import dash_bootstrap_components as dbc +from pyquac.settings import settings # the style arguments for the sidebar. We use position:fixed and a fixed width SIDEBAR_STYLE = { @@ -19,7 +20,7 @@ "height": "100%", "z-index": 1, "overflow-x": "hidden", - "transition": "all 0.5s", + "transition": settings.transition_time, # "transition-delay": "500ms", "padding": "0.5rem 1rem", "background-color": "#f8f9fa", @@ -34,7 +35,7 @@ "height": "100%", "z-index": 1, "overflow-x": "hidden", - "transition": "all 0.5s", + "transition": settings.transition_time, "padding": "0.5rem 1rem", "background-color": "#f8f9fa", } diff --git a/pyquac/datatools.py b/pyquac/datatools.py index a3014c8..91e2976 100644 --- a/pyquac/datatools.py +++ b/pyquac/datatools.py @@ -625,7 +625,9 @@ def njit_result(self): return array_to_process else: - pass + array_to_process = np.zeros(len(self.x_list) * len(self.y_list)) + array_to_process[:] = np.nan + return array_to_process def xyz_peak( self, diff --git a/pyquac/fmn_app.py b/pyquac/fmn_app.py index 0bc1523..d3630f1 100644 --- a/pyquac/fmn_app.py +++ b/pyquac/fmn_app.py @@ -4,7 +4,8 @@ This is where we define the various css items to fetch as well as the layout of our application. """ -from dash import dcc +from dash import dcc, callback +from dash.dependencies import Input, Output from dash import html import dash_bootstrap_components as dbc from dash_bootstrap_templates import load_figure_template @@ -30,12 +31,7 @@ ########################## App Layout ########################## -def conf_app( - data, - x_axis_title: str = "Voltages, V", - y_axis_title: str = "Frequencies, GHz", - cmap: str = "rdylbu", -): +def conf_app(data): def serve_layout(): """Define the layout of the application @@ -45,6 +41,20 @@ def serve_layout(): return html.Div( children=[ dcc.Store(id="side_click", data="SHOW"), + dcc.Store(id="z_store", data=data.njit_result), + dcc.Store(id="x_store", data=data.x_1d), + dcc.Store(id="y_store", data=data.y_1d), + # dcc.Store(id="update_interval", data=settings.init_interval), + # dcc.Store(id="max_interval_value", data=settings.init_max_interval), + dcc.Store(id="x_label", data=settings.init_x_label), + dcc.Store(id="y_label", data=settings.init_y_label), + dcc.Store(id="cmap", data=settings.init_cmap), + dcc.Interval( + id="interval-graph-update", + interval=settings.init_interval, + n_intervals=0, + max_intervals=settings.init_max_interval, + ), dbc.Row( [ dbc.Col(navbar), @@ -63,7 +73,10 @@ def serve_layout(): dbc.Row( children=[ figure_layout( - data, x_axis_title, y_axis_title, cmap + data, + settings.init_x_label, + settings.init_y_label, + settings.init_cmap, ), ] ), @@ -74,6 +87,10 @@ def serve_layout(): ] ) + @callback(Output("z_store", "data"), Input("interval-graph-update", "n_intervals")) + def update_fig_data(i): + return data.njit_result + app.layout = serve_layout return app diff --git a/pyquac/settings.py b/pyquac/settings.py index 700f99f..0aa22d0 100644 --- a/pyquac/settings.py +++ b/pyquac/settings.py @@ -20,6 +20,14 @@ class Settings(BaseSettings): app_link: str = r"https://fmn.bmstu.ru/" app_linkedin_url: str = r"https://fmn.bmstu.ru/" app_github_url: str = r"https://github.com/ikaryss/pyquac" + transition_time: str = "all 0.1s" + + # App settings + init_interval: int = 3000 + init_max_interval: int = -1 + init_x_label: str = "Voltages, V" + init_y_label: str = "Frequencies, GHz" + init_cmap: str = "rdylbu" class Config: """