cellects 0.1.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 (44) hide show
  1. cellects/__init__.py +0 -0
  2. cellects/__main__.py +49 -0
  3. cellects/config/__init__.py +0 -0
  4. cellects/config/all_vars_dict.py +155 -0
  5. cellects/core/__init__.py +0 -0
  6. cellects/core/cellects_paths.py +31 -0
  7. cellects/core/cellects_threads.py +1451 -0
  8. cellects/core/motion_analysis.py +2010 -0
  9. cellects/core/one_image_analysis.py +1061 -0
  10. cellects/core/one_video_per_blob.py +540 -0
  11. cellects/core/program_organizer.py +1316 -0
  12. cellects/core/script_based_run.py +154 -0
  13. cellects/gui/__init__.py +0 -0
  14. cellects/gui/advanced_parameters.py +1258 -0
  15. cellects/gui/cellects.py +189 -0
  16. cellects/gui/custom_widgets.py +790 -0
  17. cellects/gui/first_window.py +449 -0
  18. cellects/gui/if_several_folders_window.py +239 -0
  19. cellects/gui/image_analysis_window.py +2066 -0
  20. cellects/gui/required_output.py +232 -0
  21. cellects/gui/video_analysis_window.py +656 -0
  22. cellects/icons/__init__.py +0 -0
  23. cellects/icons/cellects_icon.icns +0 -0
  24. cellects/icons/cellects_icon.ico +0 -0
  25. cellects/image_analysis/__init__.py +0 -0
  26. cellects/image_analysis/cell_leaving_detection.py +54 -0
  27. cellects/image_analysis/cluster_flux_study.py +102 -0
  28. cellects/image_analysis/image_segmentation.py +706 -0
  29. cellects/image_analysis/morphological_operations.py +1635 -0
  30. cellects/image_analysis/network_functions.py +1757 -0
  31. cellects/image_analysis/one_image_analysis_threads.py +289 -0
  32. cellects/image_analysis/progressively_add_distant_shapes.py +508 -0
  33. cellects/image_analysis/shape_descriptors.py +1016 -0
  34. cellects/utils/__init__.py +0 -0
  35. cellects/utils/decorators.py +14 -0
  36. cellects/utils/formulas.py +637 -0
  37. cellects/utils/load_display_save.py +1054 -0
  38. cellects/utils/utilitarian.py +490 -0
  39. cellects-0.1.2.dist-info/LICENSE.odt +0 -0
  40. cellects-0.1.2.dist-info/METADATA +132 -0
  41. cellects-0.1.2.dist-info/RECORD +44 -0
  42. cellects-0.1.2.dist-info/WHEEL +5 -0
  43. cellects-0.1.2.dist-info/entry_points.txt +2 -0
  44. cellects-0.1.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,790 @@
1
+ #!/usr/bin/env python3
2
+ """This module contains all modified/simplified widgets from PySide6
3
+ It is made to be easier to use and to be consistant in terms of colors and sizes."""
4
+ import os
5
+ from PySide6 import QtWidgets, QtCore
6
+ from PySide6.QtGui import Qt, QImage, QPixmap, QPalette, QFont, QPen, QFontMetrics, QPainter, QPainterPath, QColor, QDoubleValidator
7
+ import numpy as np
8
+ import cv2
9
+
10
+ """
11
+ Get every available font:
12
+ from PySide6 import QtGui
13
+ fonts = QtGui.QFontDatabase()
14
+ font_names = fonts.families()
15
+ print(font_names)
16
+ "Baskerville" in font_names
17
+ """
18
+
19
+ # colorblind-friendly : rgb(42, 251, 97) , rgb(126, 85, 197)
20
+ # titlesize = 40
21
+
22
+ if os.name == 'nt':
23
+ buttonfont = QFont("Century Gothic", 17, QFont.Bold)
24
+ titlefont = f"Baskerville Old Face" #"40pt Baskerville Old Face"
25
+ textfont = "Century Gothic"
26
+ tabfont = "Baskerville Old Face" # 30pt Comic Sans MS
27
+ else:
28
+ buttonfont = QFont("Times New Roman", 17, QFont.Bold)
29
+ titlefont = "Baskerville"
30
+ textfont = "Times New Roman"
31
+ tabfont = "Baskerville"
32
+ textsize = 15
33
+
34
+ backgroundcolor = "rgb(255,255,255)"
35
+ textColor = "rgb(0, 0, 0)"
36
+ bordercolor = "rgb(255,255,255)"
37
+ selectioncolor = "rgb(1, 122, 94)"
38
+ selectionbackgroundcolor = "rgb(240,240,240)"
39
+ selectionbordercolor = "rgb(255,0,0)"
40
+ buttoncolor = "rgb(255,255,255)"
41
+ buttonclickedcolor = "rgb(100,100,120)"
42
+ buttonborder = "2px solid rgb(0, 0, 0)"
43
+ buttonangles = "13px"
44
+ rollingborder = "1px solid rgb(127, 127, 127)"
45
+ rollingangles = "4px"
46
+
47
+ night_background_color = "rgb(50,50,65)"
48
+ night_text_Color = "rgb(255, 255, 255)"
49
+ night_border_color = "rgb(50,50,65)"
50
+ night_selection_color = "rgb(1, 152, 117)"
51
+ night_selection_background_color = "rgb(50,50,65)"
52
+ night_selection_border_color = "rgb(255,0,0)"
53
+ night_button_color = "rgb(50,50,65)"
54
+ night_button_clicked_color = "rgb(100,100,120)"
55
+ night_button_border = "2px solid rgb(255, 255, 255)"
56
+
57
+
58
+ class WindowType(QtWidgets.QWidget):
59
+ resized = QtCore.Signal()
60
+ def __init__(self, parent, night_mode=False):
61
+ super().__init__()
62
+
63
+ self.setVisible(False)
64
+ self.setParent(parent)
65
+ self.frame = QtWidgets.QFrame(self)
66
+ self.frame.setGeometry(QtCore.QRect(0, 0, self.parent().screen_width, self.parent().screen_height))
67
+ self.display_image = None
68
+ # self.setFont(QFont(textfont, textsize, QFont.Medium))
69
+ # self.setStyleSheet("background-color: %s; color: %s; font: %s; border-color: %s; selection-color: %s; selection-background-color: %s" % (backgroundcolor, textColor, f"{textsize}pt {textfont};", bordercolor, selectioncolor, selectionbackgroundcolor))
70
+ # self.setStyleSheet("background-color: %s; color: %s; border-color: %s; selection-color: %s; selection-background-color: %s" % (backgroundcolor, textColor, bordercolor, selectioncolor, selectionbackgroundcolor))
71
+
72
+ self.setFont(QFont(textfont, textsize, QFont.Medium))
73
+ self.night_mode_switch(night_mode)
74
+ # self.setStyleSheet("background-color: rgb(50,50,65);"
75
+ # "color: rgb(255, 255, 255);\n" # 213, 251, 255
76
+ # "font: 15pt \"Calibri\";\n"
77
+ # "border-color: rgb(50,50,65);\n"
78
+ # # "border-color: rgb(0, 150, 75);\n"
79
+ # "selection-color: rgb(1, 152, 117);\n" # 0, 150, 75 0, 132, 66 0, 120, 215
80
+ # "selection-background-color:rgb(50,50,65);\n" # jsp
81
+ # )
82
+ # "selection-background-color:rgb(60, 60, 60);\n" # jsp
83
+ # "alternate-background-color: rgb(54, 54, 54);\n" # jsp
84
+ # "QToolTip { color: rgb(1, 152, 117); background-color: rgb(64,64,64); border: 0px; };\n"
85
+ # "")
86
+ # self.titles_font = "font: 24pt"
87
+ self.horizontal_space = QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.MinimumExpanding,
88
+ QtWidgets.QSizePolicy.Maximum)
89
+ self.vertical_space = QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.Maximum,
90
+ QtWidgets.QSizePolicy.MinimumExpanding)
91
+ # self.resized.connect(self.center_window)
92
+
93
+ def resizeEvent(self, event):
94
+ '''
95
+ # Use this signal to detect a resize event and call center window function
96
+ :param event:
97
+ :return:
98
+ '''
99
+ self.resized.emit()
100
+ if self.display_image is not None:
101
+ win_width, win_height = self.size().width(), self.size().height()
102
+ self.display_image.max_width = win_width - self.parent().image_window_width_diff
103
+ self.display_image.max_height = win_height - self.parent().image_window_height_diff
104
+ if self.display_image.max_width * self.display_image.height_width_ratio < self.display_image.max_height:
105
+ self.display_image.scaled_shape = [round(self.display_image.max_width * self.display_image.height_width_ratio), self.display_image.max_width]
106
+ else:
107
+ self.display_image.scaled_shape = [self.display_image.max_height, np.round(self.display_image.max_height / self.display_image.height_width_ratio)]
108
+
109
+ self.display_image.setMaximumHeight(self.display_image.scaled_shape[0])
110
+ self.display_image.setMaximumWidth(self.display_image.scaled_shape[1])
111
+ return super(WindowType, self).resizeEvent(event)
112
+
113
+ def center_window(self):
114
+ self.parent().center()
115
+
116
+ def night_mode_switch(self, night_mode):
117
+ if night_mode:
118
+ self.setStyleSheet(
119
+ "background-color: %s; color: %s; font: %s; border-color: %s; selection-color: %s; selection-background-color: %s" % (
120
+ night_background_color, night_text_Color, f"{textsize}pt {textfont};", night_border_color,
121
+ night_selection_color, night_selection_background_color))
122
+ else:
123
+ self.setStyleSheet(
124
+ "background-color: %s; color: %s; font: %s; border-color: %s; selection-color: %s; selection-background-color: %s" % (
125
+ backgroundcolor, textColor, f"{textsize}pt {textfont};", bordercolor, selectioncolor,
126
+ selectionbackgroundcolor))
127
+
128
+
129
+ class MainTabsType(WindowType):
130
+ def __init__(self, parent, night_mode):
131
+ super().__init__(parent, night_mode)
132
+ self.Vlayout = QtWidgets.QVBoxLayout()
133
+ self.Vlayout.setContentsMargins(9, 0, 9, 9)
134
+ self.main_tabs_widget = QtWidgets.QWidget()
135
+ self.main_tabs_layout = QtWidgets.QHBoxLayout()
136
+ self.main_tabs_layout.setContentsMargins(0, 0, 0, 0)
137
+ self.main_tabs_layout.setSpacing(0)
138
+ self.data_tab = MainTabsWidget('Data specifications', night_mode=night_mode)
139
+ self.image_tab = MainTabsWidget('Image analysis', night_mode=night_mode)
140
+ self.video_tab = MainTabsWidget('Video analysis', night_mode=night_mode)
141
+ self.main_tabs_layout.addWidget(self.data_tab)
142
+ self.main_tabs_layout.addWidget(self.image_tab)
143
+ self.main_tabs_layout.addWidget(self.video_tab)
144
+ self.main_tabs_widget.setLayout(self.main_tabs_layout)
145
+ self.Vlayout.addWidget(self.main_tabs_widget)
146
+ self.Vlayout.addItem(self.vertical_space)
147
+
148
+
149
+ class FullScreenImage(QtWidgets.QLabel):
150
+ def __init__(self, image, screen_height, screen_width):
151
+ super().__init__()
152
+ self.true_shape = image.shape
153
+ self.max_height = screen_height
154
+ self.max_width = screen_width
155
+ # self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
156
+ self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
157
+ QtWidgets.QSizePolicy.MinimumExpanding)
158
+ # self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
159
+ self.setSizePolicy(self.im_size_policy)
160
+
161
+ self.height_width_ratio = image.shape[0] / image.shape[1]
162
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
163
+ image = QImage(image.data, image.shape[1], image.shape[0], 3 * image.shape[1], QImage.Format_RGB888)
164
+ image = QPixmap(image)
165
+ self.setScaledContents(True)
166
+ if self.max_width * self.height_width_ratio < self.max_height:
167
+ self.scaled_shape = [round(self.max_width * self.height_width_ratio), self.max_width]
168
+ else:
169
+ self.scaled_shape = [self.max_height, np.round(self.max_height / self.height_width_ratio)]
170
+ # self.setFixedHeight(self.scaled_shape[0])
171
+ # self.setFixedWidth(self.scaled_shape[1])
172
+ # self.setMaximumHeight(self.scaled_shape[0])
173
+ # self.setMaximumWidth(self.scaled_shape[1])
174
+ self.setMinimumSize(self.scaled_shape[1], self.scaled_shape[0])
175
+ self.setPixmap(QPixmap(image))
176
+ self.adjustSize()
177
+
178
+
179
+ class InsertImage(QtWidgets.QLabel):
180
+ def __init__(self, image, max_height, max_width):
181
+ super().__init__()
182
+ self.true_shape = image.shape
183
+ self.max_height = max_height
184
+ self.max_width = max_width
185
+ # self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
186
+ self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
187
+ QtWidgets.QSizePolicy.MinimumExpanding)
188
+ # self.im_size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
189
+ self.setSizePolicy(self.im_size_policy)
190
+
191
+ self.height_width_ratio = image.shape[0] / image.shape[1]
192
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
193
+ image = QImage(image.data, image.shape[1], image.shape[0], 3 * image.shape[1], QImage.Format_RGB888)
194
+ image = QPixmap(image)
195
+ self.setScaledContents(True)
196
+ if self.max_width * self.height_width_ratio < self.max_height:
197
+ self.scaled_shape = [round(self.max_width * self.height_width_ratio), self.max_width]
198
+ else:
199
+ self.scaled_shape = [self.max_height, np.round(self.max_height / self.height_width_ratio)]
200
+ # self.setFixedHeight(self.scaled_shape[0])
201
+ # self.setFixedWidth(self.scaled_shape[1])
202
+ self.setMaximumHeight(self.scaled_shape[0])
203
+ self.setMaximumWidth(self.scaled_shape[1])
204
+ # self.resize(self.scaled_shape[1], self.scaled_shape[0])
205
+ # self.setMinimumSize(self.scaled_shape[0], self.scaled_shape[1])
206
+ self.setPixmap(QPixmap(image))
207
+ self.adjustSize()
208
+
209
+
210
+ def update_image(self, image, text=None, color=255):
211
+ self.true_shape = image.shape
212
+ self.height_width_ratio = image.shape[0] / image.shape[1]
213
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
214
+ image = QImage(image.data, image.shape[1], image.shape[0], 3 * image.shape[1], QImage.Format_RGB888)
215
+ image = QPixmap(image)
216
+ self.setScaledContents(True)
217
+ if self.max_width * self.height_width_ratio < self.max_height:
218
+ self.scaled_shape = [int(np.round(self.max_width * self.height_width_ratio)), self.max_width]
219
+ else:
220
+ self.scaled_shape = [self.max_height, int(np.round(self.max_height / self.height_width_ratio))]
221
+ # self.setFixedHeight(self.scaled_shape[0])
222
+ # self.setFixedWidth(self.scaled_shape[1])
223
+ self.setMaximumHeight(self.scaled_shape[0])
224
+ self.setMaximumWidth(self.scaled_shape[1])
225
+ # self.resize(self.scaled_shape[1], self.scaled_shape[0])
226
+
227
+ # if text is not None:
228
+ # pass
229
+ # pos = QtCore.QPoint(50, 50)
230
+ # painter = QPainter(self)
231
+ # painter.drawText(pos, text)
232
+ # painter.setPen(QColor(color, color, color))
233
+ self.setPixmap(QPixmap(image))
234
+
235
+ def update_image_scaling_factors(self):
236
+ self.scaling_factors = (self.true_shape[0] / self.scaled_shape[0]), (self.true_shape[1] / self.scaled_shape[1])
237
+
238
+
239
+ class PButton(QtWidgets.QPushButton):
240
+ def __init__(self, text, fade=True, night_mode=False):
241
+ """
242
+
243
+ self.setStyleSheet("background-color: rgb(107, 145, 202);\n"
244
+ "border-color: rgb(255, 255, 255);\n"
245
+ "color: rgb(0, 0, 0);\n"
246
+ "font: 17pt \"Britannic Bold\";")
247
+ :param text:
248
+ """
249
+ super().__init__()
250
+ self.setText(text)
251
+ self.night_mode_switch(night_mode)
252
+ self.setFont(buttonfont)
253
+ # self.setStyleSheet("background-color: rgb(50,50,65);\n" #50,50,65 150, 150, 150 153, 204, 205 122, 0, 61 30, 144, 220
254
+ # "border-color: rgb(255, 255, 255);\n"
255
+ # "color: rgb(255, 255, 255);\n"
256
+ # "font: 17pt \"Segoe UI Semibold\";\n"
257
+ # "border : 1px solid rgb(255, 255, 255);"
258
+ # "border-radius : 12px;\n"
259
+ # )
260
+ self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
261
+ self.setFixedWidth(len(text)*15 + 25)
262
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
263
+ # if fade:
264
+ # # self.mouseMoveEvent.connect(self.fade)
265
+ # self.clicked.connect(self.fade)
266
+
267
+ def night_mode_switch(self, night_mode):
268
+ if night_mode:
269
+ self.style = {"buttoncolor": night_button_color, "buttontextColor": night_text_Color, "buttonborder": night_button_border,
270
+ "buttonangles": buttonangles}
271
+ else:
272
+ self.style = {"buttoncolor": buttoncolor, "buttontextColor": textColor, "buttonborder": buttonborder,
273
+ "buttonangles": buttonangles}
274
+ self.update_style()
275
+
276
+ def event_filter(self, event):
277
+ if event.type() == QtCore.QEvent.MouseMove:
278
+ if event.buttons() == QtCore.Qt.NoButton:
279
+ self.fade()
280
+ else:
281
+ self.unfade()
282
+
283
+ def update_style(self):
284
+ self.setStyleSheet(
285
+ "background-color: %s; color: %s; border: %s; border-radius: %s" % tuple(self.style.values()))
286
+
287
+ def color(self, color):
288
+ self.style["buttoncolor"] = color
289
+ self.update_style()
290
+
291
+ def textcolor(self, textcolor):
292
+ self.style["buttontextColor"] = textcolor
293
+ self.update_style()
294
+
295
+ def border(self, border):
296
+ self.style["buttonborder"] = border
297
+ self.update_style()
298
+
299
+ def angles(self, angles):
300
+ self.style["buttonangles"] = angles
301
+ self.update_style()
302
+
303
+ def fade(self):
304
+ self.setWindowOpacity(0.5)
305
+ self.setStyleSheet("background-color: %s; color: %s; border: %s; border-radius: %s" % (buttonclickedcolor, textColor, buttonborder, buttonangles))
306
+ QtCore.QTimer.singleShot(300, self.unfade)
307
+
308
+ def unfade(self):
309
+ self.setWindowOpacity(1)
310
+ self.setStyleSheet("background-color: %s; color: %s; border: %s; border-radius: %s" % (buttoncolor, textColor, buttonborder, buttonangles))
311
+
312
+
313
+ class Spinbox(QtWidgets.QWidget):
314
+ valueChanged = QtCore.Signal(float)
315
+
316
+ class StepType:
317
+ DefaultStepType = 0
318
+ AdaptiveDecimalStepType = 1
319
+ def __init__(self, min=0, max=100000, val=0, decimals=None, night_mode=False):
320
+ super().__init__()
321
+ self.setFixedHeight(30)
322
+ self._minimum = min
323
+ self._maximum = max
324
+ self._value = val
325
+ if decimals is None:
326
+ decimals = 0
327
+ self._decimals = decimals
328
+ if decimals == 0:
329
+ self._singleStep = 1
330
+ else:
331
+ self._singleStep = 0.1
332
+ self._stepType = self.StepType.DefaultStepType
333
+ self._button_width = 16
334
+ self._button_height = 12
335
+
336
+ self._layout = QtWidgets.QHBoxLayout(self)
337
+ self._layout.setContentsMargins(0, 0, 0, 0)
338
+ self._layout.setSpacing(0)
339
+
340
+ self._line_edit = QtWidgets.QLineEdit(self)
341
+ self._line_edit.setAlignment(QtCore.Qt.AlignLeft)
342
+ # self._line_edit.setAlignment(QtCore.Qt.AlignRight)
343
+ self._layout.addWidget(self._line_edit)
344
+
345
+ button_layout = QtWidgets.QVBoxLayout()
346
+ button_layout.setSpacing(0)
347
+ button_layout.setContentsMargins(0, 0, 0, 0)
348
+
349
+ self._up_button = QtWidgets.QPushButton("▲", self)
350
+ self._up_button.setFixedSize(self._button_width, self._button_height)
351
+ self._up_button.clicked.connect(self.stepUp)
352
+ button_layout.addWidget(self._up_button)
353
+
354
+ self._down_button = QtWidgets.QPushButton("▼", self)
355
+ self._down_button.setFixedSize(self._button_width, self._button_height)
356
+ self._down_button.clicked.connect(self.stepDown)
357
+ button_layout.addWidget(self._down_button)
358
+
359
+ self._layout.addLayout(button_layout)
360
+
361
+ self._updateDisplayValue()
362
+ self._updateValidator()
363
+
364
+ self._line_edit.textChanged.connect(self._handleTextChanged)
365
+
366
+ self.setMinimumWidth(120)
367
+
368
+ self.setMinimum(min)
369
+ self.setMaximum(max)
370
+ self.setValue(val)
371
+ self.decimals = decimals
372
+ if decimals is not None:
373
+ self.setDecimals(decimals)
374
+ # self.setStepType(QtWidgets.QAbstractSpinBox.AdaptiveDecimalStepType)
375
+ else:
376
+ self.setDecimals(0)
377
+ self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
378
+ self.setFont(QFont(textfont, textsize, QFont.Medium))
379
+ self.night_mode_switch(night_mode)
380
+ # self.setStyleSheet("""
381
+ # QSpinBox::up-button { subcontrol-origin: border; subcontrol-position: top right; width: 16px; }
382
+ # QSpinBox::down-button { subcontrol-origin: border; subcontrol-position: bottom right; width: 16px; }
383
+ # QSpinBox::up-arrow, QSpinBox::down-arrow { width: 10px; height: 10px; }
384
+ # """)
385
+
386
+ self.setStyleSheet("""
387
+ QPushButton {
388
+ font-size: 8px;
389
+ padding: 0px;
390
+ margin: 0px;
391
+ border: 1px solid #c0c0c0;
392
+ background-color: #f0f0f0;
393
+ }
394
+ QPushButton:pressed {
395
+ background-color: #e0e0e0;
396
+ }
397
+ """)
398
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
399
+
400
+ def sizeHint(self):
401
+ fm = QFontMetrics(self.font())
402
+ max_str = f"{self._maximum:.{self._decimals}f}"
403
+ w = fm.horizontalAdvance(max_str) + self._button_width + 5 # Add some padding
404
+ h = self._line_edit.sizeHint().height()
405
+ return QtCore.QSize(np.max((w, 100)), h) # Ensure a minimum width of 100 pixels
406
+
407
+ def minimumSizeHint(self):
408
+ return QtCore.QSize(self._button_width * 3, self._line_edit.minimumSizeHint().height())
409
+
410
+ def _updateDisplayValue(self):
411
+ if self._decimals == 0:
412
+ self._line_edit.setText(f"{int(np.round(self._value))}")
413
+ else:
414
+ self._line_edit.setText(f"{self._value:.{self._decimals}f}")
415
+
416
+ def _updateValidator(self):
417
+ self._line_edit.setValidator(QDoubleValidator(self._minimum, self._maximum, self._decimals, self))
418
+
419
+ def setValue(self, value):
420
+ clamped_value = np.clip(value, self._minimum, self._maximum)
421
+ if np.abs(clamped_value - self._value) >= 1e-8: # Compare floats with small tolerance
422
+ self._value = clamped_value
423
+ self._updateDisplayValue()
424
+ self.valueChanged.emit(self._value)
425
+
426
+ def value(self):
427
+ return self._value
428
+
429
+ def setMinimum(self, minimum):
430
+ self._minimum = float(minimum)
431
+ self._updateValidator()
432
+ self.setValue(np.max((self._value, self._minimum)))
433
+
434
+ def setMaximum(self, maximum):
435
+ self._maximum = float(maximum)
436
+ self._updateValidator()
437
+ self.setValue(np.min((self._value, self._maximum)))
438
+
439
+ def setSingleStep(self, step):
440
+ self._singleStep = float(step)
441
+
442
+ def setDecimals(self, decimals):
443
+ self._decimals = int(decimals)
444
+ self._updateValidator()
445
+ self._updateDisplayValue()
446
+
447
+ def setStepType(self, step_type):
448
+ if step_type not in (self.StepType.DefaultStepType, self.StepType.AdaptiveDecimalStepType):
449
+ raise ValueError("Invalid step type")
450
+ self._stepType = step_type
451
+
452
+ def stepBy(self, steps):
453
+ if self._stepType == self.StepType.DefaultStepType:
454
+ new_value = self._value + steps * self._singleStep
455
+ else: # AdaptiveDecimalStepType
456
+ abs_value = np.abs(self._value)
457
+ if abs_value == 0:
458
+ new_value = steps * self._singleStep
459
+ else:
460
+ decade = np.floor(np.log10(abs_value))
461
+ step_size = np.power(10, decade) / 100.0
462
+ new_value = self._value + steps * step_size
463
+
464
+ self.setValue(new_value)
465
+
466
+ def stepUp(self):
467
+ self.stepBy(1)
468
+
469
+ def stepDown(self):
470
+ self.stepBy(-1)
471
+
472
+ def _handleTextChanged(self, text):
473
+ if text:
474
+ try:
475
+ value = float(text)
476
+ self.setValue(value)
477
+ except ValueError:
478
+ pass
479
+
480
+ def night_mode_switch(self, night_mode):
481
+ if night_mode:
482
+ self.setStyleSheet(
483
+ "background-color: %s; color: %s; border-color: %s; border: %s; border-radius: %s" % (
484
+ night_background_color, night_text_Color, night_border_color, rollingborder, rollingangles))
485
+ else:
486
+ self.setStyleSheet(
487
+ "background-color: %s; color: %s; border-color: %s; border: %s; border-radius: %s" % (
488
+ backgroundcolor, textColor, bordercolor, rollingborder, rollingangles))
489
+
490
+ # self.setStyleSheet("background-color: rgb(50,50,65);\n"
491
+ # "font: 16pt \"Calibri\";\n"
492
+ # "border-color: rgb(50,50,65);\n"
493
+ # # "border-color: rgb(255, 255, 255);\n"
494
+ # "color: rgb(213, 251, 255);\n")
495
+
496
+
497
+ class Combobox(QtWidgets.QComboBox):
498
+ def __init__(self, items_list, current_idx=None, night_mode=False):
499
+ super().__init__()
500
+ for item in items_list:
501
+ self.addItem(item)
502
+ if current_idx is not None:
503
+ self.setCurrentIndex(current_idx)
504
+ self.setFixedHeight(30)
505
+ #self.adjustSize()
506
+ #max([len(item) for item in items_list])
507
+ # width = self.minimumSizeHint().width()
508
+ # self.setMinimumWidth(width+30)
509
+ # self.setFixedWidth(self.minimumSizeHint().width() + 5)
510
+
511
+ # self.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Maximum)
512
+ self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
513
+ self.night_mode_switch(night_mode)
514
+
515
+ # self.setStyleSheet("QComboBox::drop - down:!editable{background: transparent; border: none;};"
516
+ # "QComboBox {border: '1px solid rgb(127, 127, 127)'; border-radius: 4px}")
517
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
518
+ def night_mode_switch(self, night_mode):
519
+ if night_mode:
520
+ self.setStyleSheet("border-color: %s; border: %s; border-radius: %s" % (
521
+ night_background_color, rollingborder, rollingangles))
522
+ else:
523
+ self.setStyleSheet("border-color: %s; border: %s; border-radius: %s" % (
524
+ backgroundcolor, rollingborder, rollingangles))
525
+
526
+
527
+ class Checkbox(QtWidgets.QCheckBox):
528
+ def __init__(self, set_checked, night_mode=None):
529
+ super().__init__()
530
+ self.setChecked(set_checked)
531
+ self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
532
+ self.setMinimumWidth(75)
533
+ # self.setStyleSheet("padding:5px")
534
+ self.setStyleSheet("margin-left:50%; margin-right:50%;")
535
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
536
+
537
+
538
+ class Title(QtWidgets.QLabel):
539
+ def __init__(self, text, tip=None):
540
+ super().__init__()
541
+ self.setText(text)
542
+ self.setAlignment(QtCore.Qt.AlignHCenter)
543
+ self.setStyleSheet("font: 24pt \"Segoe UI Semibold\";\n"
544
+ #"border: 1px solid gray;\n"
545
+ "margin-bottom: 30%;\n")
546
+ self.setMargin(2)
547
+ if tip is not None:
548
+ self.setToolTip(tip)
549
+ self.setStyleSheet("""QToolTip {
550
+ background-color: rgb(60, 60, 60);
551
+ color: rgb(1, 152, 117);
552
+ border: black solid 1px
553
+ }""")
554
+
555
+
556
+ class EditText(QtWidgets.QLineEdit):
557
+ def __init__(self, text, police=None, align='l', tip=None, night_mode=False):
558
+ super().__init__()
559
+ self.setMaximumHeight(36)
560
+ self.setText(str(text))
561
+ self.night_mode_switch(night_mode)
562
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
563
+ # self.setAttribute(QtCore.Qt.WA_MacShowFocusRect, 0)
564
+
565
+ def night_mode_switch(self, night_mode):
566
+ # self.setStyleSheet("border: %s" % bordercolor)
567
+ # self.setStyleSheet("border: %s" % night_border_color)
568
+ if night_mode:
569
+ self.setStyleSheet(
570
+ "background-color: %s; color: %s; font: %s; border-bottom: %s; border-top: %s" % (
571
+ night_background_color, night_text_Color, f"{textsize}pt {textfont};", f"1px solid #adadad", f"1px dotted grey"))
572
+ else:
573
+ self.setStyleSheet(
574
+ "background-color: %s; color: %s; font: %s; border-bottom: %s; border-top: %s" % (
575
+ backgroundcolor, textColor, f"{textsize}pt {textfont};", f"1px solid grey", f"1px dotted #adadad"))
576
+
577
+ # "QLineEdit"
578
+ # "{"
579
+ # "border : 5px solid black;"
580
+ # "}"
581
+ # "QLineEdit::hover"
582
+ # "{"
583
+ # "border-color : red green blue yellow"
584
+ # "}")
585
+
586
+ # if night_mode:
587
+ # self.setStyleSheet(
588
+ # "background-color: %s; color: %s; font: %s; border-color: %s; selection-color: %s; alternate-border-color: %s; selection-background-color: %s" % (
589
+ # night_background_color, night_text_Color, f"{textsize}pt {textfont};", night_border_color,
590
+ # night_selection_color, night_selection_border_color, night_selection_background_color))
591
+ # else:
592
+ # self.setStyleSheet(
593
+ # "background-color: %s; color: %s; font: %s; border-color: %s; selection-color: %s; alternate-border-color: %s; selection-background-color: %s" % (
594
+ # backgroundcolor, textColor, f"{textsize}pt {textfont};", bordercolor, selectioncolor,
595
+ # selectionbordercolor, selectionbackgroundcolor))
596
+
597
+
598
+ class FixedText(QtWidgets.QLabel):
599
+ def __init__(self, text, police=None, halign='l', valign="c", tip=None, night_mode=False):
600
+ super().__init__()
601
+ self.setText(text)
602
+ if halign == 'l':
603
+ self.setAlignment(QtCore.Qt.AlignLeft)
604
+ elif halign == 'r':
605
+ self.setAlignment(QtCore.Qt.AlignRight)
606
+ else:
607
+ self.setAlignment(QtCore.Qt.AlignCenter)
608
+ if valign == 't':
609
+ self.setAlignment(Qt.AlignTop)
610
+ elif valign == 'b':
611
+ self.setAlignment(Qt.AlignBottom)
612
+ else:
613
+ self.setAlignment(Qt.AlignVCenter)
614
+
615
+ if police is not None:
616
+ if police > 23:
617
+ # self.setStyleSheet("font: %s; margin-bottom: %s" % (titlefont, "30%;"))
618
+
619
+ # self.night_mode_switch(night_mode, titlefont)
620
+ self.night_mode_switch(night_mode, f"{police}pt {titlefont}")
621
+
622
+ # self.setFont(titlefont)
623
+ # self.setStyleSheet("margin-bottom: 30%;\n")
624
+ self.setMargin(2)
625
+ # else:
626
+ # self.night_mode_switch(night_mode, f"{police}pt {textfont}")
627
+ # self.night_mode_switch(night_mode, QFont(textfont, police + 2, QFont.Medium))
628
+ # self.setFont(QFont(textfont, police + 2, QFont.Medium))
629
+
630
+ else:
631
+ self.setFont(QFont(textfont, textsize + 2, QFont.Medium))
632
+
633
+ if tip is not None:
634
+ self.setToolTip(tip)
635
+ if night_mode:
636
+ self.setStyleSheet("""QToolTip {background-color: rgb(70,70,85);color: rgb(1, 152, 117);
637
+ border: white solid 1px}""")
638
+ else:
639
+ self.setStyleSheet("""QToolTip {background-color: rgb(240,240,240);
640
+ color: rgb(1, 122, 94);
641
+ border: black solid 1px
642
+ }""")
643
+
644
+ # self.setStyleSheet("""QToolTip {
645
+ # background-color: rgb(150, 150, 150);
646
+ # color: rgb(1, 1, 1);
647
+ # border: black solid 1px
648
+ # }""")
649
+
650
+ def night_mode_switch(self, night_mode, font):
651
+ if night_mode:
652
+ self.setStyleSheet("font: %s; color: %s; margin-bottom: %s" % (font, night_text_Color, "30%"))
653
+ else:
654
+ self.setStyleSheet("font: %s; color: %s; margin-bottom: %s" % (font, textColor, "30%"))
655
+
656
+
657
+ class LineWidget(QtWidgets.QWidget):
658
+ def __init__(self, ori="h", size=None, night_mode=False):
659
+ super().__init__()
660
+ self.layout = QtWidgets.QHBoxLayout()
661
+ self.line = QtWidgets.QFrame()
662
+ if ori == "h":
663
+ self.line.setFrameShape(QtWidgets.QFrame.HLine)
664
+ # self.setFrameShadow(QtWidgets.QFrame.Sunken)
665
+ else:
666
+ self.line.setFrameShape(QtWidgets.QFrame.VLine)
667
+ self.night_mode_switch(night_mode)
668
+ if size is None:
669
+ size = [1, 4]
670
+ self.line.setFixedHeight(size[0])
671
+ self.line.setFixedWidth(size[1])
672
+ horizontal_space = QtWidgets.QSpacerItem(1, 1, QtWidgets.QSizePolicy.MinimumExpanding,
673
+ QtWidgets.QSizePolicy.Maximum)
674
+ self.layout.addItem(horizontal_space)
675
+ self.layout.addWidget(self.line)
676
+ self.layout.addItem(horizontal_space)
677
+ self.setLayout(self.layout)
678
+
679
+ def night_mode_switch(self, night_mode):
680
+ if night_mode:
681
+ self.line.setStyleSheet("QFrame { background-color: rgb(255, 255, 255) }")
682
+ else:
683
+ self.line.setStyleSheet("QFrame { background-color: rgb(0, 0, 0) }")
684
+
685
+
686
+
687
+ class MainTabsWidget(QtWidgets.QPushButton):
688
+ """
689
+ A custom QPushButton that mimics an explorer tab appearance.
690
+
691
+ Features:
692
+ - Customizable text
693
+ - Night mode support
694
+ - Three states: not_in_use (grey border), in_use (black border), not_usable (grey text)
695
+ - Tooltip support for not_usable state
696
+ """
697
+
698
+ def __init__(self, text="", night_mode=False, parent=None):
699
+ super().__init__()
700
+
701
+ self.setText(text)
702
+ self.state = "not_in_use" # States: "not_in_use", "in_use", "not_usable"
703
+ self.setFont(buttonfont)
704
+
705
+
706
+ # Set basic properties
707
+ self.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
708
+ # self.setFixedWidth(len(text)*15 + 25)
709
+ self.setFixedHeight(35)
710
+ # self.setMinimumWidth(80)
711
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
712
+
713
+ self.night_mode_switch(night_mode)
714
+
715
+
716
+ def night_mode_switch(self, night_mode):
717
+ self._night_mode = night_mode
718
+ self.update_style()
719
+
720
+ def update_style(self):
721
+ """Update the widget's stylesheetµ"""
722
+
723
+ if self.state == "not_usable":
724
+ tab_text_color = "#888888"
725
+ self.setCursor(QtCore.Qt.CursorShape.ForbiddenCursor)
726
+ else:
727
+ if self._night_mode:
728
+ tab_text_color = night_text_Color
729
+ else:
730
+ tab_text_color = textColor
731
+ if self.state == "not_in_use":
732
+ self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
733
+ if self.state == "in_use":
734
+ border_width = 2
735
+ if self._night_mode:
736
+ tab_border = f"{border_width}px solid #adadad"
737
+ else:
738
+ tab_border = f"{border_width}px solid #323241"
739
+ else:
740
+ border_width = 1
741
+ if self._night_mode:
742
+ tab_border = f"{border_width}px solid #323241"
743
+ else:
744
+ tab_border = f"{border_width}px solid #adadad"
745
+
746
+ if self._night_mode:
747
+ style = {"buttoncolor": night_button_color, "buttontextColor": tab_text_color, "border": tab_border,
748
+ "font_family": tabfont, "font_size": "22pt", #"font_weight": "bold",
749
+ "border-top-color": "#323241", "border-right-color": "#323241", # invisible
750
+ "border-top-left-radius": "10", "border-bottom-left-radius": "1"}
751
+ else:
752
+ style = {"buttoncolor": buttoncolor, "buttontextColor": tab_text_color, "border": tab_border,
753
+ "font_family": tabfont, "font_size": "22pt", #"font_weight": "bold",
754
+ "border-top-color": "#ffffff", "border-right-color": "#ffffff", # invisible
755
+ "border-top-left-radius": "10", "border-bottom-left-radius": "1"
756
+ }
757
+ self.setStyleSheet(
758
+ "background-color: %s; color: %s; border: %s; font-family: %s; font-size: %s; border-top-color: %s; border-right-color: %s; border-top-left-radius: %s; border-bottom-left-radius: %s" % tuple(style.values()))
759
+
760
+
761
+ def set_in_use(self):
762
+ """Set the tab to 'in_use' state with black border."""
763
+ self.state = "in_use"
764
+ self.setToolTip("") # Clear any tooltip
765
+ self.update_style()
766
+
767
+ def set_not_in_use(self):
768
+ """Set the tab to 'not_in_use' state with grey border."""
769
+ self.state = "not_in_use"
770
+ self.setToolTip("") # Clear any tooltip
771
+ self.update_style()
772
+
773
+ def set_not_usable(self, tooltip_text="This tab is not usable"):
774
+ """
775
+ Set the tab to 'not_usable' state with grey text.
776
+
777
+ Args:
778
+ tooltip_text (str): Custom tooltip text to show when hovering
779
+ """
780
+ self.state = "not_usable"
781
+ self.setToolTip(tooltip_text)
782
+ self.update_style()
783
+
784
+ def get_state(self):
785
+ """Get the current state of the tab."""
786
+ return self.state
787
+
788
+ def is_night_mode(self):
789
+ """Check if night mode is enabled."""
790
+ return self._night_mode