Prevent crash (CAMTests) in ToolBitRecomputeObserver when the toolbit object has been deleted/missing
by catching ReferenceError before accessing its Document attribute. This ensures slotRecomputedDocument
exits gracefully if the object is no longer valid.
src/Mod/CAM/Path/Tool/toolbit/models/base.py
- Wrapped access to self.toolbit_proxy.obj.Document in try/except to handle ReferenceError
if object is deleted/missing, preventing crash during document recompute.
Introduced FLOAT_EPSILON for robust floating point comparisons, resolving cases
where Z axis retract moves were missing after geometry recompute in 3D Surface
rotational scan operations. This prevents precision errors from causing the
cutter to skip intended lifts between rings or scan lines.
src/Mod/CAM/Path/Op/Surface.py
- Added FLOAT_EPSILON constant
- Updated Z move comparison logic to use tolerance
A bug in the ToolBit model caused an infinite recompute loop and UI freeze when properties such as Diameter, Flutes, or CuttingEdgeHeight were set to expressions. The visual representation update was being triggered during document recompute, which could recursively trigger further recomputes. This fix defers visual updates by queuing them and processing only after the document recompute completes, using a document observer. The observer is cleaned up after use and on object deletion, preventing memory leaks and repeated recompute cycles.
src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- ToolBitRecomputeObserver: Document observer class that triggers queued visual updates after recompute completes via slotRecomputedDocument.
- _queue_visual_update: Queues a visual update to be processed after document recompute.
- _setup_recompute_observer: Registers the document observer for recompute completion.
- _process_queued_visual_update: Processes the queued visual update and cleans up the observer.
- onChanged: Now queues visual updates instead of calling them directly.
- onDelete: Cleans up any pending document observer before object removal.
src/Gui/QuantitySpinBox.cpp:
- Call updateExpression() after setExpression() in QuantitySpinBox
src/Gui/SpinBox.cpp:
- Call updateExpression() after setExpression() in ExpressionSpinBox
src/Gui/Widgets.cpp:
- Call onChange() after setExpression() in ExpLineEdit
src/Mod/CAM/Path/Base/Gui/Util.py:
- Connect to showFormulaDialog signal and refresh CAM QuantitySpinBox Python wrapper when dialog closes
- Replace QDoubleSpinBox widgets with Gui::QuantitySpinBox in DressUpLeadInOutEdit.ui for all lead-in/out numeric fields, enabling unit/expressions support.
- Register QuantitySpinBox as a custom widget in the .ui file.
- Refactor TaskDressupLeadInOut panel setup:
- Add setupSpinBoxes, setupGroupBoxes, setupDynamicVisibility for cleaner UI initialization.
- Use PathGuiUtil.QuantitySpinBox for all numeric fields and ensure updateWidget() is called for each.
- Centralize signal registration and field updates using getSignalsForUpdate and pageGetFields.
- Move group box signal handler to a class method.
- Share hideModes dictionary for field visibility logic.
- Add dynamic label switching for "Radius"/"Length" with translation placeholders.
- Remove the Include layers Check Box
- Improve ObjectDressup migration:
- Use shared hideModes from TaskDressupLeadInOut.
- Set default angles to 90 instead of 45.
- Preserve previous style values when migrating StyleOn/StyleOff.
- Ensure field visibility is updated after migration.
- Add Perpendicular and Tangent to lead_styles in correct order.
- Enhance _promote_toolbit to handle embedded toolbits that still have a "Custom" shape type.
- If ShapeType is "Custom" but ShapeID is present, attempt to promote to the correct shape class using ShapeID.
- Log promotion for traceability.
- This improves migration of legacy embedded toolbits and ensures proper shape assignment.