celldetective 1.3.6.post1__py3-none-any.whl → 1.3.7__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 (41) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +4 -0
  3. celldetective/filters.py +11 -2
  4. celldetective/gui/InitWindow.py +23 -9
  5. celldetective/gui/control_panel.py +19 -11
  6. celldetective/gui/generic_signal_plot.py +5 -0
  7. celldetective/gui/gui_utils.py +2 -2
  8. celldetective/gui/help/DL-segmentation-strategy.json +17 -17
  9. celldetective/gui/help/Threshold-vs-DL.json +11 -11
  10. celldetective/gui/help/cell-populations.json +5 -5
  11. celldetective/gui/help/exp-structure.json +15 -15
  12. celldetective/gui/help/feature-btrack.json +5 -5
  13. celldetective/gui/help/neighborhood.json +7 -7
  14. celldetective/gui/help/prefilter-for-segmentation.json +7 -7
  15. celldetective/gui/help/preprocessing.json +19 -19
  16. celldetective/gui/help/propagate-classification.json +7 -7
  17. celldetective/gui/neighborhood_options.py +1 -1
  18. celldetective/gui/plot_signals_ui.py +13 -9
  19. celldetective/gui/process_block.py +63 -14
  20. celldetective/gui/retrain_segmentation_model_options.py +21 -8
  21. celldetective/gui/retrain_signal_model_options.py +12 -2
  22. celldetective/gui/signal_annotator.py +9 -0
  23. celldetective/gui/signal_annotator2.py +25 -17
  24. celldetective/gui/styles.py +1 -0
  25. celldetective/gui/tableUI.py +1 -1
  26. celldetective/gui/workers.py +136 -0
  27. celldetective/io.py +54 -28
  28. celldetective/measure.py +112 -14
  29. celldetective/scripts/measure_cells.py +36 -46
  30. celldetective/scripts/segment_cells.py +35 -78
  31. celldetective/scripts/segment_cells_thresholds.py +21 -22
  32. celldetective/scripts/track_cells.py +43 -32
  33. celldetective/segmentation.py +16 -62
  34. celldetective/signals.py +11 -7
  35. celldetective/utils.py +587 -67
  36. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/METADATA +1 -1
  37. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/RECORD +41 -40
  38. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/LICENSE +0 -0
  39. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/WHEEL +0 -0
  40. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/entry_points.txt +0 -0
  41. {celldetective-1.3.6.post1.dist-info → celldetective-1.3.7.dist-info}/top_level.txt +0 -0
@@ -6,7 +6,7 @@ from PyQt5.QtGui import QIcon, QDoubleValidator
6
6
  from celldetective.gui.gui_utils import center_window
7
7
  from celldetective.gui.generic_signal_plot import GenericSignalPlotWidget
8
8
  from superqt import QLabeledSlider, QColormapComboBox, QSearchableComboBox
9
- from celldetective.utils import get_software_location, _extract_labels_from_config
9
+ from celldetective.utils import get_software_location, _extract_labels_from_config, extract_cols_from_table_list
10
10
  from celldetective.io import load_experiment_tables
11
11
  from celldetective.signals import mean_signal
12
12
  import numpy as np
@@ -15,7 +15,6 @@ import matplotlib.pyplot as plt
15
15
  plt.rcParams['svg.fonttype'] = 'none'
16
16
  from glob import glob
17
17
  from natsort import natsorted
18
- import pandas as pd
19
18
  import math
20
19
  from celldetective.gui import Styles
21
20
  from matplotlib import colormaps
@@ -92,8 +91,14 @@ class ConfigSignalPlot(QWidget, Styles):
92
91
  """)
93
92
  main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
94
93
 
94
+ pops = []
95
+ for population in ['effectors','targets','pairs']:
96
+ tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv']))
97
+ if len(tables)>0:
98
+ pops.append(population)
99
+
95
100
  labels = [QLabel('population: '), QLabel('class: '), QLabel('time of\ninterest: '), QLabel('cmap: ')]
96
- self.cb_options = [['targets','effectors'],[], [], []]
101
+ self.cb_options = [pops,[], [], []]
97
102
  self.cbs = [QComboBox() for i in range(len(labels))]
98
103
  self.cbs[-1] = QColormapComboBox()
99
104
 
@@ -161,12 +166,11 @@ class ConfigSignalPlot(QWidget, Styles):
161
166
  def set_classes_and_times(self):
162
167
 
163
168
  # Look for all classes and times
164
- tables = natsorted(glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_*.csv'])))
165
- self.all_columns = []
166
- for tab in tables:
167
- cols = pd.read_csv(tab, nrows=1).columns.tolist()
168
- self.all_columns.extend(cols)
169
- self.all_columns = np.unique(self.all_columns)
169
+ population = self.cbs[0].currentText()
170
+ tables = natsorted(glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv'])))
171
+
172
+ self.all_columns = extract_cols_from_table_list(tables)
173
+
170
174
  class_idx = np.array([s.startswith('class_') for s in self.all_columns])
171
175
  time_idx = np.array([s.startswith('t_') for s in self.all_columns])
172
176
 
@@ -1,4 +1,4 @@
1
- from PyQt5.QtWidgets import QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
1
+ from PyQt5.QtWidgets import QDialog, QFrame, QGridLayout, QComboBox, QListWidget, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QCheckBox, \
2
2
  QMessageBox, QWidget
3
3
  from PyQt5.QtCore import Qt, QSize
4
4
  from superqt.fonticon import icon
@@ -10,7 +10,7 @@ from celldetective.gui.signal_annotator import MeasureAnnotator
10
10
  from celldetective.gui.signal_annotator2 import SignalAnnotator2
11
11
  from celldetective.io import get_segmentation_models_list, control_segmentation_napari, get_signal_models_list, \
12
12
  control_tracks, load_experiment_tables, get_pair_signal_models_list
13
- from celldetective.io import locate_segmentation_model, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
13
+ from celldetective.io import locate_segmentation_model, extract_position_name, fix_missing_labels, auto_load_number_of_frames, load_frames, locate_signal_model
14
14
  from celldetective.gui import SegmentationModelLoader, ClassifierWidget, ConfigNeighborhoods, ConfigSegmentationModelTraining, ConfigTracking, SignalAnnotator, ConfigSignalModelTraining, ConfigMeasurements, ConfigSignalAnnotator, TableUI
15
15
  from celldetective.gui.gui_utils import QHSeperationLine
16
16
  from celldetective.relative_measurements import rel_measure_at_position
@@ -37,6 +37,14 @@ from celldetective.gui.layouts import CellposeParamsWidget, StarDistParamsWidget
37
37
  from celldetective.gui import Styles
38
38
  from celldetective.utils import get_software_location
39
39
 
40
+ from celldetective.gui.workers import ProgressWindow
41
+ from celldetective.gui.processes.segment_cells import SegmentCellThresholdProcess, SegmentCellDLProcess
42
+ from celldetective.gui.processes.track_cells import TrackingProcess
43
+ from celldetective.gui.processes.measure_cells import MeasurementProcess
44
+
45
+ import time
46
+ import asyncio
47
+
40
48
  class ProcessPanel(QFrame, Styles):
41
49
  def __init__(self, parent_window, mode):
42
50
 
@@ -52,6 +60,8 @@ class ProcessPanel(QFrame, Styles):
52
60
  self.wells = np.array(self.parent_window.wells,dtype=str)
53
61
  self.cellpose_calibrated = False
54
62
  self.stardist_calibrated = False
63
+ self.use_gpu = self.parent_window.parent_window.use_gpu
64
+ self.n_threads = self.parent_window.parent_window.n_threads
55
65
 
56
66
  self.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
57
67
  self.grid = QGridLayout(self)
@@ -264,7 +274,7 @@ class ProcessPanel(QFrame, Styles):
264
274
  #self.to_disable.append(self.cell_models_list)
265
275
 
266
276
  self.train_signal_model_btn = QPushButton("TRAIN")
267
- self.train_signal_model_btn.setToolTip("Open a dialog box to create a new target segmentation model.")
277
+ self.train_signal_model_btn.setToolTip("Train or retrain an event detection model\non newly annotated data.")
268
278
  self.train_signal_model_btn.setIcon(icon(MDI6.redo_variant,color='black'))
269
279
  self.train_signal_model_btn.setIconSize(QSize(20, 20))
270
280
  self.train_signal_model_btn.setStyleSheet(self.button_style_sheet_3)
@@ -284,7 +294,7 @@ class ProcessPanel(QFrame, Styles):
284
294
  self.signal_models_list.addItems(signal_models)
285
295
 
286
296
  def generate_tracking_options(self):
287
-
297
+
288
298
  grid_track = QHBoxLayout()
289
299
 
290
300
  self.track_action = QCheckBox("TRACK")
@@ -363,7 +373,7 @@ class ProcessPanel(QFrame, Styles):
363
373
  self.segment_action = QCheckBox("SEGMENT")
364
374
  self.segment_action.setStyleSheet(self.menu_check_style)
365
375
  self.segment_action.setIcon(icon(MDI6.bacteria, color='black'))
366
- self.segment_action.setToolTip(f"Segment the {self.mode} cells on the images.")
376
+ self.segment_action.setToolTip(f"Segment the {self.mode[:-1]} cells on the images.")
367
377
  self.segment_action.toggled.connect(self.enable_segmentation_model_list)
368
378
  #self.to_disable.append(self.segment_action)
369
379
  grid_segment.addWidget(self.segment_action, 90)
@@ -390,6 +400,7 @@ class ProcessPanel(QFrame, Styles):
390
400
  model_zoo_layout = QHBoxLayout()
391
401
  model_zoo_layout.addWidget(QLabel("Model zoo:"),90)
392
402
  self.seg_model_list = QComboBox()
403
+ self.seg_model_list.currentIndexChanged.connect(self.reset_generalist_setup)
393
404
  #self.to_disable.append(self.tc_seg_model_list)
394
405
  self.seg_model_list.setGeometry(50, 50, 200, 30)
395
406
  self.init_seg_model_list()
@@ -682,6 +693,10 @@ class ProcessPanel(QFrame, Styles):
682
693
  self.ConfigSignalAnnotator = ConfigSignalAnnotator(self)
683
694
  self.ConfigSignalAnnotator.show()
684
695
 
696
+ def reset_generalist_setup(self, index):
697
+ self.cellpose_calibrated = False
698
+ self.stardist_calibrated = False
699
+
685
700
  def process_population(self):
686
701
 
687
702
  # if self.parent_window.well_list.currentText().startswith('Multiple'):
@@ -750,18 +765,19 @@ class ProcessPanel(QFrame, Styles):
750
765
  self.model_name = self.seg_models[self.seg_model_list.currentIndex()]
751
766
  print(self.model_name, self.seg_model_list.currentIndex())
752
767
 
753
- if self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
768
+ if self.segment_action.isChecked() and self.model_name.startswith('CP') and self.model_name in self.seg_models_generic and not self.cellpose_calibrated:
754
769
 
755
770
  self.diamWidget = CellposeParamsWidget(self, model_name=self.model_name)
756
771
  self.diamWidget.show()
757
772
  return None
758
773
 
759
- if self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
774
+ if self.segment_action.isChecked() and self.model_name.startswith('SD') and self.model_name in self.seg_models_generic and not self.stardist_calibrated:
760
775
 
761
776
  self.diamWidget = StarDistParamsWidget(self, model_name = self.model_name)
762
777
  self.diamWidget.show()
763
778
  return None
764
779
 
780
+ self.movie_prefix = self.parent_window.movie_prefix
765
781
 
766
782
  for w_idx in self.well_index:
767
783
 
@@ -775,6 +791,7 @@ class ProcessPanel(QFrame, Styles):
775
791
 
776
792
  self.pos = natsorted(glob(well+f"{os.path.split(well)[-1].replace('W','').replace(os.sep,'')}*/"))[pos_idx]
777
793
  print(f"Position {self.pos}...\nLoading stack movie...")
794
+ self.pos_name = extract_position_name(self.pos)
778
795
 
779
796
  if not os.path.exists(self.pos + 'output/'):
780
797
  os.mkdir(self.pos + 'output/')
@@ -805,9 +822,27 @@ class ProcessPanel(QFrame, Styles):
805
822
  return None
806
823
  else:
807
824
  print(f"Segmentation from threshold config: {self.threshold_config}")
808
- segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
825
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "threshold_instructions": self.threshold_config, "use_gpu": self.use_gpu}
826
+ self.job = ProgressWindow(SegmentCellThresholdProcess, parent_window=self, title="Segment", process_args = process_args)
827
+ result = self.job.exec_()
828
+ if result == QDialog.Accepted:
829
+ pass
830
+ elif result == QDialog.Rejected:
831
+ return None
832
+ #segment_from_threshold_at_position(self.pos, self.mode, self.threshold_config, threads=self.parent_window.parent_window.n_threads)
809
833
  else:
810
- segment_at_position(self.pos, self.mode, self.model_name, stack_prefix=self.parent_window.movie_prefix, use_gpu=self.parent_window.parent_window.use_gpu, threads=self.parent_window.parent_window.n_threads)
834
+ # model = locate_segmentation_model(self.model_name)
835
+ # if model is None:
836
+ # process = {"output_dir": self.output_dir, "file": self.model_name}
837
+ # self.download_model_job = ProgressWindow(DownloadProcess, parent_window=self, title="Download", process_args = args)
838
+
839
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads, "model_name": self.model_name, "use_gpu": self.use_gpu}
840
+ self.job = ProgressWindow(SegmentCellDLProcess, parent_window=self, title="Segment", process_args = process_args)
841
+ result = self.job.exec_()
842
+ if result == QDialog.Accepted:
843
+ pass
844
+ elif result == QDialog.Rejected:
845
+ return None
811
846
 
812
847
  if self.track_action.isChecked():
813
848
  if os.path.exists(os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])) and not self.parent_window.position_list.isMultipleSelection():
@@ -819,10 +854,25 @@ class ProcessPanel(QFrame, Styles):
819
854
  returnValue = msgBox.exec()
820
855
  if returnValue == QMessageBox.No:
821
856
  return None
822
- track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
857
+
858
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads}
859
+ self.job = ProgressWindow(TrackingProcess, parent_window=self, title="Tracking", process_args=process_args)
860
+ result = self.job.exec_()
861
+ if result == QDialog.Accepted:
862
+ pass
863
+ elif result == QDialog.Rejected:
864
+ return None
865
+ #track_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
823
866
 
824
867
  if self.measure_action.isChecked():
825
- measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
868
+ process_args = {"pos": self.pos, "mode": self.mode, "n_threads": self.n_threads}
869
+ self.job = ProgressWindow(MeasurementProcess, parent_window=self, title="Measurement", process_args=process_args)
870
+ result = self.job.exec_()
871
+ if result == QDialog.Accepted:
872
+ pass
873
+ elif result == QDialog.Rejected:
874
+ return None
875
+ #measure_at_position(self.pos, self.mode, threads=self.parent_window.parent_window.n_threads)
826
876
 
827
877
  table = os.sep.join([self.pos, 'output', 'tables', f'trajectories_{self.mode}.csv'])
828
878
  if self.signal_analysis_action.isChecked() and os.path.exists(table):
@@ -845,12 +895,11 @@ class ProcessPanel(QFrame, Styles):
845
895
 
846
896
  # self.stack = None
847
897
  self.parent_window.update_position_options()
848
-
849
898
  for action in [self.segment_action, self.track_action, self.measure_action, self.signal_analysis_action]:
850
899
  if action.isChecked():
851
900
  action.setChecked(False)
852
901
 
853
- self.cellpose_calibrated = False
902
+ self.reset_generalist_setup(0)
854
903
 
855
904
  def open_napari_tracking(self):
856
905
  print(f'View the tracks before post-processing for position {self.parent_window.pos} in napari...')
@@ -1207,7 +1256,7 @@ class NeighPanel(QFrame, Styles):
1207
1256
  self.neigh_action.setChecked(False)
1208
1257
 
1209
1258
  def open_classifier_ui_pairs(self):
1210
-
1259
+
1211
1260
  self.mode = "pairs"
1212
1261
  self.load_available_tables()
1213
1262
  if self.df is None:
@@ -1,4 +1,4 @@
1
- from PyQt5.QtWidgets import QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import QDialog, QMainWindow, QApplication,QRadioButton, QMessageBox, QScrollArea, QComboBox, QFrame, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt, QSize
3
3
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
4
4
  from celldetective.gui.gui_utils import center_window
@@ -17,6 +17,8 @@ import os
17
17
  from glob import glob
18
18
  from datetime import datetime
19
19
  from celldetective.gui import Styles
20
+ from celldetective.gui.processes.train_segmentation_model import TrainSegModelProcess
21
+ from celldetective.gui.workers import ProgressWindow
20
22
 
21
23
  class ConfigSegmentationModelTraining(QMainWindow, Styles):
22
24
 
@@ -29,6 +31,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
29
31
 
30
32
  super().__init__()
31
33
  self.parent_window = parent_window
34
+ self.use_gpu = self.parent_window.use_gpu
32
35
  self.setWindowTitle("Train segmentation model")
33
36
  self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','mexican-hat.png'])))
34
37
  self.mode = self.parent_window.mode
@@ -239,7 +242,7 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
239
242
  self.augmentation_slider.setSingleStep(0.01)
240
243
  self.augmentation_slider.setTickInterval(0.01)
241
244
  self.augmentation_slider.setOrientation(1)
242
- self.augmentation_slider.setRange(1, 5)
245
+ self.augmentation_slider.setRange(0.01, 3)
243
246
  self.augmentation_slider.setValue(2.0)
244
247
 
245
248
  augmentation_hbox.addWidget(self.augmentation_slider, 70)
@@ -597,24 +600,34 @@ class ConfigSegmentationModelTraining(QMainWindow, Styles):
597
600
  epochs = self.epochs_slider.value()
598
601
  spatial_calib = float(self.spatial_calib_le.text().replace(',','.'))
599
602
 
600
- training_instructions = {'model_name': model_name,'model_type': model_type, 'pretrained': pretrained_model, 'spatial_calibration': spatial_calib, 'channel_option': channels, 'normalization_percentile': normalization_mode,
603
+ self.training_instructions = {'model_name': model_name,'model_type': model_type, 'pretrained': pretrained_model, 'spatial_calibration': spatial_calib, 'channel_option': channels, 'normalization_percentile': normalization_mode,
601
604
  'normalization_clip': clip_values,'normalization_values': norm_values, 'ds': data_folders, 'augmentation_factor': aug_factor, 'validation_split': val_split,
602
605
  'learning_rate': lr, 'batch_size': bs, 'epochs': epochs}
603
606
 
604
- print(training_instructions)
605
607
 
606
608
  model_folder = os.sep.join([self.software_models_dir,model_name, ''])
607
609
  print(model_folder)
608
610
  if not os.path.exists(model_folder):
609
611
  os.mkdir(model_folder)
610
612
 
611
- training_instructions.update({'target_directory': self.software_models_dir})
613
+ self.training_instructions.update({'target_directory': self.software_models_dir})
614
+
615
+ print(f"Set of instructions: {self.training_instructions}")
616
+
617
+ self.instructions = model_folder+"training_instructions.json"
612
618
 
613
- print(f"Set of instructions: {training_instructions}")
614
619
  with open(model_folder+"training_instructions.json", 'w') as f:
615
- json.dump(training_instructions, f, indent=4)
620
+ json.dump(self.training_instructions, f, indent=4)
616
621
 
617
- train_segmentation_model(model_folder+"training_instructions.json", use_gpu=self.parent_window.parent_window.parent_window.use_gpu)
622
+ # process_args = {"instructions": self.instructions, "use_gpu": self.use_gpu}
623
+ # self.job = ProgressWindow(TrainSegModelProcess, parent_window=self, title="Training", position_info=False, process_args=process_args)
624
+ # result = self.job.exec_()
625
+ # if result == QDialog.Accepted:
626
+ # pass
627
+ # elif result == QDialog.Rejected:
628
+ # return None
629
+
630
+ train_segmentation_model(self.instructions, use_gpu=self.parent_window.parent_window.parent_window.use_gpu)
618
631
 
619
632
  self.parent_window.init_seg_model_list()
620
633
  idx = self.parent_window.seg_model_list.findText(model_name)
@@ -1,4 +1,4 @@
1
- from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import QMainWindow, QApplication, QDialog, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
2
2
  from PyQt5.QtCore import Qt, QSize
3
3
  from PyQt5.QtGui import QDoubleValidator, QIntValidator, QIcon
4
4
  from celldetective.gui.gui_utils import center_window
@@ -16,6 +16,8 @@ from glob import glob
16
16
  from datetime import datetime
17
17
  from celldetective.gui import Styles
18
18
  from pandas.api.types import is_numeric_dtype
19
+ from celldetective.gui.processes.train_signal_model import TrainSignalModelProcess
20
+ from celldetective.gui.workers import ProgressWindow
19
21
 
20
22
  class ConfigSignalModelTraining(QMainWindow, Styles):
21
23
 
@@ -584,6 +586,14 @@ class ConfigSignalModelTraining(QMainWindow, Styles):
584
586
  with open(model_folder+"training_instructions.json", 'w') as f:
585
587
  json.dump(training_instructions, f, indent=4)
586
588
 
587
- train_signal_model(model_folder+"training_instructions.json")
589
+ # self.instructions = model_folder+"training_instructions.json"
590
+ # process_args = {"instructions": self.instructions} # "use_gpu": self.use_gpu
591
+ # self.job = ProgressWindow(TrainSignalModelProcess, parent_window=self, title="Training", position_info=False, process_args=process_args)
592
+ # result = self.job.exec_()
593
+ # if result == QDialog.Accepted:
594
+ # pass
595
+ # elif result == QDialog.Rejected:
596
+ # return None
588
597
 
598
+ train_signal_model(model_folder+"training_instructions.json")
589
599
  self.parent_window.refresh_signal_models()
@@ -83,6 +83,15 @@ class SignalAnnotator(QMainWindow, Styles):
83
83
 
84
84
  self.populate_widget()
85
85
 
86
+ def resizeEvent(self, event):
87
+
88
+ super().resizeEvent(event)
89
+
90
+ try:
91
+ self.cell_fig.tight_layout()
92
+ except:
93
+ pass
94
+
86
95
  def populate_widget(self):
87
96
 
88
97
  """
@@ -134,6 +134,14 @@ class SignalAnnotator2(QMainWindow,Styles):
134
134
 
135
135
  self.setAttribute(Qt.WA_DeleteOnClose)
136
136
 
137
+ def resizeEvent(self, event):
138
+
139
+ super().resizeEvent(event)
140
+ try:
141
+ self.cell_fig.tight_layout()
142
+ except:
143
+ pass
144
+
137
145
  def populate_widget(self):
138
146
 
139
147
  """
@@ -205,14 +213,14 @@ class SignalAnnotator2(QMainWindow,Styles):
205
213
 
206
214
  self.cell_events_hbox = QHBoxLayout()
207
215
  self.cell_events_hbox.setContentsMargins(0,0,0,0)
208
- self.cell_events_hbox.addWidget(QLabel('reference event: '), 25)
216
+ self.cell_events_hbox.addWidget(QLabel('Event: '), 25)
209
217
  self.reference_event_choice_cb = QComboBox()
210
218
  self.cell_events_hbox.addWidget(self.reference_event_choice_cb, 75)
211
219
 
212
220
  #if 'self' not in self.neighborhood_choice_cb.currentText():
213
221
  self.neigh_cell_events_hbox = QHBoxLayout()
214
222
  self.neigh_cell_events_hbox.setContentsMargins(0,0,0,0)
215
- self.neigh_lab=QLabel('neighbor event: ')
223
+ self.neigh_lab=QLabel('Event: ')
216
224
  self.neigh_cell_events_hbox.addWidget(self.neigh_lab, 25)
217
225
  self.neighbor_event_choice_cb = QComboBox()
218
226
  self.neigh_cell_events_hbox.addWidget(self.neighbor_event_choice_cb, 75)
@@ -233,7 +241,7 @@ class SignalAnnotator2(QMainWindow,Styles):
233
241
  neighbor_layout.addLayout(self.neigh_cell_events_hbox)
234
242
 
235
243
  self.cell_info_hbox.addLayout(reference_layout, 33)
236
- self.cell_info_hbox.addWidget(self.pair_info, 33, alignment=Qt.AlignCenter)
244
+ self.cell_info_hbox.addWidget(self.pair_info, 33)
237
245
  self.cell_info_hbox.addLayout(neighbor_layout, 33)
238
246
 
239
247
  self.left_panel.addLayout(self.cell_info_hbox)
@@ -2378,41 +2386,41 @@ class SignalAnnotator2(QMainWindow,Styles):
2378
2386
 
2379
2387
  df_reference = self.dataframes[self.reference_population]
2380
2388
  if self.reference_track_of_interest is not None:
2381
- reference_cell_selected = f"reference cell: {self.reference_track_of_interest}\n"
2382
- reference_cell_population = f"population: {self.reference_population}\n"
2389
+ reference_cell_selected = f"Ref #{self.reference_track_of_interest}\n"
2390
+ reference_cell_population = f"Pop: {self.reference_population}\n"
2383
2391
  #reference_cell_class = f"class: {df_reference[df_reference['TRACK_ID']==self.reference_track_of_interest, self.reference_event_choice_cb.currentText()].values[0]}\n"
2384
2392
  #reference_cell_time = f"time of interest: {df_reference[df_reference['TRACK_ID']==self.reference_track_of_interest, ''].values[0]}\n"
2385
2393
  self.reference_cell_info.setText(reference_cell_selected+reference_cell_population)
2386
2394
  else:
2387
- reference_cell_selected = f"reference cell: None\n"
2388
- reference_cell_population = f"population: {self.reference_population}\n"
2395
+ reference_cell_selected = f"Ref: None\n"
2396
+ reference_cell_population = f"Pop: {self.reference_population}\n"
2389
2397
  self.reference_cell_info.setText(reference_cell_selected+reference_cell_population)
2390
2398
 
2391
2399
  def give_neighbor_cell_information(self):
2392
2400
 
2393
2401
  if self.neighbor_track_of_interest is not None:
2394
- neighbor_cell_selected = f"neighbor cell: {self.neighbor_track_of_interest}\n"
2395
- neighbor_cell_population = f"population: {self.neighbor_population}\n"
2402
+ neighbor_cell_selected = f"Neigh #{self.neighbor_track_of_interest}\n"
2403
+ neighbor_cell_population = f"Pop: {self.neighbor_population}\n"
2396
2404
  #neighbor_cell_time = f"time of interest: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest), self.pair_time_name].to_numpy()[0]}\n"
2397
2405
  #neighbor_cell_class = f"class: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest), self.pair_class_name].to_numpy()[0]}\n"
2398
2406
  self.neighbor_cell_info.setText(neighbor_cell_selected+neighbor_cell_population) #neighbor_cell_class+neighbor_cell_time
2399
2407
  else:
2400
- neighbor_cell_selected = f"neighbor cell: None\n"
2401
- neighbor_cell_population = f"population: {self.neighbor_population}\n"
2408
+ neighbor_cell_selected = f"Neigh: None\n"
2409
+ neighbor_cell_population = f"Pop: {self.neighbor_population}\n"
2402
2410
  self.neighbor_cell_info.setText(neighbor_cell_selected+neighbor_cell_population)
2403
2411
 
2404
2412
  def give_pair_information(self):
2405
2413
 
2406
2414
  if self.neighbor_track_of_interest is not None and self.reference_track_of_interest is not None:
2407
- pair_selected = f"(reference/neighbor) pair: ({self.reference_track_of_interest},{self.neighbor_track_of_interest})\n"
2408
- pair_populations = f"populations: ({self.reference_population}, {self.neighbor_population})\n"
2415
+ pair_selected = f"Pair: ({self.reference_track_of_interest},{self.neighbor_track_of_interest})\n"
2416
+ pair_populations = "" #f"populations: ({self.reference_population}, {self.neighbor_population})\n"
2409
2417
  current_class = self.relative_class_choice_cb.currentText()
2410
- pair_class = f"interaction event class: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), current_class].values[0]}\n"
2411
- pair_time = f"time of interest: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), self.pair_time_name].values[0]}\n"
2418
+ pair_class = f"Event class: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), current_class].values[0]}\n"
2419
+ pair_time = f"Time: {self.df_relative.loc[(self.df_relative['REFERENCE_ID']==self.reference_track_of_interest)&(self.df_relative['NEIGHBOR_ID']==self.neighbor_track_of_interest)&(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population)&(~self.df_relative['status_'+self.current_neighborhood].isnull()), self.pair_time_name].values[0]}\n"
2412
2420
  self.pair_info.setText(pair_selected+pair_populations+pair_class+pair_time)
2413
2421
  else:
2414
- pair_selected = f"(reference/neighbor) pair: None\n"
2415
- pair_populations = f"populations: ({self.reference_population}, {self.neighbor_population})\n"
2422
+ pair_selected = f"Pair: None\n"
2423
+ pair_populations = "" #f"populations: ({self.reference_population}, {self.neighbor_population})\n"
2416
2424
  self.pair_info.setText(pair_selected+pair_populations)
2417
2425
 
2418
2426
 
@@ -125,6 +125,7 @@ class Styles(object):
125
125
  font-size: 8px;
126
126
  }
127
127
  QPushButton:hover {
128
+ color: white;
128
129
  background-color: #2070EB;
129
130
  }
130
131
  QPushButton:pressed {
@@ -1337,7 +1337,7 @@ class TableUI(QMainWindow, Styles):
1337
1337
  file_name += ".csv"
1338
1338
  invalid_cols = [c for c in list(self.data.columns) if c.startswith('Unnamed')]
1339
1339
  if len(invalid_cols)>0:
1340
- self.data = self.data.drop(invalid_cols, axis=1)
1340
+ self.data = self.data.drop(invalid_cols, axis=1)
1341
1341
  self.data.to_csv(file_name, index=False)
1342
1342
 
1343
1343
  def test_bool(self, array):
@@ -0,0 +1,136 @@
1
+ from multiprocessing import Queue
2
+ from PyQt5.QtWidgets import QDialog, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QLabel, QProgressBar
3
+ from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, QThreadPool, QSize, Qt
4
+ from celldetective.gui.gui_utils import center_window
5
+ import time
6
+ import math
7
+
8
+ class ProgressWindow(QDialog):
9
+
10
+ def __init__(self, process=None, parent_window=None, title="", position_info=True, process_args=None):
11
+ QDialog.__init__(self)
12
+
13
+ self.setWindowTitle(f'{title} Progress')
14
+ self.__process = process
15
+ self.parent_window = parent_window
16
+ self.position_info = position_info
17
+ if self.position_info:
18
+ self.pos_name = self.parent_window.pos_name
19
+
20
+ #self.__btn_run = QPushButton("Start")
21
+ self.__btn_stp = QPushButton("Cancel")
22
+ if self.position_info:
23
+ self.position_label = QLabel(f'Processing position {self.pos_name}...')
24
+ self.__label = QLabel("Idle")
25
+ self.time_left_lbl = QLabel('')
26
+
27
+ self.progress_bar = QProgressBar()
28
+ self.progress_bar.setValue(0)
29
+
30
+ self.__runner = Runner(process=self.__process, progress_bar=self.progress_bar, process_args=process_args, remaining_time_label=self.time_left_lbl)
31
+ self.pool = QThreadPool.globalInstance()
32
+ #print("Multithreading with maximum %d threads" % self.pool.maxThreadCount())
33
+
34
+ #self.__btn_run.clicked.connect(self.__run_net)
35
+ self.__btn_stp.clicked.connect(self.__stp_net)
36
+ self.__runner.signals.finished.connect(self.__on_finished)
37
+ self.__runner.signals.finished.connect(self.__on_error)
38
+ self.__runner.signals.update.connect(self.progress_bar.setValue)
39
+ self.__runner.signals.update_time.connect(self.time_left_lbl.setText)
40
+
41
+ self.__btn_stp.setDisabled(True)
42
+
43
+ self.layout = QVBoxLayout()
44
+ if self.position_info:
45
+ self.layout.addWidget(self.position_label)
46
+ self.layout.addWidget(self.time_left_lbl)
47
+ self.layout.addWidget(self.progress_bar)
48
+ self.btn_layout = QHBoxLayout()
49
+ self.btn_layout.addWidget(self.__btn_stp)
50
+ self.btn_layout.addWidget(self.__label)
51
+ self.layout.addLayout(self.btn_layout)
52
+
53
+ self.setLayout(self.layout)
54
+ self.setFixedSize(QSize(250, 130))
55
+ self.__run_net()
56
+ self.setModal(True)
57
+ center_window(self)
58
+
59
+ def closeEvent(self, evnt):
60
+ # if self._want_to_close:
61
+ # super(MyDialog, self).closeEvent(evnt)
62
+ # else:
63
+ evnt.ignore()
64
+ self.setWindowState(Qt.WindowMinimized)
65
+
66
+ def __run_net(self):
67
+ #self.__btn_run.setDisabled(True)
68
+ self.__btn_stp.setEnabled(True)
69
+ self.__label.setText("Running...")
70
+ self.pool.start(self.__runner)
71
+
72
+ def __stp_net(self):
73
+ self.__runner.close()
74
+ print('\n Job cancelled... Abort.')
75
+ self.reject()
76
+
77
+ def __on_finished(self):
78
+ self.__btn_stp.setDisabled(True)
79
+ self.__label.setText("\nFinished!")
80
+ self.__runner.close()
81
+ self.accept()
82
+
83
+ def __on_error(self):
84
+ self.__btn_stp.setDisabled(True)
85
+ self.__label.setText("\nError")
86
+ self.__runner.close()
87
+ self.accept()
88
+
89
+ class Runner(QRunnable):
90
+
91
+ def __init__(self, process=None, progress_bar=None, remaining_time_label=None, process_args=None):
92
+ QRunnable.__init__(self)
93
+
94
+ print(f"{process_args=}")
95
+ self.progress_bar = progress_bar
96
+ #self.parent_window = parent_window
97
+ self.remaining_time_label = remaining_time_label
98
+ self.__queue = Queue()
99
+ self.__process = process(self.__queue, process_args=process_args)
100
+ self.signals = RunnerSignal()
101
+
102
+ def run(self):
103
+ self.__process.start()
104
+ while True:
105
+ try:
106
+ data = self.__queue.get()
107
+ if len(data)==2:
108
+ progress, time = data
109
+ time = int(time)
110
+ remaining_minutes = time // 60
111
+ remaining_seconds = int(time % 60)
112
+ time_left = "About "+str(remaining_minutes)+" min and "+str(remaining_seconds)+" s remaining"
113
+ if remaining_minutes == 0:
114
+ time_left = "About "+str(remaining_seconds)+" s remaining"
115
+ self.signals.update_time.emit(time_left)
116
+ if isinstance(progress, (float,int)):
117
+ self.signals.update.emit(math.ceil(progress))
118
+ if data == "finished":
119
+ self.signals.finished.emit()
120
+ break
121
+ elif data == "error":
122
+ self.signals.error.emit()
123
+ except Exception as e:
124
+ print(e)
125
+ pass
126
+
127
+ def close(self):
128
+ self.__process.end_process()
129
+
130
+
131
+ class RunnerSignal(QObject):
132
+
133
+ update = pyqtSignal(int)
134
+ update_time = pyqtSignal(str)
135
+ finished = pyqtSignal()
136
+ error = pyqtSignal()