replace ScaledScrollBar with BigScrollBar

step 1 - manually moving the slider
This commit is contained in:
2024-04-13 08:47:36 +02:00
parent 2b65e61e43
commit 3d6cf84cd7
2 changed files with 178 additions and 4 deletions

View File

@@ -0,0 +1,173 @@
from PySide6.QtWidgets import QWidget, QStylePainter, QStyle, QStyleOptionSlider, QSlider
from PySide6.QtCore import Qt, QSize, QEvent, QRect, QPoint, Signal
class BigScrollBar(QWidget):
value_changed = Signal(str)
"""Signal emitted when the range slider 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."""
pressedControl = QStyle.SubControl.SC_None
click_offset = 0
scale = 10000
minimun = 0
value = 0
maximum = 100
def __init__(self):
super(BigScrollBar, self).__init__()
def setMinimum(self, min: int):
self.minimun = min
if self.value < self.minimun:
self.set_value(self.minimun)
def setMaximum(self, max: int):
self.maximum = max
if self.value > self.maximum:
self.set_value(self.maximum)
def paintEvent(self, event):
p = QStylePainter(self)
o = self.style_options()
print(f"style_options: sliderPosition: {o.sliderPosition}")
p.drawComplexControl(QStyle.ComplexControl.CC_ScrollBar, o)
def style_options(self) -> QStyleOptionSlider:
o = QStyleOptionSlider()
o.initFrom(self)
o.orientation = Qt.Orientation.Vertical
o.subControls = QStyle.SubControl.SC_All
if self.pressedControl != QStyle.SubControl.SC_None:
o.activeSubControls = self.pressedControl # QStyle.SubControl.SC_None
o.state = o.state | QStyle.StateFlag.State_Sunken
o.singleStep = 1
# scale values to 10000
t = self.scale / self.maximum
o.minimum = self.minimun * t
o.maximum = self.maximum * t
o.sliderPosition = int(self.value * t)
print(f"t={t}")
print(f"({self.minimun}, {self.value}, {self.maximum}) -> ({o.minimum},{o.sliderPosition},{o.maximum})")
return o
## QSize QScrollBar::sizeHint() const
# {
# ensurePolished();
# QStyleOptionSlider opt;
# initStyleOption(&opt);
# int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
# int scrollBarSliderMin = style()->pixelMetric(QStyle::PM_ScrollBarSliderMin, &opt, this);
# QSize size;
# if (opt.orientation == Qt::Horizontal)
# size = QSize(scrollBarExtent * 2 + scrollBarSliderMin, scrollBarExtent);
# else
# size = QSize(scrollBarExtent, scrollBarExtent * 2 + scrollBarSliderMin);
#
# return style()->sizeFromContents(QStyle::CT_ScrollBar, &opt, size, this);
# }
def sizeHint(self):
scroll_bar_extend = self.style().pixelMetric(QStyle.PixelMetric.PM_ScrollBarExtent)
scroll_bar_slider_min = self.style().pixelMetric(QStyle.PixelMetric.PM_ScrollBarSliderMin)
# print(f"scroll_bar_extend: {scroll_bar_extend}, scroll_bar_slider_min: {scroll_bar_slider_min}")
# for vertial
size = QSize(scroll_bar_extend, scroll_bar_extend * 2 + scroll_bar_slider_min)
o = self.style_options()
return self.style().sizeFromContents(QStyle.ContentsType.CT_ScrollBar, o, size, self)
def event(self, event: QEvent) -> bool:
# print(f"event type: {event.type()}")
if event.type() == QEvent.Type.MouseButtonPress:
pass
# print(f"mouse button pressed")
return super().event(event)
def mousePressEvent(self, event):
style_options = self.style_options()
style_options.keyboardModifiers = event.modifiers() ## not sure why, copied from https://github.com/qt/qtbase/blob/dev/src/widgets/widgets/qscrollbar.cpp#L535
self.pressedControl = self.style().hitTestComplexControl(QStyle.ComplexControl.CC_ScrollBar, style_options,
event.position().toPoint(), self)
# print(f"pressedControl {self.pressedControl}")
sr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, style_options,
QStyle.SubControl.SC_ScrollBarSlider, self)
click: QPoint = event.position().toPoint()
pressYValue = click.y() - sr.center().y() + sr.topLeft().y()
pressYValue = self.pixelPosToRangeValue(pressYValue)
# print(f"pressYValue {pressYValue}")
if self.pressedControl == QStyle.SubControl.SC_ScrollBarSlider:
self.click_offset = click.y() - sr.y()
# todo
if (self.pressedControl == QStyle.SubControl.SC_ScrollBarAddPage
or self.pressedControl == QStyle.SubControl.SC_ScrollBarSubPage) \
and event.button() == Qt.MouseButton.MiddleButton:
slider_length = sr.height()
self.set_value(self.pixelPosToRangeValue(event.position().toPoint().y()))
self.pressedControl = QStyle.SubControl.SC_ScrollBarSlider
self.click_offset = slider_length / 2
pass
def mouseReleaseEvent(self, event):
self.pressedControl = QStyle.SubControl.SC_None
def mouseMoveEvent(self, event):
if self.pressedControl == QStyle.SubControl.SC_None:
return
if self.pressedControl == QStyle.SubControl.SC_ScrollBarSlider:
click: QPoint = event.position().toPoint()
new_position = self.pixelPosToRangeValue(click.y() - self.click_offset)
m = self.style().pixelMetric(QStyle.PixelMetric.PM_MaximumDragDistance, self.style_options(), self)
if m >= 0:
r: QRect = self.rect()
r.adjust(-m, -m, m, m)
if not r.contains(event.position().toPoint()):
new_position = self.snap_back_position
# print(f"move to value: {new_position}")
self.set_value(new_position)
def pixelPosToRangeValue(self, pos: int):
opt: QStyleOptionSlider = self.style_options()
gr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, opt,
QStyle.SubControl.SC_ScrollBarGroove, self)
sr: QRect = self.style().subControlRect(QStyle.ComplexControl.CC_ScrollBar, opt,
QStyle.SubControl.SC_ScrollBarSlider, self)
# only for vertical scrollbars
sliderLength = sr.height();
sliderMin = gr.y();
sliderMax = gr.bottom() - sliderLength + 1;
val = QStyle.sliderValueFromPosition(opt.minimum, opt.maximum, pos - sliderMin,
sliderMax - sliderMin, opt.upsideDown)
t = self.scale / self.maximum
val = int(val / t)
# print(f"pixelPosToRangeValue({pos}) -> {val}")
return val
def set_value(self, value: int):
self.value = value
self.value_changed.emit(str(value))
self.repaint()

View File

@@ -11,6 +11,7 @@ from PySide6.QtGui import QMouseEvent
from PySide6.QtWidgets import *
from src.ui.ScaledScrollBar import ScaledScrollBar
from src.ui.bigtext.BigScrollBar import BigScrollBar
from src.ui.bigtext.highlight_regex import HighlightRegex
from src.ui.bigtext.highlight_selection import HighlightSelection
from src.ui.bigtext.highlighted_range import HighlightedRange
@@ -86,7 +87,7 @@ class BigText(QWidget):
self.grid.setVerticalSpacing(0)
self.setLayout(self.grid)
self.v_scroll_bar = ScaledScrollBar()
self.v_scroll_bar = BigScrollBar()
self.big_text = InnerBigText(self, model, self.v_scroll_bar)
self.big_text.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding))
@@ -97,7 +98,7 @@ class BigText(QWidget):
self.h_scroll_bar.valueChanged.connect(self.big_text.h_scroll_event)
# self.v_scroll_bar.setPageStep(1)
self.v_scroll_bar.scaledValueChanged.connect(self.big_text.v_scroll_event)
self.v_scroll_bar.value_changed.connect(self.big_text.v_scroll_event)
if show_range_slider:
self.range_limit = RangeSlider()
@@ -308,12 +309,12 @@ class InnerBigText(QWidget):
def scroll_by_lines(self, scroll_lines: int):
self.scroll_lines = scroll_lines
self.update()
self.parent.v_scroll_bar.setValue(self._byte_offset)
self.parent.v_scroll_bar.set_value(self._byte_offset)
def scroll_to_byte(self, byte_offset: int):
self._byte_offset = min(byte_offset, self.model.byte_count())
self.update()
self.parent.v_scroll_bar.setValue(self._byte_offset)
self.parent.v_scroll_bar.set_value(self._byte_offset)
# noinspection PyTypeChecker
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None: