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,495 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from PyQt5.QtWidgets import QApplication, QFrame, QSizePolicy, QWidget, QLineEdit, QListWidget, QVBoxLayout, QComboBox, \
|
|
3
|
+
QPushButton, QLabel, QHBoxLayout, QCheckBox
|
|
4
|
+
from PyQt5.QtCore import QEvent
|
|
5
|
+
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
|
|
6
|
+
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
import celldetective.extra_properties as extra_properties
|
|
9
|
+
from inspect import getmembers, isfunction
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def center_window(window):
|
|
13
|
+
"""
|
|
14
|
+
Center window in the middle of the screen.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
frameGm = window.frameGeometry()
|
|
18
|
+
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
|
|
19
|
+
centerPoint = QApplication.desktop().screenGeometry(screen).center()
|
|
20
|
+
frameGm.moveCenter(centerPoint)
|
|
21
|
+
window.move(frameGm.topLeft())
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class QHSeperationLine(QFrame):
|
|
25
|
+
'''
|
|
26
|
+
a horizontal seperation line\n
|
|
27
|
+
'''
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
super().__init__()
|
|
31
|
+
self.setMinimumWidth(1)
|
|
32
|
+
self.setFixedHeight(20)
|
|
33
|
+
self.setFrameShape(QFrame.HLine)
|
|
34
|
+
self.setFrameShadow(QFrame.Sunken)
|
|
35
|
+
self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class FeatureChoice(QWidget):
|
|
39
|
+
|
|
40
|
+
def __init__(self, parent):
|
|
41
|
+
super().__init__()
|
|
42
|
+
self.parent = parent
|
|
43
|
+
self.setWindowTitle("Add feature")
|
|
44
|
+
# Create the QComboBox and add some items
|
|
45
|
+
self.combo_box = QComboBox(self)
|
|
46
|
+
center_window(self)
|
|
47
|
+
|
|
48
|
+
standard_measurements = ["area",
|
|
49
|
+
"area_bbox",
|
|
50
|
+
"area_convex",
|
|
51
|
+
"area_filled",
|
|
52
|
+
"major_axis_length",
|
|
53
|
+
"minor_axis_length",
|
|
54
|
+
"eccentricity",
|
|
55
|
+
"equivalent_diameter_area",
|
|
56
|
+
"euler_number",
|
|
57
|
+
"extent",
|
|
58
|
+
"feret_diameter_max",
|
|
59
|
+
"orientation",
|
|
60
|
+
"perimeter",
|
|
61
|
+
"perimeter_crofton",
|
|
62
|
+
"solidity",
|
|
63
|
+
"intensity_mean",
|
|
64
|
+
"intensity_max",
|
|
65
|
+
"intensity_min",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
extra_props = getmembers(extra_properties, isfunction)
|
|
69
|
+
extra_props = [extra_props[i][0] for i in range(len(extra_props))]
|
|
70
|
+
if len(extra_props) > 0:
|
|
71
|
+
standard_measurements.extend(extra_props)
|
|
72
|
+
|
|
73
|
+
self.combo_box.addItems(standard_measurements)
|
|
74
|
+
|
|
75
|
+
self.add_btn = QPushButton("Add")
|
|
76
|
+
self.add_btn.clicked.connect(self.add_current_feature)
|
|
77
|
+
|
|
78
|
+
# Create the layout
|
|
79
|
+
layout = QVBoxLayout(self)
|
|
80
|
+
layout.addWidget(self.combo_box)
|
|
81
|
+
layout.addWidget(self.add_btn)
|
|
82
|
+
|
|
83
|
+
def add_current_feature(self):
|
|
84
|
+
filtername = self.combo_box.currentText()
|
|
85
|
+
self.parent.list_widget.addItems([filtername])
|
|
86
|
+
self.close()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FilterChoice(QWidget):
|
|
90
|
+
|
|
91
|
+
def __init__(self, parent):
|
|
92
|
+
|
|
93
|
+
super().__init__()
|
|
94
|
+
self.parent = parent
|
|
95
|
+
self.setWindowTitle("Add filter")
|
|
96
|
+
# Create the QComboBox and add some items
|
|
97
|
+
center_window(self)
|
|
98
|
+
|
|
99
|
+
self.default_params = {
|
|
100
|
+
'gauss_filter': {'sigma': 2},
|
|
101
|
+
'median_filter': {'size': 4},
|
|
102
|
+
'maximum_filter': {'size': 4},
|
|
103
|
+
'minimum_filter': {'size': 4},
|
|
104
|
+
'percentile_filter': {'percentile': 99, 'size': 4},
|
|
105
|
+
'variance_filter': {'size': 4},
|
|
106
|
+
'std_filter': {'size': 4},
|
|
107
|
+
'laplace_filter': None,
|
|
108
|
+
'abs_filter': None,
|
|
109
|
+
'ln_filter': None,
|
|
110
|
+
'subtract_filter': {'value': 1},
|
|
111
|
+
'dog_filter': {'sigma_low': 0.8, 'sigma_high': 1.6},
|
|
112
|
+
'log_filter': {'sigma': 2},
|
|
113
|
+
'tophat_filter': {'size': 4, 'connectivity': 4},
|
|
114
|
+
'otsu_filter': None,
|
|
115
|
+
'local_filter': {'block_size': 73, 'method': 'mean', 'offset': 0},
|
|
116
|
+
'niblack_filter': {'window_size': 15, 'k': 0.2},
|
|
117
|
+
# 'sauvola_filter': {'window_size': 15, 'k': 0.2}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
layout = QVBoxLayout(self)
|
|
121
|
+
self.combo_box = QComboBox(self)
|
|
122
|
+
self.combo_box.addItems(list(self.default_params.keys()))
|
|
123
|
+
self.combo_box.currentTextChanged.connect(self.update_arguments)
|
|
124
|
+
layout.addWidget(self.combo_box)
|
|
125
|
+
|
|
126
|
+
self.arguments_le = [QLineEdit() for i in range(3)]
|
|
127
|
+
self.arguments_labels = [QLabel('') for i in range(3)]
|
|
128
|
+
for i in range(2):
|
|
129
|
+
hbox = QHBoxLayout()
|
|
130
|
+
hbox.addWidget(self.arguments_labels[i], 20)
|
|
131
|
+
hbox.addWidget(self.arguments_le[i], 80)
|
|
132
|
+
layout.addLayout(hbox)
|
|
133
|
+
|
|
134
|
+
self.add_btn = QPushButton("Add")
|
|
135
|
+
self.add_btn.clicked.connect(self.add_current_feature)
|
|
136
|
+
layout.addWidget(self.add_btn)
|
|
137
|
+
|
|
138
|
+
self.combo_box.setCurrentIndex(0)
|
|
139
|
+
self.update_arguments()
|
|
140
|
+
|
|
141
|
+
def add_current_feature(self):
|
|
142
|
+
|
|
143
|
+
filtername = self.combo_box.currentText()
|
|
144
|
+
self.parent.list_widget.addItems([filtername])
|
|
145
|
+
|
|
146
|
+
filter_instructions = [filtername.split('_')[0]]
|
|
147
|
+
for a in self.arguments_le:
|
|
148
|
+
arg = a.text()
|
|
149
|
+
arg_num = arg
|
|
150
|
+
if (arg != '') and arg_num.replace('.', '').replace(',', '').isnumeric():
|
|
151
|
+
num = float(arg)
|
|
152
|
+
if num.is_integer():
|
|
153
|
+
num = int(num)
|
|
154
|
+
filter_instructions.append(num)
|
|
155
|
+
elif arg != '':
|
|
156
|
+
filter_instructions.append(arg)
|
|
157
|
+
|
|
158
|
+
print(f'You added filter {filter_instructions}.')
|
|
159
|
+
|
|
160
|
+
self.parent.items.append(filter_instructions)
|
|
161
|
+
self.close()
|
|
162
|
+
|
|
163
|
+
def update_arguments(self):
|
|
164
|
+
|
|
165
|
+
selected_filter = self.combo_box.currentText()
|
|
166
|
+
arguments = self.default_params[selected_filter]
|
|
167
|
+
if arguments is not None:
|
|
168
|
+
args = list(arguments.keys())
|
|
169
|
+
for i in range(len(args)):
|
|
170
|
+
self.arguments_labels[i].setEnabled(True)
|
|
171
|
+
self.arguments_le[i].setEnabled(True)
|
|
172
|
+
|
|
173
|
+
self.arguments_labels[i].setText(args[i])
|
|
174
|
+
self.arguments_le[i].setText(str(arguments[args[i]]))
|
|
175
|
+
|
|
176
|
+
if len(args) < 2:
|
|
177
|
+
for i in range(len(args), 2):
|
|
178
|
+
self.arguments_labels[i].setEnabled(False)
|
|
179
|
+
self.arguments_labels[i].setText('')
|
|
180
|
+
self.arguments_le[i].setEnabled(False)
|
|
181
|
+
else:
|
|
182
|
+
for i in range(2):
|
|
183
|
+
self.arguments_labels[i].setEnabled(False)
|
|
184
|
+
self.arguments_le[i].setEnabled(False)
|
|
185
|
+
self.arguments_labels[i].setText('')
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class OperationChoice(QWidget):
|
|
189
|
+
"""
|
|
190
|
+
Mini window to select an operation from numpy to apply on the ROI.
|
|
191
|
+
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
def __init__(self, parent):
|
|
195
|
+
super().__init__()
|
|
196
|
+
self.parent = parent
|
|
197
|
+
self.setWindowTitle("Add feature")
|
|
198
|
+
# Create the QComboBox and add some items
|
|
199
|
+
self.combo_box = QComboBox(self)
|
|
200
|
+
center_window(self)
|
|
201
|
+
|
|
202
|
+
self.combo_box.addItems(["mean", "median", "average", "std", "var",
|
|
203
|
+
"nanmedian", "nanmean", "nanstd", "nanvar"])
|
|
204
|
+
|
|
205
|
+
self.add_btn = QPushButton("Add")
|
|
206
|
+
self.add_btn.clicked.connect(self.add_current_feature)
|
|
207
|
+
|
|
208
|
+
# Create the layout
|
|
209
|
+
layout = QVBoxLayout(self)
|
|
210
|
+
layout.addWidget(self.combo_box)
|
|
211
|
+
layout.addWidget(self.add_btn)
|
|
212
|
+
|
|
213
|
+
def add_current_feature(self):
|
|
214
|
+
filtername = self.combo_box.currentText()
|
|
215
|
+
self.parent.list_widget.addItems([filtername])
|
|
216
|
+
self.close()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class GeometryChoice(QWidget):
|
|
220
|
+
|
|
221
|
+
def __init__(self, parent):
|
|
222
|
+
|
|
223
|
+
super().__init__()
|
|
224
|
+
self.parent = parent
|
|
225
|
+
self.setWindowTitle("Set distances")
|
|
226
|
+
center_window(self)
|
|
227
|
+
|
|
228
|
+
# Create the QComboBox and add some items
|
|
229
|
+
|
|
230
|
+
self.dist_label = QLabel('Distance [px]: ')
|
|
231
|
+
self.dist_le = QLineEdit('10')
|
|
232
|
+
|
|
233
|
+
self.dist_outer_label = QLabel('Max distance [px]')
|
|
234
|
+
self.dist_outer_le = QLineEdit('100')
|
|
235
|
+
self.outer_to_hide = [self.dist_outer_le, self.dist_outer_label]
|
|
236
|
+
|
|
237
|
+
self.outer_btn = QCheckBox('outer distance')
|
|
238
|
+
self.outer_btn.clicked.connect(self.activate_outer_value)
|
|
239
|
+
|
|
240
|
+
self.add_btn = QPushButton("Add")
|
|
241
|
+
self.add_btn.clicked.connect(self.add_current_feature)
|
|
242
|
+
|
|
243
|
+
# Create the layout
|
|
244
|
+
layout = QVBoxLayout(self)
|
|
245
|
+
dist_layout = QHBoxLayout()
|
|
246
|
+
dist_layout.addWidget(self.dist_label, 30)
|
|
247
|
+
dist_layout.addWidget(self.dist_le, 70)
|
|
248
|
+
|
|
249
|
+
self.dist_outer_layout = QHBoxLayout()
|
|
250
|
+
self.dist_outer_layout.addWidget(self.dist_outer_label, 30)
|
|
251
|
+
self.dist_outer_layout.addWidget(self.dist_outer_le, 70)
|
|
252
|
+
|
|
253
|
+
layout.addLayout(dist_layout)
|
|
254
|
+
layout.addLayout(self.dist_outer_layout)
|
|
255
|
+
layout.addWidget(self.outer_btn)
|
|
256
|
+
layout.addWidget(self.add_btn)
|
|
257
|
+
|
|
258
|
+
for el in self.outer_to_hide:
|
|
259
|
+
el.hide()
|
|
260
|
+
|
|
261
|
+
def activate_outer_value(self):
|
|
262
|
+
if self.outer_btn.isChecked():
|
|
263
|
+
self.dist_label.setText('Min distance [px]: ')
|
|
264
|
+
for el in self.outer_to_hide:
|
|
265
|
+
el.show()
|
|
266
|
+
else:
|
|
267
|
+
self.dist_label.setText('Distance [px]: ')
|
|
268
|
+
for el in self.outer_to_hide:
|
|
269
|
+
el.hide()
|
|
270
|
+
|
|
271
|
+
def add_current_feature(self):
|
|
272
|
+
|
|
273
|
+
value = self.dist_le.text()
|
|
274
|
+
if self.outer_btn.isChecked():
|
|
275
|
+
value2 = self.dist_outer_le.text()
|
|
276
|
+
values = [value + '-' + value2]
|
|
277
|
+
else:
|
|
278
|
+
values = [value]
|
|
279
|
+
self.parent.list_widget.addItems(values)
|
|
280
|
+
self.close()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class DistanceChoice(QWidget):
|
|
284
|
+
|
|
285
|
+
def __init__(self, parent):
|
|
286
|
+
super().__init__()
|
|
287
|
+
self.parent = parent
|
|
288
|
+
self.setWindowTitle("Set distances")
|
|
289
|
+
center_window(self)
|
|
290
|
+
|
|
291
|
+
# Create the QComboBox and add some items
|
|
292
|
+
|
|
293
|
+
self.dist_label = QLabel('Distance [px]: ')
|
|
294
|
+
self.dist_le = QLineEdit('10')
|
|
295
|
+
|
|
296
|
+
self.add_btn = QPushButton("Add")
|
|
297
|
+
self.add_btn.clicked.connect(self.add_current_feature)
|
|
298
|
+
|
|
299
|
+
# Create the layout
|
|
300
|
+
layout = QVBoxLayout(self)
|
|
301
|
+
dist_layout = QHBoxLayout()
|
|
302
|
+
dist_layout.addWidget(self.dist_label, 30)
|
|
303
|
+
dist_layout.addWidget(self.dist_le, 70)
|
|
304
|
+
|
|
305
|
+
layout.addLayout(dist_layout)
|
|
306
|
+
layout.addWidget(self.add_btn)
|
|
307
|
+
|
|
308
|
+
def add_current_feature(self):
|
|
309
|
+
value = self.dist_le.text()
|
|
310
|
+
values = [value]
|
|
311
|
+
self.parent.list_widget.addItems(values)
|
|
312
|
+
self.close()
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class ListWidget(QWidget):
|
|
316
|
+
"""
|
|
317
|
+
Generic list widget.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def __init__(self, parent, choiceWidget, initial_features, dtype=str, channel_names=None):
|
|
321
|
+
|
|
322
|
+
super().__init__()
|
|
323
|
+
self.parent = parent
|
|
324
|
+
self.initial_features = initial_features
|
|
325
|
+
self.choiceWidget = choiceWidget
|
|
326
|
+
self.dtype = dtype
|
|
327
|
+
self.items = []
|
|
328
|
+
self.channel_names=channel_names
|
|
329
|
+
|
|
330
|
+
self.setFixedHeight(80)
|
|
331
|
+
|
|
332
|
+
# Initialize list widget
|
|
333
|
+
self.list_widget = QListWidget()
|
|
334
|
+
self.list_widget.addItems(initial_features)
|
|
335
|
+
|
|
336
|
+
# Set up layout
|
|
337
|
+
main_layout = QVBoxLayout()
|
|
338
|
+
main_layout.addWidget(self.list_widget)
|
|
339
|
+
self.setLayout(main_layout)
|
|
340
|
+
|
|
341
|
+
def addItem(self):
|
|
342
|
+
|
|
343
|
+
"""
|
|
344
|
+
Add a new item.
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
self.addItemWindow = self.choiceWidget(self)
|
|
348
|
+
self.addItemWindow.show()
|
|
349
|
+
|
|
350
|
+
def getItems(self):
|
|
351
|
+
|
|
352
|
+
"""
|
|
353
|
+
Get all the items as a list.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
items = []
|
|
357
|
+
for x in range(self.list_widget.count()):
|
|
358
|
+
if len(self.list_widget.item(x).text().split('-')) == 2:
|
|
359
|
+
if self.list_widget.item(x).text()[0] == '-':
|
|
360
|
+
items.append(self.dtype(self.list_widget.item(x).text()))
|
|
361
|
+
else:
|
|
362
|
+
minn, maxx = self.list_widget.item(x).text().split('-')
|
|
363
|
+
to_add = [self.dtype(minn), self.dtype(maxx)]
|
|
364
|
+
items.append(to_add)
|
|
365
|
+
else:
|
|
366
|
+
items.append(self.dtype(self.list_widget.item(x).text()))
|
|
367
|
+
return items
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def removeSel(self):
|
|
372
|
+
|
|
373
|
+
"""
|
|
374
|
+
Remove selected items.
|
|
375
|
+
"""
|
|
376
|
+
|
|
377
|
+
listItems = self.list_widget.selectedItems()
|
|
378
|
+
if not listItems: return
|
|
379
|
+
for item in listItems:
|
|
380
|
+
idx = self.list_widget.row(item)
|
|
381
|
+
self.list_widget.takeItem(idx)
|
|
382
|
+
if self.items:
|
|
383
|
+
del self.items[idx]
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class FigureCanvas(QWidget):
|
|
387
|
+
"""
|
|
388
|
+
Generic figure canvas.
|
|
389
|
+
"""
|
|
390
|
+
|
|
391
|
+
def __init__(self, fig, title="", interactive=True):
|
|
392
|
+
super().__init__()
|
|
393
|
+
self.fig = fig
|
|
394
|
+
self.setWindowTitle(title)
|
|
395
|
+
center_window(self)
|
|
396
|
+
self.canvas = FigureCanvasQTAgg(self.fig)
|
|
397
|
+
self.canvas.setStyleSheet("background-color: transparent;")
|
|
398
|
+
if interactive:
|
|
399
|
+
self.toolbar = NavigationToolbar2QT(self.canvas)
|
|
400
|
+
self.layout = QVBoxLayout(self)
|
|
401
|
+
self.layout.addWidget(self.canvas)
|
|
402
|
+
if interactive:
|
|
403
|
+
self.layout.addWidget(self.toolbar)
|
|
404
|
+
|
|
405
|
+
def closeEvent(self, event):
|
|
406
|
+
""" Delete figure on closing window. """
|
|
407
|
+
# self.canvas.ax.cla() # ****
|
|
408
|
+
self.fig.clf() # ****
|
|
409
|
+
plt.close(self.fig)
|
|
410
|
+
super(FigureCanvas, self).closeEvent(event)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def color_from_status(status, recently_modified=False):
|
|
414
|
+
if not recently_modified:
|
|
415
|
+
if status == 0:
|
|
416
|
+
return 'tab:blue'
|
|
417
|
+
elif status == 1:
|
|
418
|
+
return 'tab:red'
|
|
419
|
+
elif status == 2:
|
|
420
|
+
return 'yellow'
|
|
421
|
+
else:
|
|
422
|
+
return 'k'
|
|
423
|
+
else:
|
|
424
|
+
if status == 0:
|
|
425
|
+
return 'tab:cyan'
|
|
426
|
+
elif status == 1:
|
|
427
|
+
return 'tab:orange'
|
|
428
|
+
elif status == 2:
|
|
429
|
+
return 'tab:olive'
|
|
430
|
+
else:
|
|
431
|
+
return 'k'
|
|
432
|
+
|
|
433
|
+
def color_from_state(state, recently_modified=False):
|
|
434
|
+
unique_values = np.unique(state)
|
|
435
|
+
color_map={}
|
|
436
|
+
for value in unique_values:
|
|
437
|
+
color_map[value] = plt.cm.tab10(value)
|
|
438
|
+
if value == 99:
|
|
439
|
+
color_map[value] = 'k'
|
|
440
|
+
# colors = plt.cm.tab10(len(unique_values))
|
|
441
|
+
# color_map = dict(zip(unique_values, colors))
|
|
442
|
+
# print(color_map)
|
|
443
|
+
return color_map
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def color_from_class(cclass, recently_modified=False):
|
|
449
|
+
if not recently_modified:
|
|
450
|
+
if cclass == 0:
|
|
451
|
+
return 'tab:red'
|
|
452
|
+
elif cclass == 1:
|
|
453
|
+
return 'tab:blue'
|
|
454
|
+
elif cclass == 2:
|
|
455
|
+
return 'yellow'
|
|
456
|
+
else:
|
|
457
|
+
return 'k'
|
|
458
|
+
else:
|
|
459
|
+
if cclass == 0:
|
|
460
|
+
return 'tab:orange'
|
|
461
|
+
elif cclass == 1:
|
|
462
|
+
return 'tab:cyan'
|
|
463
|
+
elif cclass == 2:
|
|
464
|
+
return 'tab:olive'
|
|
465
|
+
else:
|
|
466
|
+
return 'k'
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
class ChannelChoice(QWidget):
|
|
470
|
+
|
|
471
|
+
def __init__(self, parent):
|
|
472
|
+
super().__init__()
|
|
473
|
+
self.parent = parent
|
|
474
|
+
#self.channel_names = channel_names
|
|
475
|
+
self.setWindowTitle("Choose target channel")
|
|
476
|
+
# Create the QComboBox and add some items
|
|
477
|
+
self.combo_box = QComboBox(self)
|
|
478
|
+
center_window(self)
|
|
479
|
+
|
|
480
|
+
channels = parent.channel_names
|
|
481
|
+
|
|
482
|
+
self.combo_box.addItems(channels)
|
|
483
|
+
|
|
484
|
+
self.add_btn = QPushButton("Add")
|
|
485
|
+
self.add_btn.clicked.connect(self.add_current_channel)
|
|
486
|
+
|
|
487
|
+
# Create the layout
|
|
488
|
+
layout = QVBoxLayout(self)
|
|
489
|
+
layout.addWidget(self.combo_box)
|
|
490
|
+
layout.addWidget(self.add_btn)
|
|
491
|
+
|
|
492
|
+
def add_current_channel(self):
|
|
493
|
+
filtername = self.combo_box.currentText()
|
|
494
|
+
self.parent.list_widget.addItems([filtername])
|
|
495
|
+
self.close()
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import configparser
|
|
3
|
+
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QScrollArea, QLabel, QHBoxLayout, QLineEdit, QPushButton
|
|
4
|
+
from PyQt5.QtCore import Qt
|
|
5
|
+
import sys
|
|
6
|
+
import configparser
|
|
7
|
+
|
|
8
|
+
class ConfigEditor(QWidget):
|
|
9
|
+
|
|
10
|
+
def __init__(self, parent):
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
Load and edit the experiment config.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
self.parent = parent
|
|
19
|
+
self.config_path = self.parent.exp_config
|
|
20
|
+
|
|
21
|
+
self.setGeometry(500,200,400,700)
|
|
22
|
+
|
|
23
|
+
self.setWindowTitle("Configuration")
|
|
24
|
+
|
|
25
|
+
# Create the main layout
|
|
26
|
+
self.layout = QVBoxLayout()
|
|
27
|
+
|
|
28
|
+
# Create a scroll area to contain the main layout
|
|
29
|
+
scroll = QScrollArea()
|
|
30
|
+
scroll.setWidgetResizable(True)
|
|
31
|
+
scroll_content = QWidget()
|
|
32
|
+
self.scroll_layout = QVBoxLayout(scroll_content)
|
|
33
|
+
scroll_content.setLayout(self.scroll_layout)
|
|
34
|
+
scroll.setWidget(scroll_content)
|
|
35
|
+
|
|
36
|
+
# Add a label for each section of the config file
|
|
37
|
+
self.labels = []
|
|
38
|
+
self.edit_boxes = []
|
|
39
|
+
self.save_button = None
|
|
40
|
+
self.sections = {}
|
|
41
|
+
|
|
42
|
+
# Set the main layout
|
|
43
|
+
self.scroll_layout.addStretch()
|
|
44
|
+
self.layout.addWidget(scroll)
|
|
45
|
+
self.setLayout(self.layout)
|
|
46
|
+
|
|
47
|
+
self.load_config()
|
|
48
|
+
|
|
49
|
+
def load_config(self):
|
|
50
|
+
file_name = self.config_path
|
|
51
|
+
#self.file_edit.setText(file_name)
|
|
52
|
+
|
|
53
|
+
config = configparser.ConfigParser()
|
|
54
|
+
config.read(file_name)
|
|
55
|
+
|
|
56
|
+
# Create a layout for each section of the config file
|
|
57
|
+
for section in config.sections():
|
|
58
|
+
section_layout = QVBoxLayout()
|
|
59
|
+
section_label = QLabel('[{}]'.format(section))
|
|
60
|
+
self.labels.append(section_label)
|
|
61
|
+
|
|
62
|
+
# Create an editor box for each parameter in the section
|
|
63
|
+
for key, value in config.items(section):
|
|
64
|
+
edit_box_layout = QHBoxLayout()
|
|
65
|
+
label = QLabel(key)
|
|
66
|
+
edit_box = QLineEdit(value)
|
|
67
|
+
edit_box_layout.addWidget(label)
|
|
68
|
+
edit_box_layout.addWidget(edit_box)
|
|
69
|
+
section_layout.addLayout(edit_box_layout)
|
|
70
|
+
|
|
71
|
+
self.edit_boxes.append(edit_box)
|
|
72
|
+
self.sections[key] = (section, edit_box)
|
|
73
|
+
|
|
74
|
+
# Add the section label and editor boxes to the main layout
|
|
75
|
+
self.scroll_layout.addWidget(section_label)
|
|
76
|
+
self.scroll_layout.addLayout(section_layout)
|
|
77
|
+
|
|
78
|
+
# Add a save button
|
|
79
|
+
save_layout = QHBoxLayout()
|
|
80
|
+
save_button = QPushButton('Save')
|
|
81
|
+
save_button.setStyleSheet(self.parent.parent.button_style_sheet)
|
|
82
|
+
save_button.clicked.connect(self.save_config)
|
|
83
|
+
save_button.setShortcut("Return")
|
|
84
|
+
#save_button.setIcon(QIcon_from_svg(self.parent.abs_path+f"/icons/save.svg", color='white'))
|
|
85
|
+
|
|
86
|
+
#save_layout.addStretch()
|
|
87
|
+
save_layout.addWidget(save_button, alignment=Qt.AlignTop)
|
|
88
|
+
|
|
89
|
+
# Add the save button to the main layout
|
|
90
|
+
self.layout.addLayout(save_layout)
|
|
91
|
+
self.save_button = save_button
|
|
92
|
+
|
|
93
|
+
def save_config(self):
|
|
94
|
+
# Save the configuration to the file
|
|
95
|
+
file_name = self.config_path
|
|
96
|
+
|
|
97
|
+
config = configparser.ConfigParser()
|
|
98
|
+
|
|
99
|
+
# Update the values in the config object
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
for key, (section, edit_box) in self.sections.items():
|
|
104
|
+
if not config.has_section(section):
|
|
105
|
+
config.add_section(section)
|
|
106
|
+
config.set(section, key, edit_box.text())
|
|
107
|
+
|
|
108
|
+
# Write the config object to the file
|
|
109
|
+
with open(file_name, 'w') as f:
|
|
110
|
+
config.write(f)
|
|
111
|
+
|
|
112
|
+
self.parent.load_configuration()
|
|
113
|
+
self.close()
|