Fix SkeletonBuilder initialization from GUI (#3380)
* Refactor SkeletonBuilder init and usage
Change SkeletonBuilder to use BaseSkeletonBuilder as the first base class and make its __init__ accept config_path as a keyword-only argument. Initialize the Qt dialog with an explicit parent (parent=parent) and keep BaseSkeletonBuilder.__init__ called with config_path. Update the call site in create_videos to pass config_path=... and parent=self.root. These changes clarify MRO, enforce explicit config_path usage, and ensure the dialog gets the correct parent widget.
* Pass parent to SkeletonBuilder
Update call in label_frames.py to use explicit keyword arguments: config_path=self.root.config and parent=self.root. This ensures the SkeletonBuilder receives the parent GUI context and makes the parameter usage clearer.
* Remove parent argument from SkeletonBuilder call
Update LabelFrames.build_skeleton to instantiate SkeletonBuilder without passing parent=self.root. This aligns the call with the updated SkeletonBuilder constructor/signature and avoids supplying the GUI root object.
* Persist and show SkeletonBuilder in GUI tabs
Assign SkeletonBuilder instances to self.skeleton_builder and call show() in both create_videos and label_frames tabs. Also update the import in label_frames to use deeplabcut.gui.widgets.SkeletonBuilder. This ensures the builder window is displayed.
* Allow positional config_path in SkeletonBuilder
Change SkeletonBuilder.__init__ signature in deeplabcut/gui/widgets.py from
`def __init__(self, *, config_path, parent=None)` to
`def __init__(self, config_path, *, parent=None)` so callers can pass config_path positionally while keeping parent keyword-only. No other behavior changes.
* Init skeleton_builder and enhance SkeletonBuilder
Add skeleton_builder = None to CreateVideos and LabelFrames to ensure the attribute exists and avoid potential attribute errors. In SkeletonBuilder, instantiate FigureCanvas earlier (remove duplicate instantiation) and add an export() override that calls the base export and logs a success message. Also includes a minor whitespace/layout cleanup.
* Prevent duplicate SkeletonBuilder dialogs
Ensure only one SkeletonBuilder dialog is created per tab and properly cleaned up. CreateVideos and LabelFrames now check if self.skeleton_builder is None before instantiating, connect the widget's destroyed signal to _on_skeleton_builder_destroyed to clear the reference, and avoid reopening if already present. In widgets.SkeletonBuilder set WA_DeleteOnClose so the dialog is deleted on close and show a brief statusbar message on export. Also import Qt into widgets where needed. These changes prevent multiple dialog instances, fix cleanup, and provide user feedback on export.
* Handle missing 'skeleton' key in config
Use self.cfg.get("skeleton", []) and a local skeleton variable before iterating, replacing direct indexing of self.cfg["skeleton"]. This avoids a KeyError when the config lacks a skeleton entry while preserving existing behavior when skeleton is present.
* Fix logging call
* Refactor SkeletonBuilder UI and add logging
Introduce logging and make SkeletonBuilder UI parameters configurable. Added import logging and module logger; initialize self._ax and self.df in constructor. Replace local ax and ampl with instance attributes (self._ax, self.ampl) and expose UI parameters (lasso_select_size, clear/export button axes and labels) to avoid hard-coded values. Update build_ui to use the new attributes and wire up LassoSelector and Buttons using configurable axes/text. These changes improve configurability and prepare for further config refactoring.
* Improve skeleton export and lasso selection
Add UI feedback on export and harden export logic: wrap export in try/except, log when saving an empty or partially connected skeleton, write config and show a temporary green "Saved N" button label (with timer reference to avoid GC). Make lasso selection resolution/zoom independent by transforming keypoints and lasso verts into display coordinates, querying a KDTree built in display space using self.lasso_select_size, and ensure segments are passed as a list to set_segments before redrawing.
* Make SkeletonBuilder plot params configurable
Refactor SkeletonBuilder plotting code to use instance attributes instead of hard-coded values: rename ax to _ax for encapsulation, use self.clear_button_axes and self.export_button_axes for button placements, use self.clear_button_text and self.export_button_text for labels, and replace the inline ampl constant with self.ampl. Also attach the LassoSelector and all drawing operations to _ax and keep canvas draw_idle(). These changes allow external configuration of button layout, labels, and zoom/amplification behavior.
* Promote SkeletonBuilder defaults to class attrs
Move UI/usage parameters (lasso_select_size, clear/export button axes/text, ampl) from __init__ to class-level attributes in deeplabcut/utils/skeleton.py so they become shared defaults rather than instance-only values.
Update tests in tests/utils/test_skeleton.py: attach_fake_canvas now creates an _ax on the Figure and sets its x/y limits to ensure plotting code has an axes to operate on; test_export_sorts_pairs_and_warns_for_unconnected now uses the caplog fixture (caplog.at_level and an assertion on caplog.text) instead of pytest.warns to verify the informational log about unconnected bodyparts. These changes improve default handling and make tests more robust about logging and axis availability.
* Handle skeleton export failures in GUI
Make skeleton export report real success/failure to the Qt UI by returning a boolean from `SkeletonBuilder.export()` and conditionally showing success or failure messages in `gui/widgets.py`. The export path now logs incomplete/empty skeleton states as warnings and returns `False` on exceptions. This changes `lasso_select_size` from 5 to 10 to improve selection usability in the skeleton builder. C
Cyril Achard committed
ef2acdb4ebfbc89e323fdd85299ad8e73512266f
Parent: 59a292a
Committed by GitHub <noreply@github.com>
on 6/27/2026, 8:17:39 AM