celldetective 1.3.7.post1__py3-none-any.whl → 1.3.8__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.
- celldetective/_version.py +1 -1
- celldetective/gui/btrack_options.py +8 -8
- celldetective/gui/classifier_widget.py +8 -0
- celldetective/gui/configure_new_exp.py +1 -1
- celldetective/gui/json_readers.py +2 -4
- celldetective/gui/plot_signals_ui.py +38 -29
- celldetective/gui/process_block.py +1 -0
- celldetective/gui/processes/downloader.py +108 -0
- celldetective/gui/processes/measure_cells.py +346 -0
- celldetective/gui/processes/segment_cells.py +354 -0
- celldetective/gui/processes/track_cells.py +298 -0
- celldetective/gui/processes/train_segmentation_model.py +270 -0
- celldetective/gui/processes/train_signal_model.py +108 -0
- celldetective/gui/seg_model_loader.py +71 -25
- celldetective/gui/signal_annotator2.py +10 -7
- celldetective/gui/signal_annotator_options.py +1 -1
- celldetective/gui/tableUI.py +252 -20
- celldetective/gui/viewers.py +1 -1
- celldetective/io.py +53 -20
- celldetective/measure.py +12 -144
- celldetective/relative_measurements.py +40 -43
- celldetective/segmentation.py +48 -1
- celldetective/signals.py +84 -305
- celldetective/tracking.py +23 -24
- celldetective/utils.py +1 -1
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/METADATA +11 -2
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/RECORD +31 -25
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/WHEEL +1 -1
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/LICENSE +0 -0
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.7.post1.dist-info → celldetective-1.3.8.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from PyQt5.QtWidgets import QWidget, QGridLayout, QVBoxLayout, QLabel, QLineEdit, QHBoxLayout, QRadioButton, QFileDialog, QPushButton, QMessageBox
|
|
1
|
+
from PyQt5.QtWidgets import QWidget, QGridLayout, QComboBox, QVBoxLayout, QLabel, QLineEdit, QHBoxLayout, QRadioButton, QFileDialog, QPushButton, QMessageBox
|
|
2
2
|
from PyQt5.QtCore import Qt, QSize
|
|
3
3
|
from celldetective.gui.gui_utils import center_window
|
|
4
4
|
from celldetective.gui.layouts import ChannelNormGenerator
|
|
@@ -79,13 +79,24 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
79
79
|
self.layout.addWidget(self.open_dialog_button, 9, 0, 1, 1)
|
|
80
80
|
self.layout.addWidget(self.file_label, 9, 1, 1, 1)
|
|
81
81
|
|
|
82
|
+
|
|
83
|
+
self.merge_lbl = QLabel('Merging option: ')
|
|
84
|
+
self.merge_cb = QComboBox()
|
|
85
|
+
self.merge_cb.addItems(['OR'])
|
|
86
|
+
merge_hbox = QHBoxLayout()
|
|
87
|
+
merge_hbox.addWidget(self.merge_lbl, 33)
|
|
88
|
+
merge_hbox.addWidget(self.merge_cb, 66)
|
|
89
|
+
self.layout.addLayout(merge_hbox, 10, 0, 1, 2)
|
|
90
|
+
self.merge_lbl.hide()
|
|
91
|
+
self.merge_cb.hide()
|
|
92
|
+
|
|
82
93
|
self.upload_button = QPushButton("Upload")
|
|
83
94
|
self.upload_button.clicked.connect(self.upload_model)
|
|
84
95
|
self.upload_button.setIcon(icon(MDI6.upload,color="white"))
|
|
85
96
|
self.upload_button.setIconSize(QSize(25, 25))
|
|
86
97
|
self.upload_button.setStyleSheet(self.button_style_sheet)
|
|
87
98
|
self.upload_button.setEnabled(False)
|
|
88
|
-
self.layout.addWidget(self.upload_button,
|
|
99
|
+
self.layout.addWidget(self.upload_button, 11, 0, 1, 1)
|
|
89
100
|
|
|
90
101
|
self.base_block_options = [self.calibration_label, self.spatial_calib_le,*self.channel_layout.channel_cbs,*self.channel_layout.channel_labels,*self.channel_layout.normalization_mode_btns, *self.channel_layout.normalization_clip_btns, *self.channel_layout.normalization_min_value_lbl,
|
|
91
102
|
*self.channel_layout.normalization_min_value_le, *self.channel_layout.normalization_max_value_lbl,*self.channel_layout.normalization_max_value_le,
|
|
@@ -207,41 +218,73 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
207
218
|
self.file_dialog.setFileMode(QFileDialog.ExistingFile)
|
|
208
219
|
|
|
209
220
|
# If accepted check validity of data
|
|
210
|
-
if self.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
221
|
+
if self.seg_mode!='threshold':
|
|
222
|
+
if self.file_dialog.exec_() == QFileDialog.Accepted:
|
|
223
|
+
self.filename = self.file_dialog.selectedFiles()[0]
|
|
224
|
+
if self.seg_mode=="stardist":
|
|
225
|
+
subfiles = glob(self.filename+"/*")
|
|
226
|
+
subfiles = [s.replace('\\','/') for s in subfiles]
|
|
227
|
+
if self.filename+"/thresholds.json" in subfiles:
|
|
228
|
+
self.file_label.setText(self.filename.split("/")[-1])
|
|
229
|
+
self.modelname = self.filename.split("/")[-1]
|
|
230
|
+
self.destination = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]+f"/models/{self.target_folder}/"+self.modelname
|
|
231
|
+
self.folder_dest = self.destination
|
|
232
|
+
else:
|
|
233
|
+
msgBox = QMessageBox()
|
|
234
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
235
|
+
msgBox.setText("StarDist model not recognized... Please ensure that it contains a thresholds.json file or that it is a valid StarDist model...")
|
|
236
|
+
msgBox.setWindowTitle("Warning")
|
|
237
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
238
|
+
returnValue = msgBox.exec()
|
|
239
|
+
if returnValue == QMessageBox.Ok:
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
if self.seg_mode=="cellpose":
|
|
216
243
|
self.file_label.setText(self.filename.split("/")[-1])
|
|
217
244
|
self.modelname = self.filename.split("/")[-1]
|
|
218
|
-
|
|
219
|
-
self.folder_dest = self.
|
|
245
|
+
print(f"Transferring Cellpose model {self.filename}...")
|
|
246
|
+
self.folder_dest = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]+f"/models/{self.target_folder}/"+self.modelname
|
|
247
|
+
self.destination = self.folder_dest+f"/{self.modelname}"
|
|
248
|
+
|
|
249
|
+
else:
|
|
250
|
+
self.filename, _ = QFileDialog.getOpenFileNames(
|
|
251
|
+
None,
|
|
252
|
+
"Load threshold configuration(s)...",
|
|
253
|
+
"",
|
|
254
|
+
"Json Configs (*.json)",
|
|
255
|
+
)
|
|
256
|
+
if self.filename:
|
|
257
|
+
n_files = len(self.filename)
|
|
258
|
+
print(f'You loaded {n_files} threshold configuration files...')
|
|
259
|
+
for i,filename in enumerate(self.filename):
|
|
260
|
+
print(f"Config {i}: ",filename,"...")
|
|
261
|
+
|
|
262
|
+
if n_files==1:
|
|
263
|
+
self.merge_cb.hide()
|
|
264
|
+
self.merge_lbl.hide()
|
|
265
|
+
self.file_label.setText(self.filename[0].split("/")[-1])
|
|
220
266
|
else:
|
|
221
267
|
msgBox = QMessageBox()
|
|
222
268
|
msgBox.setIcon(QMessageBox.Warning)
|
|
223
|
-
msgBox.setText("
|
|
269
|
+
msgBox.setText("You selected more than one pipeline. Please set a merging procedure for the resulting masks...")
|
|
224
270
|
msgBox.setWindowTitle("Warning")
|
|
225
271
|
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
226
272
|
returnValue = msgBox.exec()
|
|
227
273
|
if returnValue == QMessageBox.Ok:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if self.seg_mode=="cellpose":
|
|
231
|
-
self.file_label.setText(self.filename.split("/")[-1])
|
|
232
|
-
self.modelname = self.filename.split("/")[-1]
|
|
233
|
-
print(f"Transferring Cellpose model {self.filename}...")
|
|
234
|
-
self.folder_dest = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]+f"/models/{self.target_folder}/"+self.modelname
|
|
235
|
-
self.destination = self.folder_dest+f"/{self.modelname}"
|
|
274
|
+
pass
|
|
236
275
|
|
|
237
|
-
|
|
238
|
-
|
|
276
|
+
self.merge_cb.show()
|
|
277
|
+
self.merge_lbl.show()
|
|
278
|
+
self.file_label.setText(f"{n_files} configs loaded...")
|
|
239
279
|
|
|
240
280
|
def show_seg_options(self):
|
|
241
281
|
|
|
242
282
|
"""
|
|
243
283
|
Show the relevant widgets, mask the others.
|
|
244
284
|
"""
|
|
285
|
+
|
|
286
|
+
self.filename = None
|
|
287
|
+
self.file_label.setText('No file chosen')
|
|
245
288
|
self.base_block_options = [self.calibration_label, self.spatial_calib_le,*self.channel_layout.channel_cbs,*self.channel_layout.channel_labels,*self.channel_layout.normalization_mode_btns, *self.channel_layout.normalization_clip_btns, *self.channel_layout.normalization_min_value_lbl,
|
|
246
289
|
*self.channel_layout.normalization_min_value_le, *self.channel_layout.normalization_max_value_lbl,*self.channel_layout.normalization_max_value_le,
|
|
247
290
|
self.channel_layout.add_col_btn]
|
|
@@ -250,7 +293,7 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
250
293
|
self.spatial_calib_le.setToolTip('Cellpose rescales the images such that the cells are 30.0 pixels. You can compute the scale from the training data as\n(pixel calibration [µm] in training images)*(cell diameter [px] in training images)/(30 [px]).\nIf you pass images with a different calibration to the model, they will be rescaled automatically.\nThe rescaling is ignored if you pass a diameter different from 30 px below.')
|
|
251
294
|
self.channel_layout.channel_labels[0].setText('cyto: ')
|
|
252
295
|
self.channel_layout.channel_labels[1].setText('nuclei: ')
|
|
253
|
-
for c in [self.threshold_config_button]:
|
|
296
|
+
for c in [self.threshold_config_button, self.merge_lbl, self.merge_cb]:
|
|
254
297
|
c.hide()
|
|
255
298
|
for c in self.cellpose_options+self.base_block_options:
|
|
256
299
|
c.show()
|
|
@@ -261,7 +304,7 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
261
304
|
self.channel_layout.channel_labels[1].setText('channel 2: ')
|
|
262
305
|
for c in self.base_block_options:
|
|
263
306
|
c.show()
|
|
264
|
-
for c in self.cellpose_options+[self.threshold_config_button]:
|
|
307
|
+
for c in self.cellpose_options+[self.threshold_config_button, self.merge_lbl, self.merge_cb]:
|
|
265
308
|
c.hide()
|
|
266
309
|
self.unlock_upload()
|
|
267
310
|
else:
|
|
@@ -346,7 +389,10 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
346
389
|
self.parent_window.init_seg_model_list()
|
|
347
390
|
self.close()
|
|
348
391
|
else:
|
|
349
|
-
if self.
|
|
392
|
+
if not isinstance(self.filename, list):
|
|
393
|
+
if not self.filename is None:
|
|
394
|
+
self.filename = [self.filename]
|
|
395
|
+
if self.mode=="targets":
|
|
350
396
|
self.parent_window.threshold_config_targets = self.filename
|
|
351
397
|
self.parent_window.seg_model_list.setCurrentText('Threshold')
|
|
352
398
|
print('Path to the traditional segmentation pipeline successfully set in celldetective...')
|
|
@@ -355,7 +401,7 @@ class SegmentationModelLoader(QWidget, Styles):
|
|
|
355
401
|
self.parent_window.threshold_config_effectors = self.filename
|
|
356
402
|
self.parent_window.seg_model_list.setCurrentText('Threshold')
|
|
357
403
|
print('Path to the traditional segmentation pipeline successfully set in celldetective...')
|
|
358
|
-
self.close()
|
|
404
|
+
self.close()
|
|
359
405
|
|
|
360
406
|
def generate_input_config(self):
|
|
361
407
|
|
|
@@ -1577,13 +1577,16 @@ class SignalAnnotator2(QMainWindow,Styles):
|
|
|
1577
1577
|
self.cell_ax.set_ylim(self.value_magnitude, self.non_log_ymax)
|
|
1578
1578
|
|
|
1579
1579
|
if self.reference_track_of_interest is not None and self.neighbor_track_of_interest is not None:
|
|
1580
|
-
t0 = 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.pair_time_name].dropna().
|
|
1581
|
-
|
|
1582
|
-
t0
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1580
|
+
t0 = 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.pair_time_name].dropna().values
|
|
1581
|
+
try:
|
|
1582
|
+
if t0!=[]:
|
|
1583
|
+
t0=t0[0]
|
|
1584
|
+
ymin,ymax = self.cell_ax.get_ylim()
|
|
1585
|
+
self.line_dt.set_xdata([t0, t0])
|
|
1586
|
+
self.line_dt.set_ydata([ymin,ymax])
|
|
1587
|
+
except Exception as e:
|
|
1588
|
+
print(e)
|
|
1589
|
+
|
|
1587
1590
|
self.cell_ax.legend()
|
|
1588
1591
|
self.cell_fcanvas.canvas.draw()
|
|
1589
1592
|
|
|
@@ -34,7 +34,7 @@ class ConfigSignalAnnotator(QMainWindow, Styles):
|
|
|
34
34
|
self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_targets.json"
|
|
35
35
|
elif self.mode=="effectors":
|
|
36
36
|
self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_effectors.json"
|
|
37
|
-
elif self.mode == "
|
|
37
|
+
elif self.mode == "pairs":
|
|
38
38
|
self.instructions_path = self.parent_window.exp_dir + "configs/signal_annotator_config_neighborhood.json"
|
|
39
39
|
|
|
40
40
|
exp_config = self.exp_dir +"config.ini"
|
celldetective/gui/tableUI.py
CHANGED
|
@@ -166,7 +166,7 @@ class DifferentiateColWidget(QWidget, Styles):
|
|
|
166
166
|
layout.addLayout(measurement_layout)
|
|
167
167
|
|
|
168
168
|
self.window_size_slider = QLabeledSlider()
|
|
169
|
-
self.window_size_slider.setRange(1,np.nanmax(self.parent_window.data.FRAME.to_numpy()))
|
|
169
|
+
self.window_size_slider.setRange(1,int(np.nanmax(self.parent_window.data.FRAME.to_numpy())))
|
|
170
170
|
self.window_size_slider.setValue(3)
|
|
171
171
|
window_layout = QHBoxLayout()
|
|
172
172
|
window_layout.addWidget(QLabel('window size: '), 25)
|
|
@@ -215,6 +215,108 @@ class DifferentiateColWidget(QWidget, Styles):
|
|
|
215
215
|
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
216
216
|
self.close()
|
|
217
217
|
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class OperationOnColsWidget(QWidget, Styles):
|
|
221
|
+
|
|
222
|
+
def __init__(self, parent_window, column1=None, column2=None, operation='divide'):
|
|
223
|
+
|
|
224
|
+
super().__init__()
|
|
225
|
+
self.parent_window = parent_window
|
|
226
|
+
self.column1 = column1
|
|
227
|
+
self.column2 = column2
|
|
228
|
+
self.operation = operation
|
|
229
|
+
|
|
230
|
+
self.setWindowTitle(self.operation)
|
|
231
|
+
# Create the QComboBox and add some items
|
|
232
|
+
center_window(self)
|
|
233
|
+
|
|
234
|
+
layout = QVBoxLayout(self)
|
|
235
|
+
layout.setContentsMargins(30,30,30,30)
|
|
236
|
+
|
|
237
|
+
self.col1_cb = QComboBox()
|
|
238
|
+
self.col1_cb.addItems(list(self.parent_window.data.columns))
|
|
239
|
+
if self.column1 is not None:
|
|
240
|
+
idx = self.col1_cb.findText(self.column1)
|
|
241
|
+
self.col1_cb.setCurrentIndex(idx)
|
|
242
|
+
|
|
243
|
+
numerator_layout = QHBoxLayout()
|
|
244
|
+
numerator_layout.addWidget(QLabel('column 1: '), 25)
|
|
245
|
+
numerator_layout.addWidget(self.col1_cb, 75)
|
|
246
|
+
layout.addLayout(numerator_layout)
|
|
247
|
+
|
|
248
|
+
self.col2_cb = QComboBox()
|
|
249
|
+
self.col2_cb.addItems(list(self.parent_window.data.columns))
|
|
250
|
+
if self.column2 is not None:
|
|
251
|
+
idx = self.col2_cb.findText(self.column2)
|
|
252
|
+
self.col2_cb.setCurrentIndex(idx)
|
|
253
|
+
|
|
254
|
+
denominator_layout = QHBoxLayout()
|
|
255
|
+
denominator_layout.addWidget(QLabel('column 2: '), 25)
|
|
256
|
+
denominator_layout.addWidget(self.col2_cb, 75)
|
|
257
|
+
layout.addLayout(denominator_layout)
|
|
258
|
+
|
|
259
|
+
self.submit_btn = QPushButton('Compute')
|
|
260
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
261
|
+
self.submit_btn.clicked.connect(self.compute)
|
|
262
|
+
layout.addWidget(self.submit_btn, 30)
|
|
263
|
+
|
|
264
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
265
|
+
|
|
266
|
+
def compute(self):
|
|
267
|
+
|
|
268
|
+
test = self._check_cols_before_operation()
|
|
269
|
+
if not test:
|
|
270
|
+
msgBox = QMessageBox()
|
|
271
|
+
msgBox.setIcon(QMessageBox.Warning)
|
|
272
|
+
msgBox.setText(f"Operation could not be performed, one of the column types is object...")
|
|
273
|
+
msgBox.setWindowTitle("Warning")
|
|
274
|
+
msgBox.setStandardButtons(QMessageBox.Ok)
|
|
275
|
+
returnValue = msgBox.exec()
|
|
276
|
+
if returnValue == QMessageBox.Ok:
|
|
277
|
+
return None
|
|
278
|
+
else:
|
|
279
|
+
return None
|
|
280
|
+
else:
|
|
281
|
+
if self.operation=='divide':
|
|
282
|
+
name = f"{self.col1_txt}/{self.col2_txt}"
|
|
283
|
+
with np.errstate(divide='ignore', invalid='ignore'):
|
|
284
|
+
res = np.true_divide(self.col1, self.col2)
|
|
285
|
+
res[res == np.inf] = np.nan
|
|
286
|
+
res[self.col1!=self.col1] = np.nan
|
|
287
|
+
res[self.col2!=self.col2] = np.nan
|
|
288
|
+
self.parent_window.data[name] = res
|
|
289
|
+
|
|
290
|
+
elif self.operation=='multiply':
|
|
291
|
+
name = f"{self.col1_txt}*{self.col2_txt}"
|
|
292
|
+
res = np.multiply(self.col1, self.col2)
|
|
293
|
+
|
|
294
|
+
elif self.operation=='add':
|
|
295
|
+
name = f"{self.col1_txt}+{self.col2_txt}"
|
|
296
|
+
res = np.add(self.col1, self.col2)
|
|
297
|
+
|
|
298
|
+
elif self.operation=='subtract':
|
|
299
|
+
name = f"{self.col1_txt}-{self.col2_txt}"
|
|
300
|
+
res = np.subtract(self.col1, self.col2)
|
|
301
|
+
|
|
302
|
+
self.parent_window.data[name] = res
|
|
303
|
+
self.parent_window.model = PandasModel(self.parent_window.data)
|
|
304
|
+
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
305
|
+
self.close()
|
|
306
|
+
|
|
307
|
+
def _check_cols_before_operation(self):
|
|
308
|
+
|
|
309
|
+
self.col1_txt = self.col1_cb.currentText()
|
|
310
|
+
self.col2_txt = self.col2_cb.currentText()
|
|
311
|
+
|
|
312
|
+
self.col1 = self.parent_window.data[self.col1_txt].to_numpy()
|
|
313
|
+
self.col2 = self.parent_window.data[self.col2_txt].to_numpy()
|
|
314
|
+
|
|
315
|
+
test = np.all([self.col1.dtype!='O', self.col2.dtype!='O'])
|
|
316
|
+
|
|
317
|
+
return test
|
|
318
|
+
|
|
319
|
+
|
|
218
320
|
class CalibrateColWidget(GenericOpColWidget):
|
|
219
321
|
|
|
220
322
|
def __init__(self, *args, **kwargs):
|
|
@@ -563,12 +665,34 @@ class TableUI(QMainWindow, Styles):
|
|
|
563
665
|
self.log_action = QAction('&Log (decimal)...', self)
|
|
564
666
|
self.log_action.triggered.connect(self.take_log_of_selected_feature)
|
|
565
667
|
#self.derivative_action.setShortcut("Ctrl+D")
|
|
566
|
-
self.mathMenu.addAction(self.log_action)
|
|
668
|
+
self.mathMenu.addAction(self.log_action)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
self.divide_action = QAction('&Divide...', self)
|
|
672
|
+
self.divide_action.triggered.connect(self.divide_signals)
|
|
673
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
674
|
+
self.mathMenu.addAction(self.divide_action)
|
|
675
|
+
|
|
676
|
+
self.multiply_action = QAction('&Multiply...', self)
|
|
677
|
+
self.multiply_action.triggered.connect(self.multiply_signals)
|
|
678
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
679
|
+
self.mathMenu.addAction(self.multiply_action)
|
|
680
|
+
|
|
681
|
+
self.add_action = QAction('&Add...', self)
|
|
682
|
+
self.add_action.triggered.connect(self.add_signals)
|
|
683
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
684
|
+
self.mathMenu.addAction(self.add_action)
|
|
685
|
+
|
|
686
|
+
self.subtract_action = QAction('&Subtract...', self)
|
|
687
|
+
self.subtract_action.triggered.connect(self.subtract_signals)
|
|
688
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
689
|
+
self.mathMenu.addAction(self.subtract_action)
|
|
567
690
|
|
|
568
|
-
|
|
569
|
-
self.onehot_action
|
|
570
|
-
#self.onehot_action.
|
|
571
|
-
self.
|
|
691
|
+
|
|
692
|
+
# self.onehot_action = QAction('&One hot to categorical...', self)
|
|
693
|
+
# self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
|
|
694
|
+
# #self.onehot_action.setShortcut("Ctrl+D")
|
|
695
|
+
# self.mathMenu.addAction(self.onehot_action)
|
|
572
696
|
|
|
573
697
|
def collapse_pairs_in_neigh(self):
|
|
574
698
|
|
|
@@ -734,6 +858,96 @@ class TableUI(QMainWindow, Styles):
|
|
|
734
858
|
pos_group.to_csv(pos[0]+os.sep.join(['output', 'tables', f'trajectories_{self.population}.csv']), index=False)
|
|
735
859
|
print("Done...")
|
|
736
860
|
|
|
861
|
+
def divide_signals(self):
|
|
862
|
+
|
|
863
|
+
x = self.table_view.selectedIndexes()
|
|
864
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
865
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
866
|
+
cols = np.array(list(self.data.columns))
|
|
867
|
+
if len(col_idx)>0:
|
|
868
|
+
selected_col1 = str(cols[col_idx[0]])
|
|
869
|
+
if len(col_idx)>1:
|
|
870
|
+
selected_col2 = str(cols[col_idx[1]])
|
|
871
|
+
else:
|
|
872
|
+
selected_col2 = None
|
|
873
|
+
else:
|
|
874
|
+
selected_col1 = None
|
|
875
|
+
selected_col2 = None
|
|
876
|
+
else:
|
|
877
|
+
selected_col1 = None
|
|
878
|
+
selected_col2 = None
|
|
879
|
+
|
|
880
|
+
self.divWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='divide')
|
|
881
|
+
self.divWidget.show()
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def multiply_signals(self):
|
|
885
|
+
|
|
886
|
+
x = self.table_view.selectedIndexes()
|
|
887
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
888
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
889
|
+
cols = np.array(list(self.data.columns))
|
|
890
|
+
if len(col_idx)>0:
|
|
891
|
+
selected_col1 = str(cols[col_idx[0]])
|
|
892
|
+
if len(col_idx)>1:
|
|
893
|
+
selected_col2 = str(cols[col_idx[1]])
|
|
894
|
+
else:
|
|
895
|
+
selected_col2 = None
|
|
896
|
+
else:
|
|
897
|
+
selected_col1 = None
|
|
898
|
+
selected_col2 = None
|
|
899
|
+
else:
|
|
900
|
+
selected_col1 = None
|
|
901
|
+
selected_col2 = None
|
|
902
|
+
|
|
903
|
+
self.mulWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='multiply')
|
|
904
|
+
self.mulWidget.show()
|
|
905
|
+
|
|
906
|
+
def add_signals(self):
|
|
907
|
+
|
|
908
|
+
x = self.table_view.selectedIndexes()
|
|
909
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
910
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
911
|
+
cols = np.array(list(self.data.columns))
|
|
912
|
+
if len(col_idx)>0:
|
|
913
|
+
selected_col1 = str(cols[col_idx[0]])
|
|
914
|
+
if len(col_idx)>1:
|
|
915
|
+
selected_col2 = str(cols[col_idx[1]])
|
|
916
|
+
else:
|
|
917
|
+
selected_col2 = None
|
|
918
|
+
else:
|
|
919
|
+
selected_col1 = None
|
|
920
|
+
selected_col2 = None
|
|
921
|
+
else:
|
|
922
|
+
selected_col1 = None
|
|
923
|
+
selected_col2 = None
|
|
924
|
+
|
|
925
|
+
self.addiWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='add')
|
|
926
|
+
self.addiWidget.show()
|
|
927
|
+
|
|
928
|
+
def subtract_signals(self):
|
|
929
|
+
|
|
930
|
+
x = self.table_view.selectedIndexes()
|
|
931
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
932
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
933
|
+
cols = np.array(list(self.data.columns))
|
|
934
|
+
if len(col_idx)>0:
|
|
935
|
+
selected_col1 = str(cols[col_idx[0]])
|
|
936
|
+
if len(col_idx)>1:
|
|
937
|
+
selected_col2 = str(cols[col_idx[1]])
|
|
938
|
+
else:
|
|
939
|
+
selected_col2 = None
|
|
940
|
+
else:
|
|
941
|
+
selected_col1 = None
|
|
942
|
+
selected_col2 = None
|
|
943
|
+
else:
|
|
944
|
+
selected_col1 = None
|
|
945
|
+
selected_col2 = None
|
|
946
|
+
|
|
947
|
+
self.subWidget = OperationOnColsWidget(self, column1=selected_col1, column2=selected_col2, operation='subtract')
|
|
948
|
+
self.subWidget.show()
|
|
949
|
+
|
|
950
|
+
|
|
737
951
|
def differenciate_selected_feature(self):
|
|
738
952
|
|
|
739
953
|
# check only one col selected and assert is numerical
|
|
@@ -742,9 +956,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
742
956
|
|
|
743
957
|
x = self.table_view.selectedIndexes()
|
|
744
958
|
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
745
|
-
if col_idx
|
|
959
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
746
960
|
cols = np.array(list(self.data.columns))
|
|
747
|
-
|
|
961
|
+
if len(col_idx)>0:
|
|
962
|
+
selected_col = str(cols[col_idx[0]])
|
|
963
|
+
else:
|
|
964
|
+
selected_col = None
|
|
748
965
|
else:
|
|
749
966
|
selected_col = None
|
|
750
967
|
|
|
@@ -759,9 +976,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
759
976
|
|
|
760
977
|
x = self.table_view.selectedIndexes()
|
|
761
978
|
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
762
|
-
if col_idx
|
|
979
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
763
980
|
cols = np.array(list(self.data.columns))
|
|
764
|
-
|
|
981
|
+
if len(col_idx)>0:
|
|
982
|
+
selected_col = str(cols[col_idx[0]])
|
|
983
|
+
else:
|
|
984
|
+
selected_col = None
|
|
765
985
|
else:
|
|
766
986
|
selected_col = None
|
|
767
987
|
|
|
@@ -772,9 +992,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
772
992
|
|
|
773
993
|
x = self.table_view.selectedIndexes()
|
|
774
994
|
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
775
|
-
if col_idx
|
|
995
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
776
996
|
cols = np.array(list(self.data.columns))
|
|
777
|
-
|
|
997
|
+
if len(col_idx)>0:
|
|
998
|
+
selected_col = str(cols[col_idx[0]])
|
|
999
|
+
else:
|
|
1000
|
+
selected_col = None
|
|
778
1001
|
else:
|
|
779
1002
|
selected_col = None
|
|
780
1003
|
|
|
@@ -790,9 +1013,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
790
1013
|
|
|
791
1014
|
x = self.table_view.selectedIndexes()
|
|
792
1015
|
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
793
|
-
if col_idx
|
|
1016
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
794
1017
|
cols = np.array(list(self.data.columns))
|
|
795
|
-
|
|
1018
|
+
if len(col_idx)>0:
|
|
1019
|
+
selected_col = str(cols[col_idx[0]])
|
|
1020
|
+
else:
|
|
1021
|
+
selected_col = None
|
|
796
1022
|
else:
|
|
797
1023
|
selected_col = None
|
|
798
1024
|
|
|
@@ -804,11 +1030,14 @@ class TableUI(QMainWindow, Styles):
|
|
|
804
1030
|
|
|
805
1031
|
x = self.table_view.selectedIndexes()
|
|
806
1032
|
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
807
|
-
if
|
|
1033
|
+
if isinstance(col_idx, (list, np.ndarray)):
|
|
808
1034
|
cols = np.array(list(self.data.columns))
|
|
809
|
-
|
|
1035
|
+
if len(col_idx)>0:
|
|
1036
|
+
selected_col = str(cols[col_idx[0]])
|
|
1037
|
+
else:
|
|
1038
|
+
selected_col = None
|
|
810
1039
|
else:
|
|
811
|
-
|
|
1040
|
+
selected_col = None
|
|
812
1041
|
|
|
813
1042
|
self.mergewidget = MergeOneHotWidget(self, selected_columns=selected_cols)
|
|
814
1043
|
self.mergewidget.show()
|
|
@@ -874,7 +1103,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
874
1103
|
self.projection_option.setChecked(True)
|
|
875
1104
|
self.projection_option.toggled.connect(self.enable_projection_options)
|
|
876
1105
|
self.projection_op_cb = QComboBox()
|
|
877
|
-
self.projection_op_cb.addItems(['mean','median','min','max',
|
|
1106
|
+
self.projection_op_cb.addItems(['mean','median','min','max','first','last','prod','sum'])
|
|
878
1107
|
|
|
879
1108
|
projection_layout = QHBoxLayout()
|
|
880
1109
|
projection_layout.addWidget(self.projection_option, 33)
|
|
@@ -1044,8 +1273,11 @@ class TableUI(QMainWindow, Styles):
|
|
|
1044
1273
|
all_cms = list(colormaps)
|
|
1045
1274
|
for cm in all_cms:
|
|
1046
1275
|
if hasattr(matplotlib.cm, str(cm).lower()):
|
|
1047
|
-
|
|
1048
|
-
|
|
1276
|
+
try:
|
|
1277
|
+
self.cmap_cb.addColormap(cm.lower())
|
|
1278
|
+
except:
|
|
1279
|
+
pass
|
|
1280
|
+
|
|
1049
1281
|
hbox = QHBoxLayout()
|
|
1050
1282
|
hbox.addWidget(QLabel('colormap: '), 33)
|
|
1051
1283
|
hbox.addWidget(self.cmap_cb, 66)
|
celldetective/gui/viewers.py
CHANGED
|
@@ -932,7 +932,7 @@ class CellSizeViewer(StackVisualizer):
|
|
|
932
932
|
with interactive sliders for diameter adjustment and circle display.
|
|
933
933
|
"""
|
|
934
934
|
|
|
935
|
-
def __init__(self, initial_diameter=40, set_radius_in_list=False, diameter_slider_range=(0,
|
|
935
|
+
def __init__(self, initial_diameter=40, set_radius_in_list=False, diameter_slider_range=(0,500), parent_le=None, parent_list_widget=None, *args, **kwargs):
|
|
936
936
|
# Initialize the widget and its attributes
|
|
937
937
|
|
|
938
938
|
super().__init__(*args, **kwargs)
|