diff --git a/distributed/dashboard/components/scheduler.py b/distributed/dashboard/components/scheduler.py index 2f13ed40125..e65bb759e47 100644 --- a/distributed/dashboard/components/scheduler.py +++ b/distributed/dashboard/components/scheduler.py @@ -3405,20 +3405,12 @@ def handle_selector_chng(attr, old, new): self.unit_selector = Select(title="Unit selection", options=[]) self.unit_selector.on_change("value", handle_selector_chng) self.unit_selected = "seconds" - self.task_exec_by_activity_chart = figure() - self.task_exec_by_prefix_chart = figure() - self.senddata_by_activity_chart = figure() + self.task_exec_by_activity_chart = None + self.task_exec_by_prefix_chart = None + self.senddata_by_activity_chart = None self.root = column( self.function_selector, self.unit_selector, - row( - [ - self.task_exec_by_prefix_chart, - self.task_exec_by_activity_chart, - self.senddata_by_activity_chart, - ], - sizing_mode="stretch_width", - ), sizing_mode="scale_width", ) @@ -3504,26 +3496,29 @@ def update(self): f"Filter by function ({len(self.function_selector.options)}):" ) - task_exec_piechart = self._build_task_execution_by_activity_chart( - self.task_exec_data_limited.copy() - ) - task_exec_barchart = self._build_task_execution_by_prefix_chart( - self.task_exec_data_limited.copy() - ) - senddata_piechart = self._build_senddata_chart(self.senddata.copy()) + needs_figures_row = self.task_exec_by_activity_chart is None + self._build_task_execution_by_activity_chart(self.task_exec_data_limited.copy()) + self._build_task_execution_by_prefix_chart(self.task_exec_data_limited.copy()) + self._build_senddata_chart(self.senddata.copy()) - # Replacing the child causes small blips if done every iteration vs updating - # renderers, but it's needed when new functions and/or activities show up to - # rerender plot - if self.substantial_change: - self.root.children[-1].children[0] = task_exec_barchart - self.root.children[-1].children[1] = task_exec_piechart - self.root.children[-1].children[2] = senddata_piechart + if self.substantial_change or needs_figures_row: self.substantial_change = False - else: - self.task_exec_by_prefix_chart.renderers = task_exec_piechart.renderers - self.task_exec_by_activity_chart.renderers = task_exec_barchart.renderers - self.senddata_by_activity_chart.renderers = senddata_piechart.renderers + + figures_row = row( + children=[ + self.task_exec_by_prefix_chart, + self.task_exec_by_activity_chart, + self.senddata_by_activity_chart, + ], + sizing_mode="stretch_width", + ) + + if needs_figures_row: + # First iteration, initial assignment of figures row + self.root.children.append(figures_row) + else: + # Otherwise needs forced refresh by replacing the figures row + self.root.children[-1] = figures_row def _build_task_execution_by_activity_chart( self, task_exec_data: defaultdict[str, list] @@ -3553,30 +3548,31 @@ def _build_task_execution_by_activity_chart( piechart_data["activity"] = self.task_activities self.task_exec_by_activity_src.data = piechart_data - piechart = figure( - height=500, - sizing_mode="scale_both", - title="Task execution, by activity", - tools="hover", - tooltips="@{activity}: @text", - x_range=(-0.5, 1.0), - ) - piechart.axis.axis_label = None - piechart.axis.visible = False - piechart.grid.grid_line_color = None - - piechart.wedge( - x=0, - y=1, - radius=0.4, - start_angle=cumsum("angle", include_zero=True), - end_angle=cumsum("angle"), - line_color="white", - fill_color="color", - legend_field="activity", - source=self.task_exec_by_activity_src, - ) - return piechart + if self.task_exec_by_activity_chart is None: + piechart = figure( + height=500, + sizing_mode="scale_both", + title="Task execution, by activity", + tools="hover", + tooltips="@{activity}: @text", + x_range=(-0.5, 1.0), + ) + piechart.axis.axis_label = None + piechart.axis.visible = False + piechart.grid.grid_line_color = None + + piechart.wedge( + x=0, + y=1, + radius=0.4, + start_angle=cumsum("angle", include_zero=True), + end_angle=cumsum("angle"), + line_color="white", + fill_color="color", + legend_field="activity", + source=self.task_exec_by_activity_src, + ) + self.task_exec_by_activity_chart = piechart def _build_task_execution_by_prefix_chart( self, task_exec_data: defaultdict[str, list] @@ -3621,7 +3617,7 @@ def _build_task_execution_by_prefix_chart( self.task_exec_by_prefix_src.data = dict(task_exec_data) barchart.renderers = renderers - return barchart + self.task_exec_by_prefix_chart = barchart def _build_senddata_chart(self, senddata: defaultdict[str, list]) -> figure: piedata = {} @@ -3643,29 +3639,31 @@ def _build_senddata_chart(self, senddata: defaultdict[str, list]) -> figure: piedata["color"] = small_palettes["YlGnBu"].get(len(piedata["activity"]), []) self.sendsrc.data = piedata - senddata_piechart = figure( - height=500, - sizing_mode="scale_both", - title="Send data, by activity", - tools="hover", - tooltips="@{activity}: @text", - x_range=(-0.5, 1.0), - ) - senddata_piechart.wedge( - x=0, - y=1, - radius=0.4, - start_angle=cumsum("angle", include_zero=True), - end_angle=cumsum("angle"), - line_color="white", - fill_color="color", - legend_field="activity", - source=self.sendsrc, - ) - senddata_piechart.axis.axis_label = None - senddata_piechart.axis.visible = False - senddata_piechart.grid.grid_line_color = None - return senddata_piechart + + if self.senddata_by_activity_chart is None: + senddata_piechart = figure( + height=500, + sizing_mode="scale_both", + title="Send data, by activity", + tools="hover", + tooltips="@{activity}: @text", + x_range=(-0.5, 1.0), + ) + senddata_piechart.wedge( + x=0, + y=1, + radius=0.4, + start_angle=cumsum("angle", include_zero=True), + end_angle=cumsum("angle"), + line_color="white", + fill_color="color", + legend_field="activity", + source=self.sendsrc, + ) + senddata_piechart.axis.axis_label = None + senddata_piechart.axis.visible = False + senddata_piechart.grid.grid_line_color = None + self.senddata_by_activity_chart = senddata_piechart class Contention(DashboardComponent):