feat: 推进一级关系参数化编辑与参数导出

This commit is contained in:
2026-08-13 17:50:20 +08:00
parent 19364d81b5
commit 7d814d2939
21 changed files with 2075 additions and 231 deletions
+79 -2
View File
@@ -67,7 +67,9 @@ class _PropertyTableProbe(QWidget, WindowStateMixin):
self.selected_solid_id = None
self.selected_face_id = 0
self.selected_edge_id = None
self.step_path = PROJECT_ROOT / "assets" / "models" / "probe.step"
self.current_info_values = self._plane_info()
self.executed_property_actions: list[tuple[str, str]] = []
layout = QVBoxLayout(self)
self.object_edit_box = self
@@ -91,6 +93,8 @@ class _PropertyTableProbe(QWidget, WindowStateMixin):
self.current_capability_headline = QLabel()
self.apply_property_button = QPushButton()
self.export_parameters_button = QPushButton()
self.face_width_input = QLineEdit()
self.face_height_input = QLineEdit()
@staticmethod
def _plane_info() -> dict[str, object]:
@@ -101,6 +105,8 @@ class _PropertyTableProbe(QWidget, WindowStateMixin):
"bbox_diagonal": 14.1421356237,
"local_face_width": 10.0,
"local_face_height": 10.0,
"local_face_size_edit_ready": True,
"local_face_size_edit_blocker": "",
"plane_origin": (0.0, 0.0, 0.0),
"push_pull_outward_direction": (0.0, 0.0, 1.0),
"normal": (0.0, 0.0, 1.0),
@@ -119,6 +125,14 @@ class _PropertyTableProbe(QWidget, WindowStateMixin):
def _set_control_state(self, widget, enabled: bool, _enabled_tip: str, _disabled_tip: str) -> None:
widget.setEnabled(enabled)
def resize_face_width_local(self) -> None:
self.executed_property_actions.append(("resize_face_width_local", self.face_width_input.text()))
self._after_property_edit_finished(success=True)
def resize_face_height_local(self) -> None:
self.executed_property_actions.append(("resize_face_height_local", self.face_height_input.text()))
self._after_property_edit_finished(success=True)
def statusBar(self) -> _StatusBar:
return _StatusBar()
@@ -130,6 +144,7 @@ class _ActionMessageProbe(WindowActionMixin):
class _ParameterExportActionProbe(WindowActionMixin):
def __init__(self, output_path: Path) -> None:
self.output_path = output_path
self.component_path = output_path.parent / "nodes" / "000_test" / "main.py"
self.status_bar = _StatusBarProbe()
self.info_text = ""
self.export_state_updates = 0
@@ -155,6 +170,21 @@ class _ParameterExportActionProbe(WindowActionMixin):
},
]
def _export_parametric_component_main(self, rows: list[dict[str, str]]) -> Path:
assert rows
self.component_path.parent.mkdir(parents=True, exist_ok=True)
input_rows = json.dumps(rows, ensure_ascii=False, indent=4)
self.component_path.write_text(
"INPUT_PARAMETERS = "
+ input_rows
+ "\nOUTPUT_PARAMETERS = [{'name': 'output_step', 'displayName': '输出STEP', 'type': 'file', 'ioRole': 'output', 'default': ''}]"
+ "\nPARAMETERS = INPUT_PARAMETERS + OUTPUT_PARAMETERS"
+ "\nNODE_INFO = {'parameters': PARAMETERS}"
+ "\ndef execute(inputs, params, context):\n return {'output_step': 'modified.step'}\n",
encoding="utf-8",
)
return self.component_path
def _update_parameter_export_state(self) -> None:
self.export_state_updates += 1
@@ -350,6 +380,16 @@ def _assert_property_table_editor(probe: _PropertyTableProbe) -> None:
],
f"unexpected parameter export payload: {selected_rows}",
)
component_edits = probe._selected_parameter_component_edits(selected_rows)
_assert(len(component_edits) == 1, f"selected parameter should map to one component edit: {component_edits}")
_assert(
component_edits[0]["operation"] == "resize_face_size_local",
f"face width export should map to backend resize operation: {component_edits[0]}",
)
_assert(
component_edits[0]["args"] == [0, {"param": "面内长度"}, "width"],
f"face width export should embed parameter placeholder args: {component_edits[0]}",
)
target_widget.setText("12")
probe._update_property_apply_state()
@@ -357,6 +397,28 @@ def _assert_property_table_editor(probe: _PropertyTableProbe) -> None:
_assert(any(row == target_row for row, _spec, _text in changed), "table target edit was not detected")
_assert(probe.apply_property_button.isEnabled(), "single parametric modeling button should enable for one changed row")
width_row = _row_by_label(probe, "面内宽度")
width_widget = probe.property_table.cellWidget(width_row, PROPERTY_TARGET_COLUMN)
_assert(isinstance(width_widget, QLineEdit), "second editable table row should have a target editor")
width_widget.setText("8")
probe._update_property_apply_state()
changed = probe._changed_property_rows()
_assert(len(changed) >= 2, f"two target edits should be detected: {changed}")
_assert(probe.apply_property_button.isEnabled(), "parametric modeling button should stay enabled for multiple changed rows")
probe.apply_current_property_edit()
for _index in range(6):
QApplication.processEvents()
if not getattr(probe, "property_batch_active", False):
break
_assert(
probe.executed_property_actions == [
("resize_face_width_local", "12"),
("resize_face_height_local", "8"),
],
f"batch parametric modeling should execute changed rows in table order: {probe.executed_property_actions}",
)
_assert(not getattr(probe, "property_batch_active", False), "property batch state should clear after completion")
probe.toggle_property_table_expanded()
_assert(probe.property_table_expanded, "property table expand toggle failed")
_assert("收起" in probe.property_expand_button.text(), "expanded table button should offer to collapse")
@@ -547,6 +609,7 @@ def _assert_parameter_export_action() -> None:
probe = _ParameterExportActionProbe(output_path)
probe.export_selected_parameters()
payload = json.loads(output_path.read_text(encoding="utf-8"))
component_text = probe.component_path.read_text(encoding="utf-8")
_assert(
payload == [
{
@@ -566,8 +629,22 @@ def _assert_parameter_export_action() -> None:
],
f"parameter export action wrote unexpected JSON: {payload}",
)
_assert(probe.status_bar.messages and "已导出 2 个输入参数" in probe.status_bar.messages[-1], "parameter export status should mention exported count")
_assert("data.json" in probe.info_text and "参数数量:2" in probe.info_text, "parameter export info panel summary should be useful")
_assert(
"INPUT_PARAMETERS" in component_text and "def execute(inputs, params, context):" in component_text and "面内长度" in component_text,
"parameter export should generate FlowEditor-style component main.py with embedded input parameters",
)
_assert(
not (probe.component_path.parent / "data.json").exists(),
"component export should embed parameters in main.py instead of writing component data.json",
)
_assert(
probe.status_bar.messages and "已导出 2 个输入参数" in probe.status_bar.messages[-1],
"parameter export status should mention exported count",
)
_assert(
"data.json" in probe.info_text and "参数数量:2" in probe.info_text and "组件入口:main.py" in probe.info_text,
"parameter export info panel summary should mention generated component",
)
def main() -> int: