File: slider.py

package info (click to toggle)
python-pyqtgraph 0.13.7-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 8,068 kB
  • sloc: python: 54,043; makefile: 129; ansic: 40; sh: 2
file content (132 lines) | stat: -rw-r--r-- 4,642 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import numpy as np

from ...Qt import QtCore, QtWidgets
from ..Parameter import Parameter
from .basetypes import Emitter, WidgetParameterItem


class SliderParameterItem(WidgetParameterItem):
    slider: QtWidgets.QSlider
    span: np.ndarray
    charSpan: np.ndarray

    def __init__(self, param, depth):
        # Bind emitter to self to avoid garbage collection
        self.emitter = Emitter()
        self.sigChanging = self.emitter.sigChanging
        self._suffix = None
        super().__init__(param, depth)

    def updateDisplayLabel(self, value=None):
        if value is None:
            value = self.param.value()
        self.sliderLabel.setText(self.prettyTextValue(self.slider.value()))
        value = str(value)
        if self._suffix is None:
            suffixTxt = ''
        else:
            suffixTxt = f' {self._suffix}'
        self.displayLabel.setText(value + suffixTxt)


    def setSuffix(self, suffix):
        self._suffix = suffix
        # This may be called during widget construction in which case there is no
        # displayLabel yet
        if hasattr(self, 'displayLabel'):
            self.updateDisplayLabel(self.slider.value())

    def makeWidget(self):
        param = self.param
        opts = param.opts
        opts.setdefault('limits', [0, 0])
        self._suffix = opts.get('suffix')

        self.slider = QtWidgets.QSlider()
        self.slider.setOrientation(QtCore.Qt.Orientation.Horizontal)
        lbl = self.sliderLabel = QtWidgets.QLabel()
        lbl.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

        w = QtWidgets.QWidget()
        layout = QtWidgets.QHBoxLayout()
        w.setLayout(layout)
        layout.addWidget(lbl)
        layout.addWidget(self.slider)

        def setValue(v):
            self.slider.setValue(self.spanToSliderValue(v))

        def getValue():
            return self.span[self.slider.value()].item()

        self.slider.valueChanged.connect(self.updateDisplayLabel)

        def onMove(pos):
            self.sigChanging.emit(self, self.span[pos].item())

        self.slider.sliderMoved.connect(onMove)

        w.setValue = setValue
        w.value = getValue
        w.sigChanged = self.slider.valueChanged
        w.sigChanging = self.sigChanging
        self.optsChanged(param, opts)
        return w

    def spanToSliderValue(self, v):
        return int(np.argmin(np.abs(self.span - v)))

    def prettyTextValue(self, v):
        if self._suffix is None:
            suffixTxt = ''
        else:
            suffixTxt = f' {self._suffix}'
        format_ = self.param.opts.get('format', None)
        cspan = self.charSpan
        if format_ is None:
            format_ = f'{{0:>{cspan.dtype.itemsize}}}{suffixTxt}'
        return format_.format(cspan[v].decode())

    def optsChanged(self, param, opts):
        try:
            super().optsChanged(param, opts)
        except AttributeError:
            # This may trigger while building the parameter before the widget is fully constructed.
            # This is fine, since errors are from the parent scope which will stabilize after the widget is
            # constructed anyway
            pass
        span = opts.get('span', None)
        if span is None:
            step = opts.get('step', 1)
            start, stop = opts.get('limits', param.opts['limits'])
            # Add a bit to 'stop' since python slicing excludes the last value
            span = np.arange(start, stop + step, step)
        precision = opts.get('precision', 2)
        if precision is not None:
            span = span.round(precision)
        self.span = span
        self.charSpan = np.char.array(span)
        w = self.slider
        w.setMinimum(0)
        w.setMaximum(len(span) - 1)
        if 'suffix' in opts:
            self.setSuffix(opts['suffix'])

    def limitsChanged(self, param, limits):
        self.optsChanged(param, dict(limits=limits))


class SliderParameter(Parameter):
    """
    ============== ========================================================
    **Options**
    limits         [start, stop] numbers
    step:          Defaults to 1, the spacing between each slider tick
    span:          Instead of limits + step, span can be set to specify
                   the range of slider options (e.g. np.linspace(-pi, pi, 100))
    format:        Format string to determine number of decimals to show, etc.
                   Defaults to display based on span dtype
    precision:     int number of decimals to keep for float tick spaces
    ============== ========================================================
    """
    itemClass = SliderParameterItem