More types and units

This commit is contained in:
2026-07-22 11:38:07 +02:00
parent 48ac9e2f59
commit edefb23edc
21 changed files with 1355 additions and 760 deletions

View File

@@ -60,6 +60,10 @@ Keep the root of `src/bedit` minimal. New UI modules should go into the relevant
indexes inputs and outputs separately for connection semantics. indexes inputs and outputs separately for connection semantics.
- Port types are registered in `core/port_types.py`. Only compatible types may be - Port types are registered in `core/port_types.py`. Only compatible types may be
connected. `signal` is currently the only type. connected. `signal` is currently the only type.
- Port connector type and signal value type are separate. Signal ports and
parameters carry editable value type, quantity, unit, row/column dimensions,
and description metadata. Editable quantity/unit suggestions live in
`core/physical_types.py`; unlisted values remain valid.
- Port removal or reorientation must be rejected when it would invalidate an - Port removal or reorientation must be rejected when it would invalidate an
existing connection. existing connection.
- Connections reference port IDs, never port names. - Connections reference port IDs, never port names.
@@ -168,6 +172,15 @@ Keep the root of `src/bedit` minimal. New UI modules should go into the relevant
`simulation/openModelicaPath`. The GUI passes it into `Simulation`; core must `simulation/openModelicaPath`. The GUI passes it into `Simulation`; core must
not read `QSettings`. An empty path uses OMPython/PATH discovery, while an not read `QSettings`. An empty path uses OMPython/PATH discovery, while an
explicit `.../bin/omc` path is converted to the OpenModelica home directory. explicit `.../bin/omc` path is converted to the OpenModelica home directory.
- Builds enable OpenModelica's `--unitChecking`. Completed simulation results
read per-column units from OMC's generated `<model>_init.xml`; BEdit must not
infer derivative units itself. This metadata is serialized with `.ber`/JSON
results and displayed by the simulation signal tree and plots.
- Result loading restores variables marked `alias` or `negatedAlias` in OMC's
initialization XML. Values come from OMC's result representative or a fixed
parameter/constant start value; BEdit does not infer aliases from graph edges.
- Compile requests call OMC `checkModel` before `buildModel` and log OMC's check
summary verbatim through the application-wide logger.
## Qt Designer and generated files ## Qt Designer and generated files

21
BEdit/m_Test.mo Normal file
View File

@@ -0,0 +1,21 @@
model m_Test
model m_Constant0
output Real y(quantity="Length", unit="m");
parameter Real v = 2;
equation
y = v;
end m_Constant0;
model m_Gain0
input Real u;
output Real y;
parameter Real k(quantity="Current", unit="A") = 2.5;
equation
y = k*u;
end m_Gain0;
m_Constant0 Constant0;
m_Gain0 Gain0;
equation
Gain0.u = Constant0.y;
annotation(
experiment(StartTime = 0, StopTime = 1, Tolerance = 1e-06, Interval = 0.002));
end m_Test;

View File

@@ -8,6 +8,19 @@ from uuid import uuid4
from bedit.core.port_types import PortTypeRegistry from bedit.core.port_types import PortTypeRegistry
def _dimensions(data: Any, subject: str) -> tuple[int, int]:
if not isinstance(data, dict):
raise ValueError(f"{subject} dimensions must be an object")
try:
rows = int(data.get("rows", 1))
columns = int(data.get("columns", 1))
except (TypeError, ValueError) as error:
raise ValueError(f"{subject} dimensions must be whole numbers") from error
if rows < 1 or columns < 1:
raise ValueError(f"{subject} dimensions must be at least 1")
return rows, columns
@dataclass @dataclass
class Port: class Port:
id: str id: str
@@ -17,6 +30,12 @@ class Port:
properties: dict[str, Any] = field(default_factory=dict) properties: dict[str, Any] = field(default_factory=dict)
type: str = "signal" type: str = "signal"
allows_multiple_connections: bool = False allows_multiple_connections: bool = False
value_type: str = "real"
quantity: str = ""
unit: str = ""
rows: int = 1
columns: int = 1
description: str = ""
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, Any]:
return { return {
@@ -26,11 +45,17 @@ class Port:
"properties": self.properties, "properties": self.properties,
"type": self.type, "type": self.type,
"multipleConnections": self.allows_multiple_connections, "multipleConnections": self.allows_multiple_connections,
"valueType": self.value_type,
"quantity": self.quantity,
"unit": self.unit,
"dimensions": {"rows": self.rows, "columns": self.columns},
"description": self.description,
} }
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> "Port": def from_dict(cls, data: dict[str, Any]) -> "Port":
position = data.get("position", {}) position = data.get("position", {})
rows, columns = _dimensions(data.get("dimensions", {}), "Port")
return cls( return cls(
id=str(data["id"]), id=str(data["id"]),
name=str(data.get("name", data["id"])), name=str(data.get("name", data["id"])),
@@ -39,6 +64,12 @@ class Port:
properties=dict(data.get("properties", {})), properties=dict(data.get("properties", {})),
type=str(data.get("type", "signal")), type=str(data.get("type", "signal")),
allows_multiple_connections=bool(data.get("multipleConnections", False)), allows_multiple_connections=bool(data.get("multipleConnections", False)),
value_type=str(data.get("valueType", "real")),
quantity=str(data.get("quantity", "")),
unit=str(data.get("unit", "")),
rows=rows,
columns=columns,
description=str(data.get("description", "")),
) )
@@ -166,9 +197,23 @@ class Parameter:
name: str name: str
type: str = "real" type: str = "real"
value: str = "0" value: str = "0"
quantity: str = ""
unit: str = ""
rows: int = 1
columns: int = 1
description: str = ""
def to_dict(self) -> dict[str, str]: def to_dict(self) -> dict[str, Any]:
return {"id": self.id, "name": self.name, "type": self.type, "value": self.value} return {
"id": self.id,
"name": self.name,
"type": self.type,
"value": self.value,
"quantity": self.quantity,
"unit": self.unit,
"dimensions": {"rows": self.rows, "columns": self.columns},
"description": self.description,
}
@classmethod @classmethod
def from_dict(cls, data: dict[str, Any]) -> "Parameter": def from_dict(cls, data: dict[str, Any]) -> "Parameter":
@@ -176,11 +221,17 @@ class Parameter:
raise ValueError("Each component parameter must be an object") raise ValueError("Each component parameter must be an object")
if "id" not in data: if "id" not in data:
raise ValueError("Each component parameter must have an ID") raise ValueError("Each component parameter must have an ID")
rows, columns = _dimensions(data.get("dimensions", {}), "Parameter")
return cls( return cls(
id=str(data["id"]), id=str(data["id"]),
name=str(data.get("name", "")), name=str(data.get("name", "")),
type=str(data.get("type", "real")), type=str(data.get("type", "real")),
value=str(data.get("value", "0")), value=str(data.get("value", "0")),
quantity=str(data.get("quantity", "")),
unit=str(data.get("unit", "")),
rows=rows,
columns=columns,
description=str(data.get("description", "")),
) )
@@ -453,6 +504,13 @@ class GraphDocument:
raise ValueError(f"Component {component.name!r} has duplicate parameter names") raise ValueError(f"Component {component.name!r} has duplicate parameter names")
if any(not name.strip() for name in parameter_names): if any(not name.strip() for name in parameter_names):
raise ValueError(f"Component {component.name!r} has an unnamed parameter") raise ValueError(f"Component {component.name!r} has an unnamed parameter")
if any(not parameter.type.strip() for parameter in component.parameters):
raise ValueError(f"Component {component.name!r} has an untyped parameter")
if any(
parameter.rows < 1 or parameter.columns < 1
for parameter in component.parameters
):
raise ValueError(f"Component {component.name!r} has invalid parameter dimensions")
if component.implementation_kind == "text" and component.graph.blocks: if component.implementation_kind == "text" and component.graph.blocks:
raise ValueError(f"Text component {component.name} cannot contain a graph") raise ValueError(f"Text component {component.name} cannot contain a graph")
self._validate_graph(component) self._validate_graph(component)
@@ -470,6 +528,10 @@ class GraphDocument:
raise ValueError(f"Component {owner.name} contains duplicate port IDs") raise ValueError(f"Component {owner.name} contains duplicate port IDs")
for port in (*owner.inputs, *owner.outputs): for port in (*owner.inputs, *owner.outputs):
PortTypeRegistry.get(port.type) PortTypeRegistry.get(port.type)
if not port.value_type.strip():
raise ValueError(f"Port {port.name!r} has no value type")
if port.rows < 1 or port.columns < 1:
raise ValueError(f"Port {port.name!r} has invalid dimensions")
for junction in owner.graph.junctions.values(): for junction in owner.graph.junctions.values():
PortTypeRegistry.get(junction.type) PortTypeRegistry.get(junction.type)
endpoint_counts: dict[tuple[str, str, str], int] = {} endpoint_counts: dict[tuple[str, str, str], int] = {}

View File

@@ -0,0 +1,47 @@
"""Editable suggestions for signal and parameter physical metadata.
These values populate editable combo boxes; documents may use values not listed
here. They are suggestions rather than a validation registry.
"""
VALUE_TYPES = ("real", "integer", "boolean", "string")
QUANTITIES = (
"",
"Angle",
"AngularVelocity",
"Current",
"Energy",
"Force",
"Frequency",
"Length",
"Mass",
"Power",
"Pressure",
"Temperature",
"Time",
"Torque",
"Velocity",
"Voltage",
)
UNITS = (
"",
"1",
"A",
"Hz",
"J",
"K",
"N",
"N.m",
"Pa",
"V",
"W",
"deg",
"kg",
"m",
"m/s",
"rad",
"rad/s",
"s",
)

View File

@@ -1,5 +1,5 @@
from bedit.core.simulation.service import Simulation from bedit.core.simulation.service import Simulation
from bedit.core.simulation.openmodelica import OpenModelicaInterface from bedit.core.simulation.openmodelica import ModelBuildResult, OpenModelicaInterface
from bedit.core.simulation.results import ( from bedit.core.simulation.results import (
BerSimulationResultsSerializer, BerSimulationResultsSerializer,
JsonSimulationResultsSerializer, JsonSimulationResultsSerializer,
@@ -12,6 +12,7 @@ from bedit.core.simulation.results import (
__all__ = [ __all__ = [
"OpenModelicaInterface", "OpenModelicaInterface",
"ModelBuildResult",
"Simulation", "Simulation",
"SimulationResults", "SimulationResults",
"SimulationExecutionResult", "SimulationExecutionResult",

View File

@@ -119,8 +119,11 @@ def emit_model(
parameter_type = modelica_type(parameter.get("type", "real")) parameter_type = modelica_type(parameter.get("type", "real"))
parameter_name = identifier(parameter["name"]) parameter_name = identifier(parameter["name"])
value = expand_bevalues(str(parameter.get("value", "0")), macros) value = expand_bevalues(str(parameter.get("value", "0")), macros)
dimensions = _fixed_dimensions(parameter)
attributes = _quantity_unit_attributes(parameter)
lines.append( lines.append(
f"{body_indent}parameter {parameter_type} {parameter_name} = {value};" f"{body_indent}parameter {parameter_type} {parameter_name}"
f"{dimensions}{attributes} = {value};"
) )
if implementation_kind == "text": if implementation_kind == "text":
@@ -213,14 +216,48 @@ def cleanup_graph(graph: dict[str, Any]) -> dict[str, Any]:
def _port_declaration( def _port_declaration(
port: dict[str, Any], direction: str, indent: int, macros: dict[str, str] port: dict[str, Any], direction: str, indent: int, macros: dict[str, str]
) -> str: ) -> str:
port_type = modelica_type(port.get("type", "signal")) port_type = modelica_type(port.get("valueType", "real"))
indentation = "\t" * indent indentation = "\t" * indent
port_name = identifier(port["name"]) port_name = identifier(port["name"])
dimension = f"[${port_name}_N$]" if port.get("multipleConnections", False) else "" dimensions = _port_dimensions(port, port_name)
declaration = f"{indentation}{direction} {port_type} {port_name}{dimension};" attributes = _quantity_unit_attributes(port)
declaration = (
f"{indentation}{direction} {port_type} {port_name}{dimensions}{attributes};"
)
return expand_bevalues(declaration, macros) return expand_bevalues(declaration, macros)
def _fixed_dimensions(item: dict[str, Any]) -> str:
dimensions = item.get("dimensions", {})
rows = max(1, int(dimensions.get("rows", 1)))
columns = max(1, int(dimensions.get("columns", 1)))
if rows == columns == 1:
return ""
if columns == 1:
return f"[{rows}]"
return f"[{rows},{columns}]"
def _port_dimensions(port: dict[str, Any], port_name: str) -> str:
fixed = _fixed_dimensions(port)
if not port.get("multipleConnections", False):
return fixed
entries = [f"${port_name}_N$"]
if fixed:
entries.extend(fixed[1:-1].split(","))
return f"[{','.join(entries)}]"
def _quantity_unit_attributes(item: dict[str, Any]) -> str:
attributes = []
for key in ("quantity", "unit"):
value = str(item.get(key, "")).strip()
if value:
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
attributes.append(f'{key}="{escaped}"')
return f"({', '.join(attributes)})" if attributes else ""
def _endpoint_expression( def _endpoint_expression(
endpoint: dict[str, Any], endpoint: dict[str, Any],
owner: dict[str, Any], owner: dict[str, Any],

View File

@@ -48,6 +48,14 @@ class SimulationMessage:
text: str text: str
@dataclass(frozen=True)
class ModelBuildResult:
"""OMC's model check summary and subsequent build response."""
check_summary: str
build_result: Any
class OpenModelicaInterface: class OpenModelicaInterface:
"""Asynchronous, persistent interface to one OpenModelica session. """Asynchronous, persistent interface to one OpenModelica session.
@@ -92,10 +100,21 @@ class OpenModelicaInterface:
"""Load and build one composed model as an ordered worker operation.""" """Load and build one composed model as an ordered worker operation."""
def operation(omc, _temp_dir: Path): def operation(omc, _temp_dir: Path):
unit_checking = omc.sendExpression(
'setCommandLineOptions("--unitChecking")'
)
if unit_checking is not True:
raise RuntimeError("OpenModelica could not enable unit checking")
loaded = omc.sendExpression(f"loadString({json.dumps(model)})") loaded = omc.sendExpression(f"loadString({json.dumps(model)})")
if loaded is not True: if loaded is not True:
raise RuntimeError("OpenModelica could not load the composed model") raise RuntimeError("OpenModelica could not load the composed model")
return omc.sendExpression(f"buildModel({model_name})") check_summary = omc.sendExpression(f"checkModel({model_name})")
if not isinstance(check_summary, str) or not check_summary.strip():
raise RuntimeError(
"OpenModelica did not return a model-check summary"
)
build_result = omc.sendExpression(f"buildModel({model_name})")
return ModelBuildResult(check_summary, build_result)
self._submit("build model", operation, callback, error_callback) self._submit("build model", operation, callback, error_callback)
@@ -334,16 +353,140 @@ def _run_model_with_tcp(
f"OpenModelica did not create the expected result file {result_path.name!r}" f"OpenModelica did not create the expected result file {result_path.name!r}"
) )
data = load_openmodelica_csv(result_path) data = load_openmodelica_csv(result_path)
init_path = temp_dir / f"{Path(executable).stem}_init.xml"
csv_column_count = len(data)
data = _add_openmodelica_alias_signals(init_path, data)
signal_metadata = _load_openmodelica_signal_metadata(init_path, data)
log.info( log.info(
"Loaded %d result columns from %s", len(data), result_path.name "Loaded %d result columns, restored %d OMC aliases, and found %d "
"unit definitions from %s",
csv_column_count,
len(data) - csv_column_count,
len(signal_metadata),
result_path.name,
) )
return SimulationExecutionResult( return SimulationExecutionResult(
return_code=int(return_code), return_code=int(return_code),
result_file=str(result_path), result_file=str(result_path),
data=data, data=data,
signal_metadata=signal_metadata,
) )
def _add_openmodelica_alias_signals(
path: Path, data: dict[str, list[float]]
) -> dict[str, list[float]]:
"""Restore result aliases exactly as described by OMC's init XML."""
try:
root = ET.parse(path).getroot()
except (OSError, ET.ParseError) as error:
raise RuntimeError(
f"Could not read OpenModelica aliases from {path.name}: {error}"
) from error
variables: dict[str, dict[str, str]] = {}
for scalar in root.findall(".//ScalarVariable"):
name = scalar.get("name", "")
if not name:
continue
value = next(iter(scalar), None)
variables[name] = {
"alias": scalar.get("alias", "noAlias"),
"target": scalar.get("aliasVariable", ""),
"variability": scalar.get("variability", ""),
"start": value.get("start", "") if value is not None else "",
}
restored = {name: list(values) for name, values in data.items()}
sample_count = len(next(iter(data.values()), []))
def resolve(name: str, visited: set[str] | None = None) -> list[float] | None:
if name in restored:
return restored[name]
variable = variables.get(name)
if variable is None:
return None
visited = set() if visited is None else visited
if name in visited:
return None
visited.add(name)
alias_kind = variable["alias"]
target = variable["target"]
if alias_kind in {"alias", "negatedAlias"} and target:
values = resolve(target, visited)
if values is None:
return None
return [-value for value in values] if alias_kind == "negatedAlias" else list(values)
if variable["variability"] not in {"parameter", "constant"}:
return None
try:
value = float(variable["start"])
except ValueError:
return None
return [value] * sample_count
for name, variable in variables.items():
if variable["alias"] not in {"alias", "negatedAlias"} or name in restored:
continue
values = resolve(name)
if values is not None:
restored[name] = values
return restored
def _load_openmodelica_signal_metadata(
path: Path, signal_names: dict[str, list[float]]
) -> dict[str, dict[str, str]]:
"""Read units reported by OMC in its generated initialization XML."""
try:
root = ET.parse(path).getroot()
except (OSError, ET.ParseError) as error:
raise RuntimeError(
f"Could not read OpenModelica unit metadata from {path.name}: {error}"
) from error
variables: dict[str, tuple[dict[str, str], str]] = {}
for scalar in root.findall(".//ScalarVariable"):
name = scalar.get("name", "")
if not name:
continue
value = next(iter(scalar), None)
metadata: dict[str, str] = {}
if value is not None:
for key in ("unit", "displayUnit", "quantity"):
attribute = value.get(key, "")
if attribute:
metadata[key] = attribute
description = scalar.get("description", "")
if description:
metadata["description"] = description
variables[name] = (metadata, scalar.get("aliasVariable", ""))
def resolve(name: str, visited: set[str] | None = None) -> dict[str, str]:
metadata, alias = variables.get(name, ({}, ""))
if metadata.get("unit") or not alias:
return dict(metadata)
visited = set() if visited is None else visited
if name in visited:
return dict(metadata)
visited.add(name)
inherited = resolve(alias, visited)
return {**inherited, **metadata}
metadata: dict[str, dict[str, str]] = {}
for name in signal_names:
if name not in variables:
continue
resolved = resolve(name)
if resolved:
metadata[name] = resolved
if "time" in signal_names:
metadata["time"] = {"unit": "s", "quantity": "Time"}
return metadata
def _accept_simulation_connection( def _accept_simulation_connection(
server: socket.socket, command_finished: Event server: socket.socket, command_finished: Event
) -> socket.socket: ) -> socket.socket:

View File

@@ -44,6 +44,7 @@ class SimulationResults:
status: dict[str, Any] = field(default_factory=dict) status: dict[str, Any] = field(default_factory=dict)
messages: list[dict[str, str]] = field(default_factory=list) messages: list[dict[str, str]] = field(default_factory=list)
data: dict[str, list[float]] = field(default_factory=dict) data: dict[str, list[float]] = field(default_factory=dict)
signal_metadata: dict[str, dict[str, str]] = field(default_factory=dict)
graphs: list[SimulationGraph] = field( graphs: list[SimulationGraph] = field(
default_factory=lambda: [SimulationGraph()] default_factory=lambda: [SimulationGraph()]
) )
@@ -57,6 +58,10 @@ class SimulationResults:
"status": dict(self.status), "status": dict(self.status),
"messages": [dict(message) for message in self.messages], "messages": [dict(message) for message in self.messages],
"data": {name: list(values) for name, values in self.data.items()}, "data": {name: list(values) for name, values in self.data.items()},
"signalMetadata": {
name: dict(metadata)
for name, metadata in self.signal_metadata.items()
},
"graphs": [asdict(graph) for graph in self.graphs], "graphs": [asdict(graph) for graph in self.graphs],
"metadata": dict(self.metadata), "metadata": dict(self.metadata),
} }
@@ -87,6 +92,15 @@ class SimulationResults:
str(name): [float(value) for value in values] str(name): [float(value) for value in values]
for name, values in dict(data.get("data", {})).items() for name, values in dict(data.get("data", {})).items()
}, },
signal_metadata={
str(name): {
str(key): str(value)
for key, value in dict(metadata).items()
}
for name, metadata in dict(
data.get("signalMetadata", {})
).items()
},
graphs=graphs, graphs=graphs,
metadata=dict(data.get("metadata", {})), metadata=dict(data.get("metadata", {})),
) )
@@ -101,6 +115,7 @@ class SimulationExecutionResult:
return_code: int return_code: int
result_file: str result_file: str
data: dict[str, list[float]] data: dict[str, list[float]]
signal_metadata: dict[str, dict[str, str]] = field(default_factory=dict)
def load_openmodelica_csv(path: str | Path) -> dict[str, list[float]]: def load_openmodelica_csv(path: str | Path) -> dict[str, list[float]]:

View File

@@ -5,6 +5,7 @@ from bedit.core.application_log import get_logger
from bedit.core.simulation.composer import compose_graph from bedit.core.simulation.composer import compose_graph
from bedit.core.simulation.openmodelica import ( from bedit.core.simulation.openmodelica import (
ErrorCallback, ErrorCallback,
ModelBuildResult,
OpenModelicaInterface, OpenModelicaInterface,
ResultCallback, ResultCallback,
SimulationMessage, SimulationMessage,
@@ -50,9 +51,19 @@ class Simulation:
self.compose_source(graph) self.compose_source(graph)
def _model_compiled(result): def _model_compiled(result):
log.info("Compiling OK: %s", result) if not isinstance(result, ModelBuildResult):
failure = RuntimeError(
f"OpenModelica returned an invalid build response: {result!r}"
)
if error_callback is not None:
error_callback(failure)
else:
log.error("%s", failure)
return
log.info("%s", result.check_summary.strip())
log.info("Compiling OK: %s", result.build_result)
try: try:
self.model_path = str(result[0]) self.model_path = str(result.build_result[0])
except (IndexError, TypeError) as error: except (IndexError, TypeError) as error:
failure = RuntimeError( failure = RuntimeError(
f"OpenModelica returned an invalid build result: {result!r}" f"OpenModelica returned an invalid build result: {result!r}"

View File

@@ -5,6 +5,7 @@ from PySide6.QtCore import Qt
from PySide6.QtWidgets import QDialog, QListWidgetItem, QMessageBox from PySide6.QtWidgets import QDialog, QListWidgetItem, QMessageBox
from bedit.core.model import Component, Parameter from bedit.core.model import Component, Parameter
from bedit.core.physical_types import QUANTITIES, UNITS
from bedit.gui.generated.ui_parameter_options_dialog import Ui_ParameterOptionsDialog from bedit.gui.generated.ui_parameter_options_dialog import Ui_ParameterOptionsDialog
@@ -22,19 +23,31 @@ class ParameterOptionsDialog(QDialog):
self.parameters = deepcopy(component.parameters) self.parameters = deepcopy(component.parameters)
self.read_only = read_only self.read_only = read_only
self._loading = False self._loading = False
self.ui.quantityCombo.addItems(QUANTITIES)
self.ui.unitCombo.addItems(UNITS)
self.ui.parameterList.currentRowChanged.connect(self._load_current) self.ui.parameterList.currentRowChanged.connect(self._load_current)
self.ui.addParameterButton.clicked.connect(self.add_parameter) self.ui.addParameterButton.clicked.connect(self.add_parameter)
self.ui.removeParameterButton.clicked.connect(self.remove_parameter) self.ui.removeParameterButton.clicked.connect(self.remove_parameter)
self.ui.nameEdit.textEdited.connect(self._store_current) self.ui.nameEdit.textEdited.connect(self._store_current)
self.ui.typeEdit.textEdited.connect(self._store_current) self.ui.typeCombo.currentTextChanged.connect(self._store_current)
self.ui.valueEdit.textEdited.connect(self._store_current) self.ui.valueEdit.textEdited.connect(self._store_current)
self.ui.quantityCombo.currentTextChanged.connect(self._store_current)
self.ui.unitCombo.currentTextChanged.connect(self._store_current)
self.ui.rowsSpin.valueChanged.connect(self._store_current)
self.ui.columnsSpin.valueChanged.connect(self._store_current)
self.ui.descriptionEdit.textChanged.connect(self._store_current)
self.ui.parameterSplitter.setSizes([250, 370]) self.ui.parameterSplitter.setSizes([250, 370])
if read_only: if read_only:
self.ui.addParameterButton.setEnabled(False) self.ui.addParameterButton.setEnabled(False)
self.ui.removeParameterButton.setEnabled(False) self.ui.removeParameterButton.setEnabled(False)
self.ui.nameEdit.setReadOnly(True) self.ui.nameEdit.setReadOnly(True)
self.ui.typeEdit.setReadOnly(True) self.ui.typeCombo.setEnabled(False)
self.ui.valueEdit.setReadOnly(True) self.ui.valueEdit.setReadOnly(True)
self.ui.quantityCombo.setEnabled(False)
self.ui.unitCombo.setEnabled(False)
self.ui.rowsSpin.setEnabled(False)
self.ui.columnsSpin.setEnabled(False)
self.ui.descriptionEdit.setReadOnly(True)
self._rebuild_list(0 if self.parameters else -1) self._rebuild_list(0 if self.parameters else -1)
def _rebuild_list(self, row: int = -1) -> None: def _rebuild_list(self, row: int = -1) -> None:
@@ -51,12 +64,20 @@ class ParameterOptionsDialog(QDialog):
if 0 <= row < len(self.parameters): if 0 <= row < len(self.parameters):
parameter = self.parameters[row] parameter = self.parameters[row]
self.ui.nameEdit.setText(parameter.name) self.ui.nameEdit.setText(parameter.name)
self.ui.typeEdit.setText(parameter.type) self.ui.typeCombo.setCurrentText(parameter.type)
self.ui.valueEdit.setText(parameter.value) self.ui.valueEdit.setText(parameter.value)
self.ui.quantityCombo.setCurrentText(parameter.quantity)
self.ui.unitCombo.setCurrentText(parameter.unit)
self.ui.rowsSpin.setValue(parameter.rows)
self.ui.columnsSpin.setValue(parameter.columns)
self.ui.descriptionEdit.setPlainText(parameter.description)
else: else:
self.ui.nameEdit.clear() self.ui.nameEdit.clear()
self.ui.typeEdit.clear() self.ui.typeCombo.setCurrentText("")
self.ui.valueEdit.clear() self.ui.valueEdit.clear()
self.ui.quantityCombo.setCurrentText("")
self.ui.unitCombo.setCurrentText("")
self.ui.descriptionEdit.clear()
self._loading = False self._loading = False
self._update_enabled() self._update_enabled()
@@ -64,8 +85,13 @@ class ParameterOptionsDialog(QDialog):
enabled = self.ui.parameterList.currentRow() >= 0 enabled = self.ui.parameterList.currentRow() >= 0
self.ui.removeParameterButton.setEnabled(enabled and not self.read_only) self.ui.removeParameterButton.setEnabled(enabled and not self.read_only)
self.ui.nameEdit.setEnabled(enabled) self.ui.nameEdit.setEnabled(enabled)
self.ui.typeEdit.setEnabled(enabled) self.ui.typeCombo.setEnabled(enabled and not self.read_only)
self.ui.valueEdit.setEnabled(enabled) self.ui.valueEdit.setEnabled(enabled)
self.ui.quantityCombo.setEnabled(enabled and not self.read_only)
self.ui.unitCombo.setEnabled(enabled and not self.read_only)
self.ui.rowsSpin.setEnabled(enabled and not self.read_only)
self.ui.columnsSpin.setEnabled(enabled and not self.read_only)
self.ui.descriptionEdit.setEnabled(enabled)
def _store_current(self) -> None: def _store_current(self) -> None:
row = self.ui.parameterList.currentRow() row = self.ui.parameterList.currentRow()
@@ -73,8 +99,13 @@ class ParameterOptionsDialog(QDialog):
return return
parameter = self.parameters[row] parameter = self.parameters[row]
parameter.name = self.ui.nameEdit.text() parameter.name = self.ui.nameEdit.text()
parameter.type = self.ui.typeEdit.text() parameter.type = self.ui.typeCombo.currentText().strip() or "real"
parameter.value = self.ui.valueEdit.text() parameter.value = self.ui.valueEdit.text()
parameter.quantity = self.ui.quantityCombo.currentText().strip()
parameter.unit = self.ui.unitCombo.currentText().strip()
parameter.rows = self.ui.rowsSpin.value()
parameter.columns = self.ui.columnsSpin.value()
parameter.description = self.ui.descriptionEdit.toPlainText()
self.ui.parameterList.item(row).setText( self.ui.parameterList.item(row).setText(
f"{parameter.name} [{parameter.type}] = {parameter.value}" f"{parameter.name} [{parameter.type}] = {parameter.value}"
) )

View File

@@ -5,6 +5,7 @@ from PySide6.QtCore import Qt
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, QMessageBox from PySide6.QtWidgets import QDialog, QDialogButtonBox, QListWidgetItem, QMessageBox
from bedit.core.model import Component, Port from bedit.core.model import Component, Port
from bedit.core.physical_types import QUANTITIES, UNITS
from bedit.core.port_types import PortTypeRegistry from bedit.core.port_types import PortTypeRegistry
from bedit.gui.generated.ui_port_options_dialog import Ui_PortOptionsDialog from bedit.gui.generated.ui_port_options_dialog import Ui_PortOptionsDialog
@@ -20,6 +21,7 @@ class PortOptionsDialog(QDialog):
self.ui = Ui_PortOptionsDialog() self.ui = Ui_PortOptionsDialog()
self.ui.setupUi(self) self.ui.setupUi(self)
self.setWindowTitle(f"Port Options — {component.name}") self.setWindowTitle(f"Port Options — {component.name}")
self.read_only = read_only
self.ports: list[tuple[Port, str]] = [ self.ports: list[tuple[Port, str]] = [
*((deepcopy(port), "input") for port in component.inputs), *((deepcopy(port), "input") for port in component.inputs),
*((deepcopy(port), "output") for port in component.outputs), *((deepcopy(port), "output") for port in component.outputs),
@@ -28,15 +30,23 @@ class PortOptionsDialog(QDialog):
self.ui.typeCombo.clear() self.ui.typeCombo.clear()
for port_type in PortTypeRegistry.all(): for port_type in PortTypeRegistry.all():
self.ui.typeCombo.addItem(port_type.display_name, port_type.id) self.ui.typeCombo.addItem(port_type.display_name, port_type.id)
self.ui.quantityCombo.addItems(QUANTITIES)
self.ui.unitCombo.addItems(UNITS)
self.ui.orientationCombo.setItemData(0, "input") self.ui.orientationCombo.setItemData(0, "input")
self.ui.orientationCombo.setItemData(1, "output") self.ui.orientationCombo.setItemData(1, "output")
self.ui.portList.currentRowChanged.connect(self._load_current) self.ui.portList.currentRowChanged.connect(self._load_current)
self.ui.addPortButton.clicked.connect(self.add_port) self.ui.addPortButton.clicked.connect(self.add_port)
self.ui.removePortButton.clicked.connect(self.remove_port) self.ui.removePortButton.clicked.connect(self.remove_port)
self.ui.nameEdit.textEdited.connect(self._store_current) self.ui.nameEdit.textEdited.connect(self._store_current)
self.ui.typeCombo.currentIndexChanged.connect(self._store_current) self.ui.typeCombo.currentIndexChanged.connect(self._port_type_changed)
self.ui.orientationCombo.currentIndexChanged.connect(self._store_current) self.ui.orientationCombo.currentIndexChanged.connect(self._store_current)
self.ui.multipleConnectionsCheckBox.toggled.connect(self._store_current) self.ui.multipleConnectionsCheckBox.toggled.connect(self._store_current)
self.ui.valueTypeCombo.currentTextChanged.connect(self._store_current)
self.ui.quantityCombo.currentTextChanged.connect(self._store_current)
self.ui.unitCombo.currentTextChanged.connect(self._store_current)
self.ui.rowsSpin.valueChanged.connect(self._store_current)
self.ui.columnsSpin.valueChanged.connect(self._store_current)
self.ui.descriptionEdit.textChanged.connect(self._store_current)
self.ui.portSplitter.setSizes([250, 370]) self.ui.portSplitter.setSizes([250, 370])
if read_only: if read_only:
self.ui.addPortButton.setEnabled(False) self.ui.addPortButton.setEnabled(False)
@@ -45,6 +55,7 @@ class PortOptionsDialog(QDialog):
self.ui.typeCombo.setEnabled(False) self.ui.typeCombo.setEnabled(False)
self.ui.orientationCombo.setEnabled(False) self.ui.orientationCombo.setEnabled(False)
self.ui.multipleConnectionsCheckBox.setEnabled(False) self.ui.multipleConnectionsCheckBox.setEnabled(False)
self.ui.typeOptionsStack.setEnabled(False)
self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("Close") self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("Close")
self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).hide() self.ui.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).hide()
self._rebuild_list(0 if self.ports else -1) self._rebuild_list(0 if self.ports else -1)
@@ -77,18 +88,38 @@ class PortOptionsDialog(QDialog):
self.ui.multipleConnectionsCheckBox.setChecked( self.ui.multipleConnectionsCheckBox.setChecked(
port.allows_multiple_connections port.allows_multiple_connections
) )
self.ui.valueTypeCombo.setCurrentText(port.value_type)
self.ui.quantityCombo.setCurrentText(port.quantity)
self.ui.unitCombo.setCurrentText(port.unit)
self.ui.rowsSpin.setValue(port.rows)
self.ui.columnsSpin.setValue(port.columns)
self.ui.descriptionEdit.setPlainText(port.description)
self._show_type_options(port.type)
else: else:
self.ui.nameEdit.clear() self.ui.nameEdit.clear()
self.ui.descriptionEdit.clear()
self._loading = False self._loading = False
self._update_enabled() self._update_enabled()
def _update_enabled(self) -> None: def _update_enabled(self) -> None:
enabled = self.ui.portList.currentRow() >= 0 enabled = self.ui.portList.currentRow() >= 0
self.ui.removePortButton.setEnabled(enabled) self.ui.removePortButton.setEnabled(enabled and not self.read_only)
self.ui.nameEdit.setEnabled(enabled) self.ui.nameEdit.setEnabled(enabled)
self.ui.typeCombo.setEnabled(enabled) self.ui.typeCombo.setEnabled(enabled and not self.read_only)
self.ui.orientationCombo.setEnabled(enabled) self.ui.orientationCombo.setEnabled(enabled and not self.read_only)
self.ui.multipleConnectionsCheckBox.setEnabled(enabled) self.ui.multipleConnectionsCheckBox.setEnabled(enabled and not self.read_only)
self.ui.typeOptionsStack.setEnabled(enabled and not self.read_only)
def _port_type_changed(self) -> None:
self._show_type_options(self.ui.typeCombo.currentData())
self._store_current()
def _show_type_options(self, port_type: str) -> None:
self.ui.typeOptionsStack.setCurrentWidget(
self.ui.signalOptionsPage
if port_type == "signal"
else self.ui.unsupportedTypePage
)
def _store_current(self) -> None: def _store_current(self) -> None:
row = self.ui.portList.currentRow() row = self.ui.portList.currentRow()
@@ -98,6 +129,12 @@ class PortOptionsDialog(QDialog):
port.name = self.ui.nameEdit.text() port.name = self.ui.nameEdit.text()
port.type = self.ui.typeCombo.currentData() port.type = self.ui.typeCombo.currentData()
port.allows_multiple_connections = self.ui.multipleConnectionsCheckBox.isChecked() port.allows_multiple_connections = self.ui.multipleConnectionsCheckBox.isChecked()
port.value_type = self.ui.valueTypeCombo.currentText().strip() or "real"
port.quantity = self.ui.quantityCombo.currentText().strip()
port.unit = self.ui.unitCombo.currentText().strip()
port.rows = self.ui.rowsSpin.value()
port.columns = self.ui.columnsSpin.value()
port.description = self.ui.descriptionEdit.toPlainText()
self.ports[row] = (port, self.ui.orientationCombo.currentData()) self.ports[row] = (port, self.ui.orientationCombo.currentData())
self.ui.portList.item(row).setText( self.ui.portList.item(row).setText(
f"{port.name} [{self.ports[row][1]}, {port.type}]" f"{port.name} [{self.ports[row][1]}, {port.type}]"

View File

@@ -26,10 +26,10 @@ class TextDefinitionEditor(QWidget):
self._modified = False self._modified = False
self._loading = False self._loading = False
self.ui.portsTable.horizontalHeader().setSectionResizeMode( self.ui.portsTable.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.Stretch QHeaderView.ResizeMode.Interactive
) )
self.ui.parametersTable.horizontalHeader().setSectionResizeMode( self.ui.parametersTable.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.Stretch QHeaderView.ResizeMode.Interactive
) )
self.ui.columnSplitter.setSizes([560, 340]) self.ui.columnSplitter.setSizes([560, 340])
self.ui.sourceSplitter.setSizes([180, 180, 240]) self.ui.sourceSplitter.setSizes([180, 180, 240])
@@ -78,6 +78,12 @@ class TextDefinitionEditor(QWidget):
type=type_combo.currentData(), type=type_combo.currentData(),
properties=deepcopy(name_item.data(PROPERTIES_ROLE) or {}), properties=deepcopy(name_item.data(PROPERTIES_ROLE) or {}),
allows_multiple_connections=multiple_check.isChecked(), allows_multiple_connections=multiple_check.isChecked(),
value_type=self.ui.portsTable.item(row, 4).text().strip() or "real",
quantity=self.ui.portsTable.item(row, 5).text().strip(),
unit=self.ui.portsTable.item(row, 6).text().strip(),
rows=int(self.ui.portsTable.item(row, 7).text()),
columns=int(self.ui.portsTable.item(row, 8).text()),
description=self.ui.portsTable.item(row, 9).text(),
) )
target = inputs if orientation_combo.currentData() == "input" else outputs target = inputs if orientation_combo.currentData() == "input" else outputs
target.append(port) target.append(port)
@@ -92,6 +98,11 @@ class TextDefinitionEditor(QWidget):
name=table.item(row, 0).text().strip(), name=table.item(row, 0).text().strip(),
type=table.item(row, 1).text().strip(), type=table.item(row, 1).text().strip(),
value=table.item(row, 2).text(), value=table.item(row, 2).text(),
quantity=table.item(row, 3).text().strip(),
unit=table.item(row, 4).text().strip(),
rows=int(table.item(row, 5).text()),
columns=int(table.item(row, 6).text()),
description=table.item(row, 7).text(),
) )
for row in range(table.rowCount()) for row in range(table.rowCount())
] ]
@@ -138,8 +149,17 @@ class TextDefinitionEditor(QWidget):
self._mark_modified() self._mark_modified()
def _refresh_editor_symbols(self) -> None: def _refresh_editor_symbols(self) -> None:
inputs, outputs = self.ports inputs: list[str] = []
self._set_editor_symbols(inputs, outputs, self.parameters) outputs: list[str] = []
for row in range(self.ui.portsTable.rowCount()):
name_item = self.ui.portsTable.item(row, 0)
orientation = self.ui.portsTable.cellWidget(row, 2).currentData()
(inputs if orientation == "input" else outputs).append(name_item.text())
parameters = [
self.ui.parametersTable.item(row, 0).text()
for row in range(self.ui.parametersTable.rowCount())
]
self._apply_editor_symbols(inputs, outputs, parameters)
def _set_editor_symbols( def _set_editor_symbols(
self, self,
@@ -147,11 +167,16 @@ class TextDefinitionEditor(QWidget):
outputs: list[Port], outputs: list[Port],
parameters: list[Parameter], parameters: list[Parameter],
) -> None: ) -> None:
symbols = ( self._apply_editor_symbols(
[port.name for port in inputs], [port.name for port in inputs],
[port.name for port in outputs], [port.name for port in outputs],
[parameter.name for parameter in parameters], [parameter.name for parameter in parameters],
) )
def _apply_editor_symbols(
self, inputs: list[str], outputs: list[str], parameters: list[str]
) -> None:
symbols = (inputs, outputs, parameters)
self.ui.declarationsEdit.set_symbols(*symbols) self.ui.declarationsEdit.set_symbols(*symbols)
self.ui.initialEquationsEdit.set_symbols(*symbols) self.ui.initialEquationsEdit.set_symbols(*symbols)
self.ui.equationsEdit.set_symbols(*symbols) self.ui.equationsEdit.set_symbols(*symbols)
@@ -183,6 +208,12 @@ class TextDefinitionEditor(QWidget):
multiple.setChecked(port.allows_multiple_connections) multiple.setChecked(port.allows_multiple_connections)
multiple.toggled.connect(self._symbols_modified) multiple.toggled.connect(self._symbols_modified)
table.setCellWidget(row, 3, multiple) table.setCellWidget(row, 3, multiple)
table.setItem(row, 4, QTableWidgetItem(port.value_type))
table.setItem(row, 5, QTableWidgetItem(port.quantity))
table.setItem(row, 6, QTableWidgetItem(port.unit))
table.setItem(row, 7, QTableWidgetItem(str(port.rows)))
table.setItem(row, 8, QTableWidgetItem(str(port.columns)))
table.setItem(row, 9, QTableWidgetItem(port.description))
def _append_parameter(self, parameter: Parameter) -> None: def _append_parameter(self, parameter: Parameter) -> None:
table = self.ui.parametersTable table = self.ui.parametersTable
@@ -193,6 +224,11 @@ class TextDefinitionEditor(QWidget):
table.setItem(row, 0, name) table.setItem(row, 0, name)
table.setItem(row, 1, QTableWidgetItem(parameter.type)) table.setItem(row, 1, QTableWidgetItem(parameter.type))
table.setItem(row, 2, QTableWidgetItem(parameter.value)) table.setItem(row, 2, QTableWidgetItem(parameter.value))
table.setItem(row, 3, QTableWidgetItem(parameter.quantity))
table.setItem(row, 4, QTableWidgetItem(parameter.unit))
table.setItem(row, 5, QTableWidgetItem(str(parameter.rows)))
table.setItem(row, 6, QTableWidgetItem(str(parameter.columns)))
table.setItem(row, 7, QTableWidgetItem(parameter.description))
def add_port(self) -> None: def add_port(self) -> None:
port = Port( port = Port(

View File

@@ -15,16 +15,17 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon, QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter, QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform) QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QDialog, QDialogButtonBox, from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
QFormLayout, QHBoxLayout, QLabel, QLineEdit, QDialogButtonBox, QFormLayout, QHBoxLayout, QLabel,
QListWidget, QListWidgetItem, QPushButton, QSizePolicy, QLineEdit, QListWidget, QListWidgetItem, QPlainTextEdit,
QSplitter, QVBoxLayout, QWidget) QPushButton, QSizePolicy, QSpinBox, QSplitter,
QVBoxLayout, QWidget)
class Ui_ParameterOptionsDialog(object): class Ui_ParameterOptionsDialog(object):
def setupUi(self, ParameterOptionsDialog): def setupUi(self, ParameterOptionsDialog):
if not ParameterOptionsDialog.objectName(): if not ParameterOptionsDialog.objectName():
ParameterOptionsDialog.setObjectName(u"ParameterOptionsDialog") ParameterOptionsDialog.setObjectName(u"ParameterOptionsDialog")
ParameterOptionsDialog.resize(620, 380) ParameterOptionsDialog.resize(720, 500)
self.dialogLayout = QVBoxLayout(ParameterOptionsDialog) self.dialogLayout = QVBoxLayout(ParameterOptionsDialog)
self.dialogLayout.setObjectName(u"dialogLayout") self.dialogLayout.setObjectName(u"dialogLayout")
self.parameterSplitter = QSplitter(ParameterOptionsDialog) self.parameterSplitter = QSplitter(ParameterOptionsDialog)
@@ -76,20 +77,82 @@ class Ui_ParameterOptionsDialog(object):
self.parameterDetailsForm.setWidget(1, QFormLayout.ItemRole.LabelRole, self.typeLabel) self.parameterDetailsForm.setWidget(1, QFormLayout.ItemRole.LabelRole, self.typeLabel)
self.typeEdit = QLineEdit(self.parameterDetailsPanel) self.typeCombo = QComboBox(self.parameterDetailsPanel)
self.typeEdit.setObjectName(u"typeEdit") self.typeCombo.addItem("")
self.typeCombo.addItem("")
self.typeCombo.addItem("")
self.typeCombo.addItem("")
self.typeCombo.setObjectName(u"typeCombo")
self.typeCombo.setEditable(True)
self.parameterDetailsForm.setWidget(1, QFormLayout.ItemRole.FieldRole, self.typeEdit) self.parameterDetailsForm.setWidget(1, QFormLayout.ItemRole.FieldRole, self.typeCombo)
self.rowsLabel = QLabel(self.parameterDetailsPanel)
self.rowsLabel.setObjectName(u"rowsLabel")
self.parameterDetailsForm.setWidget(2, QFormLayout.ItemRole.LabelRole, self.rowsLabel)
self.rowsSpin = QSpinBox(self.parameterDetailsPanel)
self.rowsSpin.setObjectName(u"rowsSpin")
self.rowsSpin.setMinimum(1)
self.rowsSpin.setMaximum(9999)
self.parameterDetailsForm.setWidget(2, QFormLayout.ItemRole.FieldRole, self.rowsSpin)
self.columnsLabel = QLabel(self.parameterDetailsPanel)
self.columnsLabel.setObjectName(u"columnsLabel")
self.parameterDetailsForm.setWidget(3, QFormLayout.ItemRole.LabelRole, self.columnsLabel)
self.columnsSpin = QSpinBox(self.parameterDetailsPanel)
self.columnsSpin.setObjectName(u"columnsSpin")
self.columnsSpin.setMinimum(1)
self.columnsSpin.setMaximum(9999)
self.parameterDetailsForm.setWidget(3, QFormLayout.ItemRole.FieldRole, self.columnsSpin)
self.valueLabel = QLabel(self.parameterDetailsPanel) self.valueLabel = QLabel(self.parameterDetailsPanel)
self.valueLabel.setObjectName(u"valueLabel") self.valueLabel.setObjectName(u"valueLabel")
self.parameterDetailsForm.setWidget(2, QFormLayout.ItemRole.LabelRole, self.valueLabel) self.parameterDetailsForm.setWidget(4, QFormLayout.ItemRole.LabelRole, self.valueLabel)
self.valueEdit = QLineEdit(self.parameterDetailsPanel) self.valueEdit = QLineEdit(self.parameterDetailsPanel)
self.valueEdit.setObjectName(u"valueEdit") self.valueEdit.setObjectName(u"valueEdit")
self.parameterDetailsForm.setWidget(2, QFormLayout.ItemRole.FieldRole, self.valueEdit) self.parameterDetailsForm.setWidget(4, QFormLayout.ItemRole.FieldRole, self.valueEdit)
self.quantityLabel = QLabel(self.parameterDetailsPanel)
self.quantityLabel.setObjectName(u"quantityLabel")
self.parameterDetailsForm.setWidget(5, QFormLayout.ItemRole.LabelRole, self.quantityLabel)
self.quantityCombo = QComboBox(self.parameterDetailsPanel)
self.quantityCombo.setObjectName(u"quantityCombo")
self.quantityCombo.setEditable(True)
self.parameterDetailsForm.setWidget(5, QFormLayout.ItemRole.FieldRole, self.quantityCombo)
self.unitLabel = QLabel(self.parameterDetailsPanel)
self.unitLabel.setObjectName(u"unitLabel")
self.parameterDetailsForm.setWidget(6, QFormLayout.ItemRole.LabelRole, self.unitLabel)
self.unitCombo = QComboBox(self.parameterDetailsPanel)
self.unitCombo.setObjectName(u"unitCombo")
self.unitCombo.setEditable(True)
self.parameterDetailsForm.setWidget(6, QFormLayout.ItemRole.FieldRole, self.unitCombo)
self.descriptionLabel = QLabel(self.parameterDetailsPanel)
self.descriptionLabel.setObjectName(u"descriptionLabel")
self.parameterDetailsForm.setWidget(7, QFormLayout.ItemRole.LabelRole, self.descriptionLabel)
self.descriptionEdit = QPlainTextEdit(self.parameterDetailsPanel)
self.descriptionEdit.setObjectName(u"descriptionEdit")
self.descriptionEdit.setMaximumHeight(100)
self.parameterDetailsForm.setWidget(7, QFormLayout.ItemRole.FieldRole, self.descriptionEdit)
self.parameterSplitter.addWidget(self.parameterDetailsPanel) self.parameterSplitter.addWidget(self.parameterDetailsPanel)
@@ -115,6 +178,16 @@ class Ui_ParameterOptionsDialog(object):
self.removeParameterButton.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Remove Parameter", None)) self.removeParameterButton.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Remove Parameter", None))
self.nameLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Name:", None)) self.nameLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Name:", None))
self.typeLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Type:", None)) self.typeLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Type:", None))
self.typeCombo.setItemText(0, QCoreApplication.translate("ParameterOptionsDialog", u"real", None))
self.typeCombo.setItemText(1, QCoreApplication.translate("ParameterOptionsDialog", u"integer", None))
self.typeCombo.setItemText(2, QCoreApplication.translate("ParameterOptionsDialog", u"boolean", None))
self.typeCombo.setItemText(3, QCoreApplication.translate("ParameterOptionsDialog", u"string", None))
self.rowsLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Rows:", None))
self.columnsLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Columns:", None))
self.valueLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Value:", None)) self.valueLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Value:", None))
self.quantityLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Quantity:", None))
self.unitLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Unit:", None))
self.descriptionLabel.setText(QCoreApplication.translate("ParameterOptionsDialog", u"Description:", None))
# retranslateUi # retranslateUi

View File

@@ -18,14 +18,14 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QComboBox, from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QComboBox,
QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout, QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout,
QLabel, QLineEdit, QListWidget, QListWidgetItem, QLabel, QLineEdit, QListWidget, QListWidgetItem,
QPushButton, QSizePolicy, QSplitter, QVBoxLayout, QPlainTextEdit, QPushButton, QSizePolicy, QSpinBox,
QWidget) QSplitter, QStackedWidget, QVBoxLayout, QWidget)
class Ui_PortOptionsDialog(object): class Ui_PortOptionsDialog(object):
def setupUi(self, PortOptionsDialog): def setupUi(self, PortOptionsDialog):
if not PortOptionsDialog.objectName(): if not PortOptionsDialog.objectName():
PortOptionsDialog.setObjectName(u"PortOptionsDialog") PortOptionsDialog.setObjectName(u"PortOptionsDialog")
PortOptionsDialog.resize(620, 380) PortOptionsDialog.resize(760, 421)
self.dialogLayout = QVBoxLayout(PortOptionsDialog) self.dialogLayout = QVBoxLayout(PortOptionsDialog)
self.dialogLayout.setObjectName(u"dialogLayout") self.dialogLayout.setObjectName(u"dialogLayout")
self.portSplitter = QSplitter(PortOptionsDialog) self.portSplitter = QSplitter(PortOptionsDialog)
@@ -61,6 +61,7 @@ class Ui_PortOptionsDialog(object):
self.portDetailsPanel.setObjectName(u"portDetailsPanel") self.portDetailsPanel.setObjectName(u"portDetailsPanel")
self.portDetailsForm = QFormLayout(self.portDetailsPanel) self.portDetailsForm = QFormLayout(self.portDetailsPanel)
self.portDetailsForm.setObjectName(u"portDetailsForm") self.portDetailsForm.setObjectName(u"portDetailsForm")
self.portDetailsForm.setVerticalSpacing(3)
self.portDetailsForm.setContentsMargins(0, 0, 0, 0) self.portDetailsForm.setContentsMargins(0, 0, 0, 0)
self.nameLabel = QLabel(self.portDetailsPanel) self.nameLabel = QLabel(self.portDetailsPanel)
self.nameLabel.setObjectName(u"nameLabel") self.nameLabel.setObjectName(u"nameLabel")
@@ -104,7 +105,99 @@ class Ui_PortOptionsDialog(object):
self.positionHintLabel.setObjectName(u"positionHintLabel") self.positionHintLabel.setObjectName(u"positionHintLabel")
self.positionHintLabel.setWordWrap(True) self.positionHintLabel.setWordWrap(True)
self.portDetailsForm.setWidget(4, QFormLayout.ItemRole.SpanningRole, self.positionHintLabel) self.portDetailsForm.setWidget(5, QFormLayout.ItemRole.SpanningRole, self.positionHintLabel)
self.typeOptionsStack = QStackedWidget(self.portDetailsPanel)
self.typeOptionsStack.setObjectName(u"typeOptionsStack")
self.signalOptionsPage = QWidget()
self.signalOptionsPage.setObjectName(u"signalOptionsPage")
self.signalOptionsForm = QFormLayout(self.signalOptionsPage)
self.signalOptionsForm.setObjectName(u"signalOptionsForm")
self.valueTypeLabel = QLabel(self.signalOptionsPage)
self.valueTypeLabel.setObjectName(u"valueTypeLabel")
self.signalOptionsForm.setWidget(0, QFormLayout.ItemRole.LabelRole, self.valueTypeLabel)
self.valueTypeCombo = QComboBox(self.signalOptionsPage)
self.valueTypeCombo.addItem("")
self.valueTypeCombo.addItem("")
self.valueTypeCombo.addItem("")
self.valueTypeCombo.addItem("")
self.valueTypeCombo.setObjectName(u"valueTypeCombo")
self.valueTypeCombo.setEditable(True)
self.signalOptionsForm.setWidget(0, QFormLayout.ItemRole.FieldRole, self.valueTypeCombo)
self.quantityLabel = QLabel(self.signalOptionsPage)
self.quantityLabel.setObjectName(u"quantityLabel")
self.signalOptionsForm.setWidget(1, QFormLayout.ItemRole.LabelRole, self.quantityLabel)
self.quantityCombo = QComboBox(self.signalOptionsPage)
self.quantityCombo.setObjectName(u"quantityCombo")
self.quantityCombo.setEditable(True)
self.signalOptionsForm.setWidget(1, QFormLayout.ItemRole.FieldRole, self.quantityCombo)
self.unitCombo = QComboBox(self.signalOptionsPage)
self.unitCombo.setObjectName(u"unitCombo")
self.unitCombo.setEditable(True)
self.signalOptionsForm.setWidget(2, QFormLayout.ItemRole.FieldRole, self.unitCombo)
self.unitLabel = QLabel(self.signalOptionsPage)
self.unitLabel.setObjectName(u"unitLabel")
self.signalOptionsForm.setWidget(2, QFormLayout.ItemRole.LabelRole, self.unitLabel)
self.rowsColumnsLabel = QLabel(self.signalOptionsPage)
self.rowsColumnsLabel.setObjectName(u"rowsColumnsLabel")
self.signalOptionsForm.setWidget(3, QFormLayout.ItemRole.LabelRole, self.rowsColumnsLabel)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.rowsSpin = QSpinBox(self.signalOptionsPage)
self.rowsSpin.setObjectName(u"rowsSpin")
self.rowsSpin.setMinimum(1)
self.rowsSpin.setMaximum(9999)
self.horizontalLayout.addWidget(self.rowsSpin)
self.columnsSpin = QSpinBox(self.signalOptionsPage)
self.columnsSpin.setObjectName(u"columnsSpin")
self.columnsSpin.setMinimum(1)
self.columnsSpin.setMaximum(9999)
self.horizontalLayout.addWidget(self.columnsSpin)
self.signalOptionsForm.setLayout(3, QFormLayout.ItemRole.FieldRole, self.horizontalLayout)
self.descriptionLabel = QLabel(self.signalOptionsPage)
self.descriptionLabel.setObjectName(u"descriptionLabel")
self.signalOptionsForm.setWidget(4, QFormLayout.ItemRole.LabelRole, self.descriptionLabel)
self.descriptionEdit = QPlainTextEdit(self.signalOptionsPage)
self.descriptionEdit.setObjectName(u"descriptionEdit")
self.signalOptionsForm.setWidget(4, QFormLayout.ItemRole.FieldRole, self.descriptionEdit)
self.typeOptionsStack.addWidget(self.signalOptionsPage)
self.unsupportedTypePage = QWidget()
self.unsupportedTypePage.setObjectName(u"unsupportedTypePage")
self.unsupportedTypeLayout = QVBoxLayout(self.unsupportedTypePage)
self.unsupportedTypeLayout.setObjectName(u"unsupportedTypeLayout")
self.unsupportedTypeLabel = QLabel(self.unsupportedTypePage)
self.unsupportedTypeLabel.setObjectName(u"unsupportedTypeLabel")
self.unsupportedTypeLabel.setWordWrap(True)
self.unsupportedTypeLayout.addWidget(self.unsupportedTypeLabel)
self.typeOptionsStack.addWidget(self.unsupportedTypePage)
self.portDetailsForm.setWidget(4, QFormLayout.ItemRole.SpanningRole, self.typeOptionsStack)
self.portSplitter.addWidget(self.portDetailsPanel) self.portSplitter.addWidget(self.portDetailsPanel)
@@ -129,7 +222,7 @@ class Ui_PortOptionsDialog(object):
self.addPortButton.setText(QCoreApplication.translate("PortOptionsDialog", u"Add Port", None)) self.addPortButton.setText(QCoreApplication.translate("PortOptionsDialog", u"Add Port", None))
self.removePortButton.setText(QCoreApplication.translate("PortOptionsDialog", u"Remove Port", None)) self.removePortButton.setText(QCoreApplication.translate("PortOptionsDialog", u"Remove Port", None))
self.nameLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Name:", None)) self.nameLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Name:", None))
self.typeLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Type:", None)) self.typeLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Port type:", None))
self.typeCombo.setItemText(0, QCoreApplication.translate("PortOptionsDialog", u"Signal", None)) self.typeCombo.setItemText(0, QCoreApplication.translate("PortOptionsDialog", u"Signal", None))
self.orientationLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Orientation:", None)) self.orientationLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Orientation:", None))
@@ -138,5 +231,16 @@ class Ui_PortOptionsDialog(object):
self.multipleConnectionsCheckBox.setText(QCoreApplication.translate("PortOptionsDialog", u"Allow multiple connections", None)) self.multipleConnectionsCheckBox.setText(QCoreApplication.translate("PortOptionsDialog", u"Allow multiple connections", None))
self.positionHintLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"New ports start at (0, 0) in the icon editor.", None)) self.positionHintLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"New ports start at (0, 0) in the icon editor.", None))
self.valueTypeLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Type:", None))
self.valueTypeCombo.setItemText(0, QCoreApplication.translate("PortOptionsDialog", u"real", None))
self.valueTypeCombo.setItemText(1, QCoreApplication.translate("PortOptionsDialog", u"integer", None))
self.valueTypeCombo.setItemText(2, QCoreApplication.translate("PortOptionsDialog", u"boolean", None))
self.valueTypeCombo.setItemText(3, QCoreApplication.translate("PortOptionsDialog", u"string", None))
self.quantityLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Quantity:", None))
self.unitLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Unit:", None))
self.rowsColumnsLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Matrix size:", None))
self.descriptionLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"Description:", None))
self.unsupportedTypeLabel.setText(QCoreApplication.translate("PortOptionsDialog", u"This port type does not have an options editor yet.", None))
# retranslateUi # retranslateUi

View File

@@ -78,8 +78,8 @@ class Ui_TextDefinitionEditor(object):
self.portsLayout = QVBoxLayout(self.portsGroup) self.portsLayout = QVBoxLayout(self.portsGroup)
self.portsLayout.setObjectName(u"portsLayout") self.portsLayout.setObjectName(u"portsLayout")
self.portsTable = QTableWidget(self.portsGroup) self.portsTable = QTableWidget(self.portsGroup)
if (self.portsTable.columnCount() < 4): if (self.portsTable.columnCount() < 10):
self.portsTable.setColumnCount(4) self.portsTable.setColumnCount(10)
__qtablewidgetitem = QTableWidgetItem() __qtablewidgetitem = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(0, __qtablewidgetitem) self.portsTable.setHorizontalHeaderItem(0, __qtablewidgetitem)
__qtablewidgetitem1 = QTableWidgetItem() __qtablewidgetitem1 = QTableWidgetItem()
@@ -88,10 +88,22 @@ class Ui_TextDefinitionEditor(object):
self.portsTable.setHorizontalHeaderItem(2, __qtablewidgetitem2) self.portsTable.setHorizontalHeaderItem(2, __qtablewidgetitem2)
__qtablewidgetitem3 = QTableWidgetItem() __qtablewidgetitem3 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(3, __qtablewidgetitem3) self.portsTable.setHorizontalHeaderItem(3, __qtablewidgetitem3)
__qtablewidgetitem4 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(4, __qtablewidgetitem4)
__qtablewidgetitem5 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(5, __qtablewidgetitem5)
__qtablewidgetitem6 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(6, __qtablewidgetitem6)
__qtablewidgetitem7 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(7, __qtablewidgetitem7)
__qtablewidgetitem8 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(8, __qtablewidgetitem8)
__qtablewidgetitem9 = QTableWidgetItem()
self.portsTable.setHorizontalHeaderItem(9, __qtablewidgetitem9)
self.portsTable.setObjectName(u"portsTable") self.portsTable.setObjectName(u"portsTable")
self.portsTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) self.portsTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.portsTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) self.portsTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.portsTable.setColumnCount(4) self.portsTable.setColumnCount(10)
self.portsLayout.addWidget(self.portsTable) self.portsLayout.addWidget(self.portsTable)
@@ -120,18 +132,28 @@ class Ui_TextDefinitionEditor(object):
self.parametersLayout = QVBoxLayout(self.parametersGroup) self.parametersLayout = QVBoxLayout(self.parametersGroup)
self.parametersLayout.setObjectName(u"parametersLayout") self.parametersLayout.setObjectName(u"parametersLayout")
self.parametersTable = QTableWidget(self.parametersGroup) self.parametersTable = QTableWidget(self.parametersGroup)
if (self.parametersTable.columnCount() < 3): if (self.parametersTable.columnCount() < 8):
self.parametersTable.setColumnCount(3) self.parametersTable.setColumnCount(8)
__qtablewidgetitem4 = QTableWidgetItem() __qtablewidgetitem10 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(0, __qtablewidgetitem4) self.parametersTable.setHorizontalHeaderItem(0, __qtablewidgetitem10)
__qtablewidgetitem5 = QTableWidgetItem() __qtablewidgetitem11 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(1, __qtablewidgetitem5) self.parametersTable.setHorizontalHeaderItem(1, __qtablewidgetitem11)
__qtablewidgetitem6 = QTableWidgetItem() __qtablewidgetitem12 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(2, __qtablewidgetitem6) self.parametersTable.setHorizontalHeaderItem(2, __qtablewidgetitem12)
__qtablewidgetitem13 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(3, __qtablewidgetitem13)
__qtablewidgetitem14 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(4, __qtablewidgetitem14)
__qtablewidgetitem15 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(5, __qtablewidgetitem15)
__qtablewidgetitem16 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(6, __qtablewidgetitem16)
__qtablewidgetitem17 = QTableWidgetItem()
self.parametersTable.setHorizontalHeaderItem(7, __qtablewidgetitem17)
self.parametersTable.setObjectName(u"parametersTable") self.parametersTable.setObjectName(u"parametersTable")
self.parametersTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) self.parametersTable.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
self.parametersTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) self.parametersTable.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
self.parametersTable.setColumnCount(3) self.parametersTable.setColumnCount(8)
self.parametersLayout.addWidget(self.parametersTable) self.parametersLayout.addWidget(self.parametersTable)
@@ -173,20 +195,42 @@ class Ui_TextDefinitionEditor(object):
___qtablewidgetitem = self.portsTable.horizontalHeaderItem(0) ___qtablewidgetitem = self.portsTable.horizontalHeaderItem(0)
___qtablewidgetitem.setText(QCoreApplication.translate("TextDefinitionEditor", u"Name", None)) ___qtablewidgetitem.setText(QCoreApplication.translate("TextDefinitionEditor", u"Name", None))
___qtablewidgetitem1 = self.portsTable.horizontalHeaderItem(1) ___qtablewidgetitem1 = self.portsTable.horizontalHeaderItem(1)
___qtablewidgetitem1.setText(QCoreApplication.translate("TextDefinitionEditor", u"Type", None)) ___qtablewidgetitem1.setText(QCoreApplication.translate("TextDefinitionEditor", u"Port Type", None))
___qtablewidgetitem2 = self.portsTable.horizontalHeaderItem(2) ___qtablewidgetitem2 = self.portsTable.horizontalHeaderItem(2)
___qtablewidgetitem2.setText(QCoreApplication.translate("TextDefinitionEditor", u"Orientation", None)) ___qtablewidgetitem2.setText(QCoreApplication.translate("TextDefinitionEditor", u"Orientation", None))
___qtablewidgetitem3 = self.portsTable.horizontalHeaderItem(3) ___qtablewidgetitem3 = self.portsTable.horizontalHeaderItem(3)
___qtablewidgetitem3.setText(QCoreApplication.translate("TextDefinitionEditor", u"Multiple", None)) ___qtablewidgetitem3.setText(QCoreApplication.translate("TextDefinitionEditor", u"Multiple", None))
___qtablewidgetitem4 = self.portsTable.horizontalHeaderItem(4)
___qtablewidgetitem4.setText(QCoreApplication.translate("TextDefinitionEditor", u"Value Type", None))
___qtablewidgetitem5 = self.portsTable.horizontalHeaderItem(5)
___qtablewidgetitem5.setText(QCoreApplication.translate("TextDefinitionEditor", u"Quantity", None))
___qtablewidgetitem6 = self.portsTable.horizontalHeaderItem(6)
___qtablewidgetitem6.setText(QCoreApplication.translate("TextDefinitionEditor", u"Unit", None))
___qtablewidgetitem7 = self.portsTable.horizontalHeaderItem(7)
___qtablewidgetitem7.setText(QCoreApplication.translate("TextDefinitionEditor", u"Rows", None))
___qtablewidgetitem8 = self.portsTable.horizontalHeaderItem(8)
___qtablewidgetitem8.setText(QCoreApplication.translate("TextDefinitionEditor", u"Columns", None))
___qtablewidgetitem9 = self.portsTable.horizontalHeaderItem(9)
___qtablewidgetitem9.setText(QCoreApplication.translate("TextDefinitionEditor", u"Description", None))
self.addPortButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Add Port", None)) self.addPortButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Add Port", None))
self.removePortButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Remove Port", None)) self.removePortButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Remove Port", None))
self.parametersGroup.setTitle(QCoreApplication.translate("TextDefinitionEditor", u"Parameters", None)) self.parametersGroup.setTitle(QCoreApplication.translate("TextDefinitionEditor", u"Parameters", None))
___qtablewidgetitem4 = self.parametersTable.horizontalHeaderItem(0) ___qtablewidgetitem10 = self.parametersTable.horizontalHeaderItem(0)
___qtablewidgetitem4.setText(QCoreApplication.translate("TextDefinitionEditor", u"Name", None)) ___qtablewidgetitem10.setText(QCoreApplication.translate("TextDefinitionEditor", u"Name", None))
___qtablewidgetitem5 = self.parametersTable.horizontalHeaderItem(1) ___qtablewidgetitem11 = self.parametersTable.horizontalHeaderItem(1)
___qtablewidgetitem5.setText(QCoreApplication.translate("TextDefinitionEditor", u"Type", None)) ___qtablewidgetitem11.setText(QCoreApplication.translate("TextDefinitionEditor", u"Type", None))
___qtablewidgetitem6 = self.parametersTable.horizontalHeaderItem(2) ___qtablewidgetitem12 = self.parametersTable.horizontalHeaderItem(2)
___qtablewidgetitem6.setText(QCoreApplication.translate("TextDefinitionEditor", u"Value", None)) ___qtablewidgetitem12.setText(QCoreApplication.translate("TextDefinitionEditor", u"Value", None))
___qtablewidgetitem13 = self.parametersTable.horizontalHeaderItem(3)
___qtablewidgetitem13.setText(QCoreApplication.translate("TextDefinitionEditor", u"Quantity", None))
___qtablewidgetitem14 = self.parametersTable.horizontalHeaderItem(4)
___qtablewidgetitem14.setText(QCoreApplication.translate("TextDefinitionEditor", u"Unit", None))
___qtablewidgetitem15 = self.parametersTable.horizontalHeaderItem(5)
___qtablewidgetitem15.setText(QCoreApplication.translate("TextDefinitionEditor", u"Rows", None))
___qtablewidgetitem16 = self.parametersTable.horizontalHeaderItem(6)
___qtablewidgetitem16.setText(QCoreApplication.translate("TextDefinitionEditor", u"Columns", None))
___qtablewidgetitem17 = self.parametersTable.horizontalHeaderItem(7)
___qtablewidgetitem17.setText(QCoreApplication.translate("TextDefinitionEditor", u"Description", None))
self.addParameterButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Add Parameter", None)) self.addParameterButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Add Parameter", None))
self.removeParameterButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Remove Parameter", None)) self.removeParameterButton.setText(QCoreApplication.translate("TextDefinitionEditor", u"Remove Parameter", None))
pass pass

View File

@@ -280,7 +280,20 @@ class SimulationWindow(QMainWindow):
items[path] = item items[path] = item
parent = item parent = item
parent.setData(0, Qt.ItemDataRole.UserRole, signal_name) parent.setData(0, Qt.ItemDataRole.UserRole, signal_name)
parent.setToolTip(0, signal_name) metadata = self.results.signal_metadata.get(signal_name, {})
unit = metadata.get("unit", "")
if unit:
parent.setText(0, f"{parts[-1]} [{unit}]")
tooltip = signal_name
details = [
metadata.get("quantity", ""),
f"Unit: {unit}" if unit else "",
metadata.get("description", ""),
]
details = [detail for detail in details if detail]
if details:
tooltip += "\n" + "\n".join(details)
parent.setToolTip(0, tooltip)
parent.setFlags(parent.flags() | Qt.ItemFlag.ItemIsUserCheckable) parent.setFlags(parent.flags() | Qt.ItemFlag.ItemIsUserCheckable)
parent.setCheckState(0, Qt.CheckState.Unchecked) parent.setCheckState(0, Qt.CheckState.Unchecked)
tree.expandToDepth(0) tree.expandToDepth(0)
@@ -334,7 +347,14 @@ class SimulationWindow(QMainWindow):
(trace for trace in graph.traces if trace.name == signal_name), None (trace for trace in graph.traces if trace.name == signal_name), None
) )
if enabled and existing is None: if enabled and existing is None:
graph.traces.append(SimulationTrace(name=signal_name)) metadata = self.results.signal_metadata.get(signal_name, {})
graph.traces.append(
SimulationTrace(
name=signal_name,
y_label=metadata.get("quantity", ""),
unit=metadata.get("unit", ""),
)
)
elif not enabled and existing is not None: elif not enabled and existing is not None:
graph.traces.remove(existing) graph.traces.remove(existing)
page = self.ui.graphTabs.currentWidget() page = self.ui.graphTabs.currentWidget()
@@ -492,6 +512,7 @@ class SimulationWindow(QMainWindow):
self.results.status.update(phase="Simulation finished", progress=10000) self.results.status.update(phase="Simulation finished", progress=10000)
if isinstance(result, SimulationExecutionResult): if isinstance(result, SimulationExecutionResult):
self.results.data = result.data self.results.data = result.data
self.results.signal_metadata = result.signal_metadata
available_signals = set(result.data) available_signals = set(result.data)
for graph in self.results.graphs: for graph in self.results.graphs:
graph.traces = [ graph.traces = [
@@ -499,6 +520,10 @@ class SimulationWindow(QMainWindow):
for trace in graph.traces for trace in graph.traces
if trace.name in available_signals if trace.name in available_signals
] ]
for trace in graph.traces:
metadata = result.signal_metadata.get(trace.name, {})
trace.unit = metadata.get("unit", "")
trace.y_label = metadata.get("quantity", "")
if graph.x_axis not in available_signals: if graph.x_axis not in available_signals:
graph.x_axis = ( graph.x_axis = (
"time" "time"
@@ -570,14 +595,38 @@ class GraphWorkspacePage(QWidget):
horizontal = x_values if x_values is not None else range(len(y_values)) horizontal = x_values if x_values is not None else range(len(y_values))
sample_count = min(len(horizontal), len(y_values)) sample_count = min(len(horizontal), len(y_values))
color = trace.properties.get("color") color = trace.properties.get("color")
unit = trace.unit or self.results.signal_metadata.get(
trace.name, {}
).get("unit", "")
label = f"{trace.name} [{unit}]" if unit else trace.name
self.axes.plot( self.axes.plot(
list(horizontal)[:sample_count], list(horizontal)[:sample_count],
y_values[:sample_count], y_values[:sample_count],
label=trace.name, label=label,
color=color if isinstance(color, str) and color else None, color=color if isinstance(color, str) and color else None,
) )
self.axes.set_title(self.graph.title) self.axes.set_title(self.graph.title)
self.axes.set_xlabel(self.graph.x_axis if x_values is not None else "sample") if x_values is not None:
x_unit = self.results.signal_metadata.get(
self.graph.x_axis, {}
).get("unit", "")
x_label = (
f"{self.graph.x_axis} [{x_unit}]"
if x_unit
else self.graph.x_axis
)
else:
x_label = "sample"
self.axes.set_xlabel(x_label)
units = {
trace.unit
or self.results.signal_metadata.get(trace.name, {}).get("unit", "")
for trace in self.graph.traces
if self.results.data.get(trace.name) is not None
}
units.discard("")
if len(units) == 1:
self.axes.set_ylabel(next(iter(units)))
self.axes.grid(True, alpha=0.25) self.axes.grid(True, alpha=0.25)
if self.axes.lines: if self.axes.lines:
self.axes.legend() self.axes.legend()

View File

@@ -2,13 +2,222 @@
<ui version="4.0"> <ui version="4.0">
<class>ParameterOptionsDialog</class> <class>ParameterOptionsDialog</class>
<widget class="QDialog" name="ParameterOptionsDialog"> <widget class="QDialog" name="ParameterOptionsDialog">
<property name="geometry"><rect><x>0</x><y>0</y><width>620</width><height>380</height></rect></property> <property name="geometry">
<property name="windowTitle"><string>Parameter Options</string></property> <rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>328</height>
</rect>
</property>
<property name="windowTitle">
<string>Parameter Options</string>
</property>
<layout class="QVBoxLayout" name="dialogLayout"> <layout class="QVBoxLayout" name="dialogLayout">
<item><widget class="QSplitter" name="parameterSplitter"><property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property><widget class="QWidget" name="parameterListPanel"><layout class="QVBoxLayout" name="parameterListLayout"><item><widget class="QListWidget" name="parameterList"/></item><item><layout class="QHBoxLayout" name="parameterButtonsLayout"><item><widget class="QPushButton" name="addParameterButton"><property name="text"><string>Add Parameter</string></property></widget></item><item><widget class="QPushButton" name="removeParameterButton"><property name="text"><string>Remove Parameter</string></property></widget></item></layout></item></layout></widget><widget class="QWidget" name="parameterDetailsPanel"><layout class="QFormLayout" name="parameterDetailsForm"><item row="0" column="0"><widget class="QLabel" name="nameLabel"><property name="text"><string>Name:</string></property></widget></item><item row="0" column="1"><widget class="QLineEdit" name="nameEdit"/></item><item row="1" column="0"><widget class="QLabel" name="typeLabel"><property name="text"><string>Type:</string></property></widget></item><item row="1" column="1"><widget class="QLineEdit" name="typeEdit"/></item><item row="2" column="0"><widget class="QLabel" name="valueLabel"><property name="text"><string>Value:</string></property></widget></item><item row="2" column="1"><widget class="QLineEdit" name="valueEdit"/></item></layout></widget></widget></item> <item>
<item><widget class="QDialogButtonBox" name="buttonBox"><property name="standardButtons"><set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set></property></widget></item> <widget class="QSplitter" name="parameterSplitter">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QWidget" name="parameterListPanel">
<layout class="QVBoxLayout" name="parameterListLayout">
<item>
<widget class="QListWidget" name="parameterList"/>
</item>
<item>
<layout class="QHBoxLayout" name="parameterButtonsLayout">
<item>
<widget class="QPushButton" name="addParameterButton">
<property name="text">
<string>Add Parameter</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeParameterButton">
<property name="text">
<string>Remove Parameter</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="parameterDetailsPanel">
<layout class="QFormLayout" name="parameterDetailsForm">
<property name="verticalSpacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="typeCombo">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>real</string>
</property>
</item>
<item>
<property name="text">
<string>integer</string>
</property>
</item>
<item>
<property name="text">
<string>boolean</string>
</property>
</item>
<item>
<property name="text">
<string>string</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="columnsLabel">
<property name="text">
<string>Matrix size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="columnsSpin">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="rowsSpin">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="valueEdit"/>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="quantityCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="unitCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPlainTextEdit" name="descriptionEdit"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="valueLabel">
<property name="text">
<string>Value:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="quantityLabel">
<property name="text">
<string>Quantity:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="unitLabel">
<property name="text">
<string>Unit:</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="descriptionLabel">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
<connections><connection><sender>buttonBox</sender><signal>accepted()</signal><receiver>ParameterOptionsDialog</receiver><slot>accept()</slot><hints/></connection><connection><sender>buttonBox</sender><signal>rejected()</signal><receiver>ParameterOptionsDialog</receiver><slot>reject()</slot><hints/></connection></connections> <connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ParameterOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ParameterOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@@ -2,43 +2,288 @@
<ui version="4.0"> <ui version="4.0">
<class>PortOptionsDialog</class> <class>PortOptionsDialog</class>
<widget class="QDialog" name="PortOptionsDialog"> <widget class="QDialog" name="PortOptionsDialog">
<property name="geometry"><rect><x>0</x><y>0</y><width>620</width><height>380</height></rect></property> <property name="geometry">
<property name="windowTitle"><string>Port Options</string></property> <rect>
<x>0</x>
<y>0</y>
<width>760</width>
<height>421</height>
</rect>
</property>
<property name="windowTitle">
<string>Port Options</string>
</property>
<layout class="QVBoxLayout" name="dialogLayout"> <layout class="QVBoxLayout" name="dialogLayout">
<item> <item>
<widget class="QSplitter" name="portSplitter"> <widget class="QSplitter" name="portSplitter">
<property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property> <property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QWidget" name="portListPanel"> <widget class="QWidget" name="portListPanel">
<layout class="QVBoxLayout" name="portListLayout"> <layout class="QVBoxLayout" name="portListLayout">
<item><widget class="QListWidget" name="portList"/></item> <item>
<widget class="QListWidget" name="portList"/>
</item>
<item> <item>
<layout class="QHBoxLayout" name="portButtonsLayout"> <layout class="QHBoxLayout" name="portButtonsLayout">
<item><widget class="QPushButton" name="addPortButton"><property name="text"><string>Add Port</string></property></widget></item> <item>
<item><widget class="QPushButton" name="removePortButton"><property name="text"><string>Remove Port</string></property></widget></item> <widget class="QPushButton" name="addPortButton">
<property name="text">
<string>Add Port</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removePortButton">
<property name="text">
<string>Remove Port</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="portDetailsPanel"> <widget class="QWidget" name="portDetailsPanel">
<layout class="QFormLayout" name="portDetailsForm"> <layout class="QFormLayout" name="portDetailsForm">
<item row="0" column="0"><widget class="QLabel" name="nameLabel"><property name="text"><string>Name:</string></property></widget></item> <property name="verticalSpacing">
<item row="0" column="1"><widget class="QLineEdit" name="nameEdit"/></item> <number>3</number>
<item row="1" column="0"><widget class="QLabel" name="typeLabel"><property name="text"><string>Type:</string></property></widget></item> </property>
<item row="1" column="1"><widget class="QComboBox" name="typeCombo"><item><property name="text"><string>Signal</string></property></item></widget></item> <item row="0" column="0">
<item row="2" column="0"><widget class="QLabel" name="orientationLabel"><property name="text"><string>Orientation:</string></property></widget></item> <widget class="QLabel" name="nameLabel">
<item row="2" column="1"><widget class="QComboBox" name="orientationCombo"><item><property name="text"><string>Input</string></property></item><item><property name="text"><string>Output</string></property></item></widget></item> <property name="text">
<item row="3" column="0" colspan="2"><widget class="QCheckBox" name="multipleConnectionsCheckBox"><property name="text"><string>Allow multiple connections</string></property></widget></item> <string>Name:</string>
<item row="4" column="0" colspan="2"><widget class="QLabel" name="positionHintLabel"><property name="text"><string>New ports start at (0, 0) in the icon editor.</string></property><property name="wordWrap"><bool>true</bool></property></widget></item> </property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Port type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="typeCombo">
<item>
<property name="text">
<string>Signal</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="orientationLabel">
<property name="text">
<string>Orientation:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="orientationCombo">
<item>
<property name="text">
<string>Input</string>
</property>
</item>
<item>
<property name="text">
<string>Output</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="multipleConnectionsCheckBox">
<property name="text">
<string>Allow multiple connections</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QLabel" name="positionHintLabel">
<property name="text">
<string>New ports start at (0, 0) in the icon editor.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QStackedWidget" name="typeOptionsStack">
<widget class="QWidget" name="signalOptionsPage">
<layout class="QFormLayout" name="signalOptionsForm">
<item row="0" column="0">
<widget class="QLabel" name="valueTypeLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="valueTypeCombo">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string>real</string>
</property>
</item>
<item>
<property name="text">
<string>integer</string>
</property>
</item>
<item>
<property name="text">
<string>boolean</string>
</property>
</item>
<item>
<property name="text">
<string>string</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="quantityLabel">
<property name="text">
<string>Quantity:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="quantityCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="unitCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="unitLabel">
<property name="text">
<string>Unit:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="rowsColumnsLabel">
<property name="text">
<string>Matrix size:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="rowsSpin">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="columnsSpin">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="descriptionLabel">
<property name="text">
<string>Description:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QPlainTextEdit" name="descriptionEdit"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="unsupportedTypePage">
<layout class="QVBoxLayout" name="unsupportedTypeLayout">
<item>
<widget class="QLabel" name="unsupportedTypeLabel">
<property name="text">
<string>This port type does not have an options editor yet.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
</item> </item>
<item><widget class="QDialogButtonBox" name="buttonBox"><property name="standardButtons"><set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set></property></widget></item> <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>
<connections> <connections>
<connection><sender>buttonBox</sender><signal>accepted()</signal><receiver>PortOptionsDialog</receiver><slot>accept()</slot><hints/></connection> <connection>
<connection><sender>buttonBox</sender><signal>rejected()</signal><receiver>PortOptionsDialog</receiver><slot>reject()</slot><hints/></connection> <sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PortOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PortOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection>
</connections> </connections>
</ui> </ui>

View File

@@ -40,14 +40,14 @@
<widget class="QGroupBox" name="portsGroup"> <widget class="QGroupBox" name="portsGroup">
<property name="title"><string>Ports</string></property> <property name="title"><string>Ports</string></property>
<layout class="QVBoxLayout" name="portsLayout"> <layout class="QVBoxLayout" name="portsLayout">
<item><widget class="QTableWidget" name="portsTable"><property name="selectionBehavior"><enum>QAbstractItemView::SelectionBehavior::SelectRows</enum></property><property name="selectionMode"><enum>QAbstractItemView::SelectionMode::SingleSelection</enum></property><property name="columnCount"><number>4</number></property><column><property name="text"><string>Name</string></property></column><column><property name="text"><string>Type</string></property></column><column><property name="text"><string>Orientation</string></property></column><column><property name="text"><string>Multiple</string></property></column></widget></item> <item><widget class="QTableWidget" name="portsTable"><property name="selectionBehavior"><enum>QAbstractItemView::SelectionBehavior::SelectRows</enum></property><property name="selectionMode"><enum>QAbstractItemView::SelectionMode::SingleSelection</enum></property><property name="columnCount"><number>10</number></property><column><property name="text"><string>Name</string></property></column><column><property name="text"><string>Port Type</string></property></column><column><property name="text"><string>Orientation</string></property></column><column><property name="text"><string>Multiple</string></property></column><column><property name="text"><string>Value Type</string></property></column><column><property name="text"><string>Quantity</string></property></column><column><property name="text"><string>Unit</string></property></column><column><property name="text"><string>Rows</string></property></column><column><property name="text"><string>Columns</string></property></column><column><property name="text"><string>Description</string></property></column></widget></item>
<item><layout class="QHBoxLayout" name="portButtonsLayout"><item><widget class="QPushButton" name="addPortButton"><property name="text"><string>Add Port</string></property></widget></item><item><widget class="QPushButton" name="removePortButton"><property name="text"><string>Remove Port</string></property></widget></item><item><spacer name="portButtonSpacer"><property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item></layout></item> <item><layout class="QHBoxLayout" name="portButtonsLayout"><item><widget class="QPushButton" name="addPortButton"><property name="text"><string>Add Port</string></property></widget></item><item><widget class="QPushButton" name="removePortButton"><property name="text"><string>Remove Port</string></property></widget></item><item><spacer name="portButtonSpacer"><property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item></layout></item>
</layout> </layout>
</widget> </widget>
<widget class="QGroupBox" name="parametersGroup"> <widget class="QGroupBox" name="parametersGroup">
<property name="title"><string>Parameters</string></property> <property name="title"><string>Parameters</string></property>
<layout class="QVBoxLayout" name="parametersLayout"> <layout class="QVBoxLayout" name="parametersLayout">
<item><widget class="QTableWidget" name="parametersTable"><property name="selectionBehavior"><enum>QAbstractItemView::SelectionBehavior::SelectRows</enum></property><property name="selectionMode"><enum>QAbstractItemView::SelectionMode::SingleSelection</enum></property><property name="columnCount"><number>3</number></property><column><property name="text"><string>Name</string></property></column><column><property name="text"><string>Type</string></property></column><column><property name="text"><string>Value</string></property></column></widget></item> <item><widget class="QTableWidget" name="parametersTable"><property name="selectionBehavior"><enum>QAbstractItemView::SelectionBehavior::SelectRows</enum></property><property name="selectionMode"><enum>QAbstractItemView::SelectionMode::SingleSelection</enum></property><property name="columnCount"><number>8</number></property><column><property name="text"><string>Name</string></property></column><column><property name="text"><string>Type</string></property></column><column><property name="text"><string>Value</string></property></column><column><property name="text"><string>Quantity</string></property></column><column><property name="text"><string>Unit</string></property></column><column><property name="text"><string>Rows</string></property></column><column><property name="text"><string>Columns</string></property></column><column><property name="text"><string>Description</string></property></column></widget></item>
<item><layout class="QHBoxLayout" name="parameterButtonsLayout"><item><widget class="QPushButton" name="addParameterButton"><property name="text"><string>Add Parameter</string></property></widget></item><item><widget class="QPushButton" name="removeParameterButton"><property name="text"><string>Remove Parameter</string></property></widget></item><item><spacer name="parameterButtonSpacer"><property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item></layout></item> <item><layout class="QHBoxLayout" name="parameterButtonsLayout"><item><widget class="QPushButton" name="addParameterButton"><property name="text"><string>Add Parameter</string></property></widget></item><item><widget class="QPushButton" name="removeParameterButton"><property name="text"><string>Remove Parameter</string></property></widget></item><item><spacer name="parameterButtonSpacer"><property name="orientation"><enum>Qt::Orientation::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item></layout></item>
</layout> </layout>
</widget> </widget>

View File

@@ -2,12 +2,12 @@
"format": "bedit-document", "format": "bedit-document",
"version": 1, "version": 1,
"metadata": { "metadata": {
"name": "Untitled" "name": "Current Document"
}, },
"roots": [ "roots": [
{ {
"id": "6060fdd6-e816-4da9-8bb6-fde1a66d3e92", "id": "4014ba90-5538-4050-a9be-552318197ce1",
"name": "Test", "name": "New Graph Block 1",
"position": { "position": {
"x": 0.0, "x": 0.0,
"y": 0.0 "y": 0.0
@@ -52,9 +52,7 @@
} }
] ]
}, },
"properties": { "properties": {},
"showName": false
},
"library": { "library": {
"showSubtree": true "showSubtree": true
}, },
@@ -63,10 +61,10 @@
"graph": { "graph": {
"blocks": [ "blocks": [
{ {
"id": "2e6a192c-5aeb-45d6-80ee-d64a72392b76", "id": "86ba2bf6-0da5-40a4-98e9-fb0d9c12c576",
"name": "Constant0", "name": "Constant0",
"position": { "position": {
"x": -160.0, "x": -224.0,
"y": -160.0 "y": -160.0
}, },
"rotation": 0.0, "rotation": 0.0,
@@ -87,7 +85,15 @@
} }
}, },
"type": "signal", "type": "signal",
"multipleConnections": false "multipleConnections": false,
"valueType": "real",
"quantity": "Length",
"unit": "m",
"dimensions": {
"rows": 1,
"columns": 1
},
"description": ""
} }
] ]
}, },
@@ -96,7 +102,14 @@
"id": "parameter-022177fc", "id": "parameter-022177fc",
"name": "v", "name": "v",
"type": "real", "type": "real",
"value": "2" "value": "2",
"quantity": "",
"unit": "",
"dimensions": {
"rows": 1,
"columns": 1
},
"description": ""
} }
], ],
"icon": { "icon": {
@@ -172,10 +185,10 @@
} }
}, },
{ {
"id": "f4a78255-8b73-4bb3-ae8e-24b566c72bc0", "id": "7805cb2f-c938-44b2-a1f8-e1b4af13f7bf",
"name": "gain0", "name": "Gain0",
"position": { "position": {
"x": -32.0, "x": -96.0,
"y": -160.0 "y": -160.0
}, },
"rotation": 0.0, "rotation": 0.0,
@@ -195,7 +208,15 @@
} }
}, },
"type": "signal", "type": "signal",
"multipleConnections": false "multipleConnections": false,
"valueType": "real",
"quantity": "",
"unit": "",
"dimensions": {
"rows": 1,
"columns": 1
},
"description": ""
} }
], ],
"outputs": [ "outputs": [
@@ -213,7 +234,15 @@
} }
}, },
"type": "signal", "type": "signal",
"multipleConnections": false "multipleConnections": false,
"valueType": "real",
"quantity": "",
"unit": "",
"dimensions": {
"rows": 1,
"columns": 1
},
"description": ""
} }
] ]
}, },
@@ -222,7 +251,14 @@
"id": "parameter-61a43a86", "id": "parameter-61a43a86",
"name": "k", "name": "k",
"type": "real", "type": "real",
"value": "2.5" "value": "2.5",
"quantity": "Current",
"unit": "A",
"dimensions": {
"rows": 1,
"columns": 1
},
"description": ""
} }
], ],
"icon": { "icon": {
@@ -277,676 +313,28 @@
"initialEquations": "" "initialEquations": ""
} }
} }
},
{
"id": "0b29c706-eb1b-4839-8ad2-bbed64efc01e",
"name": "const_and_time",
"position": {
"x": -160.0,
"y": -32.0
},
"rotation": 0.0,
"interface": {
"inputs": [],
"outputs": [
{
"id": "port-ddc97277",
"name": "y",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 88.0,
"y": 40.0
}
},
"type": "signal",
"multipleConnections": false
}
]
},
"parameters": [
{
"id": "parameter-022177fc",
"name": "v",
"type": "real",
"value": "4.8"
}
],
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"cornerRadius": 5.0,
"fill": "#f4f4f4",
"height": 64.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"type": "rectangle",
"width": 64.0,
"x": 32.0,
"y": 32.0
},
{
"fill": "none",
"height": 48.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#00007f",
"type": "line",
"width": 0.0,
"x": 40.0,
"y": 40.0
},
{
"fill": "none",
"height": 0.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#00007f",
"type": "line",
"width": 48.0,
"x": 40.0,
"y": 88.0
},
{
"fill": "none",
"height": 0.0,
"lineStyle": "solid",
"lineWidth": 1.0,
"stroke": "#ffaa00",
"type": "line",
"width": 48.0,
"x": 40.0,
"y": 64.0
}
]
},
"properties": {
"showName": true
},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": "y = v+sin(10*time);",
"declarations": "",
"initialEquations": ""
}
}
},
{
"id": "43f739a0-e50f-44ae-bd95-0e0c1a10d1cf",
"name": "gain1",
"position": {
"x": -32.0,
"y": -32.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-df34ce84",
"name": "u",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 64.0,
"y": 64.0
}
},
"type": "signal",
"multipleConnections": false
}
],
"outputs": [
{
"id": "port-fe9e6486",
"name": "y",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 88.0,
"y": 40.0
}
},
"type": "signal",
"multipleConnections": false
}
]
},
"parameters": [
{
"id": "parameter-61a43a86",
"name": "k",
"type": "real",
"value": "-5"
}
],
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"cornerRadius": 5.0,
"fill": "#f4f4f4",
"height": 64.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"type": "rectangle",
"width": 64.0,
"x": 32.0,
"y": 32.0
},
{
"color": "#00007f",
"fill": "#ffffff",
"fontSize": 24.0,
"height": 48.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#00007f",
"text": "K",
"type": "text",
"width": 48.0,
"x": 40.0,
"y": 40.0
}
]
},
"properties": {
"showName": true
},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": "y = k*u;",
"declarations": "",
"initialEquations": ""
}
}
},
{
"id": "59bb51be-8185-4b0b-a894-e38db61aff18",
"name": "add0",
"position": {
"x": 96.0,
"y": -96.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-c995845c",
"name": "u",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 64.0,
"y": 64.0
}
},
"type": "signal",
"multipleConnections": true
}
],
"outputs": [
{
"id": "port-a2f0dea6",
"name": "y",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 80.0,
"y": 48.0
}
},
"type": "signal",
"multipleConnections": false
}
]
},
"parameters": [],
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"fill": "#f4f4f4",
"height": 48.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"type": "circle",
"width": 48.0,
"x": 40.0,
"y": 40.0
},
{
"color": "#00007f",
"fill": "none",
"fontSize": 18.0,
"height": 32.0,
"lineStyle": "none",
"lineWidth": 1.5,
"stroke": "#00007f",
"text": "+",
"type": "text",
"width": 32.0,
"x": 48.0,
"y": 48.0
}
]
},
"properties": {
"showName": false
},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": "y = sum(u[i] for i in 1:$u_N$ );",
"declarations": "",
"initialEquations": ""
}
}
},
{
"id": "78c8fcb6-bd46-4b85-aa91-3276a8d1e8cb",
"name": "Integrate0",
"position": {
"x": 224.0,
"y": -96.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-df34ce84",
"name": "u",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 64.0,
"y": 64.0
}
},
"type": "signal",
"multipleConnections": false
}
],
"outputs": [
{
"id": "port-fe9e6486",
"name": "y",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 88.0,
"y": 40.0
}
},
"type": "signal",
"multipleConnections": false
}
]
},
"parameters": [
{
"id": "parameter-61a43a86",
"name": "y_start",
"type": "real",
"value": "0"
}
],
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"cornerRadius": 5.0,
"fill": "#f4f4f4",
"height": 64.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"type": "rectangle",
"width": 64.0,
"x": 32.0,
"y": 32.0
},
{
"color": "#00007f",
"fill": "#ffffff",
"fontSize": 24.0,
"height": 48.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#00007f",
"text": "dt",
"type": "text",
"width": 48.0,
"x": 48.0,
"y": 40.0
},
{
"type": "text",
"x": 32.0,
"y": 32.0,
"width": 32.0,
"height": 56.0,
"fill": "none",
"stroke": "#00007f",
"lineWidth": 1.5,
"lineStyle": "none",
"text": "\u222b",
"fontSize": 35.0,
"color": "#00007f"
}
]
},
"properties": {
"showName": true
},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": "der(y) = u;",
"declarations": "",
"initialEquations": "y = y_start;"
}
}
},
{
"id": "f2caf59f-5688-46b4-b17d-bdc911580d7b",
"name": "Differentiate0",
"position": {
"x": 224.0,
"y": 32.0
},
"rotation": 0.0,
"interface": {
"inputs": [
{
"id": "port-df34ce84",
"name": "u",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 64.0,
"y": 64.0
}
},
"type": "signal",
"multipleConnections": false
}
],
"outputs": [
{
"id": "port-fe9e6486",
"name": "y",
"position": {
"x": 0.0,
"y": 0.0
},
"properties": {
"iconPosition": {
"x": 88.0,
"y": 40.0
}
},
"type": "signal",
"multipleConnections": false
}
]
},
"parameters": [
{
"id": "parameter-61a43a86",
"name": "x_start",
"type": "real",
"value": "0"
},
{
"id": "parameter-d110667b",
"name": "y_start",
"type": "real",
"value": "0"
},
{
"id": "parameter-873b503d",
"name": "k",
"type": "real",
"value": "1"
},
{
"id": "parameter-a55752c7",
"name": "T",
"type": "real",
"value": "0.01"
}
],
"icon": {
"shape": "rectangle",
"fill": "#f4f4f4",
"border": "#303030",
"text": "Text",
"size": {
"width": 128.0,
"height": 128.0
},
"elements": [
{
"cornerRadius": 5.0,
"fill": "#f4f4f4",
"height": 64.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#303030",
"type": "rectangle",
"width": 64.0,
"x": 32.0,
"y": 32.0
},
{
"color": "#00007f",
"fill": "#ffffff",
"fontSize": 22.0,
"height": 48.0,
"lineStyle": "solid",
"lineWidth": 1.5,
"stroke": "#00007f",
"text": "d/dt",
"type": "text",
"width": 64.0,
"x": 32.0,
"y": 40.0
}
]
},
"properties": {
"showName": true
},
"library": {
"showSubtree": true
},
"implementation": {
"kind": "text",
"source": {
"equations": "assert(T > 0, \"Differentiate time constant T must be positive\");\n\nder(x) = (u - x)/T;\ny = (k/T)*(u - x);",
"declarations": "Real x(start=x_start);",
"initialEquations": "y = y_start;"
}
}
} }
], ],
"connections": [ "connections": [
{ {
"id": "df23ff13-2a30-4c85-be66-56f66114d58e", "id": "5582d4ad-7c7a-40fe-87d2-9b78410336b3",
"source": { "source": {
"block": "2e6a192c-5aeb-45d6-80ee-d64a72392b76", "block": "86ba2bf6-0da5-40a4-98e9-fb0d9c12c576",
"port": "port-ddc97277" "port": "port-ddc97277"
}, },
"target": { "target": {
"block": "f4a78255-8b73-4bb3-ae8e-24b566c72bc0", "block": "7805cb2f-c938-44b2-a1f8-e1b4af13f7bf",
"port": "port-df34ce84" "port": "port-df34ce84"
}, },
"name": "", "name": "",
"properties": { "properties": {
"waypoints": [] "waypoints": []
} }
},
{
"id": "00f7d291-641b-4200-afa3-f1ec941faed7",
"source": {
"block": "0b29c706-eb1b-4839-8ad2-bbed64efc01e",
"port": "port-ddc97277"
},
"target": {
"block": "43f739a0-e50f-44ae-bd95-0e0c1a10d1cf",
"port": "port-df34ce84"
},
"name": "",
"properties": {
"waypoints": []
}
},
{
"id": "7a776aab-deb5-4e50-838e-b6b4635f349c",
"source": {
"block": "f4a78255-8b73-4bb3-ae8e-24b566c72bc0",
"port": "port-fe9e6486"
},
"target": {
"block": "59bb51be-8185-4b0b-a894-e38db61aff18",
"port": "port-c995845c"
},
"name": "",
"properties": {
"waypoints": []
}
},
{
"id": "5b3d71c3-282a-4c2d-85f8-b92bba5bf2df",
"source": {
"block": "43f739a0-e50f-44ae-bd95-0e0c1a10d1cf",
"port": "port-fe9e6486"
},
"target": {
"block": "59bb51be-8185-4b0b-a894-e38db61aff18",
"port": "port-c995845c"
},
"name": "",
"properties": {
"waypoints": []
}
},
{
"id": "33e15cc2-7f33-469e-91a8-921c04f6b823",
"source": {
"block": "59bb51be-8185-4b0b-a894-e38db61aff18",
"port": "port-a2f0dea6"
},
"target": {
"block": "78c8fcb6-bd46-4b85-aa91-3276a8d1e8cb",
"port": "port-df34ce84"
},
"name": "",
"properties": {
"waypoints": []
}
},
{
"id": "26e10b34-f168-4492-b4f2-fb46d8a4aafa",
"source": {
"block": "59bb51be-8185-4b0b-a894-e38db61aff18",
"port": "port-a2f0dea6"
},
"target": {
"block": "f2caf59f-5688-46b4-b17d-bdc911580d7b",
"port": "port-df34ce84"
},
"name": "",
"properties": {
"waypoints": [
{
"x": 160.0,
"y": 96.0
}
]
}
} }
], ],
"annotations": [], "annotations": [],
"junctions": [], "junctions": [],
"simulation": { "simulation": {}
"enabled": false,
"startTime": 0.0,
"stopTime": 10.0,
"intervalMode": "intervalTime",
"numberOfIntervals": 50000,
"intervalTime": 0.002
}
} }
} }
} }

View File

@@ -6,7 +6,7 @@ model m_Test
y = v; y = v;
end m_Constant0; end m_Constant0;
model m_gain0 model m_gain0
input Real u; input Real u(quantity="Velocity", unit="m/s");
output Real y; output Real y;
parameter Real k = 2.5; parameter Real k = 2.5;
equation equation
@@ -16,7 +16,7 @@ model m_Test
output Real y; output Real y;
parameter Real v = 4.8; parameter Real v = 4.8;
equation equation
y = v+sin(time); y = v+sin(10*time);
end m_const_and_time; end m_const_and_time;
model m_gain1 model m_gain1
input Real u; input Real u;
@@ -31,14 +31,43 @@ model m_Test
equation equation
y = sum(u[i] for i in 1:2 ); y = sum(u[i] for i in 1:2 );
end m_add0; end m_add0;
model m_Integrate0
input Real u;
output Real y;
parameter Real y_start = 0;
initial equation
y = y_start;
equation
der(y) = u;
end m_Integrate0;
model m_Differentiate0
input Real u;
output Real y;
parameter Real x_start = 0;
parameter Real y_start = 0;
parameter Real k = 1;
parameter Real T = 0.01;
Real x(start=x_start);
initial equation
y = y_start;
equation
assert(T > 0, "Differentiate time constant T must be positive");
der(x) = (u - x)/T;
y = (k/T)*(u - x);
end m_Differentiate0;
m_Constant0 Constant0; m_Constant0 Constant0;
m_gain0 gain0; m_gain0 gain0;
m_const_and_time const_and_time; m_const_and_time const_and_time;
m_gain1 gain1; m_gain1 gain1;
m_add0 add0; m_add0 add0;
m_Integrate0 Integrate0;
m_Differentiate0 Differentiate0;
equation equation
gain0.u = Constant0.y; gain0.u = Constant0.y;
gain1.u = const_and_time.y; gain1.u = const_and_time.y;
add0.u[1] = gain0.y; add0.u[1] = gain0.y;
add0.u[2] = gain1.y; add0.u[2] = gain1.y;
end m_Test; Integrate0.u = add0.y;
Differentiate0.u = add0.y;
end m_Test;