@@ -203,8 +203,8 @@ def __init__(self, napari_viewer):
203
203
204
204
self ._radio_group = self ._form_mode_radio_buttons ()
205
205
206
+ self ._display = ColorSchemeDisplay (parent = self )
206
207
self ._color_scheme_display = self ._form_color_scheme_display (self .viewer )
207
-
208
208
self ._view_scheme_cb .toggled .connect (self ._show_color_scheme )
209
209
self ._view_scheme_cb .toggle ()
210
210
@@ -354,29 +354,23 @@ def _func():
354
354
return group
355
355
356
356
def _form_color_scheme_display (self , viewer ):
357
- display = ColorSchemeDisplay (parent = self )
358
- self ._update_color_scheme (display )
359
-
360
- self .viewer .layers .events .inserted .connect (
361
- partial (self ._update_color_scheme , display )
362
- )
363
-
357
+ self .viewer .layers .events .inserted .connect (self ._update_color_scheme )
364
358
return viewer .window .add_dock_widget (
365
- display , name = "Color scheme reference" , area = "left"
359
+ self . _display , name = "Color scheme reference" , area = "left"
366
360
)
367
361
368
- def _update_color_scheme (self , display ):
362
+ def _update_color_scheme (self ):
363
+ def to_hex (nparray ):
364
+ a = np .array (nparray * 255 , dtype = int )
365
+ rgb2hex = lambda r , g , b , _ : f"#{ r :02x} { g :02x} { b :02x} "
366
+ res = rgb2hex (* a )
367
+ return res
368
+
369
+ self ._display .reset ()
369
370
for layer in self .viewer .layers :
370
371
if isinstance (layer , Points ) and layer .metadata :
371
-
372
- def to_hex (nparray ):
373
- a = np .array (nparray * 255 , dtype = int )
374
- rgb2hex = lambda r , g , b , _ : f"#{ r :02x} { g :02x} { b :02x} "
375
- res = rgb2hex (* a )
376
- return res
377
-
378
372
[
379
- display .add_entry (name , to_hex (color ))
373
+ self . _display .add_entry (name , to_hex (color ))
380
374
for name , color in layer .metadata ["face_color_cycles" ][
381
375
"label"
382
376
].items ()
@@ -439,6 +433,38 @@ def on_insert(self, event):
439
433
10 , partial (self ._move_image_layer_to_bottom , event .index )
440
434
)
441
435
elif isinstance (layer , Points ):
436
+ # If the current Points layer comes from a config file, some have already
437
+ # been added and the body part names are different from the existing ones,
438
+ # then we update store's metadata and menus.
439
+ if layer .metadata .get ("project" , "" ) and self ._stores :
440
+ keypoints_menu = self ._menus [0 ].menus ["label" ]
441
+ current_keypoint_set = set (
442
+ keypoints_menu .itemText (i ) for i in range (keypoints_menu .count ())
443
+ )
444
+ new_keypoint_set = set (layer .metadata ["header" ].bodyparts )
445
+ diff = new_keypoint_set .difference (current_keypoint_set )
446
+ if diff :
447
+ answer = QMessageBox .question (self , "" , "Do you want to display the new keypoints only?" )
448
+ if answer == QMessageBox .Yes :
449
+ self .viewer .layers [- 2 ].shown = False
450
+
451
+ self .viewer .status = f"New keypoint{ 's' if len (diff ) > 1 else '' } { ', ' .join (diff )} found."
452
+ for _layer , store in self ._stores .items ():
453
+ _layer .metadata ["header" ] = layer .metadata ["header" ]
454
+ _layer .metadata ["face_color_cycles" ] = layer .metadata ["face_color_cycles" ]
455
+ _layer .face_color_cycle = layer .face_color_cycle
456
+ store .layer = _layer
457
+
458
+ for menu in self ._menus :
459
+ menu ._map_individuals_to_bodyparts ()
460
+ menu ._update_items ()
461
+
462
+ self ._update_color_scheme ()
463
+
464
+ # Remove the unnecessary layer newly added
465
+ QTimer .singleShot (10 , self .viewer .layers .pop )
466
+ return
467
+
442
468
store = keypoints .KeypointStore (self .viewer , layer )
443
469
self ._stores [layer ] = store
444
470
# TODO Set default dir of the save file dialog
@@ -470,14 +496,15 @@ def on_insert(self, event):
470
496
471
497
def on_remove (self , event ):
472
498
layer = event .value
473
- if isinstance (layer , Points ):
499
+ n_points_layer = sum (isinstance (l , Points ) for l in self .viewer .layers )
500
+ if isinstance (layer , Points ) and n_points_layer == 0 :
474
501
if self ._color_scheme_display is not None :
475
- self .viewer . window . remove_dock_widget ( self . _color_scheme_display )
502
+ self ._display . reset ( )
476
503
self ._stores .pop (layer , None )
477
504
while self ._menus :
478
505
menu = self ._menus .pop ()
479
506
self ._layout .removeWidget (menu )
480
- menu .setParent ( None )
507
+ menu .deleteLater ( )
481
508
menu .destroy ()
482
509
self ._trail_cb .setEnabled (False )
483
510
self .last_saved_label .hide ()
@@ -529,7 +556,7 @@ def toggle_edge_color(layer):
529
556
class DropdownMenu (QComboBox ):
530
557
def __init__ (self , labels : Sequence [str ], parent : Optional [QWidget ] = None ):
531
558
super ().__init__ (parent )
532
- self .addItems (labels )
559
+ self .update_items (labels )
533
560
534
561
def update_to (self , text : str ):
535
562
index = self .findText (text )
@@ -539,6 +566,10 @@ def update_to(self, text: str):
539
566
def reset (self ):
540
567
self .setCurrentIndex (0 )
541
568
569
+ def update_items (self , items ):
570
+ self .clear ()
571
+ self .addItems (items )
572
+
542
573
543
574
class KeypointsDropdownMenu (QWidget ):
544
575
def __init__ (
@@ -551,22 +582,11 @@ def __init__(
551
582
self .store .layer .events .current_properties .connect (self .update_menus )
552
583
self ._locked = False
553
584
554
- # Map individuals to their respective bodyparts
555
585
self .id2label = defaultdict (list )
556
- for keypoint in store ._keypoints :
557
- label = keypoint .label
558
- id_ = keypoint .id
559
- if label not in self .id2label [id_ ]:
560
- self .id2label [id_ ].append (label )
561
-
562
586
self .menus = dict ()
563
- if store .ids [0 ]:
564
- menu = create_dropdown_menu (store , list (self .id2label ), "id" )
565
- menu .currentTextChanged .connect (self .refresh_label_menu )
566
- self .menus ["id" ] = menu
567
- self .menus ["label" ] = create_dropdown_menu (
568
- store , self .id2label [store .ids [0 ]], "label"
569
- )
587
+ self ._map_individuals_to_bodyparts ()
588
+ self ._populate_menus ()
589
+
570
590
layout1 = QVBoxLayout ()
571
591
layout1 .addStretch (1 )
572
592
group_box = QGroupBox ("Keypoint selection" )
@@ -582,6 +602,29 @@ def __init__(
582
602
layout1 .addWidget (group_box )
583
603
self .setLayout (layout1 )
584
604
605
+ def _map_individuals_to_bodyparts (self ):
606
+ for keypoint in self .store ._keypoints :
607
+ label = keypoint .label
608
+ id_ = keypoint .id
609
+ if label not in self .id2label [id_ ]:
610
+ self .id2label [id_ ].append (label )
611
+
612
+ def _populate_menus (self ):
613
+ id_ = self .store .ids [0 ]
614
+ if id_ :
615
+ menu = create_dropdown_menu (self .store , list (self .id2label ), "id" )
616
+ menu .currentTextChanged .connect (self .refresh_label_menu )
617
+ self .menus ["id" ] = menu
618
+ self .menus ["label" ] = create_dropdown_menu (
619
+ self .store , self .id2label [id_ ], "label" ,
620
+ )
621
+
622
+ def _update_items (self ):
623
+ id_ = self .store .ids [0 ]
624
+ if id_ :
625
+ self .menus ["id" ].update_items (list (self .id2label ))
626
+ self .menus ["label" ].update_items (self .id2label [id_ ])
627
+
585
628
def _lock_current_keypoint (self ):
586
629
self ._locked = not self ._locked
587
630
if self ._locked :
@@ -833,5 +876,8 @@ def add_entry(self, name, color):
833
876
self ._layout .addWidget (
834
877
LabelPair (color , name , self ), alignment = Qt .AlignmentFlag .AlignLeft
835
878
)
836
- self ._container .setLayout (self ._layout )
837
- self ._container .update ()
879
+
880
+ def reset (self ):
881
+ self .scheme_dict = {}
882
+ for i in reversed (range (self ._layout .count ())):
883
+ self ._layout .itemAt (i ).widget ().deleteLater ()
0 commit comments