From db2499db673c5740d5dfbd826c637ebb022dfc62 Mon Sep 17 00:00:00 2001 From: Maciej Bartkowiak Date: Mon, 29 Jan 2024 20:59:24 +0000 Subject: [PATCH] Fix the output trajectory dialog for converters --- .../OutputTrajectoryConfigurator.py | 4 +- .../Jobs/CenterOfMassesTrajectory.py | 2 +- .../Framework/Jobs/CroppedTrajectory.py | 2 +- .../Jobs/GlobalMotionFilteredTrajectory.py | 2 +- .../MDANSE/Framework/Jobs/MoleculeFinder.py | 4 +- .../Framework/Jobs/RigidBodyTrajectory.py | 6 +- .../MDANSE/MolecularDynamics/Connectivity.py | 6 +- .../UnitTests/molecules/test_connectivity.py | 17 ++--- .../PyQtGUI/InputWidgets/FloatWidget.py | 9 +-- .../PyQtGUI/InputWidgets/IntegerWidget.py | 9 +-- .../InputWidgets/OutputTrajectoryWidget.py | 7 +-- .../PyQtGUI/InputWidgets/StringWidget.py | 9 +-- .../PyQtGUI/Widgets/GeneralWidgets.py | 63 ++++++++++++++++++- 13 files changed, 95 insertions(+), 45 deletions(-) diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py index 8991e7d7ef..b7afb00313 100644 --- a/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py +++ b/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py @@ -38,7 +38,7 @@ class OutputTrajectoryConfigurator(IConfigurator): conversion, you must inherit from the MDANSE.Framework.Formats.IFormat.IFormat interface. """ - _default = ("OUTPUT_TRAJECTORY", "MDTFormat") + _default = ("OUTPUT_TRAJECTORY", 64, "none") def __init__(self, name, format=None, **kwargs): """ @@ -52,7 +52,7 @@ def __init__(self, name, format=None, **kwargs): IConfigurator.__init__(self, name, **kwargs) - self._format = OutputTrajectoryConfigurator._default[-1] + self._format = "MDTFormat" self._dtype = np.float64 self._compression = "none" diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py index fcbdc54b6b..621735e3c1 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CenterOfMassesTrajectory.py @@ -85,7 +85,7 @@ def initialize(self): # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( - self.configuration["output_file"]["files"][0], + self.configuration["output_file"]["file"], chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_file"]["dtype"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py index 4e0929e34f..90573bfd44 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/CroppedTrajectory.py @@ -72,7 +72,7 @@ def initialize(self): # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( - self.configuration["output_file"]["files"][0], + self.configuration["output_file"]["file"], self.configuration["trajectory"]["instance"].chemical_system, self.numberOfSteps, self._selectedAtoms, diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py index 5628cce59c..97121496fe 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/GlobalMotionFilteredTrajectory.py @@ -99,7 +99,7 @@ def initialize(self): self._reference_atoms = AtomGroup(self._reference_atoms) self._output_trajectory = TrajectoryWriter( - self.configuration["output_file"]["files"][0], + self.configuration["output_file"]["file"], self.configuration["trajectory"]["instance"].chemical_system, self.numberOfSteps, self._selected_atoms.atom_list, diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py b/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py index 74e1a3cfb2..7080254517 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/MoleculeFinder.py @@ -51,7 +51,7 @@ class MoleculeFinder(IJob): "FramesConfigurator", {"dependencies": {"trajectory": "trajectory"}, "default": (0, -1, 1)}, ) - settings["output_files"] = ( + settings["output_file"] = ( "OutputTrajectoryConfigurator", {"format": "MDTFormat"}, ) @@ -77,7 +77,7 @@ def initialize(self): # The output trajectory is opened for writing. self._output_trajectory = TrajectoryWriter( - self.configuration["output_files"]["files"][0], + self.configuration["output_file"]["file"], chemical_system, self.numberOfSteps, positions_dtype=self.configuration["output_file"]["dtype"], diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py b/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py index 6d7649c844..d05a2362e2 100644 --- a/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py +++ b/MDANSE/Src/MDANSE/Framework/Jobs/RigidBodyTrajectory.py @@ -66,7 +66,7 @@ class RigidBodyTrajectory(IJob): ) settings["reference"] = ("IntegerConfigurator", {"mini": 0}) settings["remove_translation"] = ("BooleanConfigurator", {"default": False}) - settings["output_files"] = ("OutputTrajectoryConfigurator", {"format": "MDTFormat"}) + settings["output_file"] = ("OutputTrajectoryConfigurator", {"format": "MDTFormat"}) def initialize(self): """ """ @@ -135,7 +135,7 @@ def initialize(self): # Create trajectory self._output_trajectory = TrajectoryWriter( - self.configuration["output_files"]["files"][0], + self.configuration["output_file"]["file"], trajectory.chemical_system, self.configuration["frames"]["number"], selectedAtoms, @@ -226,7 +226,7 @@ def finalize(self): time, units={"time": "ps", "unit_cell": "nm", "coordinates": "nm"} ) - outputFile = h5py.File(self.configuration["output_files"]["files"][0], "r+") + outputFile = h5py.File(self.configuration["output_file"]["file"], "r+") n_groups = self.configuration["atom_selection"]["selection_length"] n_frames = self.configuration["frames"]["number"] diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py b/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py index 6c72a00694..fc6903d551 100644 --- a/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py +++ b/MDANSE/Src/MDANSE/MolecularDynamics/Connectivity.py @@ -20,7 +20,7 @@ from MDANSE.Chemistry import ATOMS_DATABASE from MDANSE.Chemistry.ChemicalEntity import ChemicalSystem -from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.MolecularDynamics.Trajectory import Trajectory class Connectivity: @@ -28,9 +28,9 @@ class Connectivity: and identifies potential molecules based on distances alone. """ - def __init__(self, *args, trajectory: HDFTrajectoryInputData = None, **kwargs): + def __init__(self, *args, trajectory: Trajectory = None, **kwargs): self._chemical_system = trajectory.chemical_system - self._frames = trajectory.trajectory + self._frames = trajectory self._unit_cell = self._chemical_system.configuration self._periodic = self._chemical_system.configuration.is_periodic self.check_composition(self._chemical_system) diff --git a/MDANSE/Tests/UnitTests/molecules/test_connectivity.py b/MDANSE/Tests/UnitTests/molecules/test_connectivity.py index 5d22457579..b2f4bee75f 100644 --- a/MDANSE/Tests/UnitTests/molecules/test_connectivity.py +++ b/MDANSE/Tests/UnitTests/molecules/test_connectivity.py @@ -3,6 +3,7 @@ import numpy as np from MDANSE.MolecularDynamics.Connectivity import Connectivity from MDANSE.Framework.InputData.HDFTrajectoryInputData import HDFTrajectoryInputData +from MDANSE.MolecularDynamics.Trajectory import Trajectory from MDANSE.Chemistry.Structrures import MoleculeTester @@ -12,30 +13,30 @@ @pytest.fixture(scope="module") -def trajectory() -> HDFTrajectoryInputData: +def trajectory() -> Trajectory: trajectory = HDFTrajectoryInputData(short_traj) - yield trajectory + yield trajectory.trajectory -def test_create_connectivity(trajectory: HDFTrajectoryInputData): +def test_create_connectivity(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) print(conn._unique_elements) assert len(conn._unique_elements) == 2 -def test_find_bonds(trajectory: HDFTrajectoryInputData): +def test_find_bonds(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) conn.find_bonds() assert len(conn._unique_bonds) == 40 -def test_find_molecules(trajectory: HDFTrajectoryInputData): +def test_find_molecules(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) conn.find_molecules() assert len(conn._molecules) == 20 -def test_rebuild_molecules(trajectory: HDFTrajectoryInputData): +def test_rebuild_molecules(trajectory: Trajectory): print(trajectory.chemical_system.atom_list) conn = Connectivity(trajectory=trajectory) conn.find_molecules() @@ -47,7 +48,7 @@ def test_rebuild_molecules(trajectory: HDFTrajectoryInputData): assert atoms_before == atoms_after -def test_unwrap_molecules(trajectory: HDFTrajectoryInputData): +def test_unwrap_molecules(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) conn.find_molecules() chemical_system = trajectory.chemical_system @@ -61,7 +62,7 @@ def test_unwrap_molecules(trajectory: HDFTrajectoryInputData): assert not np.allclose(original_coords, contiguous_config.coordinates) -def test_identify_molecules(trajectory: HDFTrajectoryInputData): +def test_identify_molecules(trajectory: Trajectory): conn = Connectivity(trajectory=trajectory) conn.find_molecules() chemical_system = trajectory.chemical_system diff --git a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/FloatWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/FloatWidget.py index 2486e6cb90..2a4c28a188 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/FloatWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/FloatWidget.py @@ -76,9 +76,6 @@ def newFloat(self, num: float): self._value = num self.updateValue() - @Slot() - def updateValue(self): - self._configurator.configure(self._value) - - def get_value(self): - return self._configurator["value"] + def get_widget_value(self): + """Collect the results from the input widgets and return the value.""" + return float(self._field.text()) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/IntegerWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/IntegerWidget.py index 284ec92efa..82a52b870e 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/IntegerWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/IntegerWidget.py @@ -73,9 +73,6 @@ def newInt(self, num: int): self._value = num self.updateValue() - @Slot() - def updateValue(self): - self._configurator.configure(self._value) - - def get_value(self): - return self._configurator["value"] + def get_widget_value(self): + """Collect the results from the input widgets and return the value.""" + return int(self._field.text()) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/OutputTrajectoryWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/OutputTrajectoryWidget.py index 225aa3b3a9..4c18c5baae 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/OutputTrajectoryWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/OutputTrajectoryWidget.py @@ -18,9 +18,8 @@ import os import os.path -from qtpy.QtWidgets import QComboBox, QLabel, QLineEdit, QPushButton, QFileDialog +from qtpy.QtWidgets import QComboBox, QLineEdit, QPushButton, QFileDialog from qtpy.QtCore import Qt, Slot -from qtpy.QtGui import QStandardItemModel, QStandardItem from MDANSE.MolecularDynamics.Trajectory import TrajectoryWriter from MDANSE_GUI.PyQtGUI.InputWidgets.WidgetBase import WidgetBase @@ -47,10 +46,10 @@ def __init__(self, *args, **kwargs): self.field = QLineEdit(default_value[0], self._base) self.dtype_box = QComboBox(self._base) self.dtype_box.addItems(["float16", "float32", "float64", "float128"]) - self.dtype_box.set_default("float64") + self.dtype_box.setCurrentText("float64") self.compression_box = QComboBox(self._base) self.compression_box.addItems(["none"] + TrajectoryWriter.allowed_compression) - self.compression_box.set_default("lza") + self.compression_box.setCurrentText("lzf") # self.type_box.setCurrentText(default_value[1]) browse_button = QPushButton("Browse", self._base) browse_button.clicked.connect(self.file_dialog) diff --git a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/StringWidget.py b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/StringWidget.py index f166a890f7..39f03eeab7 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/StringWidget.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/InputWidgets/StringWidget.py @@ -42,9 +42,6 @@ def default_labels(self): if self._tooltip == "": self._tooltip = "A text string variable. Do you know what to type in?" - @Slot() - def updateValue(self): - self._configurator.configure(self._field.text()) - - def get_value(self): - return self._configurator["value"] + def get_widget_value(self): + """Collect the results from the input widgets and return the value.""" + return self._field.text() diff --git a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/Widgets/GeneralWidgets.py b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/Widgets/GeneralWidgets.py index 1bbd85fdb9..368339c1b5 100644 --- a/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/Widgets/GeneralWidgets.py +++ b/MDANSE_GUI/Src/MDANSE_GUI/PyQtGUI/Widgets/GeneralWidgets.py @@ -161,8 +161,6 @@ def returnValue(self): if self.file_dialog is not None: ic(f"File Field Return Value: {self.current_value}") self.final_value.emit(self.current_value) - if self.file_direction == "out": - return (self.current_value, "MDTFormat") return self.current_value @Slot() @@ -229,6 +227,12 @@ def register_value(self, field: GeneralInput): self.fields.append(field) field.value_changed.connect(self.valueChanged) + def returnValue(self): + result = [] + for field in self.fields: + result.append(field.returnValue()) + return result + def translate_file_associations(input_str: str): """Takes the string describing valid file formats, as specified for @@ -297,6 +301,8 @@ def createInputField(*args, **kwargs): result = InputFactory.createFile(*args, direction="in", **kwargs) elif kind == "SingleOutputFileConfigurator": result = InputFactory.createFile(*args, direction="out", **kwargs) + elif kind == "OutputTrajectoryConfigurator": + result = InputFactory.createTrajectory(*args, direction="out", **kwargs) else: result = InputFactory.createBlank(*args, **kwargs) @@ -369,6 +375,59 @@ def createFile(*args, direction="in", **kwargs): layout.addWidget(button) return [base, data_handler] + def createTrajectory(*args, direction="out", **kwargs): + """Creates an input field with an additional button which + creates a FileDialog, to allow the user to browse the file system. + + Keyword Arguments: + direction -- if 'in', create a FileDialog for an exisitng file, + if 'out', a FileDialog for creating a new file. + """ + kind = kwargs.get("kind", "String") + default_value = kwargs.get("default", "") + tooltip_text = kwargs.get( + "tooltip", "Specify the name of the output trajectory." + ) + file_association = kwargs.get("wildcard", "") + qt_file_association = translate_file_associations(file_association) + base, layout = InputFactory.createBase(*args, **kwargs) + field = QLineEdit(base) + main_handler = InputGroup(base) + data_handler1 = GeneralInput( + data_type=str, + file_association=qt_file_association, + file_direction=direction, + **kwargs, + ) + data_handler2 = GeneralInput( + data_type=int, + ) + data_handler3 = GeneralInput( + data_type=str, + ) + main_handler.register_value(data_handler1) + main_handler.register_value(data_handler2) + main_handler.register_value(data_handler3) + data_handler1.string_value.connect(field.setText) + field.textChanged.connect(data_handler1.updateValue) + field.setText(str(default_value)) + field.setToolTip(tooltip_text) + layout.addWidget(field) + cbox1 = QComboBox(base) + cbox1.addItems(["16", "32", "64", "128"]) + cbox1.setCurrentText("64") + layout.addWidget(cbox1) + cbox2 = QComboBox(base) + cbox2.addItems(["no compression", "gzip"]) + cbox2.setCurrentText("gzip") + layout.addWidget(cbox2) + cbox1.currentTextChanged.connect(data_handler2.updateValue) + cbox2.currentTextChanged.connect(data_handler3.updateValue) + button = QPushButton("Browse", base) + button.clicked.connect(data_handler1.valueFromDialog) + layout.addWidget(button) + return [base, main_handler] + def createBool(*args, **kwargs): """Creates an input field for a logical variable, which is currently implemented as a check box.