celldetective 1.0.2__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/__init__.py +2 -0
- celldetective/__main__.py +432 -0
- celldetective/datasets/segmentation_annotations/blank +0 -0
- celldetective/datasets/signal_annotations/blank +0 -0
- celldetective/events.py +149 -0
- celldetective/extra_properties.py +100 -0
- celldetective/filters.py +89 -0
- celldetective/gui/__init__.py +20 -0
- celldetective/gui/about.py +44 -0
- celldetective/gui/analyze_block.py +563 -0
- celldetective/gui/btrack_options.py +898 -0
- celldetective/gui/classifier_widget.py +386 -0
- celldetective/gui/configure_new_exp.py +532 -0
- celldetective/gui/control_panel.py +438 -0
- celldetective/gui/gui_utils.py +495 -0
- celldetective/gui/json_readers.py +113 -0
- celldetective/gui/measurement_options.py +1425 -0
- celldetective/gui/neighborhood_options.py +452 -0
- celldetective/gui/plot_signals_ui.py +1042 -0
- celldetective/gui/process_block.py +1055 -0
- celldetective/gui/retrain_segmentation_model_options.py +706 -0
- celldetective/gui/retrain_signal_model_options.py +643 -0
- celldetective/gui/seg_model_loader.py +460 -0
- celldetective/gui/signal_annotator.py +2388 -0
- celldetective/gui/signal_annotator_options.py +340 -0
- celldetective/gui/styles.py +217 -0
- celldetective/gui/survival_ui.py +903 -0
- celldetective/gui/tableUI.py +608 -0
- celldetective/gui/thresholds_gui.py +1300 -0
- celldetective/icons/logo-large.png +0 -0
- celldetective/icons/logo.png +0 -0
- celldetective/icons/signals_icon.png +0 -0
- celldetective/icons/splash-test.png +0 -0
- celldetective/icons/splash.png +0 -0
- celldetective/icons/splash0.png +0 -0
- celldetective/icons/survival2.png +0 -0
- celldetective/icons/vignette_signals2.png +0 -0
- celldetective/icons/vignette_signals2.svg +114 -0
- celldetective/io.py +2050 -0
- celldetective/links/zenodo.json +561 -0
- celldetective/measure.py +1258 -0
- celldetective/models/segmentation_effectors/blank +0 -0
- celldetective/models/segmentation_generic/blank +0 -0
- celldetective/models/segmentation_targets/blank +0 -0
- celldetective/models/signal_detection/blank +0 -0
- celldetective/models/tracking_configs/mcf7.json +68 -0
- celldetective/models/tracking_configs/ricm.json +203 -0
- celldetective/models/tracking_configs/ricm2.json +203 -0
- celldetective/neighborhood.py +717 -0
- celldetective/scripts/analyze_signals.py +51 -0
- celldetective/scripts/measure_cells.py +275 -0
- celldetective/scripts/segment_cells.py +212 -0
- celldetective/scripts/segment_cells_thresholds.py +140 -0
- celldetective/scripts/track_cells.py +206 -0
- celldetective/scripts/train_segmentation_model.py +246 -0
- celldetective/scripts/train_signal_model.py +49 -0
- celldetective/segmentation.py +712 -0
- celldetective/signals.py +2826 -0
- celldetective/tracking.py +974 -0
- celldetective/utils.py +1681 -0
- celldetective-1.0.2.dist-info/LICENSE +674 -0
- celldetective-1.0.2.dist-info/METADATA +192 -0
- celldetective-1.0.2.dist-info/RECORD +66 -0
- celldetective-1.0.2.dist-info/WHEEL +5 -0
- celldetective-1.0.2.dist-info/entry_points.txt +2 -0
- celldetective-1.0.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
from PyQt5.QtWidgets import QMainWindow, QApplication, QMessageBox, QScrollArea, QComboBox, QFrame, QCheckBox, QFileDialog, QGridLayout, QTextEdit, QLineEdit, QVBoxLayout, QWidget, QLabel, QHBoxLayout, QPushButton
|
|
2
|
+
from PyQt5.QtCore import Qt, QSize
|
|
3
|
+
from PyQt5.QtGui import QIcon
|
|
4
|
+
from celldetective.gui.gui_utils import center_window, FeatureChoice, ListWidget, QHSeperationLine, FigureCanvas, DistanceChoice, OperationChoice
|
|
5
|
+
from superqt import QLabeledDoubleRangeSlider, QLabeledDoubleSlider,QLabeledSlider
|
|
6
|
+
from superqt.fonticon import icon
|
|
7
|
+
from fonticon_mdi6 import MDI6
|
|
8
|
+
from celldetective.utils import extract_experiment_channels, get_software_location
|
|
9
|
+
from celldetective.io import interpret_tracking_configuration, load_frames, auto_load_number_of_frames
|
|
10
|
+
from celldetective.measure import compute_haralick_features, contour_of_instance_segmentation
|
|
11
|
+
import numpy as np
|
|
12
|
+
from tifffile import imread
|
|
13
|
+
import json
|
|
14
|
+
from shutil import copyfile
|
|
15
|
+
import os
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
18
|
+
from glob import glob
|
|
19
|
+
from natsort import natsorted
|
|
20
|
+
from tifffile import imread
|
|
21
|
+
from pathlib import Path, PurePath
|
|
22
|
+
import gc
|
|
23
|
+
import pandas as pd
|
|
24
|
+
|
|
25
|
+
class ConfigNeighborhoods(QMainWindow):
|
|
26
|
+
|
|
27
|
+
"""
|
|
28
|
+
UI to set measurement instructions.
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, parent=None):
|
|
33
|
+
|
|
34
|
+
super().__init__()
|
|
35
|
+
self.parent = parent
|
|
36
|
+
self.setWindowTitle("Configure neighborhoods")
|
|
37
|
+
#self.setWindowIcon(QIcon(os.sep.join(['celldetective','icons','mexican-hat.png'])))
|
|
38
|
+
|
|
39
|
+
self.exp_dir = self.parent.exp_dir
|
|
40
|
+
self.neigh_instructions = self.parent.exp_dir + os.sep.join(["configs","neighborhood_instructions.json"])
|
|
41
|
+
self.clear_previous = False
|
|
42
|
+
self.not_status_reference = False
|
|
43
|
+
self.not_status_neighbor = False
|
|
44
|
+
|
|
45
|
+
exp_config = self.exp_dir +"config.ini"
|
|
46
|
+
self.channel_names, self.channels = extract_experiment_channels(exp_config)
|
|
47
|
+
self.channel_names = np.array(self.channel_names)
|
|
48
|
+
self.channels = np.array(self.channels)
|
|
49
|
+
|
|
50
|
+
self.screen_height = self.parent.parent.parent.screen_height
|
|
51
|
+
center_window(self)
|
|
52
|
+
|
|
53
|
+
self.setMinimumWidth(750)
|
|
54
|
+
self.setMinimumHeight(int(0.5*self.screen_height))
|
|
55
|
+
self.setMaximumHeight(int(0.95*self.screen_height))
|
|
56
|
+
|
|
57
|
+
self.populate_widget()
|
|
58
|
+
self.load_previous_neighborhood_instructions()
|
|
59
|
+
|
|
60
|
+
def populate_widget(self):
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
Create the multibox design.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
# Create button widget and layout
|
|
68
|
+
self.scroll_area = QScrollArea(self)
|
|
69
|
+
self.button_widget = QWidget()
|
|
70
|
+
main_layout = QVBoxLayout()
|
|
71
|
+
self.button_widget.setLayout(main_layout)
|
|
72
|
+
main_layout.setContentsMargins(30,30,30,30)
|
|
73
|
+
|
|
74
|
+
# second frame for ISOTROPIC MEASUREMENTS
|
|
75
|
+
pop_hbox = QHBoxLayout()
|
|
76
|
+
|
|
77
|
+
self.reference_population_frame = QFrame()
|
|
78
|
+
self.reference_population_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
79
|
+
self.populate_reference_frame()
|
|
80
|
+
pop_hbox.addWidget(self.reference_population_frame, 50)
|
|
81
|
+
|
|
82
|
+
self.neigh_population_frame = QFrame()
|
|
83
|
+
self.neigh_population_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
84
|
+
self.populate_neigh_frame()
|
|
85
|
+
pop_hbox.addWidget(self.neigh_population_frame, 50)
|
|
86
|
+
main_layout.addLayout(pop_hbox)
|
|
87
|
+
|
|
88
|
+
self.radii_frame = QFrame()
|
|
89
|
+
self.radii_frame.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
|
|
90
|
+
self.populate_radii_frame()
|
|
91
|
+
main_layout.addWidget(self.radii_frame)
|
|
92
|
+
|
|
93
|
+
self.clear_previous_btn = QCheckBox('clear previous neighborhoods')
|
|
94
|
+
main_layout.addWidget(self.clear_previous_btn, alignment=Qt.AlignRight)
|
|
95
|
+
|
|
96
|
+
main_layout.addWidget(QLabel(''))
|
|
97
|
+
self.submit_btn = QPushButton('Set')
|
|
98
|
+
self.submit_btn.setStyleSheet(self.parent.parent.parent.button_style_sheet)
|
|
99
|
+
self.submit_btn.clicked.connect(self.write_instructions)
|
|
100
|
+
main_layout.addWidget(self.submit_btn)
|
|
101
|
+
|
|
102
|
+
#self.populate_left_panel()
|
|
103
|
+
#grid.addLayout(self.left_side, 0, 0, 1, 1)
|
|
104
|
+
self.button_widget.adjustSize()
|
|
105
|
+
|
|
106
|
+
self.scroll_area.setAlignment(Qt.AlignCenter)
|
|
107
|
+
self.scroll_area.setWidget(self.button_widget)
|
|
108
|
+
self.scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
109
|
+
self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
110
|
+
self.scroll_area.setWidgetResizable(True)
|
|
111
|
+
self.setCentralWidget(self.scroll_area)
|
|
112
|
+
self.show()
|
|
113
|
+
|
|
114
|
+
QApplication.processEvents()
|
|
115
|
+
#self.adjustScrollArea()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def populate_reference_frame(self):
|
|
119
|
+
|
|
120
|
+
"""
|
|
121
|
+
Add widgets and layout in the reference population frame.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
grid = QVBoxLayout(self.reference_population_frame)
|
|
125
|
+
grid.setSpacing(15)
|
|
126
|
+
self.ref_lbl = QLabel("REFERENCE")
|
|
127
|
+
self.ref_lbl.setStyleSheet("""
|
|
128
|
+
font-weight: bold;
|
|
129
|
+
padding: 0px;
|
|
130
|
+
""")
|
|
131
|
+
grid.addWidget(self.ref_lbl, 30, alignment=Qt.AlignCenter)
|
|
132
|
+
self.generate_reference_contents()
|
|
133
|
+
grid.addWidget(self.ContentsReference, 70)
|
|
134
|
+
|
|
135
|
+
def populate_neigh_frame(self):
|
|
136
|
+
|
|
137
|
+
"""
|
|
138
|
+
Add widgets and layout in the neighbor population frame.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
grid = QVBoxLayout(self.neigh_population_frame)
|
|
142
|
+
grid.setSpacing(15)
|
|
143
|
+
|
|
144
|
+
self.neigh_lbl = QLabel("NEIGHBORS")
|
|
145
|
+
self.neigh_lbl.setStyleSheet("""
|
|
146
|
+
font-weight: bold;
|
|
147
|
+
padding: 0px;
|
|
148
|
+
""")
|
|
149
|
+
grid.addWidget(self.neigh_lbl, 30, alignment=Qt.AlignCenter)
|
|
150
|
+
self.generate_neighbors_contents()
|
|
151
|
+
grid.addWidget(self.ContentsNeigh, 70)
|
|
152
|
+
|
|
153
|
+
def populate_radii_frame(self):
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
Add widgets and layout in the radii frame.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
grid = QVBoxLayout(self.radii_frame)
|
|
160
|
+
|
|
161
|
+
self.dist_lbl = QLabel("NEIGHBORHOOD CUT-DISTANCES")
|
|
162
|
+
self.dist_lbl.setStyleSheet("""
|
|
163
|
+
font-weight: bold;
|
|
164
|
+
padding: 0px;
|
|
165
|
+
""")
|
|
166
|
+
grid.addWidget(self.dist_lbl, alignment=Qt.AlignCenter)
|
|
167
|
+
self.generate_radii_contents()
|
|
168
|
+
grid.addWidget(self.ContentsIso) #1, 0, 1, 4, alignment=Qt.AlignTop
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def generate_radii_contents(self):
|
|
172
|
+
|
|
173
|
+
self.ContentsIso = QFrame()
|
|
174
|
+
layout = QVBoxLayout(self.ContentsIso)
|
|
175
|
+
layout.setContentsMargins(0,0,0,0)
|
|
176
|
+
|
|
177
|
+
radii_layout = QHBoxLayout()
|
|
178
|
+
self.radii_lbl = QLabel('Cut-distance radii:')
|
|
179
|
+
self.radii_lbl.setToolTip('From reference cells, in pixel units. Define radii for neighborhood computations.')
|
|
180
|
+
radii_layout.addWidget(self.radii_lbl, 90)
|
|
181
|
+
|
|
182
|
+
self.del_radius_btn = QPushButton("")
|
|
183
|
+
self.del_radius_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
|
|
184
|
+
self.del_radius_btn.setIcon(icon(MDI6.trash_can,color="black"))
|
|
185
|
+
self.del_radius_btn.setToolTip("Remove radius")
|
|
186
|
+
self.del_radius_btn.setIconSize(QSize(20, 20))
|
|
187
|
+
radii_layout.addWidget(self.del_radius_btn, 5)
|
|
188
|
+
|
|
189
|
+
self.add_radius_btn = QPushButton("")
|
|
190
|
+
self.add_radius_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
|
|
191
|
+
self.add_radius_btn.setIcon(icon(MDI6.plus,color="black"))
|
|
192
|
+
self.add_radius_btn.setToolTip("Add radius")
|
|
193
|
+
self.add_radius_btn.setIconSize(QSize(20, 20))
|
|
194
|
+
radii_layout.addWidget(self.add_radius_btn, 5)
|
|
195
|
+
layout.addLayout(radii_layout)
|
|
196
|
+
|
|
197
|
+
self.radii_list = ListWidget(self, DistanceChoice, initial_features=["60"], dtype=int)
|
|
198
|
+
layout.addWidget(self.radii_list)
|
|
199
|
+
|
|
200
|
+
self.del_radius_btn.clicked.connect(self.radii_list.removeSel)
|
|
201
|
+
self.add_radius_btn.clicked.connect(self.radii_list.addItem)
|
|
202
|
+
|
|
203
|
+
def generate_reference_contents(self):
|
|
204
|
+
|
|
205
|
+
self.ContentsReference = QFrame()
|
|
206
|
+
layout = QVBoxLayout(self.ContentsReference)
|
|
207
|
+
layout.setContentsMargins(15,15,15,15)
|
|
208
|
+
|
|
209
|
+
pop_hbox = QHBoxLayout()
|
|
210
|
+
pop_hbox.addWidget(QLabel('population: '),30)
|
|
211
|
+
self.ref_pop_cb = QComboBox()
|
|
212
|
+
self.ref_pop_cb.addItems(['targets','effectors'])
|
|
213
|
+
pop_hbox.addWidget(self.ref_pop_cb,70)
|
|
214
|
+
layout.addLayout(pop_hbox)
|
|
215
|
+
|
|
216
|
+
status_hbox = QHBoxLayout()
|
|
217
|
+
status_hbox.addWidget(QLabel('status: '), 30)
|
|
218
|
+
self.ref_pop_status_cb = QComboBox()
|
|
219
|
+
#self.ref_pop_status_cb.addItems(['--'])
|
|
220
|
+
|
|
221
|
+
status_cb_hbox = QHBoxLayout()
|
|
222
|
+
status_cb_hbox.setContentsMargins(0,0,0,0)
|
|
223
|
+
status_cb_hbox.addWidget(self.ref_pop_status_cb,90)
|
|
224
|
+
# replace with not gate
|
|
225
|
+
|
|
226
|
+
self.ref_not_gate_btn = QPushButton("")
|
|
227
|
+
self.ref_not_gate_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
|
|
228
|
+
self.ref_not_gate_btn.setIcon(icon(MDI6.gate_not,color="black"))
|
|
229
|
+
self.ref_not_gate_btn.setToolTip("NOT (flip zeros and ones)")
|
|
230
|
+
self.ref_not_gate_btn.setIconSize(QSize(20, 20))
|
|
231
|
+
self.ref_not_gate_btn.clicked.connect(self.switch_not_reference)
|
|
232
|
+
status_cb_hbox.addWidget(self.ref_not_gate_btn, 5)
|
|
233
|
+
status_hbox.addLayout(status_cb_hbox,70)
|
|
234
|
+
layout.addLayout(status_hbox)
|
|
235
|
+
|
|
236
|
+
event_hbox = QHBoxLayout()
|
|
237
|
+
event_hbox.addWidget(QLabel('event time: '),30)
|
|
238
|
+
self.event_time_cb = QComboBox()
|
|
239
|
+
#self.event_time_cb.addItems(['--'])
|
|
240
|
+
event_hbox.addWidget(self.event_time_cb,70)
|
|
241
|
+
layout.addLayout(event_hbox)
|
|
242
|
+
|
|
243
|
+
self.set_combo_boxes_reference()
|
|
244
|
+
self.ref_pop_cb.currentIndexChanged.connect(self.set_combo_boxes_reference)
|
|
245
|
+
|
|
246
|
+
def switch_not_reference(self):
|
|
247
|
+
self.not_status_reference = not self.not_status_reference
|
|
248
|
+
if self.not_status_reference:
|
|
249
|
+
self.ref_not_gate_btn.setIcon(icon(MDI6.gate_not,color="#1565c0"))
|
|
250
|
+
self.ref_not_gate_btn.setIconSize(QSize(20, 20))
|
|
251
|
+
else:
|
|
252
|
+
self.ref_not_gate_btn.setIcon(icon(MDI6.gate_not,color="black"))
|
|
253
|
+
self.ref_not_gate_btn.setIconSize(QSize(20, 20))
|
|
254
|
+
|
|
255
|
+
def generate_neighbors_contents(self):
|
|
256
|
+
|
|
257
|
+
self.ContentsNeigh = QFrame()
|
|
258
|
+
layout = QVBoxLayout(self.ContentsNeigh)
|
|
259
|
+
layout.setContentsMargins(15,15,15,15)
|
|
260
|
+
|
|
261
|
+
pop_hbox = QHBoxLayout()
|
|
262
|
+
pop_hbox.addWidget(QLabel('population: '),30)
|
|
263
|
+
self.neigh_pop_cb = QComboBox()
|
|
264
|
+
self.neigh_pop_cb.addItems(['targets','effectors'])
|
|
265
|
+
self.neigh_pop_cb.currentIndexChanged.connect(self.set_combo_boxes_neigh)
|
|
266
|
+
|
|
267
|
+
pop_hbox.addWidget(self.neigh_pop_cb,70)
|
|
268
|
+
layout.addLayout(pop_hbox)
|
|
269
|
+
|
|
270
|
+
status_hbox = QHBoxLayout()
|
|
271
|
+
status_hbox.addWidget(QLabel('status: '),30)
|
|
272
|
+
self.neigh_pop_status_cb = QComboBox()
|
|
273
|
+
self.set_combo_boxes_neigh()
|
|
274
|
+
|
|
275
|
+
status_cb_hbox = QHBoxLayout()
|
|
276
|
+
status_cb_hbox.setContentsMargins(0,0,0,0)
|
|
277
|
+
status_cb_hbox.addWidget(self.neigh_pop_status_cb,90)
|
|
278
|
+
self.neigh_not_gate_btn = QPushButton("")
|
|
279
|
+
self.neigh_not_gate_btn.setStyleSheet(self.parent.parent.parent.button_select_all)
|
|
280
|
+
self.neigh_not_gate_btn.setIcon(icon(MDI6.gate_not,color="black"))
|
|
281
|
+
self.neigh_not_gate_btn.setToolTip("NOT (flip zeros and ones)")
|
|
282
|
+
self.neigh_not_gate_btn.setIconSize(QSize(20, 20))
|
|
283
|
+
self.neigh_not_gate_btn.clicked.connect(self.switch_not_neigh)
|
|
284
|
+
status_cb_hbox.addWidget(self.neigh_not_gate_btn, 5)
|
|
285
|
+
status_hbox.addLayout(status_cb_hbox, 70)
|
|
286
|
+
layout.addLayout(status_hbox)
|
|
287
|
+
|
|
288
|
+
self.cum_presence_btn = QCheckBox('cumulated presence')
|
|
289
|
+
layout.addWidget(self.cum_presence_btn)
|
|
290
|
+
|
|
291
|
+
self.symmetrize_btn = QCheckBox('symmetrize')
|
|
292
|
+
layout.addWidget(self.symmetrize_btn)
|
|
293
|
+
|
|
294
|
+
def switch_not_neigh(self):
|
|
295
|
+
self.not_status_neighbor = not self.not_status_neighbor
|
|
296
|
+
if self.not_status_neighbor:
|
|
297
|
+
self.neigh_not_gate_btn.setIcon(icon(MDI6.gate_not,color="#1565c0"))
|
|
298
|
+
self.neigh_not_gate_btn.setIconSize(QSize(20, 20))
|
|
299
|
+
else:
|
|
300
|
+
self.neigh_not_gate_btn.setIcon(icon(MDI6.gate_not,color="black"))
|
|
301
|
+
self.neigh_not_gate_btn.setIconSize(QSize(20, 20))
|
|
302
|
+
|
|
303
|
+
def set_combo_boxes_neigh(self):
|
|
304
|
+
pop = self.neigh_pop_cb.currentText()
|
|
305
|
+
class_cols, status_cols, _ = self.locate_population_columns(pop)
|
|
306
|
+
self.neigh_pop_status_cb.clear()
|
|
307
|
+
self.neigh_pop_status_cb.addItems(['--','class','status']+class_cols+status_cols)
|
|
308
|
+
|
|
309
|
+
def set_combo_boxes_reference(self):
|
|
310
|
+
pop = self.ref_pop_cb.currentText()
|
|
311
|
+
class_cols, status_cols, time_cols = self.locate_population_columns(pop)
|
|
312
|
+
self.ref_pop_status_cb.clear()
|
|
313
|
+
self.ref_pop_status_cb.addItems(['--','class', 'status']+class_cols+status_cols)
|
|
314
|
+
self.event_time_cb.addItems(['--', 't0']+time_cols)
|
|
315
|
+
|
|
316
|
+
def locate_population_columns(self, population):
|
|
317
|
+
|
|
318
|
+
# Look for all classes and times
|
|
319
|
+
tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv']))
|
|
320
|
+
self.all_columns = []
|
|
321
|
+
for tab in tables:
|
|
322
|
+
cols = pd.read_csv(tab, nrows=1).columns.tolist()
|
|
323
|
+
self.all_columns.extend(cols)
|
|
324
|
+
self.all_columns = np.unique(self.all_columns)
|
|
325
|
+
|
|
326
|
+
class_idx = np.array([s.startswith('class_') for s in self.all_columns])
|
|
327
|
+
status_idx = np.array([s.startswith('status_') for s in self.all_columns])
|
|
328
|
+
time_idx = np.array([s.startswith('t_') for s in self.all_columns])
|
|
329
|
+
|
|
330
|
+
if len(class_idx)>0:
|
|
331
|
+
class_columns = list(self.all_columns[class_idx])
|
|
332
|
+
for c in ['class_id', 'class_color']:
|
|
333
|
+
if c in class_columns:
|
|
334
|
+
class_columns.remove(c)
|
|
335
|
+
else:
|
|
336
|
+
class_columns = []
|
|
337
|
+
|
|
338
|
+
if len(status_idx)>0:
|
|
339
|
+
status_columns = list(self.all_columns[status_idx])
|
|
340
|
+
else:
|
|
341
|
+
status_columns = []
|
|
342
|
+
|
|
343
|
+
if len(time_idx)>0:
|
|
344
|
+
time_columns = list(self.all_columns[time_idx])
|
|
345
|
+
else:
|
|
346
|
+
time_columns = []
|
|
347
|
+
|
|
348
|
+
return class_columns, status_columns, time_columns
|
|
349
|
+
|
|
350
|
+
def write_instructions(self):
|
|
351
|
+
|
|
352
|
+
"""
|
|
353
|
+
Write the selected options in a json file for later reading by the software.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
print('Writing instructions...')
|
|
357
|
+
|
|
358
|
+
neighborhood_options = {}
|
|
359
|
+
pop = [self.ref_pop_cb.currentText(), self.neigh_pop_cb.currentText()]
|
|
360
|
+
neighborhood_options.update({'population': pop})
|
|
361
|
+
|
|
362
|
+
status_options = [self.ref_pop_status_cb.currentText(), self.neigh_pop_status_cb.currentText()]
|
|
363
|
+
for k in range(2):
|
|
364
|
+
if status_options[k]=='--':
|
|
365
|
+
status_options[k] = None
|
|
366
|
+
if pop[0]!=pop[1]:
|
|
367
|
+
mode = 'two-pop'
|
|
368
|
+
else:
|
|
369
|
+
mode = 'self'
|
|
370
|
+
|
|
371
|
+
distances = self.radii_list.getItems()
|
|
372
|
+
neighborhood_options.update({'distance': distances})
|
|
373
|
+
neighborhood_options.update({'clear_neigh': self.clear_previous_btn.isChecked()})
|
|
374
|
+
event_time_col = self.event_time_cb.currentText()
|
|
375
|
+
if event_time_col=='--':
|
|
376
|
+
event_time_col = None
|
|
377
|
+
neighborhood_options.update({'event_time_col': event_time_col})
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
neighborhood_kwargs = {'mode': mode, 'status': status_options, 'not_status_option': [self.not_status_reference, self.not_status_neighbor],
|
|
381
|
+
'compute_cum_sum': self.cum_presence_btn.isChecked(), 'attention_weight': True, 'symmetrize': self.symmetrize_btn.isChecked(),
|
|
382
|
+
'include_dead_weight': True}
|
|
383
|
+
|
|
384
|
+
neighborhood_options.update({'neighborhood_kwargs': neighborhood_kwargs})
|
|
385
|
+
|
|
386
|
+
print('Neighborhood instructions: ', neighborhood_options)
|
|
387
|
+
file_name = self.neigh_instructions
|
|
388
|
+
with open(file_name, 'w') as f:
|
|
389
|
+
json.dump(neighborhood_options, f, indent=4)
|
|
390
|
+
print('Done.')
|
|
391
|
+
self.close()
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def load_previous_neighborhood_instructions(self):
|
|
395
|
+
|
|
396
|
+
"""
|
|
397
|
+
Read the measurmeent options from a previously written json file and format properly for the UI.
|
|
398
|
+
"""
|
|
399
|
+
|
|
400
|
+
print('Reading instructions..')
|
|
401
|
+
if os.path.exists(self.neigh_instructions):
|
|
402
|
+
with open(self.neigh_instructions, 'r') as f:
|
|
403
|
+
neigh_instructions = json.load(f)
|
|
404
|
+
print(neigh_instructions)
|
|
405
|
+
|
|
406
|
+
if 'distance' in neigh_instructions:
|
|
407
|
+
distances = neigh_instructions['distance']
|
|
408
|
+
distances = [str(d) for d in distances]
|
|
409
|
+
self.radii_list.list_widget.clear()
|
|
410
|
+
self.radii_list.list_widget.addItems(distances)
|
|
411
|
+
|
|
412
|
+
if 'population' in neigh_instructions:
|
|
413
|
+
|
|
414
|
+
pop = neigh_instructions['population']
|
|
415
|
+
idx0 = self.ref_pop_cb.findText(pop[0])
|
|
416
|
+
self.ref_pop_cb.setCurrentIndex(idx0)
|
|
417
|
+
idx1 = self.neigh_pop_cb.findText(pop[1])
|
|
418
|
+
self.neigh_pop_cb.setCurrentIndex(idx1)
|
|
419
|
+
|
|
420
|
+
if 'clear_neigh' in neigh_instructions:
|
|
421
|
+
clear_neigh = neigh_instructions['clear_neigh']
|
|
422
|
+
self.clear_previous_btn.setChecked(clear_neigh)
|
|
423
|
+
|
|
424
|
+
if 'event_time_col' in neigh_instructions:
|
|
425
|
+
event_time_col = neigh_instructions['event_time_col']
|
|
426
|
+
if event_time_col is None:
|
|
427
|
+
event_time_col = '--'
|
|
428
|
+
idx = self.event_time_cb.findText(event_time_col)
|
|
429
|
+
self.event_time_cb.setCurrentIndex(idx)
|
|
430
|
+
|
|
431
|
+
if 'neighborhood_kwargs' in neigh_instructions:
|
|
432
|
+
neighborhood_kwargs = neigh_instructions['neighborhood_kwargs']
|
|
433
|
+
if 'compute_cum_sum' in neighborhood_kwargs:
|
|
434
|
+
self.cum_presence_btn.setChecked(neighborhood_kwargs['compute_cum_sum'])
|
|
435
|
+
if 'symmetrize' in neighborhood_kwargs:
|
|
436
|
+
self.symmetrize_btn.setChecked(neighborhood_kwargs['symmetrize'])
|
|
437
|
+
if 'status' in neighborhood_kwargs:
|
|
438
|
+
status_options = neighborhood_kwargs['status']
|
|
439
|
+
status_options = ['--' if s is None else s for s in status_options]
|
|
440
|
+
idx0 = self.ref_pop_status_cb.findText(status_options[0])
|
|
441
|
+
self.ref_pop_status_cb.setCurrentIndex(idx0)
|
|
442
|
+
idx1 = self.neigh_pop_status_cb.findText(status_options[1])
|
|
443
|
+
self.neigh_pop_status_cb.setCurrentIndex(idx1)
|
|
444
|
+
if 'not_status_option' in neighborhood_kwargs:
|
|
445
|
+
not_status_option = neighborhood_kwargs['not_status_option']
|
|
446
|
+
if not_status_option[0]:
|
|
447
|
+
self.ref_not_gate_btn.click()
|
|
448
|
+
if not_status_option[1]:
|
|
449
|
+
self.neigh_not_gate_btn.click()
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|