from __future__ import annotations import os from pathlib import Path import sys os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtCore import QEvent, QObject from PySide6.QtWidgets import ( QApplication, QFrame, QHBoxLayout, QLabel, QLineEdit, QPushButton, QScrollArea, QTableWidget, QVBoxLayout, QWidget, ) PROJECT_ROOT = Path(__file__).resolve().parent.parent if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from step_editor.widgets import NoWheelComboBox from step_editor.window_state import WindowStateMixin class _StatusBar: def showMessage(self, _text: str) -> None: pass class _PropertyCardProbe(QWidget, WindowStateMixin): def __init__(self) -> None: super().__init__() self.model = object() self.operation_in_progress = False self.scan_in_progress = False self.load_in_progress = False self.property_editor_updating = False self.property_table_expanded = False self.property_table_collapsed_rows = 4 self.property_editor_selected_row = None self.property_command_active_key = "" self.property_command_buttons = {} self.property_editor_specs = [] self.selected_kind = "face" self.selected_part_id = None self.selected_solid_id = None self.selected_face_id = 0 self.selected_edge_id = None self.current_info_values = {"area": 100.0} self.property_table = QTableWidget(0, 5) self.property_card_scroll = QScrollArea() self.property_card_container = QWidget() self.property_card_layout = QVBoxLayout(self.property_card_container) self.property_card_scroll.setWidget(self.property_card_container) self.property_command_summary_label = QLabel() self.property_command_bar = QFrame() self.property_command_layout = QHBoxLayout(self.property_command_bar) self.apply_property_button = QPushButton() def _selected_action_info(self) -> dict[str, object]: return { "area": 100.0, "surface": "plane", "push_pull_status": "ready", "first_level_boundary_edge_count": 4, "first_level_boundary_vertex_count": 4, "first_level_adjacent_face_count": 4, } def _set_control_state(self, widget, enabled: bool, _enabled_tip: str, _disabled_tip: str) -> None: widget.setEnabled(enabled) def statusBar(self) -> _StatusBar: return _StatusBar() class _TopLevelPropertyLabelProbe(QObject): def __init__(self) -> None: super().__init__() self.shown_labels: list[str] = [] def eventFilter(self, watched, event): if ( event.type() == QEvent.Type.Show and isinstance(watched, QLabel) and watched.isWindow() and watched.objectName().startswith("propertyCard") ): self.shown_labels.append(f"{type(watched).__name__}:{watched.objectName()}:{watched.text()}") return False def _assert(condition: bool, message: str) -> None: if not condition: raise AssertionError(message) def main() -> int: app = QApplication.instance() or QApplication([]) top_level_label_probe = _TopLevelPropertyLabelProbe() app.installEventFilter(top_level_label_probe) probe = _PropertyCardProbe() probe.show() QApplication.processEvents() probe._refresh_property_editor() QApplication.processEvents() _assert( not top_level_label_probe.shown_labels, f"property card labels were shown as transient top-level windows: {top_level_label_probe.shown_labels}", ) actionable_labels = [str(spec.get("label", "")) for _row, spec in probe._actionable_property_rows()] _assert("面积" in actionable_labels, "modifiable feature list did not expose the editable area row") _assert(not probe.property_command_buttons, "legacy command buttons should not be shown in the modifiable-feature panel") _assert(not probe.property_command_bar.isVisible(), "legacy command bar should be hidden") _assert("可修改项" in probe.property_command_summary_label.text(), "modifiable-feature summary is missing") rows = getattr(probe, "property_card_rows", {}) _assert(rows, "property card rows were not built") _assert( not any(isinstance(widgets.get("target_editor"), QLineEdit) for widgets in rows.values()), "modifiable feature rows should be compact until the user expands one", ) _assert( all(str(widgets.get("status_label").text()) == "可修改" for widgets in rows.values()), "compact modifiable rows should end with the editable status label", ) _assert( all(not str(widgets.get("current_value").toolTip()) for widgets in rows.values() if widgets.get("current_value") is not None), "compact modifiable rows should not show click-triggered tooltips", ) target_row = probe._actionable_property_rows()[0][0] probe._select_property_card_row(target_row) rows = getattr(probe, "property_card_rows", {}) editable_rows = [row for row, widgets in rows.items() if isinstance(widgets.get("action_button"), QPushButton)] target_rows = [row for row, widgets in rows.items() if isinstance(widgets.get("target_editor"), QLineEdit)] scope_rows = [row for row, widgets in rows.items() if isinstance(widgets.get("scope_combo"), NoWheelComboBox)] _assert(editable_rows, "editable property card button is missing") _assert(target_rows, "property card target editor is missing") _assert(scope_rows, "property card modeling-intent combo is missing") probe._toggle_property_card_row(target_row) QApplication.processEvents() rows = getattr(probe, "property_card_rows", {}) _assert(probe.property_editor_selected_row is None, "clicking an expanded row should collapse it") _assert( not any(isinstance(widgets.get("target_editor"), QLineEdit) for widgets in rows.values()), "collapsed modifiable rows should return to compact display", ) _assert( not any(editor.isVisible() for editor in probe.property_card_container.findChildren(QLineEdit)), "collapsed modifiable rows should not leave stale target editors visible", ) _assert( not any( label.isVisible() and "建模意图:" in label.text() for label in probe.property_card_container.findChildren(QLabel) ), "collapsed modifiable rows should not leave stale expanded hints visible", ) probe._select_property_card_row(target_row) rows = getattr(probe, "property_card_rows", {}) target_rows = [row for row, widgets in rows.items() if isinstance(widgets.get("target_editor"), QLineEdit)] _assert(target_rows, "target editor is missing after re-expanding the row") rows[target_rows[0]]["target_editor"].setText("144") probe._update_property_apply_state() changed = probe._changed_property_rows() button = rows[target_rows[0]]["action_button"] _assert(changed, "card target edit was not detected") _assert(bool(button.property("changed")), "card button did not enter changed state") _assert(button.isEnabled(), "card button should be enabled for a valid changed target") probe.toggle_property_table_expanded() _assert(probe.property_table_expanded, "property card expand toggle failed") _assert(len(probe.property_card_rows) > len(rows), "expanded property card list did not reveal more rows") expanded_editor = probe.property_card_rows[target_rows[0]].get("target_editor") _assert(isinstance(expanded_editor, QLineEdit), "expanded target editor is missing") _assert(expanded_editor.text().strip() == "144", "target value was not preserved after card rebuild") long_context = "已按“相邻特征”沿共享边拓扑探测当前特征及 3 个局部关联特征;关联尺寸可在同一参数表中直接修改。" probe.property_editor_specs = [ { "key": "feature_context_note", "label": "关联探测", "current_text": long_context, "current_raw": long_context, "target_text": "", "editable": False, "enabled": False, "status_text": "说明", "span_value_columns": True, "pin_top": True, }, { "key": "associated_face_center", "label": "相邻平面 Face 1580 · 中心", "current_text": "(-118.585, -23.3, -269.561)", "current_raw": "(-118.585, -23.3, -269.561)", "target_text": "(-118.585, -23.3, -269.561)", "editable": True, "enabled": True, "action": "move_face_center", "scope_text": "局部重建", "status_text": "可修改", }, ] probe.property_table_expanded = True probe.property_editor_selected_row = None probe._rebuild_property_cards() QApplication.processEvents() context_value = next( ( label for label in probe.property_card_rows[0]["card"].findChildren(QLabel) if label.objectName() == "propertyCardValue" ), None, ) _assert(isinstance(context_value, QLabel), "long associated detection note is missing") _assert(context_value.wordWrap(), "long associated detection note should wrap in diagnostics view") _assert("关联尺寸可在同一参数表中直接修改" in context_value.text(), "associated detection note lost its trailing text") associated_widgets = probe.property_card_rows[1] associated_card = associated_widgets["card"] associated_title = next( ( label for label in associated_card.findChildren(QLabel) if label.objectName() == "propertyCardTitle" ), None, ) _assert(isinstance(associated_title, QLabel), "long associated row title is missing") _assert(associated_title.wordWrap(), "long associated row title should use a two-line compact layout") _assert(associated_card.height() > 24, "long associated row should be taller than a one-line compact row") associated_value = associated_widgets.get("current_value") _assert(isinstance(associated_value, QLabel), "long associated row value is missing") _assert("(-118.585" in associated_value.text(), "long associated row value was hidden by the title") print("property card editor UI ok") if QApplication.instance() is app: app.quit() return 0 if __name__ == "__main__": raise SystemExit(main())