Fix KeypointStore not initializing when layers load before widget#169
Fix KeypointStore not initializing when layers load before widget#169milesAraya wants to merge 2 commits intoDeepLabCut:mainfrom
Conversation
|
Hello @milesAraya, Thanks for the PR ! This quirk is already being addressed in #163, #164 and related refactor work, which also tackle quite a few other issues with the plugin. While this minimal fix is very nice and concise, I would kindly suggest a few potential points for improvement :
For now I’ll mark this PR as draft to avoid merging overlapping fixes while the refactor is in progress. If you'd like to get involved in the refactor, you're very welcome to review relevant sections, and share feedback on the implementation suggested here and here, as well as the signal wiring throughout. Any contributions there are welcome, feel free to open a PR targeting #168 for example. Best, |
3b3940d to
4cdb6b9
Compare
|
Thanks Cyril! Good to know this is covered in the refactor. The timer approach was pragmatic, we needed a working fix for our lab's labeling workflow. Happy to see _adopt_existing_layers and the root fallback in _wire_points_layer handle this properly. |
|
@milesAraya Understood, thanks for sharing the fix ! |
Pull Request: Fix KeypointStore not initializing when layers load before widget
Current Branch: fix/retroactive-keypoint-store-init
Target Branch: main
Content
Summary
on_insertmay not fire for Points layers that were added before theKeypointControlswidget was fully initialized_storesempty, which disables labeling mode controls (Sequential/Quick/Loop), keypoint cycling (Up/Down/M keys), and dropdown menus_retroactive_store_init()toKeypointControls.__init__which retries every second (up to 10 attempts) to detect and initialize any unprocessed Points layerson_insertagainst Points layers without DLC metadata to preventKeyErrorcrashes on missingheaderDesign Decisions
action.trigger()in DeepLabCut'slaunch_napari()is asynchronous, and the delay before the widget is ready varies (observed ~3-7 seconds). A retry loop with a cap is more robust than guessing a fixed delayon_insertrather than try/except: Explicitly checking forheadermetadata is clearer than catchingKeyError, and avoids masking unrelated errors inKeypointStore.__init__on_insertlogic in_retroactive_store_init: Considered callingon_insertdirectly with a synthetic event, buton_insertusesevent.source[-1]which assumes specific event structure. Duplicating the store initialization logic is safer and more readableEvidence
_storesis empty, all mode radio buttons disabled (isEnabled() == False)_storespopulated after ~4-7 seconds, mode buttons enabled, Loop/Quick/Sequential modes functional, Up/Down keypoint cycling worksReferences
launch_napari()indeeplabcut/gui/widgets.pycallsaction.trigger()thenviewer.open(), but the widget may not be connected to layer events yetFiles changed
src/napari_deeplabcut/_widgets.py-- Add_retroactive_store_init()method with QTimer retry loop toKeypointControls.__init__; add early return guard inon_insertfor Points layers missingheadermetadataManual Testcases
python -m deeplabcutlabeled-data/video_name/)CollectedData_<scorer>layer appears in napariUnit, Integration, Contract Test Coverage
Others
Difficulties
action.trigger()(async plugin activation) andviewer.open()(sync layer loading) in DeepLabCut'slaunch_napari(). The widget'son_insertlistener connects during__init__, but__init__hasn't completed when layers are already being addedon_insertusesevent.source[-1]rather thanevent.value, so creating synthetic events for retroactive processing is fragileRisk Assessment
_storesis empty; no-ops ifon_insertalready handled layers normallyon_insertguardheadermetadata; no impact on normal DLC workflow