diff --git a/examples/generate/external/layout.yaml b/examples/generate/external/layout.yaml index 05a58ca..e7e23cf 100644 --- a/examples/generate/external/layout.yaml +++ b/examples/generate/external/layout.yaml @@ -4,15 +4,23 @@ blocks: x: 17.0 y: -157.0 orientation: normal + width: 120.0 + height: 60.0 error: x: 196.0 y: -149.0 orientation: normal + width: 120.0 + height: 60.0 pid: x: 369.0 y: -149.0 orientation: normal + width: 120.0 + height: 60.0 plant: x: 530.0 y: -150.0 orientation: normal + width: 120.0 + height: 60.0 diff --git a/pySimBlocks/gui/dialogs/settings/project.py b/pySimBlocks/gui/dialogs/settings/project.py index eaf1c31..bd2335e 100644 --- a/pySimBlocks/gui/dialogs/settings/project.py +++ b/pySimBlocks/gui/dialogs/settings/project.py @@ -19,8 +19,9 @@ # ****************************************************************************** from pathlib import Path +import os from PySide6.QtWidgets import ( - QWidget, QFormLayout, QLabel, QLineEdit, QMessageBox, QPushButton, QComboBox + QWidget, QFormLayout, QLabel, QLineEdit, QMessageBox, QPushButton, QFileDialog, QHBoxLayout ) from pySimBlocks.gui.model.project_state import ProjectState @@ -40,7 +41,16 @@ def __init__(self, project_state: ProjectState, project_controller: ProjectContr layout.addRow(QLabel("Project Settings")) self.dir_edit = QLineEdit(str(project_state.directory_path)) - layout.addRow("Directory path:", self.dir_edit) + self.dir_browse_btn = QPushButton("...") + self.dir_browse_btn.setToolTip("Select a project directory from disk") + self.dir_browse_btn.clicked.connect(self.browse_project_directory) + + dir_layout = QHBoxLayout() + dir_layout.setContentsMargins(0, 0, 0, 0) + dir_layout.addWidget(self.dir_edit) + dir_layout.addWidget(self.dir_browse_btn) + + layout.addRow("Directory path:", dir_layout) load_btn = QPushButton("Load") load_btn.clicked.connect(self.load_project) @@ -50,9 +60,18 @@ def __init__(self, project_state: ProjectState, project_controller: ProjectContr ext = project_state.external or "" self.external_edit = QLineEdit(ext) + self.external_browse_btn = QPushButton("...") + self.external_browse_btn.setToolTip("Select a Python file from disk") + self.external_browse_btn.clicked.connect(self.browse_external_file) + + external_layout = QHBoxLayout() + external_layout.setContentsMargins(0, 0, 0, 0) + external_layout.addWidget(self.external_edit) + external_layout.addWidget(self.external_browse_btn) + label = QLabel("Python file:") label.setToolTip("Relative path from project directory") - layout.addRow(label, self.external_edit) + layout.addRow(label, external_layout) @@ -69,6 +88,49 @@ def apply(self) -> bool: self.project_controller.update_project_param(path, ext) return True + def browse_external_file(self): + base_dir = Path(self.dir_edit.text()).expanduser() + if not base_dir.is_dir(): + QMessageBox.warning( + self, + "Invalid directory", + f"The directory does not exist:\n{base_dir}", + ) + return + + selected_file, _ = QFileDialog.getOpenFileName( + self, + "Select Python file", + str(base_dir), + "Python files (*.py);;All files (*)", + ) + + if not selected_file: + return + + selected_path = Path(selected_file).resolve() + try: + relative_path = selected_path.relative_to(base_dir.resolve()) + except ValueError: + relative_path = Path(os.path.relpath(str(selected_path), str(base_dir.resolve()))) + + self.external_edit.setText(str(relative_path)) + + def browse_project_directory(self): + current_dir = Path(self.dir_edit.text()).expanduser() + start_dir = current_dir if current_dir.is_dir() else Path.cwd() + + selected_dir = QFileDialog.getExistingDirectory( + self, + "Select project directory", + str(start_dir), + ) + + if not selected_dir: + return + + self.dir_edit.setText(str(Path(selected_dir).resolve())) + def load_project(self): self.apply() self.project_controller.load_project(ProjectLoaderYaml()) diff --git a/pyproject.toml b/pyproject.toml index b940645..f782675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ pySimBlocks = [ [project.optional-dependencies] examples = [ "control", + "qpsolvers[osqp,clarabel]", ] tests = [ diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 7bc36ca..0000000 --- a/requirements.txt +++ /dev/null @@ -1,47 +0,0 @@ -altair==6.0.0 -attrs==25.4.0 -blinker==1.9.0 -cachetools==6.2.2 -certifi==2025.11.12 -charset-normalizer==3.4.4 -click==8.3.1 -contourpy==1.3.2 -control==0.10.2 -cycler==0.12.1 -fonttools==4.61.0 -gitdb==4.0.12 -GitPython==3.1.45 -graphviz==0.21 -idna==3.11 -Jinja2==3.1.6 -jsonschema==4.25.1 -jsonschema-specifications==2025.9.1 -kiwisolver==1.4.9 -MarkupSafe==3.0.3 -matplotlib==3.10.7 -narwhals==2.13.0 -numpy==2.2.6 -packaging==25.0 -pandas==2.3.3 -pillow==12.0.0 -protobuf==6.33.1 -pyarrow==22.0.0 -pydeck==0.9.1 -pyparsing==3.2.5 -python-dateutil==2.9.0.post0 -pytz==2025.2 -PyYAML==6.0.3 -referencing==0.37.0 -requests==2.32.5 -rpds-py==0.30.0 -scipy==1.15.3 -six==1.17.0 -smmap==5.0.2 -streamlit==1.52.0 -tenacity==9.1.2 -toml==0.10.2 -tornado==6.5.2 -typing_extensions==4.15.0 -tzdata==2025.2 -urllib3==2.5.0 -watchdog==6.0.0