From a9f98b4353047284e26343cf3b46fd409714caba Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Mon, 9 Oct 2023 10:03:18 +0000 Subject: [PATCH 01/11] "kill" button added in the workchain selector (still no effect on click button) --- src/aiidalab_qe/common/process.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index cce2c0f6b..c53febe90 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -72,6 +72,15 @@ def __init__(self, process_label, **kwargs): layout=ipw.Layout(width="auto"), ) self.refresh_work_chains_button.on_click(self.refresh_work_chains) + + self.kill_work_chains_button = ipw.Button( + description="Kill", + tooltip="Kill the selected workflow", + button_style="danger", + icon="window-close", + layout=ipw.Layout(width="auto"), + ) + #self.refresh_work_chains_button.on_click(self.refresh_work_chains) super().__init__( children=[ @@ -79,6 +88,7 @@ def __init__(self, process_label, **kwargs): self.work_chains_selector, self.new_work_chains_button, self.refresh_work_chains_button, + self.kill_work_chains_button, ], **kwargs, ) From 0e076bf43794f6c6fafd190d70e7323fd6eb45cf Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Mon, 9 Oct 2023 10:15:35 +0000 Subject: [PATCH 02/11] "_on_click_kill_work_chain" function, to kill a running workchain. it uses the control module from aiida.engine.processes --- src/aiidalab_qe/common/process.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index c53febe90..4ddd8397d 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -4,7 +4,8 @@ import ipywidgets as ipw import traitlets as tl from aiida.tools.query.calculation import CalculationQueryBuilder - +from aiida.engine.processes import control +from aiida import orm class WorkChainSelector(ipw.HBox): """A widget to select a WorkChainNode of a given process label. @@ -80,7 +81,7 @@ def __init__(self, process_label, **kwargs): icon="window-close", layout=ipw.Layout(width="auto"), ) - #self.refresh_work_chains_button.on_click(self.refresh_work_chains) + self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) super().__init__( children=[ @@ -159,6 +160,12 @@ def refresh_work_chains(self, _=None): def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS + + def _on_click_kill_work_chain(self, _=None): + if isinstance(self.work_chains_selector.value, int): + self.refresh_work_chains() + processes = [orm.load_node(self.work_chains_selector.value)] + control.kill_processes(processes) @tl.observe("value") def _observe_value(self, change): From f59f9fc7195f2aa4574c436f9df5922ab2b07921 Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Mon, 9 Oct 2023 13:53:08 +0000 Subject: [PATCH 03/11] disabling kill button if already terminated wchain --- src/aiidalab_qe/common/process.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index 4ddd8397d..feb12f025 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -79,8 +79,14 @@ def __init__(self, process_label, **kwargs): tooltip="Kill the selected workflow", button_style="danger", icon="window-close", + disabled=False, layout=ipw.Layout(width="auto"), ) + ipw.dlink( + (self, "value"), + (self.kill_work_chains_button, "disabled"), + lambda workchain: orm.load_node(workchain).is_terminated if isinstance(workchain, int) else True, + ) self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) super().__init__( @@ -137,6 +143,7 @@ def _observe_busy(self, change): child.disabled = change["new"] def refresh_work_chains(self, _=None): + try: self.set_trait("busy", True) # disables the widget @@ -156,16 +163,18 @@ def refresh_work_chains(self, _=None): self.work_chains_selector.value = original_value finally: self.set_trait("busy", False) # reenable the widget + + self.kill_work_chains_button.disabled = orm.load_node(self.value).is_terminated if isinstance(self.value, int) else True + def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS def _on_click_kill_work_chain(self, _=None): - if isinstance(self.work_chains_selector.value, int): - self.refresh_work_chains() - processes = [orm.load_node(self.work_chains_selector.value)] - control.kill_processes(processes) + processes = [orm.load_node(self.work_chains_selector.value)] + control.kill_processes(processes) + self.refresh_work_chains() @tl.observe("value") def _observe_value(self, change): From 452042cbe3ed6306a5bf60aec863b92c13344d6f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 10:20:25 +0000 Subject: [PATCH 04/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/common/process.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index feb12f025..0e30b0719 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -3,9 +3,10 @@ import ipywidgets as ipw import traitlets as tl -from aiida.tools.query.calculation import CalculationQueryBuilder -from aiida.engine.processes import control from aiida import orm +from aiida.engine.processes import control +from aiida.tools.query.calculation import CalculationQueryBuilder + class WorkChainSelector(ipw.HBox): """A widget to select a WorkChainNode of a given process label. @@ -73,7 +74,7 @@ def __init__(self, process_label, **kwargs): layout=ipw.Layout(width="auto"), ) self.refresh_work_chains_button.on_click(self.refresh_work_chains) - + self.kill_work_chains_button = ipw.Button( description="Kill", tooltip="Kill the selected workflow", @@ -170,7 +171,7 @@ def refresh_work_chains(self, _=None): def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS - + def _on_click_kill_work_chain(self, _=None): processes = [orm.load_node(self.work_chains_selector.value)] control.kill_processes(processes) From 869e5e8d8ea848ffb8317c9eb727db6fbaea927b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 14:03:03 +0000 Subject: [PATCH 05/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/common/process.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index 0e30b0719..3ddd89187 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -86,7 +86,9 @@ def __init__(self, process_label, **kwargs): ipw.dlink( (self, "value"), (self.kill_work_chains_button, "disabled"), - lambda workchain: orm.load_node(workchain).is_terminated if isinstance(workchain, int) else True, + lambda workchain: orm.load_node(workchain).is_terminated + if isinstance(workchain, int) + else True, ) self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) @@ -144,7 +146,6 @@ def _observe_busy(self, change): child.disabled = change["new"] def refresh_work_chains(self, _=None): - try: self.set_trait("busy", True) # disables the widget @@ -164,9 +165,12 @@ def refresh_work_chains(self, _=None): self.work_chains_selector.value = original_value finally: self.set_trait("busy", False) # reenable the widget - - self.kill_work_chains_button.disabled = orm.load_node(self.value).is_terminated if isinstance(self.value, int) else True + self.kill_work_chains_button.disabled = ( + orm.load_node(self.value).is_terminated + if isinstance(self.value, int) + else True + ) def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() From e36e7ef8d3d8d1c99eb323ff5aacd32fad54210f Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Tue, 10 Oct 2023 12:31:02 +0000 Subject: [PATCH 06/11] moving the disabling of the kill button into the _observe_value method. However, in the init method I had to add the disabling, otherwise if the browser page is refreshed (or loaded for the first time), it will not be automatically disabled. I still do not understand why --- src/aiidalab_qe/common/process.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index 3ddd89187..5145db4f7 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -80,16 +80,9 @@ def __init__(self, process_label, **kwargs): tooltip="Kill the selected workflow", button_style="danger", icon="window-close", - disabled=False, + disabled=True, layout=ipw.Layout(width="auto"), ) - ipw.dlink( - (self, "value"), - (self.kill_work_chains_button, "disabled"), - lambda workchain: orm.load_node(workchain).is_terminated - if isinstance(workchain, int) - else True, - ) self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) super().__init__( @@ -104,6 +97,9 @@ def __init__(self, process_label, **kwargs): ) self.refresh_work_chains() + #the following is needed to disable the button. + self.kill_work_chains_button.disabled = True + def parse_extra_info(self, pk: int) -> dict: """Parse extra information about the work chain.""" @@ -166,12 +162,7 @@ def refresh_work_chains(self, _=None): finally: self.set_trait("busy", False) # reenable the widget - self.kill_work_chains_button.disabled = ( - orm.load_node(self.value).is_terminated - if isinstance(self.value, int) - else True - ) - + def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS @@ -190,6 +181,14 @@ def _observe_value(self, change): if new not in {pk for _, pk in self.work_chains_selector.options}: self.refresh_work_chains() + + if hasattr(self,"kill_work_chains_button"): + #when the app is loaded the first time, the button is not there so it excepts. + self.kill_work_chains_button.disabled = ( + orm.load_node(self.value).is_terminated + if isinstance(self.value, int) + else True + ) self.work_chains_selector.value = new From 63b95860dda865888c8b05d36c9b664771d19691 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 12:34:09 +0000 Subject: [PATCH 07/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/common/process.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index 5145db4f7..af16d8bc5 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -97,10 +97,9 @@ def __init__(self, process_label, **kwargs): ) self.refresh_work_chains() - #the following is needed to disable the button. + # the following is needed to disable the button. self.kill_work_chains_button.disabled = True - def parse_extra_info(self, pk: int) -> dict: """Parse extra information about the work chain.""" return dict() @@ -162,7 +161,6 @@ def refresh_work_chains(self, _=None): finally: self.set_trait("busy", False) # reenable the widget - def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS @@ -181,14 +179,14 @@ def _observe_value(self, change): if new not in {pk for _, pk in self.work_chains_selector.options}: self.refresh_work_chains() - - if hasattr(self,"kill_work_chains_button"): - #when the app is loaded the first time, the button is not there so it excepts. + + if hasattr(self, "kill_work_chains_button"): + # when the app is loaded the first time, the button is not there so it excepts. self.kill_work_chains_button.disabled = ( - orm.load_node(self.value).is_terminated - if isinstance(self.value, int) - else True - ) + orm.load_node(self.value).is_terminated + if isinstance(self.value, int) + else True + ) self.work_chains_selector.value = new From ac1a9699c5332ffdf4ceef8fa5d937cbcd4ec2ca Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Tue, 17 Oct 2023 14:38:21 +0000 Subject: [PATCH 08/11] kill button in step 4, just above the NodeTree. if no workchain is selected, the button is not shown. --- src/aiidalab_qe/app/result/__init__.py | 29 ++++++++++++++++++++++++-- src/aiidalab_qe/common/process.py | 25 ---------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/aiidalab_qe/app/result/__init__.py b/src/aiidalab_qe/app/result/__init__.py index 09d4597a6..a53d59392 100644 --- a/src/aiidalab_qe/app/result/__init__.py +++ b/src/aiidalab_qe/app/result/__init__.py @@ -2,6 +2,7 @@ import traitlets as tl from aiida import orm from aiida.engine import ProcessState +from aiida.engine.processes import control from aiidalab_widgets_base import ( AiidaNodeViewWidget, ProcessMonitor, @@ -40,8 +41,22 @@ def __init__(self, **kwargs): ], ) ipw.dlink((self, "process"), (self.process_monitor, "value")) + + self.kill_work_chains_button = ipw.Button( + description="Kill workchain", + tooltip="Kill the below workchain.", + button_style="danger", + icon="window-close", + disabled=True, + layout=ipw.Layout(width="120px",height="40px",display="none"), + ) + self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) + self.kill_work_chains_box = ipw.HBox( + children=[self.kill_work_chains_button], + layout=ipw.Layout(justify_content="flex-end") + ) - super().__init__([self.process_status], **kwargs) + super().__init__([self.kill_work_chains_box,self.process_status], **kwargs) def can_reset(self): "Do not allow reset while process is running." @@ -53,7 +68,9 @@ def reset(self): def _update_state(self): if self.process is None: self.state = self.State.INIT + self.kill_work_chains_button.display = "none" else: + self.kill_work_chains_button.layout.display = "block" process = orm.load_node(self.process) process_state = process.process_state if process_state in ( @@ -62,14 +79,22 @@ def _update_state(self): ProcessState.WAITING, ): self.state = self.State.ACTIVE + self.kill_work_chains_button.disabled = False elif ( process_state in (ProcessState.EXCEPTED, ProcessState.KILLED) or process.is_failed ): self.state = self.State.FAIL + self.kill_work_chains_button.disabled = True elif process.is_finished_ok: self.state = self.State.SUCCESS + self.kill_work_chains_button.disabled = True + + def _on_click_kill_work_chain(self, _=None): + workchain = [orm.load_node(self.process)] + control.kill_processes(workchain) + self._update_state() @tl.observe("process") def _observe_process(self, change): - self._update_state() + self._update_state() \ No newline at end of file diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index af16d8bc5..c8e593e4f 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -4,7 +4,6 @@ import ipywidgets as ipw import traitlets as tl from aiida import orm -from aiida.engine.processes import control from aiida.tools.query.calculation import CalculationQueryBuilder @@ -75,30 +74,18 @@ def __init__(self, process_label, **kwargs): ) self.refresh_work_chains_button.on_click(self.refresh_work_chains) - self.kill_work_chains_button = ipw.Button( - description="Kill", - tooltip="Kill the selected workflow", - button_style="danger", - icon="window-close", - disabled=True, - layout=ipw.Layout(width="auto"), - ) - self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) - super().__init__( children=[ self.work_chains_prompt, self.work_chains_selector, self.new_work_chains_button, self.refresh_work_chains_button, - self.kill_work_chains_button, ], **kwargs, ) self.refresh_work_chains() # the following is needed to disable the button. - self.kill_work_chains_button.disabled = True def parse_extra_info(self, pk: int) -> dict: """Parse extra information about the work chain.""" @@ -165,10 +152,6 @@ def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS - def _on_click_kill_work_chain(self, _=None): - processes = [orm.load_node(self.work_chains_selector.value)] - control.kill_processes(processes) - self.refresh_work_chains() @tl.observe("value") def _observe_value(self, change): @@ -180,14 +163,6 @@ def _observe_value(self, change): if new not in {pk for _, pk in self.work_chains_selector.options}: self.refresh_work_chains() - if hasattr(self, "kill_work_chains_button"): - # when the app is loaded the first time, the button is not there so it excepts. - self.kill_work_chains_button.disabled = ( - orm.load_node(self.value).is_terminated - if isinstance(self.value, int) - else True - ) - self.work_chains_selector.value = new From cf5b00d853c5bc42a6f7f72323596ec5c48e697f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 14:39:13 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/aiidalab_qe/app/result/__init__.py | 14 +++++++------- src/aiidalab_qe/common/process.py | 1 - 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/aiidalab_qe/app/result/__init__.py b/src/aiidalab_qe/app/result/__init__.py index a53d59392..35e700c0f 100644 --- a/src/aiidalab_qe/app/result/__init__.py +++ b/src/aiidalab_qe/app/result/__init__.py @@ -41,22 +41,22 @@ def __init__(self, **kwargs): ], ) ipw.dlink((self, "process"), (self.process_monitor, "value")) - + self.kill_work_chains_button = ipw.Button( description="Kill workchain", tooltip="Kill the below workchain.", button_style="danger", icon="window-close", disabled=True, - layout=ipw.Layout(width="120px",height="40px",display="none"), + layout=ipw.Layout(width="120px", height="40px", display="none"), ) self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) self.kill_work_chains_box = ipw.HBox( children=[self.kill_work_chains_button], - layout=ipw.Layout(justify_content="flex-end") - ) + layout=ipw.Layout(justify_content="flex-end"), + ) - super().__init__([self.kill_work_chains_box,self.process_status], **kwargs) + super().__init__([self.kill_work_chains_box, self.process_status], **kwargs) def can_reset(self): "Do not allow reset while process is running." @@ -89,7 +89,7 @@ def _update_state(self): elif process.is_finished_ok: self.state = self.State.SUCCESS self.kill_work_chains_button.disabled = True - + def _on_click_kill_work_chain(self, _=None): workchain = [orm.load_node(self.process)] control.kill_processes(workchain) @@ -97,4 +97,4 @@ def _on_click_kill_work_chain(self, _=None): @tl.observe("process") def _observe_process(self, change): - self._update_state() \ No newline at end of file + self._update_state() diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index c8e593e4f..563d559ce 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -152,7 +152,6 @@ def _on_click_new_work_chain(self, _=None): self.refresh_work_chains() self.work_chains_selector.value = self._NO_PROCESS - @tl.observe("value") def _observe_value(self, change): if change["old"] == change["new"]: From 023f0771798444d715f2b43939308a7c5734dd98 Mon Sep 17 00:00:00 2001 From: Miki Bonacci Date: Thu, 19 Oct 2023 14:39:30 +0000 Subject: [PATCH 10/11] Just removing unnecessary import --- src/aiidalab_qe/common/process.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/aiidalab_qe/common/process.py b/src/aiidalab_qe/common/process.py index 563d559ce..e68a5404f 100644 --- a/src/aiidalab_qe/common/process.py +++ b/src/aiidalab_qe/common/process.py @@ -3,7 +3,6 @@ import ipywidgets as ipw import traitlets as tl -from aiida import orm from aiida.tools.query.calculation import CalculationQueryBuilder From df1d435749d2dc3ee292ee359e07178cdbcd92d9 Mon Sep 17 00:00:00 2001 From: Jusong Yu Date: Thu, 19 Oct 2023 16:22:47 +0000 Subject: [PATCH 11/11] Move the kill button layout change to a dedicate function --- src/aiidalab_qe/app/result/__init__.py | 51 +++++++++++++++++--------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/aiidalab_qe/app/result/__init__.py b/src/aiidalab_qe/app/result/__init__.py index 35e700c0f..125c5c738 100644 --- a/src/aiidalab_qe/app/result/__init__.py +++ b/src/aiidalab_qe/app/result/__init__.py @@ -42,21 +42,18 @@ def __init__(self, **kwargs): ) ipw.dlink((self, "process"), (self.process_monitor, "value")) - self.kill_work_chains_button = ipw.Button( + self.kill_button = ipw.Button( description="Kill workchain", tooltip="Kill the below workchain.", button_style="danger", icon="window-close", - disabled=True, - layout=ipw.Layout(width="120px", height="40px", display="none"), - ) - self.kill_work_chains_button.on_click(self._on_click_kill_work_chain) - self.kill_work_chains_box = ipw.HBox( - children=[self.kill_work_chains_button], - layout=ipw.Layout(justify_content="flex-end"), + layout=ipw.Layout(width="120px", height="40px"), ) + self.kill_button.on_click(self._on_click_kill_button) + + super().__init__([self.kill_button, self.process_status], **kwargs) - super().__init__([self.kill_work_chains_box, self.process_status], **kwargs) + self._update_kill_button_layout() def can_reset(self): "Do not allow reset while process is running." @@ -66,11 +63,10 @@ def reset(self): self.process = None def _update_state(self): + """Based on the process state, update the state of the step.""" if self.process is None: self.state = self.State.INIT - self.kill_work_chains_button.display = "none" else: - self.kill_work_chains_button.layout.display = "block" process = orm.load_node(self.process) process_state = process.process_state if process_state in ( @@ -79,22 +75,43 @@ def _update_state(self): ProcessState.WAITING, ): self.state = self.State.ACTIVE - self.kill_work_chains_button.disabled = False elif ( process_state in (ProcessState.EXCEPTED, ProcessState.KILLED) or process.is_failed ): self.state = self.State.FAIL - self.kill_work_chains_button.disabled = True elif process.is_finished_ok: self.state = self.State.SUCCESS - self.kill_work_chains_button.disabled = True - def _on_click_kill_work_chain(self, _=None): + def _update_kill_button_layout(self): + """Update the layout of the kill button.""" + # If no process is selected, hide the button. + if self.process is None: + self.kill_button.layout.display = "none" + else: + self.kill_button.layout.display = "block" + + # If the step is not activated, no point to click the button, so disable it. + # Only enable it if the process is on (RUNNING, CREATED, WAITING). + if self.state is self.State.ACTIVE: + self.kill_button.disabled = False + else: + self.kill_button.disabled = True + + def _on_click_kill_button(self, _=None): + """callback for the kill button. + First kill the process, then update the kill button layout. + """ workchain = [orm.load_node(self.process)] control.kill_processes(workchain) - self._update_state() + + # update the kill button layout + self._update_kill_button_layout() @tl.observe("process") - def _observe_process(self, change): + def _observe_process(self, _): + """Callback for when the process is changed.""" + # The order of the following calls matters, + # as the self.state is updated in the _update_state method. self._update_state() + self._update_kill_button_layout()