Source code for QInstrument.widgets.QRotaryEncoder
import sys
import logging
from qtpy import QtCore
from qtpy.QtWidgets import QDial
import numpy as np
logger = logging.getLogger(__name__)
[docs]
class QRotaryEncoder(QDial):
'''QDial subclass that emits directional step signals.
Wraps the full dial range and emits :attr:`stepUp` or
:attr:`stepDown` each time the user turns the dial by one step,
correctly handling wrap-around.
Properties
==========
steps : int
Number of discrete steps in one full turn. Default: 100.
Signals
-------
stepUp
Emitted each time the dial turns one step clockwise.
stepDown
Emitted each time the dial turns one step counter-clockwise.
'''
stepUp = QtCore.Signal()
stepDown = QtCore.Signal()
def __init__(self, *args, steps: int | None = None, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.setWrapping(True)
self.setMinimum(0)
self.setSteps(steps or 100)
self._value = self.value()
self.valueChanged.connect(self._emitTick)
@QtCore.Slot(int)
def _emitTick(self, value: int) -> None:
'''Emit :attr:`stepUp` or :attr:`stepDown` based on dial movement.
Detects wrap-around by checking whether the raw delta exceeds
half the dial range, and inverts the direction accordingly.
Parameters
----------
value : int
New dial value emitted by ``valueChanged``.
'''
if value == self._value:
return
delta = value - self._value
self._value = value
direction = int(np.sign(delta))
if np.abs(delta) > self.maximum() / 2:
direction *= -1
if direction > 0:
self.stepUp.emit()
logger.debug(f'{value}: up')
else:
self.stepDown.emit()
logger.debug(f'{value}: down')
[docs]
def setSteps(self, value: int) -> None:
'''Set the number of steps in one full turn.
Parameters
----------
value : int
Number of discrete steps. Sets the dial maximum to
``value - 1`` so that the range is ``[0, value - 1]``.
'''
self._steps = int(value)
self.setMaximum(self._steps - 1)
[docs]
def steps(self) -> int:
'''Return the number of steps in one full turn.'''
return self._steps
def example() -> None:
from qtpy.QtWidgets import QApplication
def report(value: int) -> None:
print(f'value = {value:03d}', end='\r')
app = QApplication.instance() or QApplication(sys.argv)
encoder = QRotaryEncoder(steps=200)
encoder.valueChanged.connect(report)
encoder.show()
sys.exit(app.exec())
__all__ = ['QRotaryEncoder']
if __name__ == '__main__':
example()