0

我正在尝试在 PyQt(特别是 PyQt6)中实现(或者更确切地说,使用其他人的实现)QRangeSlider。我找到了这个很好的答案它使用 Qt 的内置样式在一个凹槽上绘制两个滑块句柄,方法是在绘制时在位置之间交替,并分别处理滑块句柄。这一直很好,除了一个例外:样式实际上并没有绘制。我不知道这是否是我的适应问题(我从 pyside2 转换为 pyqt6,虽然 99% 只是从 pyqt6 添加额外的枚举,如 qt.penstyle.none 之类的)还是什么,但我无法获得风格.drawcomplexcontrol 来渲染小部件的组件。通过访问它们的矩形来手动绘制组件并使用 qpainter 绘制它们可以正常工作,但这违背了使用 Qt 的本机样式看起来不错的目的。我还在另一台机器上测试了这段代码,同样的事情发生了。

代码:

from PyQt6.QtGui import QBrush, QCursor, QMouseEvent, QPaintEvent, QPainter, QPalette
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QSizePolicy, QSlider, QStyle, QStyleFactory, QStyleOptionSlider, QWidget
from PyQt6.QtCore import QRect, QSize, Qt
import sys

class RangeSlider(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)


        self.setMouseTracking(True);

        self.first_position = 1
        self.second_position = 8

        self.opt = QStyleOptionSlider()
        self.opt.minimum = 0
        self.opt.maximum = 100

        self.setTickPosition(QSlider.TickPosition.TicksAbove)
        self.setTickInterval(10)

        self.setSizePolicy(
            QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed, QSizePolicy.ControlType.Slider)
        )

    def setRangeLimit(self, minimum: int, maximum: int):
        self.opt.minimum = minimum
        self.opt.maximum = maximum

    def setRange(self, start: int, end: int):
        self.first_position = start
        self.second_position = end

    def getRange(self):
        return (self.first_position, self.second_position)

    def setTickPosition(self, position: QSlider.TickPosition):
        self.opt.tickPosition = position

    def setTickInterval(self, ti: int):
        self.opt.tickInterval = ti

    def paintEvent(self, event: QPaintEvent):

        painter = QPainter(self)

        # Draw rule
        self.opt.initFrom(self)
        self.opt.rect = self.rect()
        self.opt.sliderPosition = 0
        self.opt.subControls = QStyle.SubControl.SC_SliderGroove | QStyle.SubControl.SC_SliderTickmarks

        #   Draw GROOVE
        self.style().drawComplexControl(QStyle.ComplexControl.CC_Slider, self.opt, painter)
        self.style().drawControl(QStyle.ControlElement.CE_ScrollBarSlider, self.opt, painter);

        #  Draw INTERVAL

        color = self.palette().color(QPalette.ColorRole.Highlight)
        color.setAlpha(160)
        painter.setBrush(QBrush(color))
        painter.setPen(Qt.PenStyle.NoPen)

        self.opt.sliderPosition = self.first_position
        left_handle = (
            self.style()
            .subControlRect(QStyle.ComplexControl.CC_Slider, self.opt, QStyle.SubControl.SC_SliderHandle)
        )

        self.opt.sliderPosition = self.second_position
        right_handle = (
            self.style()
            .subControlRect(QStyle.ComplexControl.CC_Slider, self.opt, QStyle.SubControl.SC_SliderHandle)
        )

        groove_rect = self.style().subControlRect(
            QStyle.ComplexControl.CC_Slider, self.opt, QStyle.SubControl.SC_SliderGroove
        )

        print(f"Groove Rect: {groove_rect}, Left handle x: {left_handle}, right handle x: {right_handle}");

        selection = QRect(
            left_handle.right(),
            groove_rect.y(),
            right_handle.left() - left_handle.right(),
            groove_rect.height(),
        ).adjusted(-1, 1, 1, -1)

        painter.drawRect(selection)

        # Draw first handle

        self.opt.subControls = QStyle.SubControl.SC_SliderHandle
        self.opt.sliderPosition = self.first_position
        self.style().drawComplexControl(QStyle.ComplexControl.CC_Slider, self.opt, painter)
        
        painter.setPen(Qt.GlobalColor.gray);
        painter.setBrush(Qt.GlobalColor.gray);
        painter.drawRect(left_handle);
        painter.drawRect(right_handle);

        # Draw second handle
        self.opt.sliderPosition = self.second_position
        self.style().drawComplexControl(QStyle.ComplexControl.CC_Slider, self.opt, painter)

    def mousePressEvent(self, event: QMouseEvent):

        self.opt.sliderPosition = self.first_position
        self._first_sc = self.style().hitTestComplexControl(
            QStyle.ComplexControl.CC_Slider, self.opt, event.position().toPoint(), self
        )

        self.opt.sliderPosition = self.second_position
        self._second_sc = self.style().hitTestComplexControl(
            QStyle.ComplexControl.CC_Slider, self.opt, event.position().toPoint(), self
        )

    def mouseMoveEvent(self, event: QMouseEvent):
        if Qt.MouseButton.LeftButton in event.buttons():
            print("dragging")
            distance = self.opt.maximum - self.opt.minimum

            pos = self.style().sliderValueFromPosition(
                0, distance, event.position().x(), self.rect().width()
            )

            if self._first_sc == QStyle.SubControl.SC_SliderHandle:
                if pos <= self.second_position:
                    self.first_position = pos
                    self.update()
                    return

            if self._second_sc == QStyle.SubControl.SC_SliderHandle:
                if pos >= self.first_position:
                    self.second_position = pos
                    self.update()
        else:
            print("moving")
            print(self.opt.sliderPosition);
            self.opt.sliderPosition = self.first_position;
            first_hit = self.style().hitTestComplexControl(
                QStyle.ComplexControl.CC_Slider, self.opt, event.position().toPoint(), self
            ) == QStyle.SubControl.SC_SliderHandle;
            self.opt.sliderPosition = self.second_position;
            second_hit = self.style().hitTestComplexControl(
                QStyle.ComplexControl.CC_Slider, self.opt, event.position().toPoint(), self
            ) == QStyle.SubControl.SC_SliderHandle;
            self.setCursor(Qt.CursorShape.SizeHorCursor if first_hit or second_hit else QCursor());

        

    def sizeHint(self):
        """ override """
        SliderLength = 84
        TickSpace = 5

        w = SliderLength
        h = self.style().pixelMetric(QStyle.PixelMetric.PM_SliderThickness, self.opt, self)

        if (
            QSlider.TickPosition.TicksAbove == self.opt.tickPosition 
            or QSlider.TickPosition.TicksBelow == self.opt.tickPosition
                    ):
            h += TickSpace

        return (
            self.style()
            .sizeFromContents(QStyle.ContentsType.CT_Slider, self.opt, QSize(w, h), self)
            # .expandedTo(QApplication.globalStrut())
        )


if __name__ == "__main__":

    app = QApplication(sys.argv)

    main = QMainWindow();
    
    #main.setStyle(QStyleFactory.create("breeze"))

    w = RangeSlider()
    main.setCentralWidget(w);
    main.setMenuWidget(QPushButton("hello"));
    main.show();

    # q = QSlider()
    # q.show()

    app.exec()

结果图像:[手动绘制的内凹槽和手柄矩形] 我所看到的

4

0 回答 0