PaIRS-UniNa 0.2.9__cp312-cp312-macosx_11_0_universal2.whl → 0.2.10__cp312-cp312-macosx_11_0_universal2.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.
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  from .PaIRS_pypacks import *
2
3
  #from ui_Tree_Tab import Ui_TreeTab
3
4
 
@@ -10,8 +11,9 @@ font_italic=True
10
11
  font_weight=QFont.DemiBold
11
12
  backgroundcolor_none=" background-color: none;"
12
13
  backgroundcolor_changing=" background-color: rgb(255,230,230);"
14
+ backgroundcolor_hover=" background-color: rgba(0, 116, 255, 0.1);"
15
+ border_hover = " " #"border: 1px solid gray; "
13
16
  color_changing="color: rgb(33,33,255); "+backgroundcolor_changing
14
- color_changing_black="color: rgb(0,0,0); "+backgroundcolor_changing
15
17
 
16
18
  #********************************************* Operating Widgets
17
19
  def setSS(b,style):
@@ -53,6 +55,9 @@ class MyQLineEdit(QtWidgets.QLineEdit):
53
55
  def setup(self):
54
56
  if not self.initFlag:
55
57
  self.initFlag=True
58
+ self.FlagFocusIn=False
59
+ self.oldFont=None
60
+ self.oldStyle=None
56
61
  font_changing = QtGui.QFont(self.font())
57
62
  font_changing.setItalic(font_italic)
58
63
  font_changing.setWeight(font_weight)
@@ -63,21 +68,17 @@ class MyQLineEdit(QtWidgets.QLineEdit):
63
68
  if hasattr(b,'setStyleSheet'):
64
69
  if hasStyleFlag:
65
70
  if b.styleFlag: continue
66
- b.flagS=True
67
71
  b.initialStyle=b.styleSheet()+" "+backgroundcolor_none
68
72
  b.setEnabled(False)
69
73
  b.disabledStyle=b.styleSheet()
70
74
  b.setEnabled(True)
71
75
  b.setStyleSheet(setSS(b,b.initialStyle))
72
- else:
73
- b.flagS=False
74
76
  if hasattr(b,'setFont'):
75
- b.flagF=True
76
77
  b.initialFont=b.font()
77
78
  b.font_changing=font_changing
78
- else:
79
- b.flagF=False
80
79
  if hasStyleFlag: b.styleFlag=True
80
+ self.CElabs=[w for w in self.bros if isinstance(w, ClickableEditLabel)]
81
+ self.CElabs_styles=[w.styleSheet() for w in self.CElabs]
81
82
 
82
83
  def setup2(self):
83
84
  if not self.initFlag2:
@@ -99,52 +100,84 @@ class MyQLineEdit(QtWidgets.QLineEdit):
99
100
  self.setCompleterList()
100
101
 
101
102
  def enterEvent(self, event):
102
- super().enterEvent(event)
103
- if not self.font()==self.font_changing and not self.hasFocus():
103
+ if not self.hasFocus() and self.isEnabled():
104
+ self.oldFont=self.font()
105
+ self.oldStyle=self.styleSheet()
104
106
  self.setFont(self.font_changing)
105
-
107
+ self.setStyleSheet(setSS(self,self.initialStyle+" "+backgroundcolor_hover))
108
+ for k,w in enumerate(self.CElabs):
109
+ self.CElabs_styles[k]=w.styleSheet()
110
+ w.setStyleSheet(w.styleSheet()+" "+f"""
111
+ ClickableEditLabel {{
112
+ {backgroundcolor_hover};
113
+ }}
114
+ """)
115
+ w.repaint()
116
+ super().enterEvent(event)
117
+
106
118
  def leaveEvent(self, event):
119
+ if not self.hasFocus() and self.oldFont is not None:
120
+ self.setFont(self.oldFont)
121
+ self.setStyleSheet(self.oldStyle)
122
+ for k,w in enumerate(self.CElabs):
123
+ w.setStyleSheet(self.CElabs_styles[k])
124
+ w.repaint()
125
+ self.oldFont=None
126
+ self.oldStyle=None
107
127
  super().leaveEvent(event)
108
- if self.font()==self.font_changing and not self.hasFocus():
109
- self.setFont(self.initialFont)
110
128
 
111
129
  def focusInEvent(self, event):
112
130
  super().focusInEvent(event)
113
131
  for f in self.addfuncin:
114
132
  self.addfuncin[f]()
115
133
  self.focusInFun()
116
-
134
+
117
135
  def setFocus(self):
118
136
  super().setFocus()
119
137
  self.focusInFun()
120
138
 
121
139
  def focusInFun(self):
122
- self.setStyleSheet(setSS(self,self.initialStyle+" "+color_changing))
123
- self.setFont(self.font_changing)
140
+ self.setStyleFont(color_changing,self.font_changing)
141
+
142
+ def setStyleFont(self,color_changing,font):
124
143
  for b in self.bros:
125
- if (not b==self) and b.flagS:
126
- b.setStyleSheet(b.initialStyle+" "+color_changing_black)
144
+ if hasattr(b,'setInitalStyle') and hasattr(b,'FlagFocusIn') and b.FlagFocusIn:
145
+ b.setInitalStyle()
146
+ break
147
+ if not self.FlagFocusIn:
148
+ self.FlagFocusIn=True
149
+ self.setFont(font)
150
+ for b in self.bros:
151
+ if hasattr(b,'initialStyle'):
152
+ b.setStyleSheet(setSS(b,b.initialStyle+" "+color_changing))
127
153
 
128
154
  def focusOutEvent(self, event):
129
155
  super().focusOutEvent(event) #to preserve classical behaviour before adding the below
130
156
  for f in self.addfuncout:
131
157
  self.addfuncout[f]()
132
- self.focusOutFun()
158
+ self.setInitalStyle()
133
159
 
134
160
  def clearFocus(self):
135
161
  super().clearFocus()
136
- self.focusOutFun()
162
+ self.setInitalStyle()
137
163
 
138
- def focusOutFun(self):
139
- for b in self.bros:
140
- if b.flagS:
164
+ def setInitalStyle(self):
165
+ if self.FlagFocusIn:
166
+ self.FlagFocusIn=False
167
+ for b in self.bros:
141
168
  if hasattr(b,'default_stylesheet'):
142
169
  b.setStyleSheet(b.default_stylesheet)
143
- else:
144
- b.setStyleSheet(setSS(b,b.initialStyle))
145
- if b.flagF:
146
- b.setFont(b.initialFont)
147
- #self.addlab.clear()
170
+ elif hasattr(b,'initialStyle'):
171
+ b.setStyleSheet(b.initialStyle)
172
+ if hasattr(b,'initialFont'):
173
+ b.setFont(b.initialFont)
174
+ for k,w in enumerate(self.CElabs):
175
+ self.CElabs_styles[k]=w.default_stylesheet
176
+ w.setStyleSheet(self.CElabs_styles[k])
177
+ w.repaint()
178
+ self.oldFont=None
179
+ self.oldStyle=None
180
+ #self.addlab.clear()
148
181
 
149
182
  def showCompleter(self):
150
183
  if self.completer():
@@ -185,11 +218,14 @@ class MyQSpin(QtWidgets.QSpinBox):
185
218
  self.addfuncreturn={}
186
219
 
187
220
  self.setAccelerated(True)
188
- self.setGroupSeparatorShown(True)
221
+ self.setGroupSeparatorShown(True)
189
222
 
190
223
  def setup(self):
191
224
  if not self.initFlag:
192
225
  self.initFlag=True
226
+ self.FlagFocusIn=False
227
+ self.oldFont=None
228
+ self.oldStyle=None
193
229
  font_changing = QtGui.QFont(self.font())
194
230
  font_changing.setItalic(font_italic)
195
231
  font_changing.setWeight(font_weight)
@@ -200,15 +236,14 @@ class MyQSpin(QtWidgets.QSpinBox):
200
236
  b.initialFont=b.font()
201
237
  b.font_changing=font_changing
202
238
  b.styleFlag=True
203
- self.spinFontObj=[]
204
- for c in self.findChildren(QObject):
205
- if hasattr(c,'setFont'):
206
- self.spinFontObj+=[c]
207
-
239
+ #self.spinFontObj = self.findChildren(QtWidgets.QLineEdit)[0]
240
+ #self.spinFontObj.initialStyle=self.spinFontObj.styleSheet()+" "+backgroundcolor_none
241
+ #self.spinFontObj.font_changing=font_changing
242
+
208
243
  def setFocus(self):
209
244
  super().setFocus()
210
245
  self.focusInFun()
211
-
246
+
212
247
  def focusInEvent(self, event):
213
248
  super().focusInEvent(event) #to preserve classical behaviour before adding the below
214
249
  for f in self.addfuncin:
@@ -216,23 +251,51 @@ class MyQSpin(QtWidgets.QSpinBox):
216
251
  self.focusInFun()
217
252
 
218
253
  def focusInFun(self):
219
- if not self.font()==self.font_changing:
254
+ if not self.FlagFocusIn:
255
+ self.FlagFocusIn=True
256
+ #self.spinFontObj.setStyleSheet(self.spinFontObj.initialStyle+" "+color_changing)
220
257
  for b in self.bros:
221
- b.setStyleSheet(b.initialStyle+" "+color_changing)
222
- b.setFont(b.font_changing)
223
- for b in self.spinFontObj:
258
+ b.setStyleSheet(setSS(b,b.initialStyle+" "+color_changing))
224
259
  b.setFont(self.font_changing)
225
260
 
261
+ def clearFocus(self):
262
+ super().clearFocus()
263
+ self.setInitalStyle()
264
+
226
265
  def focusOutEvent(self, event):
227
266
  super().focusOutEvent(event) #to preserve classical behaviour before adding the below
228
267
  for f in self.addfuncout:
229
268
  self.addfuncout[f]()
230
- if self.font()==self.font_changing:
269
+ self.setInitalStyle()
270
+
271
+ def setInitalStyle(self):
272
+ if self.FlagFocusIn:
273
+ self.FlagFocusIn=False
274
+ #self.spinFontObj.setStyleSheet(self.spinFontObj.initialStyle)
231
275
  for b in self.bros:
232
- b.setStyleSheet(b.initialStyle)
233
- b.setFont(b.initialFont)
234
- for b in self.spinFontObj:
235
- b.setFont(self.initialFont)
276
+ b.setStyleSheet(setSS(b,b.initialStyle))
277
+ b.setFont(self.initialFont)
278
+ self.findChildren(QtWidgets.QLineEdit)[0].setFont(self.initialFont)
279
+ self.oldFont=None
280
+ self.oldStyle=None
281
+
282
+ def enterEvent(self, event):
283
+ super().enterEvent(event)
284
+ if not self.hasFocus() and self.isEnabled():
285
+ self.oldFont=self.font()
286
+ self.setFont(self.font_changing)
287
+ b=self #b=self.spinFontObj
288
+ self.oldStyle=b.styleSheet()
289
+ b.setStyleSheet(setSS(b,b.initialStyle+" "+backgroundcolor_hover+" "+border_hover))
290
+
291
+ def leaveEvent(self, event):
292
+ super().leaveEvent(event)
293
+ if not self.hasFocus() and self.oldFont is not None:
294
+ self.setFont(self.oldFont)
295
+ b=self #b=self.spinFontObj
296
+ b.setStyleSheet(self.oldStyle)
297
+ self.oldFont=None
298
+ self.oldStyle=None
236
299
 
237
300
  def keyPressEvent(self, event):
238
301
  super().keyPressEvent(event)
@@ -284,6 +347,9 @@ class MyQDoubleSpin(QtWidgets.QDoubleSpinBox):
284
347
  def setup(self):
285
348
  if not self.initFlag:
286
349
  self.initFlag=True
350
+ self.FlagFocusIn=False
351
+ self.oldFont=None
352
+ self.oldStyle=None
287
353
  font_changing = QtGui.QFont(self.font())
288
354
  font_changing.setItalic(font_italic)
289
355
  font_changing.setWeight(font_weight)
@@ -294,33 +360,67 @@ class MyQDoubleSpin(QtWidgets.QDoubleSpinBox):
294
360
  b.initialFont=b.font()
295
361
  b.font_changing=font_changing
296
362
  b.styleFlag=True
297
- self.spinFontObj=[]
298
- for c in self.findChildren(QObject):
299
- if hasattr(c,'setFont'):
300
- self.spinFontObj+=[c]
363
+ #self.spinFontObj = self.findChildren(QtWidgets.QLineEdit)[0]
364
+ #self.spinFontObj.initialStyle=self.spinFontObj.styleSheet()+" "+backgroundcolor_none
365
+ #self.spinFontObj.font_changing=font_changing
366
+
367
+ def setFocus(self):
368
+ super().setFocus()
369
+ self.focusInFun()
301
370
 
302
371
  def focusInEvent(self, event):
303
372
  super().focusInEvent(event) #to preserve classical behaviour before adding the below
304
373
  for f in self.addfuncin:
305
374
  self.addfuncin[f]()
306
- if not self.font()==self.font_changing:
375
+ self.focusInFun()
376
+
377
+ def focusInFun(self):
378
+ if not self.FlagFocusIn:
379
+ self.FlagFocusIn=True
380
+ #self.spinFontObj.setStyleSheet(self.spinFontObj.initialStyle+" "+color_changing)
307
381
  for b in self.bros:
308
- b.setStyleSheet(b.initialStyle+" "+color_changing)
309
- b.setFont(self.font_changing)
310
- for b in self.spinFontObj:
382
+ b.setStyleSheet(setSS(b,b.initialStyle+" "+color_changing))
311
383
  b.setFont(self.font_changing)
312
384
 
385
+ def clearFocus(self):
386
+ super().clearFocus()
387
+ self.setInitalStyle()
388
+
313
389
  def focusOutEvent(self, event):
314
390
  super().focusOutEvent(event) #to preserve classical behaviour before adding the below
315
391
  for f in self.addfuncout:
316
392
  self.addfuncout[f]()
317
- if self.font()==self.font_changing:
393
+ self.setInitalStyle()
394
+
395
+ def setInitalStyle(self):
396
+ if self.FlagFocusIn:
397
+ self.FlagFocusIn=False
398
+ #self.spinFontObj.setStyleSheet(self.spinFontObj.initialStyle)
318
399
  for b in self.bros:
319
- b.setStyleSheet(b.initialStyle)
320
- b.setFont(b.initialFont)
321
- for b in self.spinFontObj:
400
+ b.setStyleSheet(setSS(b,b.initialStyle))
322
401
  b.setFont(self.initialFont)
323
-
402
+ self.findChildren(QtWidgets.QLineEdit)[0].setFont(self.initialFont)
403
+ self.oldFont=None
404
+ self.oldStyle=None
405
+
406
+ def enterEvent(self, event):
407
+ super().enterEvent(event)
408
+ if not self.hasFocus() and self.isEnabled():
409
+ self.oldFont=self.font()
410
+ self.setFont(self.font_changing)
411
+ b=self #b=self.spinFontObj
412
+ self.oldStyle=b.styleSheet()
413
+ b.setStyleSheet(setSS(b,b.initialStyle+" "+backgroundcolor_hover+" "+border_hover))
414
+
415
+ def leaveEvent(self, event):
416
+ super().leaveEvent(event)
417
+ if not self.hasFocus() and self.oldFont is not None:
418
+ self.setFont(self.oldFont)
419
+ b=self #b=self.spinFontObj
420
+ b.setStyleSheet(self.oldStyle)
421
+ self.oldFont=None
422
+ self.oldStyle=None
423
+
324
424
  def keyPressEvent(self, event):
325
425
  super().keyPressEvent(event)
326
426
  if event.key() in (Qt.Key.Key_Return,Qt.Key.Key_Enter) and self.hasFocus():
@@ -366,6 +466,7 @@ class CollapsibleBox(QtWidgets.QWidget):
366
466
 
367
467
  if self.toggle_button is None:
368
468
  self.toggle_button=self.findChild(QtWidgets.QToolButton)
469
+ self.toggle_button.setObjectName("CollapsibleBox_toggle")
369
470
  self.toggle_button.setChecked(InitCheck)
370
471
  self.toggle_button.clicked.connect(self.on_click)
371
472
  self.toggle_button.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
@@ -579,6 +680,7 @@ class RichTextPushButton(QPushButton):
579
680
 
580
681
  self.lyt=self.__lyt
581
682
  self.lbl=self.__lbl
683
+ self.icnWidget=self.__icon
582
684
  self.icn=None
583
685
  return
584
686
 
@@ -682,8 +784,8 @@ class ClickableLabel(QLabel):
682
784
  def __init__(self, *args):
683
785
  super().__init__(*args)
684
786
 
685
- self.default_stylesheet = self.styleSheet()
686
- self.highlight_stylesheet = "background-color: #dcdcdc; border-radius: 3px;"
787
+ self.default_stylesheet = self.styleSheet()
788
+ self.highlight_stylesheet = "background-color: rgba(0, 116, 255, 0.1); border-radius: 3px;"
687
789
 
688
790
  self.timer = QTimer(self)
689
791
  self.timer.timeout.connect(self.resetHighlight)
@@ -703,6 +805,7 @@ class ClickableLabel(QLabel):
703
805
  warningDialog(self.window(),Message=self.toolTip(),pixmap=pixmap,title='Info')
704
806
 
705
807
  def highlight(self):
808
+ self.default_stylesheet = self.styleSheet() # <-- capture current style
706
809
  self.setStyleSheet(self.highlight_stylesheet)
707
810
  self.repaint()
708
811
 
@@ -719,12 +822,17 @@ class ClickableLabel(QLabel):
719
822
 
720
823
  class ClickableEditLabel(ClickableLabel):
721
824
  def setup(self):
722
- line_edit=QLineEdit(self)
723
- line_edit.setPalette(self.palette())
724
- line_edit_bg_color_str = line_edit.palette().color(QPalette.ColorRole.Base).name()
725
- self.default_stylesheet=self.styleSheet()+f"ClickableEditLabel{{background-color: {line_edit_bg_color_str}}};"
825
+ le = QLineEdit()
826
+ bg = le.palette().color(QPalette.Base)
827
+ bg_rgba = f"rgba({bg.red()}, {bg.green()}, {bg.blue()}, {bg.alpha()})"
828
+
829
+ self.default_stylesheet = self.styleSheet() + f"""
830
+ ClickableEditLabel {{
831
+ background-color: {bg_rgba};
832
+ }}
833
+ """
726
834
  self.setStyleSheet(self.default_stylesheet)
727
- line_edit.setParent(None)
835
+ le.setParent(None)
728
836
 
729
837
  class CustomLineEdit(QLineEdit):
730
838
  cancelEditing = Signal()
@@ -837,6 +945,238 @@ class EditableLabel(QWidget):
837
945
  self.label.show()
838
946
  self.updateLabel()
839
947
 
948
+ def setButtonHoverStyle(w:QWidget,FlagBorder=True,borderRadius=6,FlagCls=True):
949
+ if FlagCls:
950
+ cls = w.metaObject().className()
951
+ base = w.styleSheet() or ""
952
+ if cls not in base: base=f"{cls} {{" +f"{base}"+"}"
953
+ if base and not base.endswith("\n"):
954
+ base += "\n"
955
+ else: cls=base=''
956
+
957
+ """"
958
+ style = (
959
+ f"{cls}:hover {{"
960
+ "background: qlineargradient("
961
+ "x1:0, y1:0, x2:0, y2:1, "
962
+ "stop:0 rgba(0, 116, 255, 0.05), "
963
+ "stop:0.2 rgba(0, 116, 255, 0.15), "
964
+ "stop:0.8 rgba(0, 116, 255, 0.15), "
965
+ "stop:1 rgba(0, 116, 255, 0.05));"
966
+ f"border-radius: {borderRadius}px;"
967
+ f"{'border: 1px solid gray;' if FlagBorder else ''}"
968
+ "padding: 2px 2px;"
969
+ "}"
970
+ )
971
+ """
972
+
973
+ style = (
974
+ f"{cls}:hover {{"
975
+ f"border-radius: {borderRadius}px;"
976
+ f"background-color: none;"
977
+ f"{'border: 1px solid gray;' if FlagBorder else ''}"
978
+ "padding: 2px 2px;"
979
+ "}"
980
+ )
981
+
982
+ w.setStyleSheet(base + style)
983
+
984
+ def apply_hover_glow_label(
985
+ w: QLabel,
986
+ *,
987
+ color="#0051FF",
988
+ blur=18,
989
+ max_alpha=170,
990
+ duration_ms=160,
991
+ ):
992
+ if getattr(w, "_hoverGlowInstalled", False):
993
+ w._hoverGlowColor = QColor(color)
994
+ w._hoverGlowBlur = float(blur)
995
+ w._hoverGlowMaxAlpha = int(max_alpha)
996
+ w._hoverGlowDuration = int(duration_ms)
997
+ w._hoverGlowEffect.setBlurRadius(w._hoverGlowBlur)
998
+ return
999
+
1000
+ w._hoverGlowInstalled = True
1001
+ w._hoverGlowColor = QColor(color)
1002
+ w._hoverGlowBlur = float(blur)
1003
+ w._hoverGlowMaxAlpha = int(max_alpha)
1004
+ w._hoverGlowDuration = int(duration_ms)
1005
+
1006
+ eff = QGraphicsDropShadowEffect(w)
1007
+ eff.setOffset(0, 0)
1008
+ eff.setBlurRadius(w._hoverGlowBlur)
1009
+
1010
+ c = QColor(w._hoverGlowColor)
1011
+ c.setAlpha(0)
1012
+ eff.setColor(c)
1013
+
1014
+ w.setGraphicsEffect(eff)
1015
+ w._hoverGlowEffect = eff
1016
+
1017
+ def _set_alpha(val):
1018
+ col = QColor(w._hoverGlowColor)
1019
+ col.setAlpha(int(val))
1020
+ w._hoverGlowEffect.setColor(col)
1021
+
1022
+ # ✅ Animate a plain value (no Qt property needed)
1023
+ w._hoverGlowAnimAlpha = QVariantAnimation(w)
1024
+ w._hoverGlowAnimAlpha.setEasingCurve(QEasingCurve.OutCubic)
1025
+ w._hoverGlowAnimAlpha.valueChanged.connect(_set_alpha)
1026
+
1027
+ class _GlowFilter(QObject):
1028
+ def eventFilter(self, obj, ev):
1029
+ if obj is not w:
1030
+ return False
1031
+
1032
+ t = ev.type()
1033
+ if t in (QEvent.Enter, QEvent.HoverEnter):
1034
+ w._hoverGlowAnimAlpha.stop()
1035
+ w._hoverGlowAnimAlpha.setDuration(w._hoverGlowDuration)
1036
+ w._hoverGlowAnimAlpha.setStartValue(w._hoverGlowEffect.color().alpha())
1037
+ w._hoverGlowAnimAlpha.setEndValue(w._hoverGlowMaxAlpha)
1038
+ w._hoverGlowAnimAlpha.start()
1039
+
1040
+ elif t in (QEvent.Leave, QEvent.HoverLeave):
1041
+ w._hoverGlowAnimAlpha.stop()
1042
+ w._hoverGlowAnimAlpha.setDuration(w._hoverGlowDuration)
1043
+ w._hoverGlowAnimAlpha.setStartValue(w._hoverGlowEffect.color().alpha())
1044
+ w._hoverGlowAnimAlpha.setEndValue(0)
1045
+ w._hoverGlowAnimAlpha.start()
1046
+
1047
+ return False
1048
+
1049
+ w._hoverGlowFilter = _GlowFilter(w)
1050
+ w.installEventFilter(w._hoverGlowFilter)
1051
+
1052
+ w.setAttribute(Qt.WA_Hover, True)
1053
+ w.setMouseTracking(True)
1054
+
1055
+ def remove_hover_glow_label(w: QLabel):
1056
+ """Removes the hover glow behavior and restores a clean state."""
1057
+ if not getattr(w, "_hoverGlowInstalled", False):
1058
+ return
1059
+
1060
+ # Stop animation cleanly
1061
+ anim = getattr(w, "_hoverGlowAnimAlpha", None)
1062
+ if anim is not None:
1063
+ anim.stop()
1064
+
1065
+ # Remove event filter
1066
+ filt = getattr(w, "_hoverGlowFilter", None)
1067
+ if filt is not None:
1068
+ try:
1069
+ w.removeEventFilter(filt)
1070
+ except RuntimeError:
1071
+ pass
1072
+
1073
+ # Remove graphics effect
1074
+ w.setGraphicsEffect(None)
1075
+
1076
+ # Restore hover-related flags (safe defaults)
1077
+ w.setAttribute(Qt.WA_Hover, False)
1078
+ w.setMouseTracking(False)
1079
+
1080
+ # Cleanup attributes
1081
+ for attr in (
1082
+ "_hoverGlowInstalled",
1083
+ "_hoverGlowColor",
1084
+ "_hoverGlowBlur",
1085
+ "_hoverGlowMaxAlpha",
1086
+ "_hoverGlowDuration",
1087
+ "_hoverGlowEffect",
1088
+ "_hoverGlowAnimAlpha",
1089
+ "_hoverGlowFilter",
1090
+ ):
1091
+ if hasattr(w, attr):
1092
+ delattr(w, attr)
1093
+
1094
+ class SliderHandleCursorFilter(QObject):
1095
+ def eventFilter(self, obj, event):
1096
+ if isinstance(obj, QSlider) and event.type() == QEvent.MouseMove:
1097
+
1098
+ opt = QStyleOptionSlider()
1099
+ obj.initStyleOption(opt)
1100
+
1101
+ handle_rect = obj.style().subControlRect(
1102
+ QStyle.CC_Slider,
1103
+ opt,
1104
+ QStyle.SC_SliderHandle,
1105
+ obj
1106
+ )
1107
+
1108
+ if handle_rect.contains(event.position().toPoint()):
1109
+ obj.setCursor(QCursor(Qt.OpenHandCursor))
1110
+ else:
1111
+ obj.unsetCursor()
1112
+
1113
+ return False
1114
+
1115
+
1116
+ def changes(self,TabType,filename,title=" Changes"):
1117
+ FlagShow=False
1118
+ if self.logChanges:
1119
+ if self.logChanges.isVisible():
1120
+ FlagShow=True
1121
+ if FlagShow:
1122
+ self.logChanges.hide()
1123
+ self.logChanges.show()
1124
+ else:
1125
+ self.logChanges=TabType(self,True)
1126
+ self.logChanges.resize(720,720)
1127
+ self.logChanges.show()
1128
+ self.logChanges.ui.progress_Proc.hide()
1129
+ self.logChanges.ui.button_close_tab.hide()
1130
+ icon=QPixmap(''+ icons_path +'news.png')
1131
+ self.logChanges.ui.icon.setPixmap(icon)
1132
+ apply_hover_glow_label(self.logChanges.ui.icon)
1133
+ self.logChanges.setWindowIcon(self.windowIcon())
1134
+ self.logChanges.setWindowTitle(title)
1135
+ self.logChanges.ui.name_tab.setText(title)
1136
+
1137
+ self.logChanges.ui.log.setLineWrapColumnOrWidth(self.logChanges.ui.log.width()-20)
1138
+ base="""
1139
+ QTextEdit {
1140
+ border: 1px solid #2a2a2a;
1141
+ border-radius: 6px;
1142
+
1143
+ padding: 2px;
1144
+
1145
+ selection-background-color: #0051FF;
1146
+ selection-color: #FFFFFF;
1147
+ }
1148
+ """
1149
+ self.logChanges.ui.log.setStyleSheet(base+"\n"+gPaIRS_QMenu_style)
1150
+
1151
+ def setFontPixelSize(logChanges:type(self.logChanges),fPixSize):
1152
+ logfont=self.font()
1153
+ logfont.setFamily(fontName)
1154
+ logfont.setPixelSize(fPixSize+2)
1155
+ logChanges.ui.log.setFont(logfont)
1156
+ fPixSize_TabNames=min([fPixSize*2,30])
1157
+ lab=logChanges.ui.name_tab
1158
+ font=lab.font()
1159
+ font.setPixelSize(fPixSize_TabNames)
1160
+ lab.setFont(font)
1161
+ self.logChanges.setFontPixelSize=lambda fS: setFontPixelSize(self.logChanges,fS)
1162
+ self.logChanges.setFontPixelSize(self.TABpar.fontPixelSize)
1163
+ def logResizeEvent(logChanges:type(self.logChanges),e):
1164
+ super(type(logChanges),logChanges).resizeEvent(e)
1165
+ logChanges.ui.log.setLineWrapColumnOrWidth(logChanges.ui.log.width()-20)
1166
+ self.logChanges.ui.log.resizeEvent=lambda e: logResizeEvent(self.logChanges,e)
1167
+
1168
+ self.logChanges.ui.icon.addfuncclick['whatsnew']=self.whatsNew
1169
+ self.logChanges.ui.icon.setCustomCursor()
1170
+
1171
+ try:
1172
+ file = open(filename, "rb")
1173
+ content = file.read().decode("utf-8")
1174
+ self.logChanges.ui.log.setText(content)
1175
+ file.close()
1176
+ except Exception as inst:
1177
+ pri.Error.red(f'There was a problem while reading the file {filename}:\n{inst}')
1178
+ self.logChanges.ui.log.setText(f'No information about PaIRS-UniNa updates available!\n\nSorry for this, try to reinstall PaIRS-UniNa or alternatively contact the authors at {__mail__}.')
1179
+ return
840
1180
 
841
1181
  #********************************************* Matplotlib
842
1182
  import io
@@ -988,6 +1328,7 @@ class MplCanvas(FigureCanvasQTAgg):
988
1328
  fig2.lbl:QLabel=lbl
989
1329
  def contextMenuEventFig2(event):
990
1330
  contextMenu = QMenu()
1331
+ contextMenu.setStyleSheet(self.parent().parent().gui.ui.menu.styleSheet())
991
1332
  copy2clipboard = contextMenu.addAction("Copy to clipboard ("+QS_copy2clipboard.key().toString(QKeySequence.NativeText)+")")
992
1333
  contextMenu.addSeparator()
993
1334
  scaleDown = contextMenu.addAction("Scale down ("+QS_scaleDown.key().toString(QKeySequence.NativeText)+")")
@@ -1039,7 +1380,7 @@ class MplCanvas(FigureCanvasQTAgg):
1039
1380
  """
1040
1381
 
1041
1382
  def showTip(self,obj,message):
1042
- showTip(obj,message)
1383
+ show_mouse_tooltip(obj,message)
1043
1384
 
1044
1385
  def posWindow(self,ind):
1045
1386
  w=h=0
@@ -1115,12 +1456,12 @@ def setAppGuiPalette(self:QWidget,palette:QPalette=None):
1115
1456
  c.setPalette(palette)
1116
1457
  if hasattr(c,'initialStyle') and hasattr(c, 'setStyleSheet'):
1117
1458
  c.setStyleSheet(c.initialStyle)
1118
- for c in f.findChildren(QObject):
1119
- c:MyQLineEdit
1120
- if isinstance(c, MyQLineEdit) and hasattr(c, 'setup'):
1121
- c.initFlag=False
1122
- c.styleFlag=False
1123
- c.setup()
1459
+ for c in f.findChildren(MyQLineEdit):
1460
+ c.initFlag=False
1461
+ c.styleFlag=False
1462
+ c.setup()
1463
+ for c in f.findChildren(ClickableEditLabel):
1464
+ c.setup()
1124
1465
  for c in f.findChildren(QObject):
1125
1466
  if hasattr(c,'setup2'):
1126
1467
  c.initFlag2=False
@@ -1131,3 +1472,162 @@ def setAppGuiPalette(self:QWidget,palette:QPalette=None):
1131
1472
  if hasattr(self,'w_Vis'): self.w_Vis.addPlotToolBar()
1132
1473
  except:
1133
1474
  pri.Error.red("***** Error while setting the application palette! *****")
1475
+
1476
+
1477
+ class Toast(QFrame):
1478
+ """
1479
+ Tooltip-like custom toast that appears at the current mouse cursor position.
1480
+ Non-native, does not steal focus. Auto-hides after timeout_ms.
1481
+ """
1482
+
1483
+ def __init__(
1484
+ self,
1485
+ parent: QWidget,
1486
+ msg: str,
1487
+ *,
1488
+ timeout_ms: int = 2500,
1489
+ offset: QPoint = QPoint(10, 15), # offset from cursor
1490
+ min_width: int = 0,
1491
+ max_width: int = 460,
1492
+ fade_in_ms: int = 80,
1493
+ fade_out_ms: int = 130,
1494
+ ):
1495
+ super().__init__(parent)
1496
+
1497
+ self.setObjectName("PaIRSToast")
1498
+ self.setWindowFlags(Qt.FramelessWindowHint | Qt.ToolTip)
1499
+ #self.setAttribute(Qt.WA_TranslucentBackground, True)
1500
+ self.setAttribute(Qt.WA_ShowWithoutActivating, True)
1501
+
1502
+ self._label = QLabel(msg, self)
1503
+ self._label.setObjectName("PaIRSToastLabel")
1504
+ self._label.setWordWrap(True)
1505
+
1506
+ lay = QHBoxLayout(self)
1507
+ lay.setContentsMargins(4,3,4,3)
1508
+ lay.addWidget(self._label)
1509
+
1510
+ # Classic tooltip-like styling
1511
+ self.setStyleSheet("""
1512
+ QFrame#PaIRSToast {
1513
+ background-color: #ffffdc; /* classic tooltip-ish */
1514
+ color: #000000;
1515
+ border: 1px solid rgba(0, 0, 0, 0.45);
1516
+ border-radius: 4px;
1517
+ }
1518
+ QLabel#PaIRSToastLabel {
1519
+ color: #000000;
1520
+ }
1521
+ """)
1522
+
1523
+ # Width clamp + nice wrapping
1524
+ # Reset any previous constraints (important if a previous tooltip was wider)
1525
+ self.setMinimumSize(min_width, 0)
1526
+ self.setMaximumSize(max_width, 16777215)
1527
+
1528
+ self._label.setMinimumSize(min_width, 0)
1529
+ self._label.setMaximumSize(max_width, 16777215)
1530
+
1531
+ # Make sure the label doesn't "expand"
1532
+ self._label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
1533
+
1534
+ font=self.window().font()
1535
+ self._label.setFont(font)
1536
+ self._apply_size(msg)
1537
+
1538
+ # Position at cursor (global)
1539
+ self._move_to_cursor(offset)
1540
+
1541
+ # Opacity animation
1542
+ self.setWindowOpacity(0.0)
1543
+ self._anim = QPropertyAnimation(self, b"windowOpacity", self)
1544
+ self._anim.setEasingCurve(QEasingCurve.OutCubic)
1545
+
1546
+ # Auto hide
1547
+ self._timer = QTimer(self)
1548
+ self._timer.setSingleShot(True)
1549
+ self._timer.timeout.connect(lambda: self.fade_out(duration_ms=fade_out_ms))
1550
+
1551
+ self.show()
1552
+ self.raise_()
1553
+ self.fade_in(duration_ms=fade_in_ms)
1554
+ self._timer.start(timeout_ms)
1555
+
1556
+ def _apply_size(self, msg: str):
1557
+ self._label.setText(msg)
1558
+
1559
+ # Let QLabel compute the correct wrapped size
1560
+ self._label.adjustSize()
1561
+ sh = self._label.sizeHint()
1562
+
1563
+ # Lock label to its real hint size
1564
+ self._label.setFixedSize(sh)
1565
+
1566
+ # Now shrink the frame to content + margins
1567
+ self.adjustSize()
1568
+
1569
+ # Lock the whole toaster too (prevents extra blank area)
1570
+ self.setFixedSize(self.sizeHint())
1571
+
1572
+ def _move_to_cursor(self, offset: QPoint):
1573
+ parent = self.parent()
1574
+ if hasattr(parent, "cursorRect"):
1575
+ rect = parent.cursorRect() # QRect in coordinate del widget
1576
+ p = parent.mapToGlobal(rect.bottomRight())
1577
+ else:
1578
+ p = QCursor.pos() + offset
1579
+
1580
+ # Keep inside the current screen geometry
1581
+ screen = self.screen()
1582
+ if screen is None:
1583
+ self.move(p)
1584
+ return
1585
+
1586
+ geo = screen.availableGeometry()
1587
+ self.adjustSize()
1588
+ w, h = self.width(), self.height()
1589
+
1590
+ x = p.x()
1591
+ y = p.y()
1592
+
1593
+ if x + w > geo.right():
1594
+ x = geo.right() - w
1595
+ if y + h > geo.bottom():
1596
+ y = geo.bottom() - h
1597
+ if x < geo.left():
1598
+ x = geo.left()
1599
+ if y < geo.top():
1600
+ y = geo.top()
1601
+
1602
+ self.move(QPoint(x, y))
1603
+
1604
+ def fade_in(self, *, duration_ms: int = 80):
1605
+ self._anim.stop()
1606
+ self._anim.setDuration(duration_ms)
1607
+ self._anim.setStartValue(self.windowOpacity())
1608
+ self._anim.setEndValue(1.0)
1609
+ self._anim.start()
1610
+
1611
+ def fade_out(self, *, duration_ms: int = 130):
1612
+ self._anim.stop()
1613
+ self._anim.setDuration(duration_ms)
1614
+ self._anim.setStartValue(self.windowOpacity())
1615
+ self._anim.setEndValue(0.0)
1616
+ self._anim.finished.connect(self.close)
1617
+ self._anim.start()
1618
+
1619
+ def show_mouse_tooltip(parent: QWidget, msg: str, *, timeout_ms: int = 2500):
1620
+ """
1621
+ Convenience function: show a tooltip-like toaster at the mouse cursor.
1622
+ Keeps a reference on parent to avoid garbage collection.
1623
+ """
1624
+ old = getattr(parent, "_pairs_mouse_toaster", None)
1625
+ if old is not None and old.isVisible():
1626
+ old.close()
1627
+
1628
+ if msg:
1629
+ t = Toast(parent, msg, timeout_ms=timeout_ms)
1630
+ else: t=None
1631
+ parent._pairs_mouse_toaster = t
1632
+ return t
1633
+