feat: 完善参数化关系式与SCDM模型修改验证

This commit is contained in:
2026-08-17 18:53:03 +08:00
parent a633b5a338
commit 722256a41f
7 changed files with 1798 additions and 83 deletions
+20 -3
View File
@@ -287,6 +287,16 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
self.property_command_buttons: dict[str, QPushButton] = {}
self.relation_formula_items: list[dict[str, object]] = []
self.relation_formula_next_id = 1
self.relation_formula_base_snapshot: dict[object, object] | None = None
self.relation_formula_replay_queue: list[dict[str, object]] = []
self.relation_formula_replay_total = 0
self.relation_formula_replay_done = 0
self.relation_formula_replay_active = False
self.relation_formula_replay_current_id: int | None = None
self.relation_formula_replay_callback_seen = False
self._relation_formula_replay_running_action = False
self._relation_formula_object_label_cache_key: object = None
self._relation_formula_object_label_cache: dict[str, object] = {}
self._build_ui()
self._build_vtk()
@@ -1292,7 +1302,7 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
self.relation_formula_box.setObjectName("relationFormulaBox")
help_tip(
self.relation_formula_box,
"用 FaceID.参数 = 表达式 的形式建立关系式。第一版会先计算公式并回填当前参数表目标值,再执行参数化建模",
"用 FaceID.参数 = 表达式 的形式建立关系式。添加后会立即按当前公式组重新计算并修改模型",
)
relation_layout = QVBoxLayout(self.relation_formula_box)
relation_layout.setContentsMargins(6, 8, 6, 6)
@@ -1305,13 +1315,20 @@ class StepEditorWindow(WindowCoreMixin, WindowStateMixin, WindowActionMixin, Inf
self.relation_formula_input.setPlaceholderText("Face87.直径 = Face85.直径")
help_tip(
self.relation_formula_input,
"示例:Face87.直径 = Face85.直径,或 Face87.位置 = Face85.位置 + (0, 0, -3.5)。输入 Face87. 后会提示当前可用参数。",
"示例:Face87.直径 = Face85.直径,或 Face87.位置 = Face85.位置 + (0, 0, -3.5)。输入 Face87. 后会提示可用参数。",
)
self.relation_formula_completer_model = QStringListModel(self)
self.relation_formula_completer = QCompleter(self.relation_formula_completer_model, self)
self.relation_formula_completer.setCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
self.relation_formula_completer.setFilterMode(Qt.MatchFlag.MatchStartsWith)
self.relation_formula_completer.setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
self.relation_formula_input.setCompleter(self.relation_formula_completer)
self.relation_formula_completer.setMaxVisibleItems(12)
self.relation_formula_completer.activated[str].connect(self._on_relation_formula_completion_activated)
relation_popup = self.relation_formula_completer.popup()
if relation_popup is not None:
relation_popup.setFocusPolicy(Qt.FocusPolicy.NoFocus)
relation_popup.installEventFilter(self)
self.relation_formula_completer.setWidget(self.relation_formula_input)
self.relation_formula_input.installEventFilter(self)
self.relation_formula_input.textChanged.connect(self._on_relation_formula_input_changed)
self.relation_formula_input.returnPressed.connect(self.add_relation_formula)
+18
View File
@@ -4808,6 +4808,24 @@ class StepModel(FeatureMixin, ExportMixin, TransformMixin, OperationMixin, Polyd
self._editable_feature_candidates_cache.clear()
self._cylindrical_feature_candidates_cache.clear()
def mark_external_recognition_stale(self, reason: str = "modified-topology") -> None:
self._topology_refresh_generation = max(int(getattr(self, "_topology_refresh_generation", 0) or 0), 2)
self._asitus_hole_regions_attempted = False
self._asitus_hole_regions_loading = False
self._asitus_hole_region_cache.clear()
self._asitus_face_relation_cache.clear()
self._asitus_adjacency_relation_cache.clear()
self._asitus_geometric_relation_cache.clear()
self._asitus_hole_recognition_info.clear()
self._asitus_hole_recognition_info.update(
{
"ok": False,
"reason": str(reason or "modified-topology"),
"message": "Analysis Situs result ignored because the model topology has changed.",
}
)
self._clear_same_domain_dependent_caches()
def _install_asitus_relation_summary(self, result: dict[str, object]) -> None:
faces = result.get("faces", ())
if isinstance(faces, (tuple, list)):
+17 -1
View File
@@ -8257,6 +8257,10 @@ class WindowActionMixin:
new_model.filename = self.step_path
except Exception:
pass
try:
new_model.mark_external_recognition_stale("isolated-edit-result")
except Exception:
pass
child_message = str(response.get("message") or "隔离子进程编辑完成。")
self._preserve_isolated_face_logical_id(new_model, context, child_message)
started = time.perf_counter()
@@ -9186,8 +9190,11 @@ class WindowActionMixin:
timings["finish_ui"] = time.perf_counter() - finish_started
locator_note = self._locate_operation_record(record)
relation_note = ""
relation_dependency_ids: list[int] = []
relation_replay_active = bool(getattr(self, "relation_formula_replay_active", False))
if hasattr(self, "_refresh_relation_formulas_after_model_edit"):
relation_note = self._refresh_relation_formulas_after_model_edit()
relation_note = self._refresh_relation_formulas_after_model_edit(context=context)
relation_dependency_ids = list(getattr(self, "_last_relation_formula_refresh_affected_ids", []) or [])
if relation_note:
locator_note = f"{locator_note}\n{relation_note}" if locator_note else relation_note
except Exception as exc:
@@ -9220,6 +9227,15 @@ class WindowActionMixin:
self.set_plain_info(f"{record.detail}{timing_detail}\n\n{locator_note}")
if hasattr(self, "_after_property_edit_finished"):
self._after_property_edit_finished(success=True)
if (
relation_dependency_ids
and not relation_replay_active
and hasattr(self, "_queue_relation_formula_dependency_reapply")
and hasattr(self, "_run_pending_relation_formula_dependency_reapply")
):
self._queue_relation_formula_dependency_reapply(relation_dependency_ids)
if not bool(getattr(self, "property_batch_active", False)):
self._run_pending_relation_formula_dependency_reapply()
@Slot(str)
def _fail_edit_action(self, message: str) -> None:
+43 -1
View File
@@ -179,12 +179,48 @@ class WindowCoreMixin:
elif watched is getattr(self, "property_table", None):
if event.type() == QEvent.Type.Resize and hasattr(self, "_resize_property_table_columns"):
QTimer.singleShot(0, self._resize_property_table_columns)
elif watched is self._relation_formula_popup_widget():
if event.type() == QEvent.Type.KeyPress and event.key() == Qt.Key.Key_Backspace:
editor = getattr(self, "relation_formula_input", None)
popup = self._relation_formula_popup_widget()
if editor is not None and hasattr(editor, "backspace"):
if popup is not None and hasattr(popup, "hide"):
popup.hide()
if hasattr(editor, "setFocus"):
editor.setFocus(Qt.FocusReason.OtherFocusReason)
editor.backspace()
if hasattr(self, "_update_relation_formula_completions"):
QTimer.singleShot(0, self._update_relation_formula_completions)
event.accept()
return True
elif watched is getattr(self, "relation_formula_input", None):
if event.type() == QEvent.Type.KeyPress and event.key() == Qt.Key.Key_Tab:
if event.type() == QEvent.Type.MouseButtonPress:
if hasattr(self, "_hide_relation_formula_completion_popup"):
self._hide_relation_formula_completion_popup()
if hasattr(watched, "setFocus"):
watched.setFocus(Qt.FocusReason.MouseFocusReason)
if event.type() in {QEvent.Type.ShortcutOverride, QEvent.Type.KeyPress} and event.key() == Qt.Key.Key_Tab:
if bool(getattr(self, "_relation_formula_tab_completion_accepted", False)):
self._relation_formula_tab_completion_accepted = False
event.accept()
return True
if hasattr(self, "_accept_relation_formula_completion") and self._accept_relation_formula_completion():
self._relation_formula_tab_completion_accepted = event.type() == QEvent.Type.ShortcutOverride
if self._relation_formula_tab_completion_accepted:
QTimer.singleShot(0, lambda: setattr(self, "_relation_formula_tab_completion_accepted", False))
event.accept()
return True
return super().eventFilter(watched, event)
def _relation_formula_popup_widget(self):
completer = getattr(self, "relation_formula_completer", None)
if completer is None or not hasattr(completer, "popup"):
return None
try:
return completer.popup()
except RuntimeError:
return None
def _should_suppress_transient_tooltip(self, watched) -> bool:
if not isinstance(watched, QWidget):
return False
@@ -1274,6 +1310,8 @@ class WindowCoreMixin:
self.load_in_progress = False
self.pending_load_path = None
self._update_action_states()
if hasattr(self, "_update_relation_formula_buttons"):
self._update_relation_formula_buttons()
if float(getattr(self, "preview_load_deflection", 0.0) or 0.0) > float(
getattr(self, "initial_load_deflection", 0.0) or 0.0
):
@@ -1337,6 +1375,8 @@ class WindowCoreMixin:
self.load_in_progress = False
self.pending_load_path = None
self._update_action_states()
if hasattr(self, "_update_relation_formula_buttons"):
self._update_relation_formula_buttons()
self._request_thread_quit(self.load_thread)
self._request_thread_quit(self.load_refine_thread)
@@ -1387,6 +1427,8 @@ class WindowCoreMixin:
self.redo_stack.clear()
self.operation_history.clear()
self.redo_history.clear()
if hasattr(self, "_clear_relation_formula_runtime_state"):
self._clear_relation_formula_runtime_state(clear_items=True)
self.clear_diff_preview(render=False)
if hasattr(self, "history_list"):
self.history_list.clear()
File diff suppressed because it is too large Load Diff