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.
Files changed (66) hide show
  1. celldetective/__init__.py +2 -0
  2. celldetective/__main__.py +432 -0
  3. celldetective/datasets/segmentation_annotations/blank +0 -0
  4. celldetective/datasets/signal_annotations/blank +0 -0
  5. celldetective/events.py +149 -0
  6. celldetective/extra_properties.py +100 -0
  7. celldetective/filters.py +89 -0
  8. celldetective/gui/__init__.py +20 -0
  9. celldetective/gui/about.py +44 -0
  10. celldetective/gui/analyze_block.py +563 -0
  11. celldetective/gui/btrack_options.py +898 -0
  12. celldetective/gui/classifier_widget.py +386 -0
  13. celldetective/gui/configure_new_exp.py +532 -0
  14. celldetective/gui/control_panel.py +438 -0
  15. celldetective/gui/gui_utils.py +495 -0
  16. celldetective/gui/json_readers.py +113 -0
  17. celldetective/gui/measurement_options.py +1425 -0
  18. celldetective/gui/neighborhood_options.py +452 -0
  19. celldetective/gui/plot_signals_ui.py +1042 -0
  20. celldetective/gui/process_block.py +1055 -0
  21. celldetective/gui/retrain_segmentation_model_options.py +706 -0
  22. celldetective/gui/retrain_signal_model_options.py +643 -0
  23. celldetective/gui/seg_model_loader.py +460 -0
  24. celldetective/gui/signal_annotator.py +2388 -0
  25. celldetective/gui/signal_annotator_options.py +340 -0
  26. celldetective/gui/styles.py +217 -0
  27. celldetective/gui/survival_ui.py +903 -0
  28. celldetective/gui/tableUI.py +608 -0
  29. celldetective/gui/thresholds_gui.py +1300 -0
  30. celldetective/icons/logo-large.png +0 -0
  31. celldetective/icons/logo.png +0 -0
  32. celldetective/icons/signals_icon.png +0 -0
  33. celldetective/icons/splash-test.png +0 -0
  34. celldetective/icons/splash.png +0 -0
  35. celldetective/icons/splash0.png +0 -0
  36. celldetective/icons/survival2.png +0 -0
  37. celldetective/icons/vignette_signals2.png +0 -0
  38. celldetective/icons/vignette_signals2.svg +114 -0
  39. celldetective/io.py +2050 -0
  40. celldetective/links/zenodo.json +561 -0
  41. celldetective/measure.py +1258 -0
  42. celldetective/models/segmentation_effectors/blank +0 -0
  43. celldetective/models/segmentation_generic/blank +0 -0
  44. celldetective/models/segmentation_targets/blank +0 -0
  45. celldetective/models/signal_detection/blank +0 -0
  46. celldetective/models/tracking_configs/mcf7.json +68 -0
  47. celldetective/models/tracking_configs/ricm.json +203 -0
  48. celldetective/models/tracking_configs/ricm2.json +203 -0
  49. celldetective/neighborhood.py +717 -0
  50. celldetective/scripts/analyze_signals.py +51 -0
  51. celldetective/scripts/measure_cells.py +275 -0
  52. celldetective/scripts/segment_cells.py +212 -0
  53. celldetective/scripts/segment_cells_thresholds.py +140 -0
  54. celldetective/scripts/track_cells.py +206 -0
  55. celldetective/scripts/train_segmentation_model.py +246 -0
  56. celldetective/scripts/train_signal_model.py +49 -0
  57. celldetective/segmentation.py +712 -0
  58. celldetective/signals.py +2826 -0
  59. celldetective/tracking.py +974 -0
  60. celldetective/utils.py +1681 -0
  61. celldetective-1.0.2.dist-info/LICENSE +674 -0
  62. celldetective-1.0.2.dist-info/METADATA +192 -0
  63. celldetective-1.0.2.dist-info/RECORD +66 -0
  64. celldetective-1.0.2.dist-info/WHEEL +5 -0
  65. celldetective-1.0.2.dist-info/entry_points.txt +2 -0
  66. 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()