new text mode editor
This commit is contained in:
1
BEdit/src/bedit/gui/editors/__init__.py
Normal file
1
BEdit/src/bedit/gui/editors/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Reusable editing widgets."""
|
||||
193
BEdit/src/bedit/gui/editors/text_definition.py
Normal file
193
BEdit/src/bedit/gui/editors/text_definition.py
Normal file
@@ -0,0 +1,193 @@
|
||||
from copy import deepcopy
|
||||
from uuid import uuid4
|
||||
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QComboBox, QHeaderView, QTableWidgetItem, QWidget
|
||||
|
||||
from bedit.core.model import Parameter, Port
|
||||
from bedit.core.port_types import PortTypeRegistry
|
||||
from bedit.gui.generated.ui_text_definition_editor import Ui_TextDefinitionEditor
|
||||
|
||||
|
||||
ID_ROLE = Qt.ItemDataRole.UserRole
|
||||
PROPERTIES_ROLE = Qt.ItemDataRole.UserRole + 1
|
||||
|
||||
|
||||
class TextDefinitionEditor(QWidget):
|
||||
"""Editor for a text component's equations, ports, and parameters."""
|
||||
|
||||
modifiedChanged = Signal(bool)
|
||||
definitionEdited = Signal()
|
||||
|
||||
def __init__(self, parent=None) -> None:
|
||||
super().__init__(parent)
|
||||
self.ui = Ui_TextDefinitionEditor()
|
||||
self.ui.setupUi(self)
|
||||
self._modified = False
|
||||
self._loading = False
|
||||
self.ui.portsTable.horizontalHeader().setSectionResizeMode(
|
||||
QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
self.ui.parametersTable.horizontalHeader().setSectionResizeMode(
|
||||
QHeaderView.ResizeMode.Stretch
|
||||
)
|
||||
self.ui.columnSplitter.setSizes([560, 340])
|
||||
self.ui.definitionSplitter.setSizes([300, 300])
|
||||
self.ui.equationsEdit.textChanged.connect(self._mark_modified)
|
||||
self.ui.portsTable.cellChanged.connect(self._mark_modified)
|
||||
self.ui.parametersTable.cellChanged.connect(self._mark_modified)
|
||||
self.ui.addPortButton.clicked.connect(self.add_port)
|
||||
self.ui.removePortButton.clicked.connect(self.remove_port)
|
||||
self.ui.addParameterButton.clicked.connect(self.add_parameter)
|
||||
self.ui.removeParameterButton.clicked.connect(self.remove_parameter)
|
||||
self.ui.portsTable.itemSelectionChanged.connect(self._update_buttons)
|
||||
self.ui.parametersTable.itemSelectionChanged.connect(self._update_buttons)
|
||||
self._update_buttons()
|
||||
|
||||
@property
|
||||
def is_modified(self) -> bool:
|
||||
return self._modified
|
||||
|
||||
@property
|
||||
def equations(self) -> str:
|
||||
return self.ui.equationsEdit.toPlainText()
|
||||
|
||||
@property
|
||||
def ports(self) -> tuple[list[Port], list[Port]]:
|
||||
inputs: list[Port] = []
|
||||
outputs: list[Port] = []
|
||||
for row in range(self.ui.portsTable.rowCount()):
|
||||
name_item = self.ui.portsTable.item(row, 0)
|
||||
type_combo = self.ui.portsTable.cellWidget(row, 1)
|
||||
orientation_combo = self.ui.portsTable.cellWidget(row, 2)
|
||||
port = Port(
|
||||
id=name_item.data(ID_ROLE),
|
||||
name=name_item.text().strip(),
|
||||
type=type_combo.currentData(),
|
||||
properties=deepcopy(name_item.data(PROPERTIES_ROLE) or {}),
|
||||
)
|
||||
target = inputs if orientation_combo.currentData() == "input" else outputs
|
||||
target.append(port)
|
||||
return inputs, outputs
|
||||
|
||||
@property
|
||||
def parameters(self) -> list[Parameter]:
|
||||
table = self.ui.parametersTable
|
||||
return [
|
||||
Parameter(
|
||||
id=table.item(row, 0).data(ID_ROLE),
|
||||
name=table.item(row, 0).text().strip(),
|
||||
type=table.item(row, 1).text().strip(),
|
||||
value=table.item(row, 2).text(),
|
||||
)
|
||||
for row in range(table.rowCount())
|
||||
]
|
||||
|
||||
def set_definition(
|
||||
self,
|
||||
equations: str,
|
||||
inputs: list[Port],
|
||||
outputs: list[Port],
|
||||
parameters: list[Parameter],
|
||||
) -> None:
|
||||
self._loading = True
|
||||
self.ui.equationsEdit.setPlainText(equations)
|
||||
self.ui.portsTable.setRowCount(0)
|
||||
for port in inputs:
|
||||
self._append_port(port, "input")
|
||||
for port in outputs:
|
||||
self._append_port(port, "output")
|
||||
self.ui.parametersTable.setRowCount(0)
|
||||
for parameter in parameters:
|
||||
self._append_parameter(parameter)
|
||||
self._loading = False
|
||||
self.set_modified(False)
|
||||
self._update_buttons()
|
||||
|
||||
def set_modified(self, modified: bool) -> None:
|
||||
if self._modified != modified:
|
||||
self._modified = modified
|
||||
self.modifiedChanged.emit(modified)
|
||||
|
||||
def _mark_modified(self, *_args) -> None:
|
||||
if not self._loading:
|
||||
self.set_modified(True)
|
||||
self.definitionEdited.emit()
|
||||
|
||||
def _new_combo(self, values: list[tuple[str, str]], current: str) -> QComboBox:
|
||||
combo = QComboBox(self)
|
||||
for label, value in values:
|
||||
combo.addItem(label, value)
|
||||
combo.setCurrentIndex(max(0, combo.findData(current)))
|
||||
combo.currentIndexChanged.connect(self._mark_modified)
|
||||
return combo
|
||||
|
||||
def _append_port(self, port: Port, orientation: str) -> None:
|
||||
table = self.ui.portsTable
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
name = QTableWidgetItem(port.name)
|
||||
name.setData(ID_ROLE, port.id)
|
||||
name.setData(PROPERTIES_ROLE, deepcopy(port.properties))
|
||||
table.setItem(row, 0, name)
|
||||
types = [(item.display_name, item.id) for item in PortTypeRegistry.all()]
|
||||
table.setCellWidget(row, 1, self._new_combo(types, port.type))
|
||||
table.setCellWidget(
|
||||
row,
|
||||
2,
|
||||
self._new_combo([("Input", "input"), ("Output", "output")], orientation),
|
||||
)
|
||||
|
||||
def _append_parameter(self, parameter: Parameter) -> None:
|
||||
table = self.ui.parametersTable
|
||||
row = table.rowCount()
|
||||
table.insertRow(row)
|
||||
name = QTableWidgetItem(parameter.name)
|
||||
name.setData(ID_ROLE, parameter.id)
|
||||
table.setItem(row, 0, name)
|
||||
table.setItem(row, 1, QTableWidgetItem(parameter.type))
|
||||
table.setItem(row, 2, QTableWidgetItem(parameter.value))
|
||||
|
||||
def add_port(self) -> None:
|
||||
port = Port(
|
||||
id=f"port-{uuid4().hex[:8]}",
|
||||
name=f"Port {self.ui.portsTable.rowCount() + 1}",
|
||||
type="signal",
|
||||
properties={"iconPosition": {"x": 0.0, "y": 0.0}},
|
||||
)
|
||||
self._loading = True
|
||||
self._append_port(port, "input")
|
||||
self._loading = False
|
||||
self.ui.portsTable.selectRow(self.ui.portsTable.rowCount() - 1)
|
||||
self._mark_modified()
|
||||
|
||||
def remove_port(self) -> None:
|
||||
row = self.ui.portsTable.currentRow()
|
||||
if row >= 0:
|
||||
self.ui.portsTable.removeRow(row)
|
||||
self._mark_modified()
|
||||
self._update_buttons()
|
||||
|
||||
def add_parameter(self) -> None:
|
||||
parameter = Parameter(
|
||||
id=f"parameter-{uuid4().hex[:8]}",
|
||||
name=f"Parameter {self.ui.parametersTable.rowCount() + 1}",
|
||||
)
|
||||
self._loading = True
|
||||
self._append_parameter(parameter)
|
||||
self._loading = False
|
||||
self.ui.parametersTable.selectRow(self.ui.parametersTable.rowCount() - 1)
|
||||
self._mark_modified()
|
||||
|
||||
def remove_parameter(self) -> None:
|
||||
row = self.ui.parametersTable.currentRow()
|
||||
if row >= 0:
|
||||
self.ui.parametersTable.removeRow(row)
|
||||
self._mark_modified()
|
||||
self._update_buttons()
|
||||
|
||||
def _update_buttons(self) -> None:
|
||||
self.ui.removePortButton.setEnabled(self.ui.portsTable.currentRow() >= 0)
|
||||
self.ui.removeParameterButton.setEnabled(
|
||||
self.ui.parametersTable.currentRow() >= 0
|
||||
)
|
||||
Reference in New Issue
Block a user