pySimBlocks 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- examples/advanced/hardware/block_diagram.py +162 -0
- examples/advanced/hardware/camera.py +93 -0
- examples/advanced/hardware/gui_emio.py +231 -0
- examples/advanced/hardware/lmi.py +69 -0
- examples/advanced/hardware/main.py +57 -0
- examples/advanced/hardware/parameters.py +54 -0
- examples/advanced/qp_problem/params.py +10 -0
- examples/advanced/qp_problem/run.py +14 -0
- examples/advanced/sofa/gui/finger/Finger.py +108 -0
- examples/advanced/sofa/gui/finger/FingerController.py +53 -0
- examples/advanced/sofa/gui/finger/parameters_auto.py +22 -0
- examples/advanced/sofa/gui/sofa_plant/run.py +14 -0
- examples/advanced/sofa/python/Finger.py +107 -0
- examples/advanced/sofa/python/FingerController.py +48 -0
- examples/advanced/sofa/python/closedloop.py +64 -0
- examples/advanced/sofa/python/openloop.py +58 -0
- examples/basics/algebraic_function/g.py +7 -0
- examples/basics/algebraic_function/run.py +14 -0
- examples/basics/dc_motor/gui/run.py +14 -0
- examples/basics/dc_motor/multiple_order.py +91 -0
- examples/basics/dc_motor/openloop.py +62 -0
- examples/basics/dc_motor/pi.py +99 -0
- examples/basics/dc_motor/pid.py +118 -0
- examples/basics/dc_motor/pid_block.py +82 -0
- examples/basics/dc_motor/pid_block_vs_manual.py +143 -0
- examples/basics/external/parameters.py +4 -0
- examples/basics/external/run.py +14 -0
- examples/basics/file_sources/temp.py +16 -0
- examples/basics/observer.py +76 -0
- examples/basics/state_feedback/gui/parameters.py +9 -0
- examples/basics/state_feedback/gui/run.py +14 -0
- examples/basics/state_feedback/state_feedback.py +102 -0
- examples/basics/state_space/linear/gui/run.py +14 -0
- examples/basics/state_space/linear/ss_block.py +89 -0
- examples/basics/state_space/linear/ss_manual.py +105 -0
- examples/basics/state_space/non_linear/nlss.py +47 -0
- examples/basics/state_space/non_linear/run.py +14 -0
- examples/basics/state_space/polytope/params.py +39 -0
- examples/basics/state_space/polytope/weights.py +12 -0
- examples/comparison/simulink/convert.py +29 -0
- examples/comparison/simulink/openloop.py +127 -0
- examples/comparison/simulink/pi.py +148 -0
- examples/comparison/simulink/pid.py +154 -0
- examples/comparison/simulink/pid_backward.py +155 -0
- examples/quick_start/filter.py +35 -0
- examples/quick_start/gui/run.py +14 -0
- examples/tutorials/tutorial_1_python/main.py +99 -0
- examples/tutorials/tutorial_3_sofa/finger/Finger.py +120 -0
- examples/tutorials/tutorial_3_sofa/finger/FingerController.py +37 -0
- pySimBlocks/__init__.py +35 -0
- pySimBlocks/blocks/__init__.py +68 -0
- pySimBlocks/blocks/controllers/__init__.py +27 -0
- pySimBlocks/blocks/controllers/pid.py +250 -0
- pySimBlocks/blocks/controllers/state_feedback.py +141 -0
- pySimBlocks/blocks/interfaces/__init__.py +27 -0
- pySimBlocks/blocks/interfaces/external_input.py +107 -0
- pySimBlocks/blocks/interfaces/external_output.py +113 -0
- pySimBlocks/blocks/observers/__init__.py +23 -0
- pySimBlocks/blocks/observers/luenberger.py +175 -0
- pySimBlocks/blocks/operators/__init__.py +47 -0
- pySimBlocks/blocks/operators/algebraic_function.py +258 -0
- pySimBlocks/blocks/operators/dead_zone.py +186 -0
- pySimBlocks/blocks/operators/delay.py +254 -0
- pySimBlocks/blocks/operators/demux.py +123 -0
- pySimBlocks/blocks/operators/discrete_derivator.py +237 -0
- pySimBlocks/blocks/operators/discrete_integrator.py +265 -0
- pySimBlocks/blocks/operators/gain.py +294 -0
- pySimBlocks/blocks/operators/mux.py +131 -0
- pySimBlocks/blocks/operators/product.py +194 -0
- pySimBlocks/blocks/operators/rate_limiter.py +214 -0
- pySimBlocks/blocks/operators/saturation.py +178 -0
- pySimBlocks/blocks/operators/sum.py +188 -0
- pySimBlocks/blocks/operators/zero_order_hold.py +141 -0
- pySimBlocks/blocks/optimizers/__init__.py +23 -0
- pySimBlocks/blocks/optimizers/quadratic_program.py +326 -0
- pySimBlocks/blocks/sources/__init__.py +37 -0
- pySimBlocks/blocks/sources/chirp.py +168 -0
- pySimBlocks/blocks/sources/constant.py +82 -0
- pySimBlocks/blocks/sources/file_source.py +294 -0
- pySimBlocks/blocks/sources/function_source.py +197 -0
- pySimBlocks/blocks/sources/ramp.py +99 -0
- pySimBlocks/blocks/sources/sinusoidal.py +113 -0
- pySimBlocks/blocks/sources/step.py +138 -0
- pySimBlocks/blocks/sources/white_noise.py +107 -0
- pySimBlocks/blocks/systems/__init__.py +27 -0
- pySimBlocks/blocks/systems/linear_state_space.py +180 -0
- pySimBlocks/blocks/systems/non_linear_state_space.py +309 -0
- pySimBlocks/blocks/systems/polytopic_state_space.py +193 -0
- pySimBlocks/blocks/systems/sofa/__init__.py +30 -0
- pySimBlocks/blocks/systems/sofa/sofa_controller.py +472 -0
- pySimBlocks/blocks/systems/sofa/sofa_exchange_i_o.py +120 -0
- pySimBlocks/blocks/systems/sofa/sofa_plant.py +306 -0
- pySimBlocks/cli.py +127 -0
- pySimBlocks/core/__init__.py +32 -0
- pySimBlocks/core/block.py +167 -0
- pySimBlocks/core/block_source.py +94 -0
- pySimBlocks/core/config.py +90 -0
- pySimBlocks/core/fixed_time_manager.py +46 -0
- pySimBlocks/core/model.py +250 -0
- pySimBlocks/core/scheduler.py +30 -0
- pySimBlocks/core/simulator.py +329 -0
- pySimBlocks/core/task.py +73 -0
- pySimBlocks/docs/blocks/controllers/pid.md +78 -0
- pySimBlocks/docs/blocks/controllers/state_feedback.md +60 -0
- pySimBlocks/docs/blocks/interfaces/external_input.md +39 -0
- pySimBlocks/docs/blocks/interfaces/external_output.md +39 -0
- pySimBlocks/docs/blocks/observers/luenberger.md +70 -0
- pySimBlocks/docs/blocks/operators/algebraic_function.md +76 -0
- pySimBlocks/docs/blocks/operators/dead_zone.md +67 -0
- pySimBlocks/docs/blocks/operators/delay.md +76 -0
- pySimBlocks/docs/blocks/operators/demux.md +64 -0
- pySimBlocks/docs/blocks/operators/discrete_derivator.md +61 -0
- pySimBlocks/docs/blocks/operators/discrete_integrator.md +87 -0
- pySimBlocks/docs/blocks/operators/gain.md +97 -0
- pySimBlocks/docs/blocks/operators/mux.md +64 -0
- pySimBlocks/docs/blocks/operators/product.md +51 -0
- pySimBlocks/docs/blocks/operators/rate_limiter.md +70 -0
- pySimBlocks/docs/blocks/operators/saturation.md +62 -0
- pySimBlocks/docs/blocks/operators/sum.md +49 -0
- pySimBlocks/docs/blocks/operators/zero_order_hold.md +61 -0
- pySimBlocks/docs/blocks/optimizers/quadratic_program.md +98 -0
- pySimBlocks/docs/blocks/sources/chirp.md +102 -0
- pySimBlocks/docs/blocks/sources/constant.md +53 -0
- pySimBlocks/docs/blocks/sources/file_source.md +51 -0
- pySimBlocks/docs/blocks/sources/function_source.md +52 -0
- pySimBlocks/docs/blocks/sources/ramp.md +58 -0
- pySimBlocks/docs/blocks/sources/sinusoidal.md +60 -0
- pySimBlocks/docs/blocks/sources/step.md +63 -0
- pySimBlocks/docs/blocks/sources/white_noise.md +58 -0
- pySimBlocks/docs/blocks/systems/linear_state_space.md +66 -0
- pySimBlocks/docs/blocks/systems/non_linear_state_space.md +90 -0
- pySimBlocks/docs/blocks/systems/polytopic_state_space.md +72 -0
- pySimBlocks/docs/blocks/systems/sofa/sofa_exchange_i_o.md +63 -0
- pySimBlocks/docs/blocks/systems/sofa/sofa_plant.md +62 -0
- pySimBlocks/gui/__init__.py +20 -0
- pySimBlocks/gui/addons/__init__.py +19 -0
- pySimBlocks/gui/addons/sofa/__init__.py +19 -0
- pySimBlocks/gui/addons/sofa/sofa_dialog.py +190 -0
- pySimBlocks/gui/addons/sofa/sofa_service.py +209 -0
- pySimBlocks/gui/blocks/block_dialog_session.py +47 -0
- pySimBlocks/gui/blocks/block_meta.py +408 -0
- pySimBlocks/gui/blocks/controllers/pid.py +116 -0
- pySimBlocks/gui/blocks/controllers/state_feedback.py +80 -0
- pySimBlocks/gui/blocks/interfaces/external_input.py +64 -0
- pySimBlocks/gui/blocks/interfaces/external_output.py +63 -0
- pySimBlocks/gui/blocks/observers/luenberger.py +108 -0
- pySimBlocks/gui/blocks/operators/algebraic_function.py +232 -0
- pySimBlocks/gui/blocks/operators/dead_zone.py +79 -0
- pySimBlocks/gui/blocks/operators/delay.py +79 -0
- pySimBlocks/gui/blocks/operators/demux.py +98 -0
- pySimBlocks/gui/blocks/operators/discrete_derivator.py +67 -0
- pySimBlocks/gui/blocks/operators/discrete_integrator.py +80 -0
- pySimBlocks/gui/blocks/operators/gain.py +79 -0
- pySimBlocks/gui/blocks/operators/mux.py +100 -0
- pySimBlocks/gui/blocks/operators/product.py +109 -0
- pySimBlocks/gui/blocks/operators/rate_limiter.py +79 -0
- pySimBlocks/gui/blocks/operators/saturation.py +72 -0
- pySimBlocks/gui/blocks/operators/sum.py +91 -0
- pySimBlocks/gui/blocks/operators/zero_order_hold.py +64 -0
- pySimBlocks/gui/blocks/optimizers/quadratic_program.py +128 -0
- pySimBlocks/gui/blocks/parameter_meta.py +34 -0
- pySimBlocks/gui/blocks/port_meta.py +31 -0
- pySimBlocks/gui/blocks/sources/chirp.py +146 -0
- pySimBlocks/gui/blocks/sources/constant.py +63 -0
- pySimBlocks/gui/blocks/sources/file_source.py +196 -0
- pySimBlocks/gui/blocks/sources/function_source.py +202 -0
- pySimBlocks/gui/blocks/sources/ramp.py +73 -0
- pySimBlocks/gui/blocks/sources/sinusoidal.py +81 -0
- pySimBlocks/gui/blocks/sources/step.py +83 -0
- pySimBlocks/gui/blocks/sources/white_noise.py +70 -0
- pySimBlocks/gui/blocks/systems/linear_state_space.py +102 -0
- pySimBlocks/gui/blocks/systems/non_linear_state_space.py +233 -0
- pySimBlocks/gui/blocks/systems/polytopic_state_space.py +109 -0
- pySimBlocks/gui/blocks/systems/sofa/sofa_exchange_i_o.py +221 -0
- pySimBlocks/gui/blocks/systems/sofa/sofa_plant.py +220 -0
- pySimBlocks/gui/dialogs/__init__.py +19 -0
- pySimBlocks/gui/dialogs/block_dialog.py +125 -0
- pySimBlocks/gui/dialogs/display_yaml_dialog.py +89 -0
- pySimBlocks/gui/dialogs/help_dialog.py +39 -0
- pySimBlocks/gui/dialogs/plot_dialog.py +230 -0
- pySimBlocks/gui/dialogs/settings/plots.py +187 -0
- pySimBlocks/gui/dialogs/settings/project.py +146 -0
- pySimBlocks/gui/dialogs/settings/simulation.py +137 -0
- pySimBlocks/gui/dialogs/settings_dialog.py +88 -0
- pySimBlocks/gui/dialogs/unsaved_dialog.py +92 -0
- pySimBlocks/gui/editor.py +49 -0
- pySimBlocks/gui/graphics/__init__.py +29 -0
- pySimBlocks/gui/graphics/block_item.py +289 -0
- pySimBlocks/gui/graphics/connection_item.py +266 -0
- pySimBlocks/gui/graphics/port_item.py +153 -0
- pySimBlocks/gui/graphics/theme.py +119 -0
- pySimBlocks/gui/main_window.py +162 -0
- pySimBlocks/gui/models/__init__.py +31 -0
- pySimBlocks/gui/models/block_instance.py +114 -0
- pySimBlocks/gui/models/connection_instance.py +43 -0
- pySimBlocks/gui/models/port_instance.py +52 -0
- pySimBlocks/gui/models/project_simulation_params.py +50 -0
- pySimBlocks/gui/models/project_state.py +114 -0
- pySimBlocks/gui/project_controller.py +302 -0
- pySimBlocks/gui/services/__init__.py +20 -0
- pySimBlocks/gui/services/project_loader.py +381 -0
- pySimBlocks/gui/services/project_saver.py +68 -0
- pySimBlocks/gui/services/simulation_runner.py +66 -0
- pySimBlocks/gui/services/yaml_tools.py +261 -0
- pySimBlocks/gui/widgets/__init__.py +19 -0
- pySimBlocks/gui/widgets/block_list.py +131 -0
- pySimBlocks/gui/widgets/diagram_view.py +312 -0
- pySimBlocks/gui/widgets/toolbar_view.py +161 -0
- pySimBlocks/project/__init__.py +35 -0
- pySimBlocks/project/build_model.py +92 -0
- pySimBlocks/project/generate_run_script.py +109 -0
- pySimBlocks/project/generate_sofa_controller.py +215 -0
- pySimBlocks/project/load_project_config.py +197 -0
- pySimBlocks/project/load_simulation_config.py +166 -0
- pySimBlocks/project/load_simulator.py +45 -0
- pySimBlocks/project/plot_from_config.py +174 -0
- pySimBlocks/project/pySimBlocks_blocks_index.yaml +106 -0
- pySimBlocks/real_time/__init__.py +23 -0
- pySimBlocks/real_time/real_time_runner.py +148 -0
- pySimBlocks/tools/__init__.py +19 -0
- pySimBlocks/tools/blocks_registry.py +141 -0
- pySimBlocks/tools/generate_blocks_index.py +124 -0
- pysimblocks-0.1.0.dist-info/METADATA +180 -0
- pysimblocks-0.1.0.dist-info/RECORD +262 -0
- pysimblocks-0.1.0.dist-info/WHEEL +5 -0
- pysimblocks-0.1.0.dist-info/entry_points.txt +2 -0
- pysimblocks-0.1.0.dist-info/licenses/LICENSE.md +165 -0
- pysimblocks-0.1.0.dist-info/top_level.txt +3 -0
- tests/blocks/controllers/test_pid.py +143 -0
- tests/blocks/controllers/test_state_feedback.py +193 -0
- tests/blocks/observers/test_luenberger.py +225 -0
- tests/blocks/operators/test_algebraic_function.py +230 -0
- tests/blocks/operators/test_dead_zone.py +106 -0
- tests/blocks/operators/test_delay.py +167 -0
- tests/blocks/operators/test_demux.py +111 -0
- tests/blocks/operators/test_discrete_derivator.py +160 -0
- tests/blocks/operators/test_discrete_integrator.py +149 -0
- tests/blocks/operators/test_gain.py +182 -0
- tests/blocks/operators/test_mux.py +101 -0
- tests/blocks/operators/test_product.py +171 -0
- tests/blocks/operators/test_rate_limiter.py +166 -0
- tests/blocks/operators/test_saturation.py +157 -0
- tests/blocks/operators/test_sum.py +183 -0
- tests/blocks/operators/test_zero_order_hold.py +106 -0
- tests/blocks/sources/test_chirp.py +157 -0
- tests/blocks/sources/test_constant.py +55 -0
- tests/blocks/sources/test_file_source.py +185 -0
- tests/blocks/sources/test_function_source.py +135 -0
- tests/blocks/sources/test_ramp.py +110 -0
- tests/blocks/sources/test_sinusoidal.py +113 -0
- tests/blocks/sources/test_step.py +76 -0
- tests/blocks/sources/test_white_noise.py +108 -0
- tests/blocks/systems/test_linear_state_space.py +135 -0
- tests/blocks/systems/test_polytopic_state_space.py +133 -0
- tests/core/test_core_errors_and_determinism.py +82 -0
- tests/core/test_core_multirate_tasks.py +88 -0
- tests/core/test_core_two_phase_and_propagation.py +132 -0
- tests/gui/main_window_open_test.py +162 -0
- tests/gui/test_block_meta_param_cache.py +16 -0
- tests/gui/test_file_source_meta.py +17 -0
- tests/gui/test_function_source_meta.py +28 -0
- tests/regression/test_simulink_dc_motor.py +217 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
from pySimBlocks.gui.blocks.block_meta import BlockMeta
|
|
22
|
+
from pySimBlocks.gui.blocks.parameter_meta import ParameterMeta
|
|
23
|
+
from pySimBlocks.gui.blocks.port_meta import PortMeta
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ZeroOrderHoldMeta(BlockMeta):
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.name = "ZeroOrderHold"
|
|
30
|
+
self.category = "operators"
|
|
31
|
+
self.type = "zero_order_hold"
|
|
32
|
+
self.summary = "Zero-Order Hold (ZOH) block."
|
|
33
|
+
self.description = (
|
|
34
|
+
"Samples the input signal at discrete instants and holds its value\n"
|
|
35
|
+
"constant between sampling instants. The held output is updated when\n"
|
|
36
|
+
"the elapsed time since the last sampling instant reaches the sampling\n"
|
|
37
|
+
"period."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self.parameters = [
|
|
41
|
+
ParameterMeta(
|
|
42
|
+
name="sample_time",
|
|
43
|
+
type="float",
|
|
44
|
+
required=True
|
|
45
|
+
)
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
self.inputs = [
|
|
49
|
+
PortMeta(
|
|
50
|
+
name="in",
|
|
51
|
+
display_as="in",
|
|
52
|
+
shape=["n", "m"],
|
|
53
|
+
description="Input signal."
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
self.outputs = [
|
|
58
|
+
PortMeta(
|
|
59
|
+
name="out",
|
|
60
|
+
display_as="out",
|
|
61
|
+
shape=["n", "m"],
|
|
62
|
+
description="Held output signal."
|
|
63
|
+
)
|
|
64
|
+
]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
from pySimBlocks.gui.blocks.block_meta import BlockMeta
|
|
22
|
+
from pySimBlocks.gui.blocks.parameter_meta import ParameterMeta
|
|
23
|
+
from pySimBlocks.gui.blocks.port_meta import PortMeta
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class QuadraticProgramMeta(BlockMeta):
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.name = "QuadraticProgram"
|
|
30
|
+
self.category = "optimizers"
|
|
31
|
+
self.type = "quadratic_program"
|
|
32
|
+
self.summary = "General time-varying quadratic program (QP) solver."
|
|
33
|
+
self.description = (
|
|
34
|
+
"Solves at each simulation step the quadratic program:\n"
|
|
35
|
+
"$$\n"
|
|
36
|
+
"\\begin{aligned}\n"
|
|
37
|
+
"\\min_x \\quad & \\tfrac12 x^T P x + q^T x \\\\\n"
|
|
38
|
+
"\\text{s.t.} \\quad\n"
|
|
39
|
+
"& Gx \\le h \\\\\n"
|
|
40
|
+
"& Ax = b \\\\\n"
|
|
41
|
+
"& \\ell \\le x \\le u\n"
|
|
42
|
+
"\\end{aligned}\n"
|
|
43
|
+
"$$\n"
|
|
44
|
+
"All problem data are provided as inputs and may vary with time.\n"
|
|
45
|
+
"Inequality, equality, and bound constraints are optional."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self.parameters = [
|
|
49
|
+
ParameterMeta(
|
|
50
|
+
name="solver",
|
|
51
|
+
type="string",
|
|
52
|
+
autofill=True,
|
|
53
|
+
default="clarabel",
|
|
54
|
+
description="Name of the QP solver used by qpsolvers."
|
|
55
|
+
)
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
self.inputs = [
|
|
59
|
+
PortMeta(
|
|
60
|
+
name="P",
|
|
61
|
+
display_as="P",
|
|
62
|
+
shape=["n", "n"],
|
|
63
|
+
description="Quadratic cost matrix (Hessian). Must be square and compatible with size."
|
|
64
|
+
),
|
|
65
|
+
PortMeta(
|
|
66
|
+
name="q",
|
|
67
|
+
display_as="q",
|
|
68
|
+
shape=["n"],
|
|
69
|
+
description="Linear cost vector. Accepted shapes are (n,) or (n,1)."
|
|
70
|
+
),
|
|
71
|
+
PortMeta(
|
|
72
|
+
name="G",
|
|
73
|
+
display_as="G",
|
|
74
|
+
shape=["m", "n"],
|
|
75
|
+
description="Inequality constraint matrix. Must be provided together with h."
|
|
76
|
+
),
|
|
77
|
+
PortMeta(
|
|
78
|
+
name="h",
|
|
79
|
+
display_as="h",
|
|
80
|
+
shape=["m"],
|
|
81
|
+
description="Inequality constraint vector. Accepted shapes are (m,) or (m,1)."
|
|
82
|
+
),
|
|
83
|
+
PortMeta(
|
|
84
|
+
name="A",
|
|
85
|
+
display_as="A",
|
|
86
|
+
shape=["p", "n"],
|
|
87
|
+
description="Equality constraint matrix. Must be provided together with b."
|
|
88
|
+
),
|
|
89
|
+
PortMeta(
|
|
90
|
+
name="b",
|
|
91
|
+
display_as="b",
|
|
92
|
+
shape=["p"],
|
|
93
|
+
description="Equality constraint vector. Accepted shapes are (p,) or (p,1)."
|
|
94
|
+
),
|
|
95
|
+
PortMeta(
|
|
96
|
+
name="lb",
|
|
97
|
+
display_as="lb",
|
|
98
|
+
shape=["n"],
|
|
99
|
+
description="Lower bound on decision variables. Accepted shapes are (n,) or (n,1)."
|
|
100
|
+
),
|
|
101
|
+
PortMeta(
|
|
102
|
+
name="ub",
|
|
103
|
+
display_as="ub",
|
|
104
|
+
shape=["n"],
|
|
105
|
+
description="Upper bound on decision variables. Accepted shapes are (n,) or (n,1)."
|
|
106
|
+
)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
self.outputs = [
|
|
110
|
+
PortMeta(
|
|
111
|
+
name="x",
|
|
112
|
+
display_as="x",
|
|
113
|
+
shape=["n", 1],
|
|
114
|
+
description="Optimal solution of the quadratic program."
|
|
115
|
+
),
|
|
116
|
+
PortMeta(
|
|
117
|
+
name="status",
|
|
118
|
+
display_as="status",
|
|
119
|
+
shape=[1, 1],
|
|
120
|
+
description="Solver status code:\n0 = optimal solution found,\n1 = problem infeasible,\n2 = solver or numerical error."
|
|
121
|
+
),
|
|
122
|
+
PortMeta(
|
|
123
|
+
name="cost",
|
|
124
|
+
display_as="cost",
|
|
125
|
+
shape=[1, 1],
|
|
126
|
+
description="Optimal value of the quadratic cost function."
|
|
127
|
+
)
|
|
128
|
+
]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from typing import Any, List, Optional
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class ParameterMeta:
|
|
28
|
+
name: str
|
|
29
|
+
type: str
|
|
30
|
+
required: bool = False
|
|
31
|
+
autofill: bool = False
|
|
32
|
+
default: Optional[Any] = None
|
|
33
|
+
enum: List[Any] = field(default_factory=list)
|
|
34
|
+
description: str = ""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class PortMeta:
|
|
28
|
+
name: str
|
|
29
|
+
display_as: str
|
|
30
|
+
shape: list[Any]
|
|
31
|
+
description: str = ""
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
from pySimBlocks.gui.blocks.block_meta import BlockMeta
|
|
22
|
+
from pySimBlocks.gui.blocks.parameter_meta import ParameterMeta
|
|
23
|
+
from pySimBlocks.gui.blocks.port_meta import PortMeta
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ChirpMeta(BlockMeta):
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.name = "Chirp"
|
|
30
|
+
self.category = "sources"
|
|
31
|
+
self.type = "chirp"
|
|
32
|
+
self.summary = "Multi-dimensional chirp signal source (linear or logarithmic)."
|
|
33
|
+
|
|
34
|
+
self.description = (
|
|
35
|
+
"Generates a frequency-sweep (chirp) signal.\n\n"
|
|
36
|
+
|
|
37
|
+
"Linear mode:\n"
|
|
38
|
+
"$$\n"
|
|
39
|
+
"\\phi_i(t) = 2\\pi\\left(f_{0,i}\\tau + "
|
|
40
|
+
"\\tfrac{1}{2}k_i\\tau^2\\right) + \\varphi_i\n"
|
|
41
|
+
"$$\n"
|
|
42
|
+
"with:\n"
|
|
43
|
+
"$$\n"
|
|
44
|
+
"k_i = \\frac{f_{1,i} - f_{0,i}}{T_i}\n"
|
|
45
|
+
"$$\n\n"
|
|
46
|
+
|
|
47
|
+
"Logarithmic mode:\n"
|
|
48
|
+
"$$\n"
|
|
49
|
+
"\\phi_i(t) = "
|
|
50
|
+
"\\frac{2\\pi f_{0,i} T_i}{\\ln(f_{1,i}/f_{0,i})}"
|
|
51
|
+
"\\left[\\left(\\frac{f_{1,i}}{f_{0,i}}\\right)^{\\tau/T_i} - 1\\right]"
|
|
52
|
+
" + \\varphi_i\n"
|
|
53
|
+
"$$\n\n"
|
|
54
|
+
|
|
55
|
+
"Output:\n"
|
|
56
|
+
"$$\n"
|
|
57
|
+
"y_i(t) = A_i \\sin(\\phi_i(t)) + o_i\n"
|
|
58
|
+
"$$\n\n"
|
|
59
|
+
|
|
60
|
+
"with:\n"
|
|
61
|
+
"$$\n"
|
|
62
|
+
"\\tau = \\max(0, t - t_{0,i})\n"
|
|
63
|
+
"$$\n"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self.parameters = [
|
|
67
|
+
|
|
68
|
+
ParameterMeta(
|
|
69
|
+
name="amplitude",
|
|
70
|
+
type="scalar | vector | matrix",
|
|
71
|
+
required=True,
|
|
72
|
+
autofill=True,
|
|
73
|
+
default=[[1.0]],
|
|
74
|
+
description="Chirp amplitude."
|
|
75
|
+
),
|
|
76
|
+
|
|
77
|
+
ParameterMeta(
|
|
78
|
+
name="f0",
|
|
79
|
+
type="scalar | vector | matrix",
|
|
80
|
+
required=True,
|
|
81
|
+
autofill=True,
|
|
82
|
+
default=[[1.0]],
|
|
83
|
+
description="Start frequency in Hertz."
|
|
84
|
+
),
|
|
85
|
+
|
|
86
|
+
ParameterMeta(
|
|
87
|
+
name="f1",
|
|
88
|
+
type="scalar | vector | matrix",
|
|
89
|
+
required=True,
|
|
90
|
+
autofill=True,
|
|
91
|
+
default=[[10.0]],
|
|
92
|
+
description="End frequency in Hertz."
|
|
93
|
+
),
|
|
94
|
+
|
|
95
|
+
ParameterMeta(
|
|
96
|
+
name="duration",
|
|
97
|
+
type="scalar | vector | matrix",
|
|
98
|
+
required=True,
|
|
99
|
+
autofill=True,
|
|
100
|
+
default=[[5.0]],
|
|
101
|
+
description="Frequency sweep duration in seconds."
|
|
102
|
+
),
|
|
103
|
+
|
|
104
|
+
ParameterMeta(
|
|
105
|
+
name="mode",
|
|
106
|
+
type="enum",
|
|
107
|
+
required=True,
|
|
108
|
+
autofill=True,
|
|
109
|
+
default="linear",
|
|
110
|
+
enum=["linear", "log"],
|
|
111
|
+
description="Sweep mode: 'linear' or 'log'."
|
|
112
|
+
),
|
|
113
|
+
|
|
114
|
+
ParameterMeta(
|
|
115
|
+
name="start_time",
|
|
116
|
+
type="scalar | vector | matrix",
|
|
117
|
+
description="Time at which the chirp starts."
|
|
118
|
+
),
|
|
119
|
+
|
|
120
|
+
ParameterMeta(
|
|
121
|
+
name="phase",
|
|
122
|
+
type="scalar | vector | matrix",
|
|
123
|
+
description="Initial phase in radians."
|
|
124
|
+
),
|
|
125
|
+
|
|
126
|
+
ParameterMeta(
|
|
127
|
+
name="offset",
|
|
128
|
+
type="scalar | vector | matrix",
|
|
129
|
+
description="Constant offset added to the signal."
|
|
130
|
+
),
|
|
131
|
+
|
|
132
|
+
ParameterMeta(
|
|
133
|
+
name="sample_time",
|
|
134
|
+
type="float",
|
|
135
|
+
description="Block execution period."
|
|
136
|
+
)
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
self.outputs = [
|
|
140
|
+
PortMeta(
|
|
141
|
+
name="out",
|
|
142
|
+
display_as="out",
|
|
143
|
+
shape=["n", "m"],
|
|
144
|
+
description="Chirp output signal."
|
|
145
|
+
)
|
|
146
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
from pySimBlocks.gui.blocks.block_meta import BlockMeta
|
|
22
|
+
from pySimBlocks.gui.blocks.parameter_meta import ParameterMeta
|
|
23
|
+
from pySimBlocks.gui.blocks.port_meta import PortMeta
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ConstantMeta(BlockMeta):
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.name = "Constant"
|
|
30
|
+
self.category = "sources"
|
|
31
|
+
self.type = "constant"
|
|
32
|
+
self.summary = "Constant signal source."
|
|
33
|
+
self.description = (
|
|
34
|
+
"Generates a constant output signal:\n"
|
|
35
|
+
"$$\n"
|
|
36
|
+
"y(t) = c"
|
|
37
|
+
"$$\n"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self.parameters = [
|
|
41
|
+
ParameterMeta(
|
|
42
|
+
name = "value",
|
|
43
|
+
type = "scalar | vector | matrix",
|
|
44
|
+
required = True,
|
|
45
|
+
autofill = True,
|
|
46
|
+
default = [[1.0]],
|
|
47
|
+
description = "Constant output value."
|
|
48
|
+
),
|
|
49
|
+
|
|
50
|
+
ParameterMeta(
|
|
51
|
+
name = "sample_time",
|
|
52
|
+
type = "float",
|
|
53
|
+
)
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
self.outputs = [
|
|
57
|
+
PortMeta(
|
|
58
|
+
name="out",
|
|
59
|
+
display_as="out",
|
|
60
|
+
shape=["n", "m"],
|
|
61
|
+
description="Constant output signal."
|
|
62
|
+
)
|
|
63
|
+
]
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
# pySimBlocks
|
|
3
|
+
# Copyright (c) 2026 Université de Lille & INRIA
|
|
4
|
+
# ******************************************************************************
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify it
|
|
6
|
+
# under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or (at your
|
|
8
|
+
# option) any later version.
|
|
9
|
+
#
|
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
12
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
13
|
+
# for more details.
|
|
14
|
+
#
|
|
15
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
# ******************************************************************************
|
|
18
|
+
# Authors: see Authors.txt
|
|
19
|
+
# ******************************************************************************
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any, Dict
|
|
26
|
+
|
|
27
|
+
from PySide6.QtWidgets import QFormLayout, QLabel, QLineEdit, QPushButton
|
|
28
|
+
|
|
29
|
+
from pySimBlocks.gui.blocks.block_meta import BlockMeta
|
|
30
|
+
from pySimBlocks.gui.blocks.parameter_meta import ParameterMeta
|
|
31
|
+
from pySimBlocks.gui.blocks.port_meta import PortMeta
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FileSourceMeta(BlockMeta):
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self.name = "FileSource"
|
|
38
|
+
self.category = "sources"
|
|
39
|
+
self.type = "file_source"
|
|
40
|
+
self.summary = "Read a sequence of samples from a CSV, NPY, or NPZ file."
|
|
41
|
+
self.description = (
|
|
42
|
+
"Loads samples from file and outputs one sample per simulation step.\n\n"
|
|
43
|
+
"- `.npz`: reads one array from the archive (`key` mandatory).\n"
|
|
44
|
+
"- `.npy`: reads one array from a NPY file (`key` unused).\n"
|
|
45
|
+
"- `.csv`: reads one numeric CSV column (`key` = column name).\n\n"
|
|
46
|
+
"Each step emits one row as a column vector."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self.parameters = [
|
|
50
|
+
ParameterMeta(
|
|
51
|
+
name="file_path",
|
|
52
|
+
type="str",
|
|
53
|
+
required=True,
|
|
54
|
+
description="Path to the source file."
|
|
55
|
+
),
|
|
56
|
+
ParameterMeta(
|
|
57
|
+
name="key",
|
|
58
|
+
type="str",
|
|
59
|
+
description="NPZ array key or CSV column name."
|
|
60
|
+
),
|
|
61
|
+
ParameterMeta(
|
|
62
|
+
name="repeat",
|
|
63
|
+
type="enum",
|
|
64
|
+
autofill=True,
|
|
65
|
+
default=False,
|
|
66
|
+
enum=[False, True],
|
|
67
|
+
description="If true, replay samples from the beginning after end of file."
|
|
68
|
+
),
|
|
69
|
+
ParameterMeta(
|
|
70
|
+
name="use_time",
|
|
71
|
+
type="enum",
|
|
72
|
+
autofill=True,
|
|
73
|
+
default=False,
|
|
74
|
+
enum=[False, True],
|
|
75
|
+
description="If true (NPZ/CSV), use 'time' data and apply ZOH at simulation time t."
|
|
76
|
+
),
|
|
77
|
+
ParameterMeta(
|
|
78
|
+
name="sample_time",
|
|
79
|
+
type="float",
|
|
80
|
+
description="Block execution period."
|
|
81
|
+
),
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
self.outputs = [
|
|
85
|
+
PortMeta(
|
|
86
|
+
name="out",
|
|
87
|
+
display_as="out",
|
|
88
|
+
shape=["n", 1],
|
|
89
|
+
description="Current output sample."
|
|
90
|
+
)
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# ------------------------------------------------------
|
|
94
|
+
def is_parameter_active(self,
|
|
95
|
+
param_name: str,
|
|
96
|
+
instance_params: Dict[str, Any]) -> bool:
|
|
97
|
+
file_path = str(instance_params.get("file_path", "") or "")
|
|
98
|
+
ext = file_path.rsplit(".", 1)[-1].lower() if "." in file_path else ""
|
|
99
|
+
|
|
100
|
+
if param_name == "key":
|
|
101
|
+
return ext in {"npz", "csv"}
|
|
102
|
+
|
|
103
|
+
if param_name == "use_time":
|
|
104
|
+
return ext in {"npz", "csv"}
|
|
105
|
+
|
|
106
|
+
return super().is_parameter_active(param_name, instance_params)
|
|
107
|
+
|
|
108
|
+
# --------------------------------------------------------------------------
|
|
109
|
+
# Dialog Methods
|
|
110
|
+
# --------------------------------------------------------------------------
|
|
111
|
+
def build_param(
|
|
112
|
+
self,
|
|
113
|
+
session,
|
|
114
|
+
form: QFormLayout,
|
|
115
|
+
readonly: bool = False,
|
|
116
|
+
):
|
|
117
|
+
name_edit = QLineEdit(session.instance.name)
|
|
118
|
+
name_edit.textChanged.connect(
|
|
119
|
+
lambda val: self._on_param_changed(val, "name", session, readonly)
|
|
120
|
+
)
|
|
121
|
+
form.addRow(QLabel("Block name:"), name_edit)
|
|
122
|
+
if readonly:
|
|
123
|
+
name_edit.setReadOnly(True)
|
|
124
|
+
session.name_edit = name_edit
|
|
125
|
+
|
|
126
|
+
for pmeta in self.parameters:
|
|
127
|
+
if pmeta.name == "file_path":
|
|
128
|
+
self.build_file_param_row(
|
|
129
|
+
session,
|
|
130
|
+
form,
|
|
131
|
+
pmeta,
|
|
132
|
+
readonly=readonly,
|
|
133
|
+
file_filter="Data files (*.npz *.npy *.csv);;All files (*)",
|
|
134
|
+
)
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
label, widget = self._create_param_row(session, pmeta, readonly)
|
|
138
|
+
if widget is None:
|
|
139
|
+
continue
|
|
140
|
+
if readonly:
|
|
141
|
+
self._set_readonly_style(widget)
|
|
142
|
+
|
|
143
|
+
form.addRow(label, widget)
|
|
144
|
+
session.param_widgets[pmeta.name] = widget
|
|
145
|
+
session.param_labels[pmeta.name] = label
|
|
146
|
+
|
|
147
|
+
# ------------------------------------------------------
|
|
148
|
+
def build_post_param(self, session, form: QFormLayout, readonly: bool = False):
|
|
149
|
+
open_btn = QPushButton("Open file")
|
|
150
|
+
open_btn.clicked.connect(lambda: self._open_file_from_session(session))
|
|
151
|
+
form.addRow(QLabel(""), open_btn)
|
|
152
|
+
session.open_file_btn = open_btn
|
|
153
|
+
self._refresh_open_button_state(session)
|
|
154
|
+
|
|
155
|
+
# ------------------------------------------------------
|
|
156
|
+
def refresh_form(self, session):
|
|
157
|
+
super().refresh_form(session)
|
|
158
|
+
self._refresh_open_button_state(session)
|
|
159
|
+
|
|
160
|
+
# ------------------------------------------------------
|
|
161
|
+
def _resolve_file_path(self, session) -> Path | None:
|
|
162
|
+
raw = session.local_params.get("file_path")
|
|
163
|
+
if not raw:
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
path = Path(str(raw)).expanduser()
|
|
167
|
+
if not path.is_absolute() and session.project_dir is not None:
|
|
168
|
+
path = (session.project_dir / path).resolve()
|
|
169
|
+
return path
|
|
170
|
+
|
|
171
|
+
# ------------------------------------------------------
|
|
172
|
+
def _refresh_open_button_state(self, session) -> None:
|
|
173
|
+
btn = getattr(session, "open_file_btn", None)
|
|
174
|
+
if btn is None:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
target = self._resolve_file_path(session)
|
|
178
|
+
exists = target is not None and target.is_file()
|
|
179
|
+
btn.setEnabled(exists)
|
|
180
|
+
if exists:
|
|
181
|
+
btn.setToolTip(str(target))
|
|
182
|
+
else:
|
|
183
|
+
btn.setToolTip("Set a valid existing file_path to open the file.")
|
|
184
|
+
|
|
185
|
+
# ------------------------------------------------------
|
|
186
|
+
def _open_file_from_session(self, session) -> None:
|
|
187
|
+
target = self._resolve_file_path(session)
|
|
188
|
+
if target is None or not target.is_file():
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
if sys.platform.startswith("darwin"):
|
|
192
|
+
subprocess.Popen(["open", str(target)])
|
|
193
|
+
elif os.name == "nt":
|
|
194
|
+
os.startfile(str(target))
|
|
195
|
+
else:
|
|
196
|
+
subprocess.Popen(["xdg-open", str(target)])
|