fix(datum): prevent RuntimeError from deleted Qt widgets in params UI
QFormLayout.removeRow() destroys the widgets it owns, transferring ownership to Qt which immediately deletes the underlying C++ objects. The update_params_ui() method was calling removeRow(0) in a loop to clear the parameter fields before re-populating them for the new mode. This destroyed the long-lived QDoubleSpinBox instances (offset_spin, angle_spin, param_spin, x/y/z_spin) stored as instance attributes. On the next call to update_params_ui() — triggered by selection changes, row removal, or mode override — the method attempted to addRow() with these same Python references, but the C++ objects behind them had already been freed by Qt. This produced: RuntimeError: Internal C++ object (PySide6.QtWidgets.QDoubleSpinBox) already deleted. The fix replaces the removeRow() loop with a new _clear_params_layout() method that uses QLayout.takeAt() to detach items from the layout without destroying them. Each widget is hidden and reparented to None (releasing Qt's ownership) so it survives the layout clearing and can be safely re-added with addRow() and show() on the next mode switch.
This commit is contained in:
@@ -458,11 +458,24 @@ class DatumCreatorTaskPanel:
|
||||
self.update_params_ui(mode_id)
|
||||
return
|
||||
|
||||
def _clear_params_layout(self):
|
||||
"""Remove all rows from params_layout without deleting the widgets.
|
||||
|
||||
QFormLayout.removeRow() destroys the widgets it owns. Instead we
|
||||
detach every item from the layout (which relinquishes ownership)
|
||||
and hide the widgets so they can be re-added later.
|
||||
"""
|
||||
while self.params_layout.count():
|
||||
item = self.params_layout.takeAt(0)
|
||||
widget = item.widget()
|
||||
if widget is not None:
|
||||
widget.hide()
|
||||
widget.setParent(None)
|
||||
|
||||
def update_params_ui(self, mode_id):
|
||||
"""Update parameters UI based on mode."""
|
||||
# Clear existing params
|
||||
while self.params_layout.rowCount() > 0:
|
||||
self.params_layout.removeRow(0)
|
||||
# Clear existing params without destroying widgets
|
||||
self._clear_params_layout()
|
||||
|
||||
if mode_id is None:
|
||||
self.params_group.setVisible(False)
|
||||
@@ -471,18 +484,26 @@ class DatumCreatorTaskPanel:
|
||||
self.params_group.setVisible(True)
|
||||
|
||||
if mode_id in ("offset_face", "offset_plane"):
|
||||
self.offset_spin.show()
|
||||
self.params_layout.addRow("Offset:", self.offset_spin)
|
||||
elif mode_id == "angled":
|
||||
self.angle_spin.show()
|
||||
self.params_layout.addRow("Angle:", self.angle_spin)
|
||||
elif mode_id == "normal_edge":
|
||||
self.param_spin.show()
|
||||
self.params_layout.addRow("Position (0-1):", self.param_spin)
|
||||
elif mode_id == "tangent_cyl":
|
||||
self.angle_spin.show()
|
||||
self.params_layout.addRow("Angle:", self.angle_spin)
|
||||
elif mode_id == "point_xyz":
|
||||
self.x_spin.show()
|
||||
self.y_spin.show()
|
||||
self.z_spin.show()
|
||||
self.params_layout.addRow("X:", self.x_spin)
|
||||
self.params_layout.addRow("Y:", self.y_spin)
|
||||
self.params_layout.addRow("Z:", self.z_spin)
|
||||
elif mode_id == "point_edge":
|
||||
self.param_spin.show()
|
||||
self.params_layout.addRow("Position (0-1):", self.param_spin)
|
||||
else:
|
||||
# No parameters needed
|
||||
|
||||
Reference in New Issue
Block a user