1
1
from __future__ import annotations
2
2
3
- import contextlib
4
3
from enum import IntEnum , IntFlag , auto
5
4
from functools import partial
6
5
from typing import TYPE_CHECKING , Any , overload
7
6
8
7
from qtpy import QtGui
9
8
from qtpy .QtCore import Property , QPoint , QSize , Qt , Signal
10
- from qtpy .QtGui import QFontMetrics , QValidator
9
+ from qtpy .QtGui import QDoubleValidator , QFontMetrics , QValidator
11
10
from qtpy .QtWidgets import (
12
11
QAbstractSlider ,
13
12
QBoxLayout ,
14
- QDoubleSpinBox ,
15
13
QHBoxLayout ,
14
+ QLineEdit ,
16
15
QSlider ,
17
- QSpinBox ,
18
16
QStyle ,
19
17
QStyleOptionSpinBox ,
20
18
QVBoxLayout ,
@@ -660,7 +658,7 @@ def _setBarColor(self, color: str) -> None:
660
658
"""The color of the bar between the first and last handle."""
661
659
662
660
663
- class SliderLabel (QDoubleSpinBox ):
661
+ class SliderLabel (QLineEdit ):
664
662
def __init__ (
665
663
self ,
666
664
slider : QSlider ,
@@ -670,52 +668,139 @@ def __init__(
670
668
) -> None :
671
669
super ().__init__ (parent = parent )
672
670
self ._slider = slider
671
+ self ._prefix = ""
672
+ self ._suffix = ""
673
+ self ._min = slider .minimum ()
674
+ self ._max = slider .maximum ()
675
+ self ._value = self ._min
676
+ self ._callback = connect
677
+ self ._decimals = - 1
673
678
self .setFocusPolicy (Qt .FocusPolicy .ClickFocus )
674
679
self .setMode (EdgeLabelMode .LabelIsValue )
675
680
self .setDecimals (0 )
681
+ self .setText (str (self ._value ))
682
+ validator = QDoubleValidator (self )
683
+ validator .setNotation (QDoubleValidator .Notation .ScientificNotation )
684
+ self .setValidator (validator )
676
685
677
- self .setRange (slider .minimum (), slider .maximum ())
678
686
slider .rangeChanged .connect (self ._update_size )
679
687
self .setAlignment (alignment )
680
- self .setButtonSymbols (QSpinBox .ButtonSymbols .NoButtons )
681
688
self .setStyleSheet ("background:transparent; border: 0;" )
682
689
if connect is not None :
683
- self .editingFinished .connect (lambda : connect ( self .value ()) )
690
+ self .editingFinished .connect (self ._editing_finished )
684
691
self .editingFinished .connect (self ._silent_clear_focus )
685
692
self ._update_size ()
686
693
694
+ def _editing_finished (self ):
695
+ self ._silent_clear_focus ()
696
+ self .setValue (float (self .text ()))
697
+ if self ._callback :
698
+ self ._callback (self .value ())
699
+
700
+ def setRange (self , min_ : float , max_ : float ) -> None :
701
+ if self ._mode == EdgeLabelMode .LabelIsRange :
702
+ max_val = max (abs (min_ ), abs (max_ ))
703
+ n_digits = max (len (str (int (max_val ))), 7 )
704
+ upper_bound = int ("9" * n_digits )
705
+ self ._min = - upper_bound
706
+ self ._max = upper_bound
707
+ self ._update_size ()
708
+ else :
709
+ max_ = max (max_ , min_ )
710
+ self ._min = min_
711
+ self ._max = max_
712
+
687
713
def setDecimals (self , prec : int ) -> None :
688
- super ().setDecimals (prec )
714
+ # super().setDecimals(prec)
715
+ self ._decimals = prec
689
716
self ._update_size ()
690
717
718
+ def decimals (self ) -> int :
719
+ """Return the number of decimals used in the label."""
720
+ return self ._decimals
721
+
722
+ def value (self ) -> float :
723
+ return self ._value
724
+
691
725
def setValue (self , val : Any ) -> None :
692
- super ().setValue (val )
726
+ if val < self ._min :
727
+ val = self ._min
728
+ elif val > self ._max :
729
+ val = self ._max
730
+ self ._value = val
731
+ self .updateText ()
732
+
733
+ def updateText (self ) -> None :
734
+ val = float (self ._value )
735
+ use_scientific = (abs (val ) < 0.0001 or abs (val ) > 9999999.0 ) and val != 0.0
736
+ font_metrics = QFontMetrics (self .font ())
737
+ eight_len = _fm_width (font_metrics , "8" )
738
+
739
+ available_chars = self .width () // eight_len
740
+
741
+ total , _fraction = f"{ val :.<f} " .split ("." )
742
+
743
+ if len (total ) > available_chars :
744
+ use_scientific = True
745
+
746
+ if self ._decimals < 0 :
747
+ if use_scientific :
748
+ mantissa , exponent = f"{ val :.{available_chars }e} " .split ("e" )
749
+ mantissa = mantissa .rstrip ("0" ).rstrip ("." )
750
+ if len (mantissa ) + len (exponent ) + 1 < available_chars :
751
+ text = f"{ mantissa } e{ exponent } "
752
+ else :
753
+ decimals = max (available_chars - len (exponent ) - 3 , 2 )
754
+ text = f"{ val :.{decimals }e} "
755
+
756
+ else :
757
+ decimals = max (available_chars - len (total ) - 1 , 2 )
758
+ text = f"{ val :.{decimals }f} "
759
+ text = text .rstrip ("0" ).rstrip ("." )
760
+ else :
761
+ if use_scientific :
762
+ mantissa , exponent = f"{ val :.{self ._decimals }e} " .split ("e" )
763
+ mantissa = mantissa .rstrip ("0" ).rstrip ("." )
764
+ text = f"{ mantissa } e{ exponent } "
765
+ else :
766
+ text = f"{ val :.{self ._decimals }f} "
767
+ if text == "" :
768
+ text = "0"
769
+ self .setText (text )
693
770
if self ._mode == EdgeLabelMode .LabelIsRange :
694
771
self ._update_size ()
695
772
696
- def setMaximum (self , max : float ) -> None :
697
- super ().setMaximum (max )
698
- if self ._mode == EdgeLabelMode .LabelIsValue :
699
- self ._update_size ()
773
+ def minimum (self ):
774
+ return self ._min
700
775
701
- def setMinimum (self , min : float ) -> None :
702
- super ().setMinimum (min )
703
- if self ._mode == EdgeLabelMode .LabelIsValue :
704
- self ._update_size ()
776
+ def setMaximum (self , max_ : float ) -> None :
777
+ self .setRange (self ._min , max_ )
778
+
779
+ def maximum (self ):
780
+ return self ._max
781
+
782
+ def setMinimum (self , min_ : float ) -> None :
783
+ self .setRange (min_ , self ._max )
705
784
706
785
def setMode (self , opt : EdgeLabelMode ) -> None :
707
786
# when the edge labels are controlling slider range,
708
787
# we want them to have a big range, but not have a huge label
709
788
self ._mode = opt
710
- if opt == EdgeLabelMode .LabelIsRange :
711
- self .setMinimum (- 9999999 )
712
- self .setMaximum (9999999 )
713
- with contextlib .suppress (Exception ):
714
- self ._slider .rangeChanged .disconnect (self .setRange )
715
- else :
716
- self .setMinimum (self ._slider .minimum ())
717
- self .setMaximum (self ._slider .maximum ())
718
- self ._slider .rangeChanged .connect (self .setRange )
789
+ self .setRange (self ._slider .minimum (), self ._slider .maximum ())
790
+ self ._update_size ()
791
+
792
+ def prefix (self ) -> str :
793
+ return self ._prefix
794
+
795
+ def setPrefix (self , prefix : str ) -> None :
796
+ self ._prefix = prefix
797
+ self ._update_size ()
798
+
799
+ def suffix (self ) -> str :
800
+ return self ._suffix
801
+
802
+ def setSuffix (self , suffix : str ) -> None :
803
+ self ._suffix = suffix
719
804
self ._update_size ()
720
805
721
806
# --------------- private ----------------
@@ -732,21 +817,19 @@ def _update_size(self, *_: Any) -> None:
732
817
733
818
if self ._mode & EdgeLabelMode .LabelIsValue :
734
819
# determine width based on min/max/specialValue
735
- mintext = self . textFromValue (self .minimum ())[:18 ]
736
- maxtext = self . textFromValue (self .maximum ())[:18 ]
820
+ mintext = str (self .minimum ())[:18 ]
821
+ maxtext = str (self .maximum ())[:18 ]
737
822
w = max (0 , _fm_width (fm , mintext + fixed_content ))
738
823
w = max (w , _fm_width (fm , maxtext + fixed_content ))
739
- if self .specialValueText ():
740
- w = max (w , _fm_width (fm , self .specialValueText ()))
741
824
if self ._mode & EdgeLabelMode .LabelIsRange :
742
825
w += 8 # it seems as thought suffix() is not enough
743
826
else :
744
- w = max (0 , _fm_width (fm , self . textFromValue (self .value ()))) + 3
827
+ w = max (0 , _fm_width (fm , str (self .value ()))) + 3
745
828
746
829
w += 3 # cursor blinking space
747
830
# get the final size hint
748
831
opt = QStyleOptionSpinBox ()
749
- self .initStyleOption (opt )
832
+ # self.initStyleOption(opt)
750
833
size = self .style ().sizeFromContents (
751
834
QStyle .ContentsType .CT_SpinBox , opt , QSize (w , h ), self
752
835
)
0 commit comments