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