celldetective 1.3.4.post1__py3-none-any.whl → 1.3.6.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +10 -5
  3. celldetective/filters.py +11 -0
  4. celldetective/gui/btrack_options.py +151 -1
  5. celldetective/gui/classifier_widget.py +44 -15
  6. celldetective/gui/configure_new_exp.py +13 -0
  7. celldetective/gui/control_panel.py +4 -2
  8. celldetective/gui/generic_signal_plot.py +2 -6
  9. celldetective/gui/gui_utils.py +170 -12
  10. celldetective/gui/measurement_options.py +85 -54
  11. celldetective/gui/neighborhood_options.py +1 -1
  12. celldetective/gui/plot_signals_ui.py +3 -4
  13. celldetective/gui/process_block.py +8 -6
  14. celldetective/gui/signal_annotator.py +10 -3
  15. celldetective/gui/signal_annotator2.py +146 -193
  16. celldetective/gui/survival_ui.py +121 -34
  17. celldetective/gui/tableUI.py +26 -12
  18. celldetective/gui/thresholds_gui.py +9 -52
  19. celldetective/gui/viewers.py +58 -21
  20. celldetective/io.py +1087 -161
  21. celldetective/measure.py +175 -102
  22. celldetective/preprocessing.py +2 -2
  23. celldetective/relative_measurements.py +6 -9
  24. celldetective/scripts/measure_cells.py +13 -3
  25. celldetective/scripts/segment_cells.py +0 -1
  26. celldetective/scripts/track_cells.py +25 -1
  27. celldetective/signals.py +9 -7
  28. celldetective/tracking.py +130 -81
  29. celldetective/utils.py +28 -7
  30. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/METADATA +3 -2
  31. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/RECORD +35 -35
  32. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/WHEEL +0 -0
  34. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/entry_points.txt +0 -0
  35. {celldetective-1.3.4.post1.dist-info → celldetective-1.3.6.post1.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ from celldetective.gui import Styles
7
7
  from celldetective.gui.gui_utils import center_window
8
8
  from superqt import QLabeledDoubleRangeSlider, QSearchableComboBox
9
9
  from celldetective.utils import extract_experiment_channels, get_software_location, _get_img_num_per_channel
10
- from celldetective.io import auto_load_number_of_frames, load_frames
10
+ from celldetective.io import auto_load_number_of_frames, load_frames, get_experiment_metadata
11
11
  from celldetective.gui.gui_utils import FigureCanvas, color_from_status, color_from_class
12
12
  import json
13
13
  import numpy as np
@@ -66,7 +66,10 @@ class SignalAnnotator2(QMainWindow,Styles):
66
66
  't0', 'POSITION_X', 'POSITION_Y', 'position', 'well', 'well_index', 'well_name', 'pos_name',
67
67
  'index', 'relxy', 'tc', 'nk', 'concentration', 'antibody', 'cell_type', 'pharmaceutical_agent',
68
68
  'reference_population', 'neighbor_population']
69
-
69
+ meta = get_experiment_metadata(self.exp_dir)
70
+ if meta is not None:
71
+ keys = list(meta.keys())
72
+ self.cols_to_remove.extend(keys)
70
73
 
71
74
  # Read instructions from target block for now...
72
75
  self.mode = "neighborhood"
@@ -429,27 +432,12 @@ class SignalAnnotator2(QMainWindow,Styles):
429
432
  contrast_hbox.addWidget(self.contrast_slider,90)
430
433
  self.right_panel.addLayout(contrast_hbox, 5)
431
434
 
432
- # speed_hbox = QHBoxLayout()
433
- # speed_hbox.setContentsMargins(150,5,150,5)
434
- # self.interval_slider = QLabeledSlider()
435
- # self.interval_slider.setSingleStep(1)
436
- # self.interval_slider.setTickInterval(1)
437
- # self.interval_slider.setOrientation(1)
438
- # self.interval_slider.setRange(1, 10000)
439
- # self.interval_slider.setValue(self.speed)
440
- # self.interval_slider.valueChanged.connect(self.interval_slider_action)
441
- # speed_hbox.addWidget(QLabel('interval (ms): '))
442
- # speed_hbox.addWidget(self.interval_slider,90)
443
- # self.right_panel.addLayout(speed_hbox, 10)
444
-
445
- #self.selected_populationulate_left_panel()
446
- #grid.addLayout(self.left_side, 0, 0, 1, 1)
447
-
448
435
  main_layout.addLayout(self.left_panel, 35)
449
436
  main_layout.addLayout(self.right_panel, 65)
450
437
  self.button_widget.adjustSize()
451
438
  self.compute_status_and_colors_reference()
452
439
 
440
+ self.extract_relevant_events()
453
441
 
454
442
  self.setCentralWidget(self.button_widget)
455
443
  self.show()
@@ -673,23 +661,11 @@ class SignalAnnotator2(QMainWindow,Styles):
673
661
  self.df_relative.loc[(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population),self.relative_time] = 0.1
674
662
  else:
675
663
  self.df_relative.loc[(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population),self.relative_time] = -1
676
- self.relative_class_choice_cb.disconnect()
677
- self.relative_class_choice_cb.clear()
678
- cols = np.array(self.df_relative.columns)
679
- self.relative_class_cols = np.array([c.startswith('class') for c in list(self.df_relative.columns)])
680
- self.relative_class_cols = list(cols[self.relative_class_cols])
681
- try:
682
- self.relative_class_cols.remove('class_color')
683
- self.relative_class_cols.remove('class_id')
684
- except:
685
- pass
686
- self.relative_class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_pair)
687
- self.relative_class_choice_cb.addItems(self.relative_class_cols)
688
- idx = self.relative_class_choice_cb.findText(self.relative_class)
689
- self.relative_class_choice_cb.setCurrentIndex(idx)
664
+
690
665
 
691
- self.pair_class_name = self.relative_class
666
+ self.extract_relevant_events()
692
667
 
668
+ self.pair_class_name = self.relative_class
693
669
  self.pair_time_name = self.relative_time
694
670
  self.pair_status_name = self.relative_status
695
671
 
@@ -705,167 +681,131 @@ class SignalAnnotator2(QMainWindow,Styles):
705
681
 
706
682
  df_reference = self.dataframes[self.reference_population]
707
683
  self.reference_class_name = self.reference_event_choice_cb.currentText()
708
- self.expected_reference_status = 'status_'
709
- suffix = self.reference_class_name.replace('class','').replace('_','')
710
- if suffix!='':
711
- self.expected_reference_status+='_'+suffix
712
- self.expected_reference_time = 't_'+suffix
713
- else:
714
- self.expected_reference_time = 't0'
715
684
 
716
- self.reference_time_name = self.expected_reference_time
717
- self.reference_status_name = self.expected_reference_status
685
+ if self.reference_class_name!='':
718
686
 
719
- if self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns) and not self.reference_status_name in list(df_reference.columns):
720
- # only create the status column if it does not exist to not erase static classification results
721
- self.make_reference_status_column()
722
- elif self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns):
723
- # all good, do nothing
724
- pass
725
- else:
726
- if not self.reference_status_name in list(df_reference.columns):
727
- df_reference[self.reference_status_name] = 0
728
- df_reference['status_color'] = color_from_status(0)
729
- df_reference['class_color'] = color_from_class(1)
687
+ self.expected_reference_status = 'status_'
688
+ suffix = self.reference_class_name.replace('class','').replace('_','')
689
+ if suffix!='':
690
+ self.expected_reference_status+='_'+suffix
691
+ self.expected_reference_time = 't_'+suffix
692
+ else:
693
+ self.expected_reference_time = 't0'
730
694
 
731
- if not self.reference_class_name in list(df_reference.columns):
732
- df_reference[self.reference_class_name] = 1
733
- if not self.reference_time_name in list(df_reference.columns):
734
- df_reference[self.reference_time_name] = -1
695
+ self.reference_time_name = self.expected_reference_time
696
+ self.reference_status_name = self.expected_reference_status
735
697
 
736
- df_reference['status_color'] = [color_from_status(i) for i in df_reference[self.reference_status_name].to_numpy()]
737
- df_reference['class_color'] = [color_from_class(i) for i in df_reference[self.reference_class_name].to_numpy()]
698
+ if self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns) and not self.reference_status_name in list(df_reference.columns):
699
+ # only create the status column if it does not exist to not erase static classification results
700
+ self.make_reference_status_column()
701
+ elif self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns):
702
+ # all good, do nothing
703
+ pass
704
+ else:
705
+ if not self.reference_status_name in list(df_reference.columns):
706
+ df_reference[self.reference_status_name] = 0
707
+ df_reference['status_color'] = color_from_status(0)
708
+ df_reference['class_color'] = color_from_class(1)
738
709
 
739
- if self.reference_population=='targets':
740
- self.extract_scatter_from_target_trajectories()
741
- else:
742
- self.extract_scatter_from_effector_trajectories()
710
+ if not self.reference_class_name in list(df_reference.columns):
711
+ df_reference[self.reference_class_name] = 1
712
+ if not self.reference_time_name in list(df_reference.columns):
713
+ df_reference[self.reference_time_name] = -1
714
+
715
+ df_reference['status_color'] = [color_from_status(i) for i in df_reference[self.reference_status_name].to_numpy()]
716
+ df_reference['class_color'] = [color_from_class(i) for i in df_reference[self.reference_class_name].to_numpy()]
717
+
718
+ if self.reference_population=='targets':
719
+ self.extract_scatter_from_target_trajectories()
720
+ else:
721
+ self.extract_scatter_from_effector_trajectories()
743
722
 
744
723
 
745
724
  def compute_status_and_colors_neighbor(self):
746
725
 
747
726
  df_neighbors = self.dataframes[self.neighbor_population]
748
727
  self.neighbor_class_name = self.neighbor_event_choice_cb.currentText()
749
- self.expected_neighbor_status = 'status_'
750
- suffix = self.neighbor_class_name.replace('class','').replace('_','')
751
- if suffix!='':
752
- self.expected_neighbor_status+='_'+suffix
753
- self.expected_neighbor_time = 't_'+suffix
754
- else:
755
- self.expected_neighbor_time = 't0'
756
728
 
757
- self.neighbor_time_name = self.expected_neighbor_time
758
- self.neighbor_status_name = self.expected_neighbor_status
729
+ if self.neighbor_class_name!='':
759
730
 
760
- if self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns) and not self.neighbor_status_name in list(df_neighbors.columns):
761
- # only create the status column if it does not exist to not erase static classification results
762
- self.make_neighbor_status_column()
763
- elif self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns):
764
- # all good, do nothing
765
- pass
766
- else:
767
- if not self.neighbor_status_name in list(df_neighbors.columns):
768
- df_neighbors[self.neighbor_status_name] = 0
769
- df_neighbors['status_color'] = color_from_status(0)
770
- df_neighbors['class_color'] = color_from_class(1)
731
+ self.expected_neighbor_status = 'status_'
732
+ suffix = self.neighbor_class_name.replace('class','').replace('_','')
733
+ if suffix!='':
734
+ self.expected_neighbor_status+='_'+suffix
735
+ self.expected_neighbor_time = 't_'+suffix
736
+ else:
737
+ self.expected_neighbor_time = 't0'
771
738
 
772
- if not self.neighbor_class_name in list(df_neighbors.columns):
773
- df_neighbors[self.neighbor_class_name] = 1
774
- if not self.neighbor_time_name in list(df_neighbors.columns):
775
- df_neighbors[self.neighbor_time_name] = -1
739
+ self.neighbor_time_name = self.expected_neighbor_time
740
+ self.neighbor_status_name = self.expected_neighbor_status
776
741
 
777
- df_neighbors['status_color'] = [color_from_status(i) for i in df_neighbors[self.neighbor_status_name].to_numpy()]
778
- df_neighbors['class_color'] = [color_from_class(i) for i in df_neighbors[self.neighbor_class_name].to_numpy()]
742
+ if self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns) and not self.neighbor_status_name in list(df_neighbors.columns):
743
+ # only create the status column if it does not exist to not erase static classification results
744
+ self.make_neighbor_status_column()
745
+ elif self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns):
746
+ # all good, do nothing
747
+ pass
748
+ else:
749
+ if not self.neighbor_status_name in list(df_neighbors.columns):
750
+ df_neighbors[self.neighbor_status_name] = 0
751
+ df_neighbors['status_color'] = color_from_status(0)
752
+ df_neighbors['class_color'] = color_from_class(1)
779
753
 
780
- if self.neighbor_population=='targets':
781
- self.extract_scatter_from_target_trajectories()
782
- else:
783
- self.extract_scatter_from_effector_trajectories()
754
+ if not self.neighbor_class_name in list(df_neighbors.columns):
755
+ df_neighbors[self.neighbor_class_name] = 1
756
+ if not self.neighbor_time_name in list(df_neighbors.columns):
757
+ df_neighbors[self.neighbor_time_name] = -1
758
+
759
+ df_neighbors['status_color'] = [color_from_status(i) for i in df_neighbors[self.neighbor_status_name].to_numpy()]
760
+ df_neighbors['class_color'] = [color_from_class(i) for i in df_neighbors[self.neighbor_class_name].to_numpy()]
784
761
 
785
- # if self.df_effectors is not None:
786
- # if self.reference_population=='effectors':
787
- # self.effector_class_name = self.reference_event_choice_cb.currentText()
788
- # elif self.neighbor_population == 'effectors':
789
- # self.effector_class_name = self.neighbor_event_choice_cb.currentText()
790
- # else:
791
- # self.effector_class_name=''
792
- # #self.effector_class_name = self.effector_class_choice_cb.currentText()
793
- # self.effector_expected_status = 'status'
794
- # suffix = self.effector_class_name.replace('class','').replace('_','')
795
- # if suffix!='':
796
- # self.effector_expected_status+='_'+suffix
797
- # self.effector_expected_time = 't_'+suffix
798
- # else:
799
- # self.effector_expected_time = 't0'
800
-
801
- # self.effector_time_name = self.effector_expected_time
802
- # self.effector_status_name = self.effector_expected_status
803
-
804
- # print('selection and expected names: ', self.effector_class_name, self.effector_expected_time, self.effector_expected_status)
805
-
806
- # if self.effector_time_name in self.df_effectors.columns and self.effector_class_name in self.df_effectors.columns and not self.effector_status_name in self.df_effectors.columns:
807
- # # only create the status column if it does not exist to not erase static classification results
808
- # self.make_effector_status_column()
809
- # elif self.effector_time_name in self.df_effectors.columns and self.effector_class_name in self.df_effectors.columns:
810
- # # all good, do nothing
811
- # pass
812
- # else:
813
- # if not self.effector_status_name in self.df_effectors.columns:
814
- # self.df_effectors[self.effector_status_name] = 0
815
- # self.df_effectors['status_color'] = color_from_status(0)
816
- # self.df_effectors['class_color'] = color_from_class(1)
817
-
818
- # if not self.effector_class_name in self.df_effectors.columns:
819
- # self.df_effectors[self.effector_class_name] = 1
820
- # if not self.effector_time_name in self.df_effectors.columns:
821
- # self.df_effectors[self.effector_time_name] = -1
822
-
823
- # self.df_effectors['status_color'] = [color_from_status(i) for i in self.df_effectors[self.effector_status_name].to_numpy()]
824
- # self.df_effectors['class_color'] = [color_from_class(i) for i in self.df_effectors[self.effector_class_name].to_numpy()]
825
-
826
- # self.extract_scatter_from_effector_trajectories()
762
+ if self.neighbor_population=='targets':
763
+ self.extract_scatter_from_target_trajectories()
764
+ else:
765
+ self.extract_scatter_from_effector_trajectories()
827
766
 
828
767
  def compute_status_and_colors_pair(self):
829
768
 
830
769
  self.pair_class_name = self.relative_class_choice_cb.currentText()
831
- print(f'{self.pair_class_name=}')
832
-
833
- self.pair_expected_status = 'status'
834
- suffix = self.pair_class_name.replace('class','').replace('_','',1)
835
- if suffix!='':
836
- self.pair_expected_status+='_'+suffix
837
- self.pair_expected_time = 't0_'+suffix
838
- if not self.pair_expected_time in list(self.df_relative.columns):
839
- self.pair_expected_time = 't_'+suffix
840
- else:
841
- self.pair_expected_time = 't0'
770
+
771
+ if self.pair_class_name!='':
772
+
773
+ self.pair_expected_status = 'status'
774
+ suffix = self.pair_class_name.replace('class','').replace('_','',1)
775
+ if suffix!='':
776
+ self.pair_expected_status+='_'+suffix
777
+ self.pair_expected_time = 't0_'+suffix
778
+ if not self.pair_expected_time in list(self.df_relative.columns):
779
+ self.pair_expected_time = 't_'+suffix
780
+ else:
781
+ self.pair_expected_time = 't0'
842
782
 
843
- self.pair_time_name = self.pair_expected_time
844
- self.pair_status_name = self.pair_expected_status
783
+ self.pair_time_name = self.pair_expected_time
784
+ self.pair_status_name = self.pair_expected_status
845
785
 
846
- if self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns and not self.pair_status_name in self.df_relative.columns:
847
- # only create the status column if it does not exist to not erase static classification results
848
- self.make_relative_status_column()
849
- elif self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns:
850
- # all good, do nothing
851
- pass
852
- else:
853
- if not self.pair_status_name in self.df_relative.columns:
854
- self.df_relative[self.pair_status_name] = 0
855
- self.df_relative['status_color'] = color_from_status(0)
856
- self.df_relative['class_color'] = color_from_class(1)
786
+ if self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns and not self.pair_status_name in self.df_relative.columns:
787
+ # only create the status column if it does not exist to not erase static classification results
788
+ self.make_relative_status_column()
789
+ elif self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns:
790
+ # all good, do nothing
791
+ pass
792
+ else:
793
+ if not self.pair_status_name in self.df_relative.columns:
794
+ self.df_relative[self.pair_status_name] = 0
795
+ self.df_relative['status_color'] = color_from_status(0)
796
+ self.df_relative['class_color'] = color_from_class(1)
857
797
 
858
- if not self.pair_class_name in self.df_relative.columns:
859
- self.df_relative[self.pair_time_name] = 1
860
- if not self.pair_time_name in self.df_relative.columns:
861
- self.df_relative[self.pair_time_name] = -1
798
+ if not self.pair_class_name in self.df_relative.columns:
799
+ self.df_relative[self.pair_time_name] = 1
800
+ if not self.pair_time_name in self.df_relative.columns:
801
+ self.df_relative[self.pair_time_name] = -1
862
802
 
863
- self.df_relative['status_color'] = [color_from_status(i) for i in self.df_relative[self.pair_status_name].to_numpy()]
864
- self.df_relative['class_color'] = [color_from_class(i) for i in self.df_relative[self.pair_class_name].to_numpy()]
803
+ self.df_relative['status_color'] = [color_from_status(i) for i in self.df_relative[self.pair_status_name].to_numpy()]
804
+ self.df_relative['class_color'] = [color_from_class(i) for i in self.df_relative[self.pair_class_name].to_numpy()]
865
805
 
866
- self.extract_scatter_from_lines()
867
- self.give_pair_information()
868
- self.plot_signals()
806
+ self.extract_scatter_from_lines()
807
+ self.give_pair_information()
808
+ self.plot_signals()
869
809
 
870
810
  def contrast_slider_action(self):
871
811
 
@@ -1329,30 +1269,6 @@ class SignalAnnotator2(QMainWindow,Styles):
1329
1269
  self.MinMaxScaler_effectors.fit(x)
1330
1270
 
1331
1271
 
1332
- # def make_effector_status_column(self):
1333
- # print('remaking the status column for the effectors')
1334
- # for tid, group in self.df_effectors.groupby('TRACK_ID'):
1335
-
1336
- # indices = group.index
1337
- # t0 = group[self.].to_numpy()[0]
1338
- # cclass = group[self.class_name].to_numpy()[0]
1339
- # timeline = group['FRAME'].to_numpy()
1340
- # status = np.zeros_like(timeline)
1341
- # if t0 > 0:
1342
- # status[timeline >= t0] = 1.
1343
- # if cclass == 2:
1344
- # status[:] = 2
1345
- # if cclass > 2:
1346
- # status[:] = 42
1347
- # status_color = [color_from_status(s) for s in status]
1348
- # class_color = [color_from_class(cclass) for i in range(len(status))]
1349
-
1350
- # self.df_tracks.loc[indices, self.status_name] = status
1351
- # self.df_tracks.loc[indices, 'status_color'] = status_color
1352
- # self.df_tracks.loc[indices, 'class_color'] = class_color
1353
-
1354
-
1355
-
1356
1272
  def locate_relative_tracks(self):
1357
1273
 
1358
1274
  population = 'relative'
@@ -1392,6 +1308,9 @@ class SignalAnnotator2(QMainWindow,Styles):
1392
1308
  self.relative_time_name = 't0'
1393
1309
  self.relative_status_name = 'status'
1394
1310
 
1311
+ for col in list(self.df_relative.columns):
1312
+ if np.all(self.df_relative[col].isnull()) or self.df_relative[col].dtype==object:
1313
+ self.cols_to_remove.append(col)
1395
1314
 
1396
1315
  self.MinMaxScaler_pairs = MinMaxScaler()
1397
1316
  self.pair_columns = list(self.df_relative.columns)
@@ -1408,7 +1327,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1408
1327
  pass
1409
1328
 
1410
1329
  x = self.df_relative[self.pair_columns].values
1411
- self.MinMaxScaler_pairs.fit(x)
1330
+ self.MinMaxScaler_pairs.fit(x)
1412
1331
 
1413
1332
 
1414
1333
  def set_reference_and_neighbor_populations(self):
@@ -1813,6 +1732,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1813
1732
  self.cancel_selection()
1814
1733
  self.set_reference_and_neighbor_populations()
1815
1734
  # Update reference classes and neighbor classes
1735
+ self.extract_relevant_events()
1816
1736
  self.fill_class_cbs()
1817
1737
 
1818
1738
  self.update_cell_events()
@@ -1820,6 +1740,39 @@ class SignalAnnotator2(QMainWindow,Styles):
1820
1740
  # self.draw_frame(self.framedata)
1821
1741
  self.plot_signals()
1822
1742
 
1743
+ def extract_relevant_events(self):
1744
+
1745
+ if self.reference_population!=self.neighbor_population:
1746
+ pattern = "_2_"
1747
+ else:
1748
+ pattern = "_self_"
1749
+
1750
+ try:
1751
+ self.relative_class_choice_cb.disconnect()
1752
+ except:
1753
+ pass
1754
+
1755
+ self.relative_class_choice_cb.clear()
1756
+
1757
+ cols = list(self.df_relative.columns)
1758
+ self.relative_class_cols = [c for c in cols if c.startswith('class')]
1759
+ for c in ['class_color', 'class_id']:
1760
+ if c in self.relative_class_cols:
1761
+ self.relative_class_cols.remove(c)
1762
+
1763
+ # Keep only valid classes for which there are values
1764
+ self.new_relative_class_cols = []
1765
+ for c in self.relative_class_cols:
1766
+ if ~self.df_relative.loc[(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population),c].isnull().all():
1767
+ self.new_relative_class_cols.append(c)
1768
+
1769
+ if len(self.new_relative_class_cols)>0:
1770
+ self.relative_class = self.new_relative_class_cols[0]
1771
+ self.relative_class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_pair)
1772
+ self.relative_class_choice_cb.addItems(self.new_relative_class_cols)
1773
+ idx = self.relative_class_choice_cb.findText(self.relative_class)
1774
+ self.relative_class_choice_cb.setCurrentIndex(idx)
1775
+
1823
1776
 
1824
1777
  def closeEvent(self, event):
1825
1778