From 25618273ae87d39c4dced37a0eecd7b85b716162 Mon Sep 17 00:00:00 2001 From: Edan Bainglass Date: Sun, 6 Oct 2024 10:18:41 +0000 Subject: [PATCH] Update view/controller observables --- src/aiidalab_qe/app/configuration/hubbard.py | 46 ++++----- .../app/configuration/magnetization.py | 87 ++++++++-------- src/aiidalab_qe/app/configuration/pseudos.py | 99 +++++++++---------- src/aiidalab_qe/app/main.py | 5 +- src/aiidalab_qe/app/result/__init__.py | 19 ++-- src/aiidalab_qe/app/structure/__init__.py | 28 +++--- src/aiidalab_qe/app/submission/__init__.py | 57 +++++------ src/aiidalab_qe/plugins/pdos/setting.py | 38 ++++--- src/aiidalab_qe/plugins/xas/setting.py | 27 +++-- src/aiidalab_qe/plugins/xps/model.py | 4 +- src/aiidalab_qe/plugins/xps/setting.py | 32 +++--- 11 files changed, 206 insertions(+), 236 deletions(-) diff --git a/src/aiidalab_qe/app/configuration/hubbard.py b/src/aiidalab_qe/app/configuration/hubbard.py index 31314c69a..a7f50ee8a 100644 --- a/src/aiidalab_qe/app/configuration/hubbard.py +++ b/src/aiidalab_qe/app/configuration/hubbard.py @@ -1,7 +1,5 @@ import ipywidgets as ipw -import traitlets as tl -from aiida import orm from aiida_quantumespresso.data.hubbard_structure import HubbardStructureData from .model import ConfigurationModel @@ -10,14 +8,6 @@ class HubbardSettings(ipw.VBox): """Widget for setting up Hubbard parameters.""" - input_structure = tl.Union( - [ - tl.Instance(orm.StructureData), - tl.Instance(orm.KpointsData), - ], - allow_none=True, - ) - def __init__(self, model: ConfigurationModel, **kwargs): from aiidalab_qe.common.widgets import LoadingWidget @@ -27,6 +17,10 @@ def __init__(self, model: ConfigurationModel, **kwargs): ) self._model = model + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) self.links = [] self.eigenvalues_widget_links = [] @@ -78,6 +72,7 @@ def render(self): self.hubbard_widget = ipw.VBox() self.eigenvalues_widget = ipw.VBox() + self.container = ipw.VBox() self.children = [ @@ -90,31 +85,27 @@ def render(self): self.container, ] - ipw.dlink( - (self._model, "input_structure"), - (self, "input_structure"), - ) - self.rendered = True + self._build_hubbard_widget() + def reset(self): """Reset the widget.""" self._unsubscribe() self._model.advanced.hubbard.reset() - @tl.observe("input_structure") - def _on_input_structure_change(self, change): + def _on_input_structure_change(self, _): self._unsubscribe() self._model.advanced.hubbard.update() self._build_hubbard_widget() - if isinstance(change["new"], HubbardStructureData): + if isinstance(self._model.input_structure, HubbardStructureData): self._model.advanced.hubbard.set_parameters_from_hubbard_structure() - def _on_hubbard_check(self, change): - self._toggle_hubbard_widget(change) + def _on_hubbard_check(self, _): + self._toggle_hubbard_widget() - def _on_eigenvalues_check(self, change): - self._toggle_eigenvalues_widget(change) + def _on_eigenvalues_check(self, _): + self._toggle_eigenvalues_widget() def _build_hubbard_widget(self): """Build the widget for defining Hubbard U values @@ -124,6 +115,8 @@ def _build_hubbard_widget(self): hubbard_widget (ipywidgets.VBox): The widget containing the input fields for defining Hubbard U values. """ + if not self.rendered: + return children = [] @@ -241,16 +234,17 @@ def update(index, spin, state, symbol, value): self.eigenvalues_widget.children = children - def _toggle_hubbard_widget(self, change): - self.container.children = [self.hubbard_widget] if change["new"] else [] + def _toggle_hubbard_widget(self): + widget = [self.hubbard_widget] if self._model.advanced.hubbard.activate else [] + self.container.children = widget - def _toggle_eigenvalues_widget(self, change): + def _toggle_eigenvalues_widget(self): self.hubbard_widget.children = ( [ *self.hubbard_widget.children, self.eigenvalues_widget, ] - if change["new"] + if self._model.advanced.hubbard.eigenvalues_label else [*self.hubbard_widget.children][:-1] ) diff --git a/src/aiidalab_qe/app/configuration/magnetization.py b/src/aiidalab_qe/app/configuration/magnetization.py index f6b0d6511..e738ead07 100644 --- a/src/aiidalab_qe/app/configuration/magnetization.py +++ b/src/aiidalab_qe/app/configuration/magnetization.py @@ -1,7 +1,4 @@ import ipywidgets as ipw -import traitlets as tl - -from aiida import orm from .model import ConfigurationModel @@ -20,16 +17,6 @@ class MagnetizationSettings(ipw.VBox): input_structure(StructureData): trait that contains the input_structure (confirmed structure from previous step) """ - input_structure = tl.Union( - [ - tl.Instance(orm.StructureData), - tl.Instance(orm.KpointsData), - ], - allow_none=True, - ) - electronic_type = tl.Unicode() - magnetization_type = tl.Unicode() - def __init__(self, model: ConfigurationModel, **kwargs): from aiidalab_qe.common.widgets import LoadingWidget @@ -40,8 +27,20 @@ def __init__(self, model: ConfigurationModel, **kwargs): ) self._model = model + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) + self._model.workchain.observe( + self._on_electronic_type_change, + "electronic_type", + ) + self._model.advanced.magnetization.observe( + self._on_magnetization_type_change, + "type", + ) - self.kind_widget_links = [] + self.links = [] self.rendered = False @@ -96,48 +95,38 @@ def render(self): self.children = [self.description] - with self.hold_trait_notifications(): - ipw.dlink( - (self._model, "input_structure"), - (self, "input_structure"), - ) - ipw.dlink( - (self._model.workchain, "electronic_type"), - (self, "electronic_type"), - ) - ipw.dlink( - (self._model.advanced.magnetization, "type"), - (self, "magnetization_type"), - ) - self.rendered = True + self._build_kinds_widget() + self._switch_widgets() + self._toggle_widgets() + def reset(self): self._model.advanced.magnetization.reset() - @tl.observe("input_structure") - def _on_input_structure_change(self, change): + def _on_input_structure_change(self, _): self._model.advanced.magnetization.update() - self._build_kinds_widget(change) + self._build_kinds_widget() - @tl.observe("electronic_type") - def _on_electronic_type_change(self, change): - self._switch_widgets(change) + def _on_electronic_type_change(self, _): + self._switch_widgets() - @tl.observe("magnetization_type") - def _on_magnetization_type_change(self, change): - self._toggle_widgets(change) + def _on_magnetization_type_change(self, _): + self._toggle_widgets() + + def _build_kinds_widget(self): + if not self.rendered: + return - def _build_kinds_widget(self, change): children = [] - if (input_structure := change["new"]) is None: + if self._model.input_structure is None: labels = [] - for link in self.kind_widget_links: + for link in self.links: link.unlink() - self.kind_widget_links.clear() + self.links.clear() else: - labels = input_structure.get_kind_names() + labels = self._model.input_structure.get_kind_names() for label in labels: kind_widget = ipw.BoundedFloatText( @@ -158,7 +147,7 @@ def _build_kinds_widget(self, change): }, ], ) - self.kind_widget_links.append(link) + self.links.append(link) ipw.dlink( (self._model.advanced, "override"), (kind_widget, "disabled"), @@ -168,16 +157,20 @@ def _build_kinds_widget(self, change): self.kinds.children = children - def _switch_widgets(self, change): + def _switch_widgets(self): + if not self.rendered: + return children = [self.description] - if change["new"] == "metal": + if self._model.workchain.electronic_type == "metal": children.extend([self.magnetization_type_toggle, self.container]) else: children.append(self.tot_magnetization) self.children = children - def _toggle_widgets(self, change): - if change["new"] == "tot_magnetization": + def _toggle_widgets(self): + if not self.rendered: + return + if self._model.advanced.magnetization.type == "tot_magnetization": self.container.children = [self.tot_magnetization] else: self.container.children = [self.kinds] diff --git a/src/aiidalab_qe/app/configuration/pseudos.py b/src/aiidalab_qe/app/configuration/pseudos.py index b5aa9fa07..9fab8b916 100644 --- a/src/aiidalab_qe/app/configuration/pseudos.py +++ b/src/aiidalab_qe/app/configuration/pseudos.py @@ -21,17 +21,6 @@ class PseudoSettings(ipw.VBox): """Widget to set the pseudopotentials for the calculation.""" - input_structure = tl.Union( - [ - tl.Instance(orm.StructureData), - tl.Instance(orm.KpointsData), - ], - allow_none=True, - ) - spin_orbit = tl.Unicode() - family = tl.Unicode() - override = tl.Bool() - def __init__(self, model: ConfigurationModel, **kwargs): super().__init__( children=[LoadingWidget("Loading pseudopotentials widget")], @@ -39,11 +28,26 @@ def __init__(self, model: ConfigurationModel, **kwargs): ) self._model = model - + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) + self._model.advanced.observe( + self._on_spin_orbit_change, + "spin_orbit", + ) + self._model.advanced.observe( + self._on_override_change, + "override", + ) self._model.advanced.pseudos.observe( self._on_family_parameters_change, ["library", "functional"], ) + self._model.advanced.pseudos.observe( + self._on_family_change, + "family", + ) self.links = [] @@ -232,55 +236,41 @@ def render(self): self._status_message, ] - with self.hold_trait_notifications(): - ipw.dlink( - (self._model, "input_structure"), - (self, "input_structure"), - ) - ipw.dlink( - (self._model.advanced, "spin_orbit"), - (self, "spin_orbit"), - ) - ipw.dlink( - (self._model.advanced.pseudos, "family"), - (self, "family"), - ) - ipw.dlink( - (self._model.advanced, "override"), - (self, "override"), - ) - self.rendered = True + self._build_setter_widgets() + self._toggle_setter_widgets() + self._update_library_options() + self._update_family_link() + def reset(self): self._unsubscribe() self._model.advanced.pseudos.reset() - @tl.observe("input_structure") - def _on_input_structure_change(self, _=None): + def _on_input_structure_change(self, _): self._unsubscribe() self._model.advanced.pseudos.update() self._build_setter_widgets() - @tl.observe("spin_orbit") def _on_spin_orbit_change(self, _): self._update_library_options() - @tl.observe("family") - def _on_family_change(self, _=None): + def _on_override_change(self, _): + self._toggle_setter_widgets() + + def _on_family_parameters_change(self, _): + self._model.advanced.pseudos.update_family() + + def _on_family_change(self, _): self._update_family_link() self._model.advanced.pseudos.update_default_pseudos() self._model.advanced.pseudos.update_default_cutoffs() - @tl.observe("override") - def _on_override_change(self, change): - self._toggle_setter_widgets(change) - - def _on_family_parameters_change(self, _=None): - self._model.advanced.pseudos.update_family() - def _update_library_options(self): """Update pseudo library selection options w.r.t spin orbit.""" + if not self.rendered: + return + if self._model.advanced.spin_orbit == "soc": self.library.options = [ "PseudoDojo standard", @@ -297,6 +287,9 @@ def _update_library_options(self): self.family_help.value = self.PSEUDO_HELP_WO_SOC def _update_family_link(self): + if not self.rendered: + return + library, accuracy = self._model.advanced.pseudos.library.split() if library == "SSSP": pseudo_family_link = ( @@ -315,17 +308,10 @@ def _update_family_link(self): """ - def _toggle_setter_widgets(self, change): - if change["new"]: - self.container.children = [ - self.setter_widget_helper, - self.setter_widget, - ] - else: - self.container.children = [] - def _build_setter_widgets(self): """Build the pseudo setter widgets.""" + if not self.rendered: + return children = [] @@ -367,6 +353,17 @@ def _build_setter_widgets(self): self.setter_widget.children = children + def _toggle_setter_widgets(self): + if not self.rendered: + return + if self._model.advanced.override: + self.container.children = [ + self.setter_widget_helper, + self.setter_widget, + ] + else: + self.container.children = [] + def _unsubscribe(self): for link in self.links: link.unlink() diff --git a/src/aiidalab_qe/app/main.py b/src/aiidalab_qe/app/main.py index f5631b275..c4336fe89 100644 --- a/src/aiidalab_qe/app/main.py +++ b/src/aiidalab_qe/app/main.py @@ -35,18 +35,15 @@ def __init__(self, qe_auto_setup=True): model=struct_model, auto_advance=True, ) - self.configure_step = ConfigureQeAppWorkChainStep( model=config_model, auto_advance=True, ) - self.submit_step = SubmitQeAppWorkChainStep( model=submit_model, auto_advance=True, qe_auto_setup=qe_auto_setup, ) - self.results_step = ViewQeAppWorkChainStatusAndResultsStep(model=results_model) # Link the models of the application steps @@ -105,7 +102,7 @@ def __init__(self, qe_auto_setup=True): ) ipw.dlink( - (self.submit_step, "process"), + (submit_model, "process"), (self.work_chain_selector, "value"), transform=lambda node: None if node is None else node.pk, ) diff --git a/src/aiidalab_qe/app/result/__init__.py b/src/aiidalab_qe/app/result/__init__.py index aa2c330ef..c709c073e 100644 --- a/src/aiidalab_qe/app/result/__init__.py +++ b/src/aiidalab_qe/app/result/__init__.py @@ -22,8 +22,6 @@ class ViewQeAppWorkChainStatusAndResultsStep(ipw.VBox, WizardAppWidgetStep): - process = tl.Unicode(allow_none=True) - def __init__(self, model: ResultsModel, **kwargs): from aiidalab_qe.common.widgets import LoadingWidget @@ -107,11 +105,6 @@ def render(self): "process", ) - ipw.dlink( - (self._model, "process"), - (self, "process"), - ) - self.rendered = True def can_reset(self): @@ -129,14 +122,14 @@ def _on_process_change(self, _): self._update_kill_button_layout() def _on_click_kill_button(self, _=None): - workchain = [orm.load_node(self.process)] + workchain = [orm.load_node(self._model.process)] control.kill_processes(workchain) self._update_kill_button_layout() def _update_kill_button_layout(self): if ( - self.process is None - or self.process == "" + self._model.process is None + or self._model.process == "" or self.state in ( self.State.SUCCESS, @@ -145,17 +138,17 @@ def _update_kill_button_layout(self): ): self.kill_button.layout.display = "none" else: - process = orm.load_node(self.process) + process = orm.load_node(self._model.process) if process.is_finished or process.is_excepted: self.kill_button.layout.display = "none" else: self.kill_button.layout.display = "block" def _update_state(self): - if self.process is None: + if self._model.process is None: self.state = self.State.INIT else: - process = orm.load_node(self.process) + process = orm.load_node(self._model.process) process_state = process.process_state if process_state in ( ProcessState.CREATED, diff --git a/src/aiidalab_qe/app/structure/__init__.py b/src/aiidalab_qe/app/structure/__init__.py index 2677f5fc0..5b334e709 100644 --- a/src/aiidalab_qe/app/structure/__init__.py +++ b/src/aiidalab_qe/app/structure/__init__.py @@ -6,7 +6,6 @@ import pathlib import ipywidgets as ipw -import traitlets as tl from aiida import orm from aiida_quantumespresso.data.hubbard_structure import HubbardStructureData @@ -48,8 +47,6 @@ class StructureSelectionStep(ipw.VBox, WizardAppWidgetStep): structure importers and the structure editors can be extended by plugins. """ - structure = tl.Instance(orm.StructureData, allow_none=True) - def __init__(self, model: StructureModel, **kwargs): from aiidalab_qe.common.widgets import LoadingWidget @@ -63,6 +60,10 @@ def __init__(self, model: StructureModel, **kwargs): self._on_confirmation_change, "confirmed", ) + self._model.observe( + self._on_structure_change, + "structure", + ) self.rendered = False @@ -135,13 +136,12 @@ def render(self): lambda state: state != self.State.CONFIGURED, ) self.confirm_button.on_click(self.confirm) + self.message_area = ipw.HTML() - # Create directional link from the (read-only) 'structure_node' traitlet of the - # structure manager to our 'structure' traitlet: ipw.dlink( (self.manager, "structure_node"), - (self, "structure"), + (self._model, "structure"), ) self.children = [ @@ -163,11 +163,6 @@ def render(self): self.confirm_button, ] - ipw.dlink( - (self._model, "structure"), - (self, "structure"), - ) - self.rendered = True def set_structure(self, structure): @@ -175,11 +170,11 @@ def set_structure(self, structure): def is_saved(self): """Check if the current structure is confirmed.""" - return self.structure == self._model.confirmed_structure + return self._model.structure == self._model.confirmed_structure def confirm(self, _=None): self.manager.store_structure() - self._model.confirmed_structure = self.structure + self._model.confirmed_structure = self._model.structure self._model.confirmed = True self.message_area.value = "" @@ -194,7 +189,6 @@ def reset(self): self.manager.viewer.structure = None self.manager.output.value = "" - @tl.observe("structure") def _on_structure_change(self, _): self._model.reset() self._update_widget_text() @@ -204,17 +198,17 @@ def _on_confirmation_change(self, _): self._update_state() def _update_widget_text(self): - if self.structure is None: + if self._model.structure is None: self.structure_name_text.value = "" self.message_area.value = "" else: self.manager.output.value = "" - self.structure_name_text.value = str(self.structure.get_formula()) + self.structure_name_text.value = str(self._model.structure.get_formula()) def _update_state(self): if self._model.confirmed: self.state = self.State.SUCCESS - elif self.structure is None: + elif self._model.structure is None: self.state = self.State.READY else: self.state = self.State.CONFIGURED diff --git a/src/aiidalab_qe/app/submission/__init__.py b/src/aiidalab_qe/app/submission/__init__.py index 77c958f3b..d1c26e556 100644 --- a/src/aiidalab_qe/app/submission/__init__.py +++ b/src/aiidalab_qe/app/submission/__init__.py @@ -14,7 +14,6 @@ from aiida.engine import ProcessBuilderNamespace from aiida.engine import submit as aiida_submit from aiida.orm.utils.serialize import serialize -from aiida_quantumespresso.data.hubbard_structure import HubbardStructureData from aiidalab_qe.app.parameters import DEFAULT_PARAMETERS from aiidalab_qe.app.utils import get_entry_items from aiidalab_qe.common.setup_codes import QESetupWidget @@ -38,16 +37,6 @@ class SubmitQeAppWorkChainStep(ipw.VBox, WizardAppWidgetStep): previous_step_state = tl.UseEnum(WizardAppWidgetStep.State) - input_structure = tl.Union( - [ - tl.Instance(orm.StructureData), - tl.Instance(HubbardStructureData), - ], - allow_none=True, - ) - process = tl.Instance(orm.WorkChainNode, allow_none=True) - input_parameters = tl.Dict() - internal_submission_blockers = tl.List(tl.Unicode()) external_submission_blockers = tl.List(tl.Unicode()) @@ -60,6 +49,18 @@ def __init__(self, model: SubmissionModel, qe_auto_setup=True, **kwargs): ) self._model = model + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) + self._model.observe( + self._on_process_change, + "process", + ) + self._model.observe( + self._on_input_parameters_change, + "input_parameters", + ) # # TODO for testing only - remove in PR # self._model.observe( @@ -250,20 +251,6 @@ def render(self): self.submit_button, ] - with self.hold_trait_notifications(): - ipw.dlink( - (self._model, "input_structure"), - (self, "input_structure"), - ) - ipw.dlink( - (self._model, "process"), - (self, "process"), - ) - ipw.dlink( - (self._model, "input_parameters"), - (self, "input_parameters"), - ) - self.rendered = True def set_submission_parameters(self, parameters): @@ -281,11 +268,14 @@ def reset(self): def _on_previous_step_state_change(self, _): self._update() - @tl.observe("input_structure") + @tl.observe("internal_submission_blockers", "external_submission_blockers") + def _on_submission_blockers_change(self, _): + self._update_submission_blocker_message() + self._update_state() + def _on_input_structure_change(self, _): self._update() - @tl.observe("process") def _on_process_change(self, change): with self.hold_trait_notifications(): process_node = change["new"] @@ -294,15 +284,9 @@ def _on_process_change(self, change): self._model.input_structure = process_node.inputs.structure self._update_state() - @tl.observe("input_parameters") def _on_input_parameters_change(self, _): self._update() - @tl.observe("internal_submission_blockers", "external_submission_blockers") - def _on_submission_blockers_change(self, _): - self._update_submission_blocker_message() - self._update_state() - def _on_submission(self, _): self._submit() self._update_state() @@ -367,7 +351,10 @@ def _submit(self): process.base.extras.set("ui_parameters", serialize(parameters)) # store the workchain name in extras, this will help to filter the workchain in the future process.base.extras.set("workchain", parameters["workchain"]) # type: ignore - process.base.extras.set("structure", self.input_structure.get_formula()) + process.base.extras.set( + "structure", + self._model.input_structure.get_formula(), + ) self._model.process = process def _update(self): @@ -472,7 +459,7 @@ def _create_builder(self, parameters) -> ProcessBuilderNamespace: parameters.update(submission_parameters) builder = QeAppWorkChain.get_builder_from_protocol( - structure=self.input_structure, + structure=self._model.input_structure, parameters=deepcopy(parameters), # TODO why deepcopy again? ) diff --git a/src/aiidalab_qe/plugins/pdos/setting.py b/src/aiidalab_qe/plugins/pdos/setting.py index f6b4681c4..771dfd124 100644 --- a/src/aiidalab_qe/plugins/pdos/setting.py +++ b/src/aiidalab_qe/plugins/pdos/setting.py @@ -1,12 +1,12 @@ """Panel for Pdos plugin.""" import ipywidgets as ipw -import traitlets as tl from aiida import orm from aiida_quantumespresso.calculations.functions.create_kpoints_from_distance import ( create_kpoints_from_distance, ) +from aiidalab_qe.app.configuration.model import ConfigurationModel from aiidalab_qe.common.panel import SettingPanel @@ -14,8 +14,20 @@ class Setting(SettingPanel): title = "PDOS" identifier = "pdos" - input_structure = tl.Instance(orm.StructureData, allow_none=True) - protocol = tl.Unicode(allow_none=True) + def __init__(self, config_model: ConfigurationModel, **kwargs): + super().__init__(config_model, **kwargs) + ipw.dlink( + (self._config_model.workchain, "protocol"), + (self._model, "protocol"), + ) + self._config_model.observe( + self._on_input_structure_change, + "input_structure", + ) + self._config_model.workchain.observe( + self._on_protocol_change, + "protocol", + ) def render(self): if self.rendered: @@ -56,30 +68,14 @@ def render(self): ), ] - with self.hold_trait_notifications(): - ipw.dlink( - (self._config_model, "input_structure"), - (self, "input_structure"), - ) - ipw.dlink( - (self._config_model.workchain, "protocol"), - (self._model, "protocol"), - ) - ipw.dlink( - (self._model, "protocol"), - (self, "protocol"), - ) - self.rendered = True def reset(self): self._model.reset() - @tl.observe("input_structure") def _on_input_structure_change(self, _=None): self._update_mesh() - @tl.observe("protocol") def _on_protocol_change(self, _): self._model.update() self._update_mesh() @@ -88,11 +84,11 @@ def _on_kpoints_distance_change(self, _=None): self._update_mesh() def _update_mesh(self, _=None): - if self.input_structure is None: + if self._config_model.input_structure is None: self._model.mesh_grid = "" else: mesh = create_kpoints_from_distance.process_class._func( - self.input_structure, + self._config_model.input_structure, orm.Float(self._model.kpoints_distance), orm.Bool(False), ) diff --git a/src/aiidalab_qe/plugins/xas/setting.py b/src/aiidalab_qe/plugins/xas/setting.py index 8012c3ce9..91cd771bf 100644 --- a/src/aiidalab_qe/plugins/xas/setting.py +++ b/src/aiidalab_qe/plugins/xas/setting.py @@ -2,6 +2,7 @@ import ipywidgets as ipw +from aiidalab_qe.app.configuration.model import ConfigurationModel from aiidalab_qe.common.panel import SettingPanel @@ -11,6 +12,17 @@ class Setting(SettingPanel): # TODO: The element selection should lock the "Confirm" button if no elements have been selected for XAS calculation. + def __init__(self, config_model: ConfigurationModel, **kwargs): + super().__init__(config_model, **kwargs) + ipw.dlink( + (self._config_model, "input_structure"), + (self._model, "input_structure"), + ) + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) + def render(self): if self.rendered: return @@ -113,24 +125,19 @@ def render(self): ), ] - self._model.observe( - self._on_input_structure_change, - "input_structure", - ) - - ipw.dlink( - (self._config_model, "input_structure"), - (self._model, "input_structure"), - ) - self.rendered = True + self._build_element_core_treatment_widget() + def _on_input_structure_change(self, _): self._unsubscribe() self._model.update() self._build_element_core_treatment_widget() def _build_element_core_treatment_widget(self): + if not self.rendered: + return + children = [] info = "Recommended: {recommended} (PBE Core-Hole Pseudopotential)" diff --git a/src/aiidalab_qe/plugins/xps/model.py b/src/aiidalab_qe/plugins/xps/model.py index 66c3a6d86..550ddaacc 100644 --- a/src/aiidalab_qe/plugins/xps/model.py +++ b/src/aiidalab_qe/plugins/xps/model.py @@ -1,6 +1,6 @@ import traitlets as tl -from aiida.orm import Group, QueryBuilder +from aiida.orm import Group, QueryBuilder, StructureData from aiidalab_qe.common.panel import SettingsModel BASE_URL = "https://github.com/superstar54/xps-data/raw/main/pseudo_demo/" @@ -9,6 +9,8 @@ class XpsModel(SettingsModel): """Model for the XPS plugin.""" + input_structure = tl.Instance(StructureData, allow_none=True) + core_hole_treatment = tl.Unicode("xch_smear") pseudo_group = tl.Unicode("pseudo_demo_pbe") structure_type = tl.Unicode("crystal") diff --git a/src/aiidalab_qe/plugins/xps/setting.py b/src/aiidalab_qe/plugins/xps/setting.py index 72c1c94ff..4bcc7ea2d 100644 --- a/src/aiidalab_qe/plugins/xps/setting.py +++ b/src/aiidalab_qe/plugins/xps/setting.py @@ -1,18 +1,29 @@ """Panel for XPS plugin.""" import ipywidgets as ipw -import traitlets as tl from aiida.common import NotExistent -from aiida.orm import Group, QueryBuilder, StructureData, load_group +from aiida.orm import Group, QueryBuilder, load_group +from aiidalab_qe.app.configuration.model import ConfigurationModel from aiidalab_qe.common.panel import SettingPanel +from .model import BASE_URL + class Setting(SettingPanel): title = "XPS Settings" identifier = "xps" - input_structure = tl.Instance(StructureData, allow_none=True) + def __init__(self, config_model: ConfigurationModel, **kwargs): + super().__init__(config_model, **kwargs) + ipw.dlink( + (self._config_model, "input_structure"), + (self._model, "input_structure"), + ) + self._model.observe( + self._on_input_structure_change, + "input_structure", + ) def render(self): if self.rendered: @@ -112,18 +123,14 @@ def render(self): ipw.HBox([self.core_level_list]), ] - ipw.dlink( - (self._config_model, "input_structure"), - (self, "input_structure"), - ) - self.rendered = True + self._build_core_level_list() + def reset(self): self._unsubscribe() self._model.reset() - @tl.observe("input_structure") def _on_input_structure_change(self, _=None): self._update() @@ -136,12 +143,15 @@ def _update(self): self._build_core_level_list() def _build_core_level_list(self): - if self.input_structure is None: + if not self.rendered: + return + + if self._model.input_structure is None: return children = [] - kind_list = [Kind.symbol for Kind in self.input_structure.kinds] + kind_list = [Kind.symbol for Kind in self._model.input_structure.kinds] qb = QueryBuilder() qb.append(