From 27cea51bb1c4dc37ddbb51f46bd0ba88e9ab4dfd Mon Sep 17 00:00:00 2001 From: c-bata Date: Sat, 5 Mar 2022 12:44:18 +0900 Subject: [PATCH 1/4] Add Dark Mode support --- optuna_dashboard/_app.py | 5 +++ optuna_dashboard/static/components/App.tsx | 38 +++++++++++++------ .../static/components/GraphEdf.tsx | 2 + .../static/components/GraphHistory.tsx | 2 + .../GraphHyperparameterImportances.tsx | 2 + .../components/GraphIntermediateValues.tsx | 2 + .../components/GraphParallelCoordinate.tsx | 4 +- .../static/components/GraphParetoFront.tsx | 2 + .../static/components/GraphSlice.tsx | 2 + .../static/components/PlotlyDarkMode.ts | 26 +++++++++++++ 10 files changed, 72 insertions(+), 13 deletions(-) create mode 100644 optuna_dashboard/static/components/PlotlyDarkMode.ts diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 47320fb8..f92eedaa 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -61,6 +61,11 @@ INDEX_HTML = """ margin: 0; padding: 0; } + @media (prefers-color-scheme: dark) { + body { + background-color: #121212; + } + } diff --git a/optuna_dashboard/static/components/App.tsx b/optuna_dashboard/static/components/App.tsx index e0c3ae5d..2488d3c6 100644 --- a/optuna_dashboard/static/components/App.tsx +++ b/optuna_dashboard/static/components/App.tsx @@ -1,25 +1,39 @@ -import React, { FC } from "react" +import React, { FC, useMemo } from "react" import { RecoilRoot } from "recoil" import { BrowserRouter as Router, Switch, Route } from "react-router-dom" import { SnackbarProvider } from "notistack" import { StudyDetail } from "./StudyDetail" import { StudyList } from "./StudyList" +import {createTheme, useMediaQuery, ThemeProvider} from "@mui/material" export const App: FC = () => { + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + const theme = useMemo( + () => + createTheme({ + palette: { + mode: prefersDarkMode ? 'dark' : 'light', + }, + }), + [prefersDarkMode], + ); + return ( - - - - } - /> - } /> - - - + + + + + } + /> + } /> + + + + ) } diff --git a/optuna_dashboard/static/components/GraphEdf.tsx b/optuna_dashboard/static/components/GraphEdf.tsx index 57cb81fc..9aa2b07d 100644 --- a/optuna_dashboard/static/components/GraphEdf.tsx +++ b/optuna_dashboard/static/components/GraphEdf.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-edf" @@ -95,6 +96,7 @@ const plotEdf = (study: StudyDetail, objectiveId: number) => { r: 50, b: 50, }, + template: plotlyDarkTemplate, } const values = completedTrials.map((t) => target(t)) diff --git a/optuna_dashboard/static/components/GraphHistory.tsx b/optuna_dashboard/static/components/GraphHistory.tsx index 686dd75b..7adb030c 100644 --- a/optuna_dashboard/static/components/GraphHistory.tsx +++ b/optuna_dashboard/static/components/GraphHistory.tsx @@ -15,6 +15,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-history" @@ -191,6 +192,7 @@ const plotHistory = ( type: xAxis === "number" ? "linear" : "date", }, showlegend: false, + template: plotlyDarkTemplate, } let filteredTrials = study.trials.filter( diff --git a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx index 296f71be..c7ea5b94 100644 --- a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx +++ b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx @@ -12,6 +12,7 @@ import { } from "@mui/material" import { getParamImportances } from "../apiClient" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-hyperparameter-importances" // To match colors used by plot_param_importances in optuna. @@ -131,6 +132,7 @@ const plotParamImportances = (paramsImportanceData: ParamImportances) => { b: 50, }, showlegend: false, + template: plotlyDarkTemplate, } const plotData: Partial[] = [ diff --git a/optuna_dashboard/static/components/GraphIntermediateValues.tsx b/optuna_dashboard/static/components/GraphIntermediateValues.tsx index 28bc3b0f..7fcca521 100644 --- a/optuna_dashboard/static/components/GraphIntermediateValues.tsx +++ b/optuna_dashboard/static/components/GraphIntermediateValues.tsx @@ -1,6 +1,7 @@ import * as plotly from "plotly.js-dist" import React, { FC, useEffect } from "react" import { Grid, Typography } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-intermediate-values" @@ -39,6 +40,7 @@ const plotIntermediateValue = (trials: Trial[]) => { r: 50, b: 0, }, + template: plotlyDarkTemplate, } if (trials.length === 0) { plotly.react(plotDomId, [], layout) diff --git a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx index 5c43b524..5612e9b7 100644 --- a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx +++ b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-parallel-coordinate" @@ -34,7 +35,7 @@ export const GraphParallelCoordinate: FC<{ - Parallel coordinate + Parallel Coordinate {study !== null && study.directions.length !== 1 ? ( { r: 50, b: 0, }, + template: plotlyDarkTemplate, } if (study.trials.length === 0) { diff --git a/optuna_dashboard/static/components/GraphParetoFront.tsx b/optuna_dashboard/static/components/GraphParetoFront.tsx index c60f450a..bd8a26c6 100644 --- a/optuna_dashboard/static/components/GraphParetoFront.tsx +++ b/optuna_dashboard/static/components/GraphParetoFront.tsx @@ -10,6 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-pareto-front" @@ -100,6 +101,7 @@ const plotParetoFront = ( r: 50, b: 0, }, + template: plotlyDarkTemplate, } const trials: Trial[] = study ? study.trials : [] diff --git a/optuna_dashboard/static/components/GraphSlice.tsx b/optuna_dashboard/static/components/GraphSlice.tsx index 98d99050..1fb131c2 100644 --- a/optuna_dashboard/static/components/GraphSlice.tsx +++ b/optuna_dashboard/static/components/GraphSlice.tsx @@ -12,6 +12,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" +import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-slice" @@ -163,6 +164,7 @@ const plotSlice = ( }, plot_bgcolor: "#E5ecf6", showlegend: false, + template: plotlyDarkTemplate, } const filteredTrials = trials.filter( diff --git a/optuna_dashboard/static/components/PlotlyDarkMode.ts b/optuna_dashboard/static/components/PlotlyDarkMode.ts new file mode 100644 index 00000000..c715ede8 --- /dev/null +++ b/optuna_dashboard/static/components/PlotlyDarkMode.ts @@ -0,0 +1,26 @@ +import * as plotly from "plotly.js-dist" + +export const plotlyDarkTemplate: Partial = { + layout: { + "font": {"color": "#f2f5fa"}, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "zerolinecolor": "#283442", + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "zerolinecolor": "#283442", + }, + "ternary": { + "aaxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, + "baxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, + "caxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, + "bgcolor": "rgb(17,17,17)" + }, + "colorway": ["#636efa", "#EF553B", "#00cc96", "#ab63fa", "#19d3f3", "#e763fa", "#fecb52", "#ffa15a", "#ff6692", "#b6e880"], + "plot_bgcolor": "#222222", + "paper_bgcolor": "#222222", + }, +} From af3ea02dc603007874c6d537248301e020b0d325 Mon Sep 17 00:00:00 2001 From: c-bata Date: Sat, 5 Mar 2022 12:53:54 +0900 Subject: [PATCH 2/4] Embed plotly_dark Template --- .../static/components/GraphSlice.tsx | 25 ---------------- .../static/components/PlotlyDarkMode.ts | 30 ++++--------------- 2 files changed, 6 insertions(+), 49 deletions(-) diff --git a/optuna_dashboard/static/components/GraphSlice.tsx b/optuna_dashboard/static/components/GraphSlice.tsx index 1fb131c2..dab8700c 100644 --- a/optuna_dashboard/static/components/GraphSlice.tsx +++ b/optuna_dashboard/static/components/GraphSlice.tsx @@ -143,26 +143,19 @@ const plotSlice = ( xaxis: { title: selected || "", type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", zerolinewidth: 1.5, - linecolor: "#f2f5fa", linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, automargin: true, }, yaxis: { title: "Objective Values", type: logYScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", zerolinewidth: 2, - linecolor: "#f2f5fa", linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, automargin: true, }, - plot_bgcolor: "#E5ecf6", showlegend: false, template: plotlyDarkTemplate, } @@ -197,23 +190,14 @@ const plotSlice = ( x: valuesNum, y: objectiveValues, mode: "markers", - marker: { - color: "#185799", - }, }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", zerolinewidth: 1.5, - linecolor: "#f2f5fa", linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, - tickfont: { - color: "#000000", - }, automargin: true, // Otherwise the label is outside of the plot } plotly.react(plotDomId, trace, layout) @@ -230,23 +214,14 @@ const plotSlice = ( x: valuesCategorical, y: objectiveValues, mode: "markers", - marker: { - color: "#185799", - }, }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinecolor: "#f2f5fa", zerolinewidth: 1.5, - linecolor: "#f2f5fa", linewidth: 5, - gridcolor: "#f2f5fa", gridwidth: 1, - tickfont: { - color: "#000000", - }, tickvals: tickvals, ticktext: vocabArr, automargin: true, // Otherwise the label is outside of the plot diff --git a/optuna_dashboard/static/components/PlotlyDarkMode.ts b/optuna_dashboard/static/components/PlotlyDarkMode.ts index c715ede8..f7d4d966 100644 --- a/optuna_dashboard/static/components/PlotlyDarkMode.ts +++ b/optuna_dashboard/static/components/PlotlyDarkMode.ts @@ -1,26 +1,8 @@ import * as plotly from "plotly.js-dist" -export const plotlyDarkTemplate: Partial = { - layout: { - "font": {"color": "#f2f5fa"}, - "xaxis": { - "gridcolor": "#283442", - "linecolor": "#506784", - "zerolinecolor": "#283442", - }, - "yaxis": { - "gridcolor": "#283442", - "linecolor": "#506784", - "zerolinecolor": "#283442", - }, - "ternary": { - "aaxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, - "baxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, - "caxis": {"ticks": "", "gridcolor": "#506784", "linecolor": "#506784"}, - "bgcolor": "rgb(17,17,17)" - }, - "colorway": ["#636efa", "#EF553B", "#00cc96", "#ab63fa", "#19d3f3", "#e763fa", "#fecb52", "#ffa15a", "#ff6692", "#b6e880"], - "plot_bgcolor": "#222222", - "paper_bgcolor": "#222222", - }, -} +// Following template is extracted from the sdist of plotly Python library. +// See https://github.com/plotly/plotly.py/blob/v5.6.0/packages/python/plotly/templategen/__init__.py and +// https://github.com/plotly/plotly.py/blob/v5.6.0/packages/python/plotly/templategen/definitions.py + +//@ts-ignore +export const plotlyDarkTemplate: Partial = {"data":{"bar":[{"error_x":{"color":"#f2f5fa"},"error_y":{"color":"#f2f5fa"},"marker":{"line":{"color":"rgb(17,17,17)","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"barpolar":[{"marker":{"line":{"color":"rgb(17,17,17)","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"carpet":[{"aaxis":{"endlinecolor":"#A2B1C6","gridcolor":"#506784","linecolor":"#506784","minorgridcolor":"#506784","startlinecolor":"#A2B1C6"},"baxis":{"endlinecolor":"#A2B1C6","gridcolor":"#506784","linecolor":"#506784","minorgridcolor":"#506784","startlinecolor":"#A2B1C6"},"type":"carpet"}],"choropleth":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"choropleth"}],"contour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"contour"}],"contourcarpet":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"contourcarpet"}],"heatmap":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmap"}],"heatmapgl":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"heatmapgl"}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"histogram2d":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2d"}],"histogram2dcontour":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"histogram2dcontour"}],"mesh3d":[{"colorbar":{"outlinewidth":0,"ticks":""},"type":"mesh3d"}],"parcoords":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"parcoords"}],"pie":[{"automargin":true,"type":"pie"}],"scatter":[{"marker":{"line":{"color":"#283442"}},"type":"scatter"}],"scatter3d":[{"line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatter3d"}],"scattercarpet":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattercarpet"}],"scattergeo":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattergeo"}],"scattergl":[{"marker":{"line":{"color":"#283442"}},"type":"scattergl"}],"scattermapbox":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scattermapbox"}],"scatterpolar":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolar"}],"scatterpolargl":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterpolargl"}],"scatterternary":[{"marker":{"colorbar":{"outlinewidth":0,"ticks":""}},"type":"scatterternary"}],"surface":[{"colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"type":"surface"}],"table":[{"cells":{"fill":{"color":"#506784"},"line":{"color":"rgb(17,17,17)"}},"header":{"fill":{"color":"#2a3f5f"},"line":{"color":"rgb(17,17,17)"}},"type":"table"}]},"layout":{"annotationdefaults":{"arrowcolor":"#f2f5fa","arrowhead":0,"arrowwidth":1},"autotypenumbers":"strict","coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]],"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]},"colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#f2f5fa"},"geo":{"bgcolor":"rgb(17,17,17)","lakecolor":"rgb(17,17,17)","landcolor":"rgb(17,17,17)","showlakes":true,"showland":true,"subunitcolor":"#506784"},"hoverlabel":{"align":"left"},"hovermode":"closest","mapbox":{"style":"dark"},"paper_bgcolor":"rgb(17,17,17)","plot_bgcolor":"rgb(17,17,17)","polar":{"angularaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"bgcolor":"rgb(17,17,17)","radialaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""}},"scene":{"xaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"},"yaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"},"zaxis":{"backgroundcolor":"rgb(17,17,17)","gridcolor":"#506784","gridwidth":2,"linecolor":"#506784","showbackground":true,"ticks":"","zerolinecolor":"#C8D4E3"}},"shapedefaults":{"line":{"color":"#f2f5fa"}},"sliderdefaults":{"bgcolor":"#C8D4E3","bordercolor":"rgb(17,17,17)","borderwidth":1,"tickwidth":0},"ternary":{"aaxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"baxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""},"bgcolor":"rgb(17,17,17)","caxis":{"gridcolor":"#506784","linecolor":"#506784","ticks":""}},"title":{"x":0.05},"updatemenudefaults":{"bgcolor":"#506784","borderwidth":0},"xaxis":{"automargin":true,"gridcolor":"#283442","linecolor":"#506784","ticks":"","title":{"standoff":15},"zerolinecolor":"#283442","zerolinewidth":2},"yaxis":{"automargin":true,"gridcolor":"#283442","linecolor":"#506784","ticks":"","title":{"standoff":15},"zerolinecolor":"#283442","zerolinewidth":2}}} From de54417553b140310cd7df21c96b7669a2448ac4 Mon Sep 17 00:00:00 2001 From: c-bata Date: Sat, 5 Mar 2022 13:52:40 +0900 Subject: [PATCH 3/4] Switch template by Theme --- optuna_dashboard/static/components/GraphEdf.tsx | 8 ++++---- .../static/components/GraphHistory.tsx | 9 ++++++--- .../GraphHyperparameterImportances.tsx | 8 ++++---- .../components/GraphIntermediateValues.tsx | 11 ++++++----- .../components/GraphParallelCoordinate.tsx | 8 ++++---- .../static/components/GraphParetoFront.tsx | 9 +++++---- .../static/components/GraphSlice.tsx | 17 +++++------------ .../static/components/StudyList.tsx | 8 ++++++-- 8 files changed, 40 insertions(+), 38 deletions(-) diff --git a/optuna_dashboard/static/components/GraphEdf.tsx b/optuna_dashboard/static/components/GraphEdf.tsx index 9aa2b07d..3d87d325 100644 --- a/optuna_dashboard/static/components/GraphEdf.tsx +++ b/optuna_dashboard/static/components/GraphEdf.tsx @@ -26,9 +26,9 @@ export const Edf: FC<{ useEffect(() => { if (study != null) { - plotEdf(study, objectiveId) + plotEdf(study, objectiveId, theme.palette.mode) } - }, [study, objectiveId]) + }, [study, objectiveId, theme.palette.mode]) return ( @@ -64,7 +64,7 @@ export const Edf: FC<{ ) } -const plotEdf = (study: StudyDetail, objectiveId: number) => { +const plotEdf = (study: StudyDetail, objectiveId: number, mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -96,7 +96,7 @@ const plotEdf = (study: StudyDetail, objectiveId: number) => { r: 50, b: 50, }, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const values = completedTrials.map((t) => target(t)) diff --git a/optuna_dashboard/static/components/GraphHistory.tsx b/optuna_dashboard/static/components/GraphHistory.tsx index 7adb030c..bcd635eb 100644 --- a/optuna_dashboard/static/components/GraphHistory.tsx +++ b/optuna_dashboard/static/components/GraphHistory.tsx @@ -60,7 +60,8 @@ export const GraphHistory: FC<{ xAxis, logScale, filterCompleteTrial, - filterPrunedTrial + filterPrunedTrial, + theme.palette.mode ) } }, [ @@ -70,6 +71,7 @@ export const GraphHistory: FC<{ xAxis, filterPrunedTrial, filterCompleteTrial, + theme.palette.mode, ]) return ( @@ -172,7 +174,8 @@ const plotHistory = ( xAxis: string, logScale: boolean, filterCompleteTrial: boolean, - filterPrunedTrial: boolean + filterPrunedTrial: boolean, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -192,7 +195,7 @@ const plotHistory = ( type: xAxis === "number" ? "linear" : "date", }, showlegend: false, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } let filteredTrials = study.trials.filter( diff --git a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx index c7ea5b94..28263427 100644 --- a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx +++ b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx @@ -60,13 +60,13 @@ export const GraphHyperparameterImportances: FC<{ studyId, objectiveId ) - plotParamImportances(paramsImportanceData) + plotParamImportances(paramsImportanceData, theme.palette.mode) } if (numOfTrials > 0) { fetchAndPlotParamImportances(studyId, objectiveId) } - }, [numOfTrials, objectiveId]) + }, [numOfTrials, objectiveId, theme.palette.mode]) return ( @@ -103,7 +103,7 @@ export const GraphHyperparameterImportances: FC<{ ) } -const plotParamImportances = (paramsImportanceData: ParamImportances) => { +const plotParamImportances = (paramsImportanceData: ParamImportances, mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -132,7 +132,7 @@ const plotParamImportances = (paramsImportanceData: ParamImportances) => { b: 50, }, showlegend: false, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const plotData: Partial[] = [ diff --git a/optuna_dashboard/static/components/GraphIntermediateValues.tsx b/optuna_dashboard/static/components/GraphIntermediateValues.tsx index 7fcca521..475a11dd 100644 --- a/optuna_dashboard/static/components/GraphIntermediateValues.tsx +++ b/optuna_dashboard/static/components/GraphIntermediateValues.tsx @@ -1,6 +1,6 @@ import * as plotly from "plotly.js-dist" import React, { FC, useEffect } from "react" -import { Grid, Typography } from "@mui/material" +import {Grid, Typography, useTheme} from "@mui/material" import {plotlyDarkTemplate} from "./PlotlyDarkMode"; const plotDomId = "graph-intermediate-values" @@ -8,9 +8,10 @@ const plotDomId = "graph-intermediate-values" export const GraphIntermediateValues: FC<{ trials: Trial[] }> = ({ trials = [] }) => { + const theme = useTheme() useEffect(() => { - plotIntermediateValue(trials) - }, [trials]) + plotIntermediateValue(trials, theme.palette.mode) + }, [trials, theme.palette.mode]) return ( @@ -28,7 +29,7 @@ export const GraphIntermediateValues: FC<{ ) } -const plotIntermediateValue = (trials: Trial[]) => { +const plotIntermediateValue = (trials: Trial[], mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -40,7 +41,7 @@ const plotIntermediateValue = (trials: Trial[]) => { r: 50, b: 0, }, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } if (trials.length === 0) { plotly.react(plotDomId, [], layout) diff --git a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx index 5612e9b7..29a90862 100644 --- a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx +++ b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx @@ -26,9 +26,9 @@ export const GraphParallelCoordinate: FC<{ useEffect(() => { if (study !== null) { - plotCoordinate(study, objectiveId) + plotCoordinate(study, objectiveId, theme.palette.mode) } - }, [study, objectiveId]) + }, [study, objectiveId, theme.palette.mode]) return ( @@ -65,7 +65,7 @@ export const GraphParallelCoordinate: FC<{ ) } -const plotCoordinate = (study: StudyDetail, objectiveId: number) => { +const plotCoordinate = (study: StudyDetail, objectiveId: number, mode: string) => { if (document.getElementById(plotDomId) === null) { return } @@ -77,7 +77,7 @@ const plotCoordinate = (study: StudyDetail, objectiveId: number) => { r: 50, b: 0, }, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } if (study.trials.length === 0) { diff --git a/optuna_dashboard/static/components/GraphParetoFront.tsx b/optuna_dashboard/static/components/GraphParetoFront.tsx index bd8a26c6..8482e19e 100644 --- a/optuna_dashboard/static/components/GraphParetoFront.tsx +++ b/optuna_dashboard/static/components/GraphParetoFront.tsx @@ -31,9 +31,9 @@ export const GraphParetoFront: FC<{ useEffect(() => { if (study != null) { - plotParetoFront(study, objectiveXId, objectiveYId) + plotParetoFront(study, objectiveXId, objectiveYId, theme.palette.mode) } - }, [study, objectiveXId, objectiveYId]) + }, [study, objectiveXId, objectiveYId, theme.palette.mode]) return ( @@ -88,7 +88,8 @@ export const GraphParetoFront: FC<{ const plotParetoFront = ( study: StudyDetail, objectiveXId: number, - objectiveYId: number + objectiveYId: number, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -101,7 +102,7 @@ const plotParetoFront = ( r: 50, b: 0, }, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const trials: Trial[] = study ? study.trials : [] diff --git a/optuna_dashboard/static/components/GraphSlice.tsx b/optuna_dashboard/static/components/GraphSlice.tsx index dab8700c..84ba0e67 100644 --- a/optuna_dashboard/static/components/GraphSlice.tsx +++ b/optuna_dashboard/static/components/GraphSlice.tsx @@ -39,8 +39,8 @@ export const GraphSlice: FC<{ } useEffect(() => { - plotSlice(trials, objectiveId, selected, logXScale, logYScale) - }, [trials, objectiveId, selected, logXScale, logYScale]) + plotSlice(trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode) + }, [trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode]) const handleObjectiveChange = (event: SelectChangeEvent) => { setObjectiveId(event.target.value as number) @@ -127,7 +127,8 @@ const plotSlice = ( objectiveId: number, selected: string | null, logXScale: boolean, - logYScale: boolean + logYScale: boolean, + mode: string ) => { if (document.getElementById(plotDomId) === null) { return @@ -143,21 +144,17 @@ const plotSlice = ( xaxis: { title: selected || "", type: logXScale ? "log" : "linear", - zerolinewidth: 1.5, - linewidth: 5, gridwidth: 1, automargin: true, }, yaxis: { title: "Objective Values", type: logYScale ? "log" : "linear", - zerolinewidth: 2, - linewidth: 5, gridwidth: 1, automargin: true, }, showlegend: false, - template: plotlyDarkTemplate, + template: mode === "dark" ? plotlyDarkTemplate : {}, } const filteredTrials = trials.filter( @@ -195,8 +192,6 @@ const plotSlice = ( layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinewidth: 1.5, - linewidth: 5, gridwidth: 1, automargin: true, // Otherwise the label is outside of the plot } @@ -219,8 +214,6 @@ const plotSlice = ( layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", - zerolinewidth: 1.5, - linewidth: 5, gridwidth: 1, tickvals: tickvals, ticktext: vocabArr, diff --git a/optuna_dashboard/static/components/StudyList.tsx b/optuna_dashboard/static/components/StudyList.tsx index 8f428450..2cfbd659 100644 --- a/optuna_dashboard/static/components/StudyList.tsx +++ b/optuna_dashboard/static/components/StudyList.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect } from "react" +import React, {FC, useEffect, useMemo} from "react" import { useRecoilValue } from "recoil" import { Link } from "react-router-dom" import { @@ -55,6 +55,10 @@ export const StudyList: FC = () => { const [directions, setDirections] = React.useState([ "minimize", ]) + const linkColor= useMemo( + () => theme.palette.mode === 'dark' ? theme.palette.primary.light : theme.palette.primary.dark, + [theme.palette.mode], + ); const action = actionCreator() const studies = useRecoilValue(studySummariesState) @@ -78,7 +82,7 @@ export const StudyList: FC = () => { label: "Name", sortable: true, toCellValue: (i) => ( - + {studies[i].study_name} ), From fa8bd05f25787e32bcfea7335af84e300074f437 Mon Sep 17 00:00:00 2001 From: c-bata Date: Sat, 5 Mar 2022 17:10:59 +0900 Subject: [PATCH 4/4] Toggle DarkMode with Button --- .prettierignore | 1 + optuna_dashboard/_app.py | 5 -- optuna_dashboard/static/components/App.tsx | 51 +++++++++++++------ .../static/components/GraphEdf.tsx | 2 +- .../static/components/GraphHistory.tsx | 4 +- .../GraphHyperparameterImportances.tsx | 7 ++- .../components/GraphIntermediateValues.tsx | 4 +- .../components/GraphParallelCoordinate.tsx | 8 ++- .../static/components/GraphParetoFront.tsx | 2 +- .../static/components/GraphSlice.tsx | 11 +++- .../static/components/StudyDetail.tsx | 18 ++++++- .../static/components/StudyList.tsx | 36 ++++++++++--- 12 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..baa31f10 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +optuna_dashboard/static/components/PlotlyDarkMode.ts \ No newline at end of file diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index f92eedaa..47320fb8 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -61,11 +61,6 @@ INDEX_HTML = """ margin: 0; padding: 0; } - @media (prefers-color-scheme: dark) { - body { - background-color: #121212; - } - } diff --git a/optuna_dashboard/static/components/App.tsx b/optuna_dashboard/static/components/App.tsx index 2488d3c6..ca3c5327 100644 --- a/optuna_dashboard/static/components/App.tsx +++ b/optuna_dashboard/static/components/App.tsx @@ -1,38 +1,57 @@ -import React, { FC, useMemo } from "react" +import React, { FC, useMemo, useState, useEffect } from "react" import { RecoilRoot } from "recoil" import { BrowserRouter as Router, Switch, Route } from "react-router-dom" import { SnackbarProvider } from "notistack" +import { createTheme, useMediaQuery, ThemeProvider, Box } from "@mui/material" import { StudyDetail } from "./StudyDetail" import { StudyList } from "./StudyList" -import {createTheme, useMediaQuery, ThemeProvider} from "@mui/material" export const App: FC = () => { - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)") + const [colorMode, setColorMode] = useState<"light" | "dark">("light") + useEffect(() => { + setColorMode(prefersDarkMode ? "dark" : "light") + }, [prefersDarkMode]) const theme = useMemo( () => createTheme({ palette: { - mode: prefersDarkMode ? 'dark' : 'light', + mode: colorMode, }, }), - [prefersDarkMode], - ); + [colorMode] + ) + const toggleColorMode = () => { + setColorMode(colorMode === "dark" ? "light" : "dark") + } return ( - - - - + + + + } - /> - } /> - - - + children={} + /> + } + /> + + + + ) diff --git a/optuna_dashboard/static/components/GraphEdf.tsx b/optuna_dashboard/static/components/GraphEdf.tsx index 3d87d325..f799e04a 100644 --- a/optuna_dashboard/static/components/GraphEdf.tsx +++ b/optuna_dashboard/static/components/GraphEdf.tsx @@ -10,7 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-edf" diff --git a/optuna_dashboard/static/components/GraphHistory.tsx b/optuna_dashboard/static/components/GraphHistory.tsx index bcd635eb..9607c63f 100644 --- a/optuna_dashboard/static/components/GraphHistory.tsx +++ b/optuna_dashboard/static/components/GraphHistory.tsx @@ -15,7 +15,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-history" @@ -61,7 +61,7 @@ export const GraphHistory: FC<{ logScale, filterCompleteTrial, filterPrunedTrial, - theme.palette.mode + theme.palette.mode ) } }, [ diff --git a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx index 28263427..f2a8454c 100644 --- a/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx +++ b/optuna_dashboard/static/components/GraphHyperparameterImportances.tsx @@ -12,7 +12,7 @@ import { } from "@mui/material" import { getParamImportances } from "../apiClient" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-hyperparameter-importances" // To match colors used by plot_param_importances in optuna. @@ -103,7 +103,10 @@ export const GraphHyperparameterImportances: FC<{ ) } -const plotParamImportances = (paramsImportanceData: ParamImportances, mode: string) => { +const plotParamImportances = ( + paramsImportanceData: ParamImportances, + mode: string +) => { if (document.getElementById(plotDomId) === null) { return } diff --git a/optuna_dashboard/static/components/GraphIntermediateValues.tsx b/optuna_dashboard/static/components/GraphIntermediateValues.tsx index 475a11dd..09f4c146 100644 --- a/optuna_dashboard/static/components/GraphIntermediateValues.tsx +++ b/optuna_dashboard/static/components/GraphIntermediateValues.tsx @@ -1,7 +1,7 @@ import * as plotly from "plotly.js-dist" import React, { FC, useEffect } from "react" -import {Grid, Typography, useTheme} from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { Grid, Typography, useTheme } from "@mui/material" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-intermediate-values" diff --git a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx index 29a90862..2b1f6ef4 100644 --- a/optuna_dashboard/static/components/GraphParallelCoordinate.tsx +++ b/optuna_dashboard/static/components/GraphParallelCoordinate.tsx @@ -10,7 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-parallel-coordinate" @@ -65,7 +65,11 @@ export const GraphParallelCoordinate: FC<{ ) } -const plotCoordinate = (study: StudyDetail, objectiveId: number, mode: string) => { +const plotCoordinate = ( + study: StudyDetail, + objectiveId: number, + mode: string +) => { if (document.getElementById(plotDomId) === null) { return } diff --git a/optuna_dashboard/static/components/GraphParetoFront.tsx b/optuna_dashboard/static/components/GraphParetoFront.tsx index 8482e19e..eeae1c6c 100644 --- a/optuna_dashboard/static/components/GraphParetoFront.tsx +++ b/optuna_dashboard/static/components/GraphParetoFront.tsx @@ -10,7 +10,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-pareto-front" diff --git a/optuna_dashboard/static/components/GraphSlice.tsx b/optuna_dashboard/static/components/GraphSlice.tsx index 84ba0e67..b4a10198 100644 --- a/optuna_dashboard/static/components/GraphSlice.tsx +++ b/optuna_dashboard/static/components/GraphSlice.tsx @@ -12,7 +12,7 @@ import { SelectChangeEvent, useTheme, } from "@mui/material" -import {plotlyDarkTemplate} from "./PlotlyDarkMode"; +import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-slice" @@ -39,7 +39,14 @@ export const GraphSlice: FC<{ } useEffect(() => { - plotSlice(trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode) + plotSlice( + trials, + objectiveId, + selected, + logXScale, + logYScale, + theme.palette.mode + ) }, [trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode]) const handleObjectiveChange = (event: SelectChangeEvent) => { diff --git a/optuna_dashboard/static/components/StudyDetail.tsx b/optuna_dashboard/static/components/StudyDetail.tsx index 2ee51108..9f7862d5 100644 --- a/optuna_dashboard/static/components/StudyDetail.tsx +++ b/optuna_dashboard/static/components/StudyDetail.tsx @@ -26,6 +26,8 @@ import FormControlLabel from "@mui/material/FormControlLabel" import MuiDialogTitle from "@mui/material/DialogTitle" import MuiDialogContent from "@mui/material/DialogContent" import CloseIcon from "@mui/icons-material/Close" +import Brightness4Icon from "@mui/icons-material/Brightness4" +import Brightness7Icon from "@mui/icons-material/Brightness7" import { DataGridColumn, DataGrid } from "./DataGrid" import { GraphParallelCoordinate } from "./GraphParallelCoordinate" @@ -51,7 +53,9 @@ export const useStudyDetailValue = (studyId: number): StudyDetail | null => { return studyDetails[studyId] || null } -export const StudyDetail: FC = () => { +export const StudyDetail: FC<{ + toggleColorMode: () => void +}> = ({ toggleColorMode }) => { const theme = useTheme() const action = actionCreator() const { studyId } = useParams() @@ -251,6 +255,18 @@ export const StudyDetail: FC = () => { {APP_BAR_TITLE} + { + toggleColorMode() + }} + color="inherit" + > + {theme.palette.mode === "dark" ? ( + + ) : ( + + )} + diff --git a/optuna_dashboard/static/components/StudyList.tsx b/optuna_dashboard/static/components/StudyList.tsx index 2cfbd659..ae31eb24 100644 --- a/optuna_dashboard/static/components/StudyList.tsx +++ b/optuna_dashboard/static/components/StudyList.tsx @@ -1,4 +1,4 @@ -import React, {FC, useEffect, useMemo} from "react" +import React, { FC, useEffect, useMemo } from "react" import { useRecoilValue } from "recoil" import { Link } from "react-router-dom" import { @@ -31,8 +31,12 @@ import { Add, AddBox, Delete, Refresh, Remove } from "@mui/icons-material" import { actionCreator } from "../action" import { DataGrid, DataGridColumn } from "./DataGrid" import { studySummariesState } from "../state" +import Brightness7Icon from "@mui/icons-material/Brightness7" +import Brightness4Icon from "@mui/icons-material/Brightness4" -export const StudyList: FC = () => { +export const StudyList: FC<{ + toggleColorMode: () => void +}> = ({ toggleColorMode }) => { const theme = useTheme() const [newStudySelectionAnchorEl, setNewStudySelectionAnchorEl] = @@ -55,10 +59,13 @@ export const StudyList: FC = () => { const [directions, setDirections] = React.useState([ "minimize", ]) - const linkColor= useMemo( - () => theme.palette.mode === 'dark' ? theme.palette.primary.light : theme.palette.primary.dark, - [theme.palette.mode], - ); + const linkColor = useMemo( + () => + theme.palette.mode === "dark" + ? theme.palette.primary.light + : theme.palette.primary.dark, + [theme.palette.mode] + ) const action = actionCreator() const studies = useRecoilValue(studySummariesState) @@ -82,7 +89,10 @@ export const StudyList: FC = () => { label: "Name", sortable: true, toCellValue: (i) => ( - + {studies[i].study_name} ), @@ -216,6 +226,18 @@ export const StudyList: FC = () => { {APP_BAR_TITLE} + { + toggleColorMode() + }} + color="inherit" + > + {theme.palette.mode === "dark" ? ( + + ) : ( + + )} +