celldetective 1.3.9.post5__py3-none-any.whl → 1.4.1__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 (94) hide show
  1. celldetective/__init__.py +0 -3
  2. celldetective/_version.py +1 -1
  3. celldetective/events.py +2 -4
  4. celldetective/exceptions.py +11 -0
  5. celldetective/extra_properties.py +132 -0
  6. celldetective/filters.py +7 -1
  7. celldetective/gui/InitWindow.py +37 -46
  8. celldetective/gui/__init__.py +3 -9
  9. celldetective/gui/about.py +19 -15
  10. celldetective/gui/analyze_block.py +34 -19
  11. celldetective/gui/base_annotator.py +786 -0
  12. celldetective/gui/base_components.py +23 -0
  13. celldetective/gui/classifier_widget.py +86 -94
  14. celldetective/gui/configure_new_exp.py +163 -46
  15. celldetective/gui/control_panel.py +76 -146
  16. celldetective/gui/{signal_annotator.py → event_annotator.py} +533 -1438
  17. celldetective/gui/generic_signal_plot.py +11 -13
  18. celldetective/gui/gui_utils.py +54 -23
  19. celldetective/gui/help/neighborhood.json +2 -2
  20. celldetective/gui/json_readers.py +5 -4
  21. celldetective/gui/layouts.py +265 -31
  22. celldetective/gui/{signal_annotator2.py → pair_event_annotator.py} +433 -635
  23. celldetective/gui/plot_measurements.py +21 -17
  24. celldetective/gui/plot_signals_ui.py +125 -72
  25. celldetective/gui/process_block.py +283 -188
  26. celldetective/gui/processes/compute_neighborhood.py +594 -0
  27. celldetective/gui/processes/downloader.py +37 -34
  28. celldetective/gui/processes/measure_cells.py +19 -8
  29. celldetective/gui/processes/segment_cells.py +47 -11
  30. celldetective/gui/processes/track_cells.py +18 -13
  31. celldetective/gui/seg_model_loader.py +21 -62
  32. celldetective/gui/settings/__init__.py +7 -0
  33. celldetective/gui/settings/_settings_base.py +70 -0
  34. celldetective/gui/{retrain_signal_model_options.py → settings/_settings_event_model_training.py} +54 -109
  35. celldetective/gui/{measurement_options.py → settings/_settings_measurements.py} +54 -92
  36. celldetective/gui/{neighborhood_options.py → settings/_settings_neighborhood.py} +10 -13
  37. celldetective/gui/settings/_settings_segmentation.py +49 -0
  38. celldetective/gui/{retrain_segmentation_model_options.py → settings/_settings_segmentation_model_training.py} +38 -92
  39. celldetective/gui/{signal_annotator_options.py → settings/_settings_signal_annotator.py} +78 -103
  40. celldetective/gui/{btrack_options.py → settings/_settings_tracking.py} +85 -116
  41. celldetective/gui/styles.py +2 -1
  42. celldetective/gui/survival_ui.py +49 -95
  43. celldetective/gui/tableUI.py +53 -25
  44. celldetective/gui/table_ops/__init__.py +0 -0
  45. celldetective/gui/table_ops/merge_groups.py +118 -0
  46. celldetective/gui/thresholds_gui.py +617 -1221
  47. celldetective/gui/viewers.py +107 -42
  48. celldetective/gui/workers.py +8 -4
  49. celldetective/io.py +137 -57
  50. celldetective/links/zenodo.json +145 -144
  51. celldetective/measure.py +94 -53
  52. celldetective/neighborhood.py +342 -268
  53. celldetective/preprocessing.py +56 -35
  54. celldetective/regionprops/_regionprops.py +16 -5
  55. celldetective/relative_measurements.py +50 -29
  56. celldetective/scripts/analyze_signals.py +4 -1
  57. celldetective/scripts/measure_cells.py +5 -5
  58. celldetective/scripts/measure_relative.py +20 -12
  59. celldetective/scripts/segment_cells.py +4 -10
  60. celldetective/scripts/segment_cells_thresholds.py +3 -3
  61. celldetective/scripts/track_cells.py +10 -8
  62. celldetective/scripts/train_segmentation_model.py +18 -6
  63. celldetective/signals.py +29 -14
  64. celldetective/tracking.py +14 -3
  65. celldetective/utils.py +91 -62
  66. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/METADATA +24 -16
  67. celldetective-1.4.1.dist-info/RECORD +123 -0
  68. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/WHEEL +1 -1
  69. tests/gui/__init__.py +0 -0
  70. tests/gui/test_new_project.py +228 -0
  71. tests/gui/test_project.py +99 -0
  72. tests/test_preprocessing.py +2 -2
  73. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +0 -79
  74. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  75. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +0 -37
  76. celldetective/models/segmentation_effectors/test-transfer/config_input.json +0 -39
  77. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  78. celldetective/models/signal_detection/NucCond/classification_loss.png +0 -0
  79. celldetective/models/signal_detection/NucCond/classifier.h5 +0 -0
  80. celldetective/models/signal_detection/NucCond/config_input.json +0 -1
  81. celldetective/models/signal_detection/NucCond/log_classifier.csv +0 -126
  82. celldetective/models/signal_detection/NucCond/log_regressor.csv +0 -282
  83. celldetective/models/signal_detection/NucCond/regression_loss.png +0 -0
  84. celldetective/models/signal_detection/NucCond/regressor.h5 +0 -0
  85. celldetective/models/signal_detection/NucCond/scores.npy +0 -0
  86. celldetective/models/signal_detection/NucCond/test_confusion_matrix.png +0 -0
  87. celldetective/models/signal_detection/NucCond/test_regression.png +0 -0
  88. celldetective/models/signal_detection/NucCond/validation_confusion_matrix.png +0 -0
  89. celldetective/models/signal_detection/NucCond/validation_regression.png +0 -0
  90. celldetective-1.3.9.post5.dist-info/RECORD +0 -129
  91. tests/test_qt.py +0 -103
  92. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/entry_points.txt +0 -0
  93. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info/licenses}/LICENSE +0 -0
  94. {celldetective-1.3.9.post5.dist-info → celldetective-1.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,23 @@
1
+ from PyQt5.QtWidgets import QMainWindow, QWidget, QDialog
2
+ from PyQt5.QtCore import Qt
3
+ from celldetective.gui import Styles
4
+
5
+
6
+ class CelldetectiveWidget(QWidget, Styles):
7
+ def __init__(self, *args, **kwargs):
8
+ super().__init__(*args, **kwargs)
9
+ self.setWindowIcon(self.celldetective_icon)
10
+ self.setAttribute(Qt.WA_DeleteOnClose)
11
+
12
+
13
+ class CelldetectiveMainWindow(QMainWindow, Styles):
14
+ def __init__(self, *args, **kwargs):
15
+ super().__init__(*args, **kwargs)
16
+ self.setWindowIcon(self.celldetective_icon)
17
+ self.setAttribute(Qt.WA_DeleteOnClose)
18
+
19
+
20
+ class CelldetectiveDialog(QDialog, Styles):
21
+ def __init__(self, *args, **kwargs):
22
+ super().__init__(*args, **kwargs)
23
+ self.setWindowIcon(self.celldetective_icon)
@@ -1,7 +1,7 @@
1
- from PyQt5.QtWidgets import QWidget, QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, \
1
+ from PyQt5.QtWidgets import QLineEdit, QMessageBox, QHBoxLayout, QVBoxLayout, QPushButton, QLabel, \
2
2
  QCheckBox, QRadioButton, QButtonGroup, QComboBox
3
3
  from PyQt5.QtCore import Qt, QSize
4
- from superqt import QLabeledSlider,QLabeledDoubleSlider, QSearchableComboBox
4
+ from superqt import QLabeledSlider, QLabeledDoubleSlider, QSearchableComboBox
5
5
  from superqt.fonticon import icon
6
6
  from fonticon_mdi6 import MDI6
7
7
 
@@ -10,12 +10,13 @@ import numpy as np
10
10
  import matplotlib.pyplot as plt
11
11
  import json
12
12
 
13
+ from celldetective.exceptions import EmptyQueryError, MissingColumnsError, QueryError
13
14
  from celldetective.gui.gui_utils import FigureCanvas, center_window, color_from_status, help_generic
14
- from celldetective.gui import Styles
15
+ from celldetective.gui.base_components import CelldetectiveWidget
15
16
  from celldetective.utils import get_software_location
16
17
  from celldetective.measure import classify_cells_from_query, interpret_track_classification
17
18
 
18
- class ClassifierWidget(QWidget, Styles):
19
+ class ClassifierWidget(CelldetectiveWidget):
19
20
 
20
21
  def __init__(self, parent_window):
21
22
 
@@ -33,7 +34,7 @@ class ClassifierWidget(QWidget, Styles):
33
34
 
34
35
  is_number = np.vectorize(lambda x: np.issubdtype(x, np.number))
35
36
  is_number_test = is_number(self.df.dtypes)
36
- self.cols = [col for t,col in zip(is_number_test,self.df.columns) if t]
37
+ self.cols = [col for t, col in zip(is_number_test, self.df.columns) if t]
37
38
 
38
39
  self.class_name = 'custom'
39
40
  self.name_le = QLineEdit(self.class_name)
@@ -44,7 +45,7 @@ class ClassifierWidget(QWidget, Styles):
44
45
 
45
46
 
46
47
  layout = QVBoxLayout(self)
47
- layout.setContentsMargins(30,30,30,30)
48
+ layout.setContentsMargins(30, 30, 30, 30)
48
49
 
49
50
  name_layout = QHBoxLayout()
50
51
  name_layout.addWidget(QLabel('class name: '), 33)
@@ -55,7 +56,7 @@ class ClassifierWidget(QWidget, Styles):
55
56
  fig_btn_hbox.addWidget(QLabel(''), 95)
56
57
  self.project_times_btn = QPushButton('')
57
58
  self.project_times_btn.setStyleSheet(self.parent_window.parent_window.parent_window.button_select_all)
58
- self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
59
+ self.project_times_btn.setIcon(icon(MDI6.math_integral, color="black"))
59
60
  self.project_times_btn.setToolTip("Project measurements at all times.")
60
61
  self.project_times_btn.setIconSize(QSize(20, 20))
61
62
  self.project_times = False
@@ -70,8 +71,8 @@ class ClassifierWidget(QWidget, Styles):
70
71
  # slider
71
72
  self.frame_slider = QLabeledSlider()
72
73
  self.frame_slider.setSingleStep(1)
73
- self.frame_slider.setOrientation(1)
74
- self.frame_slider.setRange(0,int(self.df.FRAME.max()) - 1)
74
+ self.frame_slider.setOrientation(Qt.Horizontal)
75
+ self.frame_slider.setRange(0, int(self.df.FRAME.max()) - 1)
75
76
  self.frame_slider.setValue(0)
76
77
  self.currentFrame = 0
77
78
 
@@ -84,8 +85,8 @@ class ClassifierWidget(QWidget, Styles):
84
85
  # transparency slider
85
86
  self.alpha_slider = QLabeledDoubleSlider()
86
87
  self.alpha_slider.setSingleStep(0.001)
87
- self.alpha_slider.setOrientation(1)
88
- self.alpha_slider.setRange(0,1)
88
+ self.alpha_slider.setOrientation(Qt.Horizontal)
89
+ self.alpha_slider.setRange(0, 1)
89
90
  self.alpha_slider.setValue(1.0)
90
91
  self.alpha_slider.setDecimals(3)
91
92
 
@@ -95,8 +96,8 @@ class ClassifierWidget(QWidget, Styles):
95
96
  layout.addLayout(slider_alpha_hbox)
96
97
 
97
98
 
98
- self.features_cb = [QSearchableComboBox() for i in range(2)]
99
- self.log_btns = [QPushButton() for i in range(2)]
99
+ self.features_cb = [QSearchableComboBox() for _ in range(2)]
100
+ self.log_btns = [QPushButton() for _ in range(2)]
100
101
 
101
102
  for i in range(2):
102
103
  hbox_feat = QHBoxLayout()
@@ -106,7 +107,7 @@ class ClassifierWidget(QWidget, Styles):
106
107
  layout.addLayout(hbox_feat)
107
108
 
108
109
  self.features_cb[i].clear()
109
- self.features_cb[i].addItems(sorted(list(self.cols),key=str.lower))
110
+ self.features_cb[i].addItems(sorted(list(self.cols), key=str.lower))
110
111
  self.features_cb[i].currentTextChanged.connect(self.update_props_scatter)
111
112
  self.features_cb[i].setCurrentIndex(i)
112
113
 
@@ -134,15 +135,15 @@ class ClassifierWidget(QWidget, Styles):
134
135
  self.time_corr.setEnabled(False)
135
136
 
136
137
  time_prop_hbox = QHBoxLayout()
137
- time_prop_hbox.addWidget(self.time_corr,alignment=Qt.AlignCenter)
138
+ time_prop_hbox.addWidget(self.time_corr, alignment=Qt.AlignCenter)
138
139
 
139
140
  self.help_propagate_btn = QPushButton()
140
- self.help_propagate_btn.setIcon(icon(MDI6.help_circle,color=self.help_color))
141
+ self.help_propagate_btn.setIcon(icon(MDI6.help_circle, color=self.help_color))
141
142
  self.help_propagate_btn.setIconSize(QSize(20, 20))
142
143
  self.help_propagate_btn.clicked.connect(self.help_propagate)
143
144
  self.help_propagate_btn.setStyleSheet(self.button_select_all)
144
145
  self.help_propagate_btn.setToolTip("Help.")
145
- time_prop_hbox.addWidget(self.help_propagate_btn,5,alignment=Qt.AlignRight)
146
+ time_prop_hbox.addWidget(self.help_propagate_btn, 5, alignment=Qt.AlignRight)
146
147
 
147
148
  layout.addLayout(time_prop_hbox)
148
149
 
@@ -154,23 +155,23 @@ class ClassifierWidget(QWidget, Styles):
154
155
 
155
156
  time_corr_layout = QHBoxLayout()
156
157
  time_corr_layout.addWidget(self.unique_state_btn, 33, alignment=Qt.AlignCenter)
157
- time_corr_layout.addWidget(self.irreversible_event_btn, 33,alignment=Qt.AlignCenter)
158
- time_corr_layout.addWidget(self.transient_event_btn, 33,alignment=Qt.AlignCenter)
158
+ time_corr_layout.addWidget(self.irreversible_event_btn, 33, alignment=Qt.AlignCenter)
159
+ time_corr_layout.addWidget(self.transient_event_btn, 33, alignment=Qt.AlignCenter)
159
160
  layout.addLayout(time_corr_layout)
160
161
 
161
162
  self.prereq_event_check = QCheckBox('prerequisite event:')
162
163
  self.prereq_event_check.toggled.connect(self.activate_prereq_cb)
163
164
  self.prereq_event_cb = QComboBox()
164
- event_cols = ['--'] + [c.replace('t_','') for c in self.cols if c.startswith('t_')]
165
+ event_cols = ['--'] + [c.replace('t_', '') for c in self.cols if c.startswith('t_')]
165
166
  self.prereq_event_cb.addItems(event_cols)
166
167
  self.prereq_event_check.setEnabled(False)
167
168
  self.prereq_event_cb.setEnabled(False)
168
169
 
169
170
  self.r2_slider = QLabeledDoubleSlider()
170
171
  self.r2_slider.setValue(0.75)
171
- self.r2_slider.setRange(0,1)
172
+ self.r2_slider.setRange(0, 1)
172
173
  self.r2_slider.setSingleStep(0.01)
173
- self.r2_slider.setOrientation(1)
174
+ self.r2_slider.setOrientation(Qt.Horizontal)
174
175
  self.r2_label = QLabel('R2 tolerance:')
175
176
  self.r2_label.setToolTip('Minimum R2 between the fit sigmoid and the binary response to the filters to accept the event.')
176
177
 
@@ -196,7 +197,7 @@ class ClassifierWidget(QWidget, Styles):
196
197
  wg.setEnabled(False)
197
198
 
198
199
  prereq_layout = QHBoxLayout()
199
- prereq_layout.setContentsMargins(30,0,0,0)
200
+ prereq_layout.setContentsMargins(30, 0, 0, 0)
200
201
  prereq_layout.addWidget(self.prereq_event_check, 20)
201
202
  prereq_layout.addWidget(self.prereq_event_cb, 80)
202
203
  layout.addLayout(prereq_layout)
@@ -212,7 +213,6 @@ class ClassifierWidget(QWidget, Styles):
212
213
  self.frame_slider.valueChanged.connect(self.set_frame)
213
214
  self.alpha_slider.valueChanged.connect(self.set_transparency)
214
215
 
215
- self.setAttribute(Qt.WA_DeleteOnClose)
216
216
 
217
217
  def activate_prereq_cb(self):
218
218
  if self.prereq_event_check.isChecked():
@@ -270,11 +270,10 @@ class ClassifierWidget(QWidget, Styles):
270
270
  Define properties scatter.
271
271
  """
272
272
 
273
- self.fig_props, self.ax_props = plt.subplots(figsize=(4,4),tight_layout=True)
273
+ self.fig_props, self.ax_props = plt.subplots(figsize=(4, 4), tight_layout=True)
274
274
  self.propscanvas = FigureCanvas(self.fig_props, interactive=True)
275
275
  self.fig_props.set_facecolor('none')
276
- self.fig_props.canvas.setStyleSheet("background-color: transparent;")
277
- self.scat_props = self.ax_props.scatter([],[], color="k", alpha=self.currentAlpha)
276
+ self.scat_props = self.ax_props.scatter([], [], color="k", alpha=self.currentAlpha)
278
277
  self.propscanvas.canvas.draw_idle()
279
278
  self.propscanvas.canvas.setMinimumHeight(self.screen_height//5)
280
279
 
@@ -295,25 +294,24 @@ class ClassifierWidget(QWidget, Styles):
295
294
  self.log_btns[0].setEnabled(True)
296
295
 
297
296
  if np.any(self.df[self.features_cb[1].currentText()].to_numpy() <= 0.):
298
- if self.ax_props.get_xscale()=='log':
297
+ if self.ax_props.get_xscale() == 'log':
299
298
  self.log_btns[1].click()
300
299
  self.log_btns[1].setEnabled(False)
301
300
  else:
302
301
  self.log_btns[1].setEnabled(True)
303
302
  except Exception as e:
304
- #print(e)
305
- pass
303
+ print(e)
306
304
 
307
305
  class_name = self.class_name
308
306
 
309
307
  try:
310
308
 
311
309
  if not self.project_times:
312
- self.scat_props.set_offsets(self.df.loc[self.df['FRAME']==self.currentFrame,[self.features_cb[1].currentText(),self.features_cb[0].currentText()]].to_numpy())
313
- colors = [color_from_status(c) for c in self.df.loc[self.df['FRAME']==self.currentFrame,class_name].to_numpy()]
310
+ self.scat_props.set_offsets(self.df.loc[self.df['FRAME'] == self.currentFrame, [self.features_cb[1].currentText(), self.features_cb[0].currentText()]].to_numpy())
311
+ colors = [color_from_status(c) for c in self.df.loc[self.df['FRAME'] == self.currentFrame, class_name].to_numpy()]
314
312
  self.scat_props.set_facecolor(colors)
315
313
  else:
316
- self.scat_props.set_offsets(self.df[[self.features_cb[1].currentText(),self.features_cb[0].currentText()]].to_numpy())
314
+ self.scat_props.set_offsets(self.df[[self.features_cb[1].currentText(), self.features_cb[0].currentText()]].to_numpy())
317
315
  colors = [color_from_status(c) for c in self.df[class_name].to_numpy()]
318
316
  self.scat_props.set_facecolor(colors)
319
317
 
@@ -333,18 +331,18 @@ class ClassifierWidget(QWidget, Styles):
333
331
 
334
332
  x_padding = (max_x - min_x) * 0.05
335
333
  y_padding = (max_y - min_y) * 0.05
336
- if x_padding==0:
334
+ if x_padding == 0:
337
335
  x_padding = 0.05
338
- if y_padding==0:
336
+ if y_padding == 0:
339
337
  y_padding = 0.05
340
338
 
341
- if min_x==min_x and max_x==max_x:
342
- if self.ax_props.get_xscale()=='linear':
339
+ if min_x == min_x and max_x == max_x:
340
+ if self.ax_props.get_xscale() == 'linear':
343
341
  self.ax_props.set_xlim(min_x - x_padding, max_x + x_padding)
344
342
  else:
345
343
  self.ax_props.set_xlim(min_x, max_x)
346
- if min_y==min_y and max_y==max_y:
347
- if self.ax_props.get_yscale()=='linear':
344
+ if min_y == min_y and max_y == max_y:
345
+ if self.ax_props.get_yscale() == 'linear':
348
346
  self.ax_props.set_ylim(min_y - y_padding, max_y + y_padding)
349
347
  else:
350
348
  self.ax_props.set_ylim(min_y, max_y)
@@ -354,32 +352,36 @@ class ClassifierWidget(QWidget, Styles):
354
352
  self.propscanvas.canvas.draw_idle()
355
353
 
356
354
  except Exception as e:
357
- pass
355
+ print("Exception L355 ", e)
356
+
357
+ def show_warning(self, message: str):
358
+ msgBox = QMessageBox()
359
+ msgBox.setIcon(QMessageBox.Warning)
360
+ link = "https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html"
361
+ msgBox.setText(f"{message}<br><br>See <a href='{link}'>documentation</a>.")
362
+ msgBox.setWindowTitle("Warning")
363
+ msgBox.setStandardButtons(QMessageBox.Ok)
364
+ msgBox.exec()
358
365
 
359
366
  def apply_property_query(self):
360
367
 
361
368
  query = self.property_query_le.text()
362
-
363
369
  try:
364
370
  self.df = classify_cells_from_query(self.df, self.name_le.text(), query)
371
+ except EmptyQueryError as e:
372
+ self.show_warning(str(e))
373
+ except MissingColumnsError as e:
374
+ self.show_warning(f"{e}. Please check your column names.")
375
+ except QueryError as e:
376
+ self.show_warning(f"{e}. Wrap features in backticks if needed.")
365
377
  except Exception as e:
366
- msgBox = QMessageBox()
367
- msgBox.setIcon(QMessageBox.Warning)
368
- link = "https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html"
369
- msg = "The query could not be understood. Please write a valid query following <a href='%s'>the documentation</a>. Wrap features in backticks (e.g. `feature` > 1) to facilitate query interpretation. " % link
370
- msgBox.setText(msg)
371
- msgBox.setWindowTitle("Warning")
372
- msgBox.setStandardButtons(QMessageBox.Ok)
373
- returnValue = msgBox.exec()
374
- if returnValue == QMessageBox.Ok:
375
- self.auto_close = False
376
- return None
378
+ self.show_warning(f"Unexpected error: {e}")
377
379
 
378
380
  self.class_name = "status_"+self.name_le.text()
379
381
  if self.df is None:
380
382
  msgBox = QMessageBox()
381
383
  msgBox.setIcon(QMessageBox.Warning)
382
- msgBox.setText(f"The query could not be understood. No filtering was applied. {e}")
384
+ msgBox.setText(f"The query could not be understood. No filtering was applied.")
383
385
  msgBox.setWindowTitle("Warning")
384
386
  msgBox.setStandardButtons(QMessageBox.Ok)
385
387
  returnValue = msgBox.exec()
@@ -390,22 +392,18 @@ class ClassifierWidget(QWidget, Styles):
390
392
  self.update_props_scatter(feature_changed=False)
391
393
 
392
394
  def set_frame(self, value):
393
- xlim=self.ax_props.get_xlim()
394
- ylim=self.ax_props.get_ylim()
395
+ xlim = self.ax_props.get_xlim()
396
+ ylim = self.ax_props.get_ylim()
395
397
  self.currentFrame = value
396
398
  self.update_props_scatter(feature_changed=False)
397
399
  self.ax_props.set_xlim(xlim)
398
400
  self.ax_props.set_ylim(ylim)
399
401
 
400
-
401
402
  def set_transparency(self, value):
402
- xlim=self.ax_props.get_xlim()
403
- ylim=self.ax_props.get_ylim()
403
+ xlim = self.ax_props.get_xlim()
404
+ ylim = self.ax_props.get_ylim()
404
405
  self.currentAlpha = value
405
- #fc = self.scat_props.get_facecolors()
406
- #fc[:, 3] = value
407
- #self.scat_props.set_facecolors(fc)
408
- #self.propscanvas.canvas.draw_idle()
406
+
409
407
  self.update_props_scatter(feature_changed=False)
410
408
  self.ax_props.set_xlim(xlim)
411
409
  self.ax_props.set_ylim(ylim)
@@ -413,12 +411,12 @@ class ClassifierWidget(QWidget, Styles):
413
411
  def switch_projection(self):
414
412
  if self.project_times:
415
413
  self.project_times = False
416
- self.project_times_btn.setIcon(icon(MDI6.math_integral,color="black"))
414
+ self.project_times_btn.setIcon(icon(MDI6.math_integral, color="black"))
417
415
  self.project_times_btn.setIconSize(QSize(20, 20))
418
416
  self.frame_slider.setEnabled(True)
419
417
  else:
420
418
  self.project_times = True
421
- self.project_times_btn.setIcon(icon(MDI6.math_integral_box,color="black"))
419
+ self.project_times_btn.setIcon(icon(MDI6.math_integral_box, color="black"))
422
420
  self.project_times_btn.setIconSize(QSize(20, 20))
423
421
  self.frame_slider.setEnabled(False)
424
422
  self.update_props_scatter(feature_changed=False)
@@ -432,12 +430,13 @@ class ClassifierWidget(QWidget, Styles):
432
430
 
433
431
  if self.time_corr.isChecked():
434
432
  self.class_name_user = 'class_'+self.name_le.text()
435
- print(f'User defined class name: {self.class_name_user}.')
433
+ print(f'User defined class name: {self.class_name_user}...')
436
434
  if self.class_name_user in self.df.columns:
437
435
 
438
436
  msgBox = QMessageBox()
439
437
  msgBox.setIcon(QMessageBox.Information)
440
- msgBox.setText(f"The class column {self.class_name_user} already exists in the table.\nProceeding will reclassify. Do you want to continue?")
438
+ msgBox.setText(f"The class column {self.class_name_user} already exists in the table.\nProceeding will "
439
+ f"reclassify. Do you want to continue?")
441
440
  msgBox.setWindowTitle("Warning")
442
441
  msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
443
442
  returnValue = msgBox.exec()
@@ -447,7 +446,6 @@ class ClassifierWidget(QWidget, Styles):
447
446
  return None
448
447
 
449
448
  name_map = {self.class_name: self.class_name_user}
450
- print(f"{name_map=}")
451
449
  self.df = self.df.drop(list(set(name_map.values()) & set(self.df.columns)), axis=1).rename(columns=name_map)
452
450
  self.df.reset_index(inplace=True, drop=True)
453
451
 
@@ -455,7 +453,7 @@ class ClassifierWidget(QWidget, Styles):
455
453
  if self.prereq_event_check.isChecked() and "t_"+self.prereq_event_cb.currentText() in self.cols:
456
454
  pre_event = self.prereq_event_cb.currentText()
457
455
 
458
- self.df = interpret_track_classification(self.df, self.class_name_user, irreversible_event=self.irreversible_event_btn.isChecked(), unique_state=self.unique_state_btn.isChecked(), transient_event=self.transient_event_btn.isChecked(),r2_threshold=self.r2_slider.value(), pre_event=pre_event)
456
+ self.df = interpret_track_classification(self.df, self.class_name_user, irreversible_event=self.irreversible_event_btn.isChecked(), unique_state=self.unique_state_btn.isChecked(), transient_event=self.transient_event_btn.isChecked(), r2_threshold=self.r2_slider.value(), pre_event=pre_event)
459
457
 
460
458
  else:
461
459
  self.group_name_user = 'group_' + self.name_le.text()
@@ -465,7 +463,8 @@ class ClassifierWidget(QWidget, Styles):
465
463
  msgBox = QMessageBox()
466
464
  msgBox.setIcon(QMessageBox.Information)
467
465
  msgBox.setText(
468
- f"The group column {self.group_name_user} already exists in the table.\nProceeding will reclassify. Do you want to continue?")
466
+ f"The group column {self.group_name_user} already exists in the table.\nProceeding will "
467
+ f"reclassify. Do you want to continue?")
469
468
  msgBox.setWindowTitle("Warning")
470
469
  msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
471
470
  returnValue = msgBox.exec()
@@ -477,18 +476,18 @@ class ClassifierWidget(QWidget, Styles):
477
476
  name_map = {self.class_name: self.group_name_user}
478
477
  self.df = self.df.drop(list(set(name_map.values()) & set(self.df.columns)), axis=1).rename(columns=name_map)
479
478
  print(self.df.columns)
480
- #self.df[self.group_name_user] = self.df[self.group_name_user].replace({0: 1, 1: 0})
479
+ # self.df[self.group_name_user] = self.df[self.group_name_user].replace({0: 1, 1: 0})
481
480
  self.df.reset_index(inplace=True, drop=True)
482
481
 
483
482
  if 'custom' in list(self.df.columns):
484
- self.df = self.df.drop(['custom'],axis=1)
483
+ self.df = self.df.drop(['custom'], axis=1)
485
484
 
486
- self.fig_props.set_size_inches(4,3)
485
+ self.fig_props.set_size_inches(4, 3)
487
486
  self.fig_props.suptitle(self.property_query_le.text(), fontsize=10)
488
487
  self.fig_props.tight_layout()
489
- for pos,pos_group in self.df.groupby('position'):
490
- self.fig_props.savefig(pos+os.sep.join(['output',f'{self.class_name}.png']), bbox_inches='tight', dpi=300)
491
- pos_group.to_csv(pos+os.sep.join(['output', 'tables', f'trajectories_{self.mode}.csv']), index=False)
488
+ for pos, pos_group in self.df.groupby('position'):
489
+ self.fig_props.savefig(str(pos)+os.sep.join(['output', f'{self.class_name}.png']), bbox_inches='tight', dpi=300)
490
+ pos_group.to_csv(str(pos)+os.sep.join(['output', 'tables', f'trajectories_{self.mode}.csv']), index=False)
492
491
 
493
492
  self.parent_window.parent_window.update_position_options()
494
493
  self.close()
@@ -500,7 +499,7 @@ class ClassifierWidget(QWidget, Styles):
500
499
  Helper for segmentation strategy between threshold-based and Deep learning.
501
500
  """
502
501
 
503
- dict_path = os.sep.join([get_software_location(),'celldetective','gui','help','propagate-classification.json'])
502
+ dict_path = os.sep.join([get_software_location(), 'celldetective', 'gui', 'help', 'propagate-classification.json'])
504
503
 
505
504
  with open(dict_path) as f:
506
505
  d = json.load(f)
@@ -524,53 +523,46 @@ class ClassifierWidget(QWidget, Styles):
524
523
  Switch threshold histogram to log scale. Auto adjust.
525
524
  """
526
525
 
527
- if i==1:
526
+ if i == 1:
528
527
  try:
529
528
  feat_x = self.features_cb[1].currentText()
530
529
  min_x = self.df.dropna(subset=feat_x)[feat_x].min()
531
530
  max_x = self.df.dropna(subset=feat_x)[feat_x].max()
532
531
  x_padding = (max_x - min_x) * 0.05
533
- if x_padding==0:
532
+ if x_padding == 0:
534
533
  x_padding = 0.05
535
534
 
536
- if self.ax_props.get_xscale()=='linear':
535
+ if self.ax_props.get_xscale() == 'linear':
537
536
  self.ax_props.set_xlim(min_x, max_x)
538
537
  self.ax_props.set_xscale('log')
539
- self.log_btns[i].setIcon(icon(MDI6.math_log,color="#1565c0"))
538
+ self.log_btns[i].setIcon(icon(MDI6.math_log, color="#1565c0"))
540
539
  else:
541
540
  self.ax_props.set_xscale('linear')
542
541
  self.ax_props.set_xlim(min_x - x_padding, max_x + x_padding)
543
- self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
542
+ self.log_btns[i].setIcon(icon(MDI6.math_log, color="black"))
544
543
  except Exception as e:
545
544
  print(e)
546
- elif i==0:
545
+ elif i == 0:
547
546
  try:
548
547
  feat_y = self.features_cb[0].currentText()
549
548
  min_y = self.df.dropna(subset=feat_y)[feat_y].min()
550
549
  max_y = self.df.dropna(subset=feat_y)[feat_y].max()
551
550
  y_padding = (max_y - min_y) * 0.05
552
- if y_padding==0:
551
+ if y_padding == 0:
553
552
  y_padding = 0.05
554
553
 
555
- if self.ax_props.get_yscale()=='linear':
554
+ if self.ax_props.get_yscale() == 'linear':
556
555
  self.ax_props.set_ylim(min_y, max_y)
557
556
  self.ax_props.set_yscale('log')
558
- self.log_btns[i].setIcon(icon(MDI6.math_log,color="#1565c0"))
557
+ self.log_btns[i].setIcon(icon(MDI6.math_log, color="#1565c0"))
559
558
  else:
560
559
  self.ax_props.set_yscale('linear')
561
560
  self.ax_props.set_ylim(min_y - y_padding, max_y + y_padding)
562
- self.log_btns[i].setIcon(icon(MDI6.math_log,color="black"))
561
+ self.log_btns[i].setIcon(icon(MDI6.math_log, color="black"))
563
562
  except Exception as e:
564
563
  print(e)
565
564
 
566
565
  self.ax_props.autoscale()
567
566
  self.propscanvas.canvas.draw_idle()
568
567
 
569
- print('Done.')
570
-
571
-
572
-
573
-
574
-
575
-
576
-
568
+ print('Done.')