import math from PySide6.QtWidgets import QScrollBar from PySide6.QtCore import Signal import logging log = logging.getLogger("scaledScrollBar") class ScaledScrollBar(QScrollBar): is_huge = False scaledValueChanged = Signal(str) """Signal emitted when the scroll bar value changes. **Note**: The value is a string and must be parsed into an int. QT's signal api only supports 32bit integers. Ints larger than 2**32-1 will overflow. Probably because there is some C/C++ code involved. We work around this by converting the python int into a string.""" def __init__(self): super(ScaledScrollBar, self).__init__() self.real_maximum = self.maximum() super().valueChanged.connect(self._valueChanged) def setValue(self, value: int) -> None: if self.is_huge: real_position = value / self.real_maximum super().setValue(round(self.maximum() * real_position)) else: super().setValue(value) def setMaximum(self, maximum: int) -> None: if maximum > 2 ** 31: new_maximum = 1000 * math.log2(maximum) super().setMaximum(math.ceil(new_maximum)) self.real_maximum = maximum if not self.is_huge: old_position = self.value() / self.maximum() self.setValue(round(new_maximum * old_position)) self.is_huge = True else: self.is_huge = False super().setMaximum(maximum) def _valueChanged(self, value: int): if self.is_huge: real_value = (value / self.maximum()) * self.real_maximum # print("scaled value changed: %d -> %d (%f)" % (value, real_value, value / self.maximum())) self.scaledValueChanged.emit(str(int(real_value))) else: self.scaledValueChanged.emit(str(int(value)))