PyQt5弯曲QSlider

时间:2018-12-03 23:32:53

标签: python pyqt5 qpainter

我一直在使用绘画事件自定义PyQt小部件。我一直在尝试自定义QSlider小部件,并取得了一些成功,主要是使用CSS样式。但是,我很难用QPainterPath使它弯曲,因为它看起来总是平坦的。这是否超出了此小部件的功能(这会让我感到惊讶)?以下是我最近的许多尝试的成功尝试。我尝试使用路径点而不是cubicTo()。谁能帮助我或指出正确的方向?

from PyQt5 import QtGui, QtWidgets, QtCore

class slider(QtWidgets.QSlider):
    def __init__(self, parent=None):
        super(slider, self).__init__(parent)
        self.parent = parent
        self.setMinimum(10)
        self.setMaximum(30)
        self.setValue(20)
        self.setTickPosition(QtWidgets.QSlider.TicksBelow)
        self.setTickInterval(5)

    def paintEvent(self, event):
        qp = QtGui.QPainter()
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.Antialiasing)
        self.drawCurve(qp)
        qp.end()

    def drawCurve(self, qp):
        path = QtGui.QPainterPath()
        path.moveTo(30, 30)
        path.cubicTo(30, 30, 200, 350, 200, 30)
        qp.drawPath(path)

1 个答案:

答案 0 :(得分:0)

要具有深度感,只能选择正确的颜色,因为此QPainterPathStroker也被使用。另一方面,我添加了缩放QPainterPath的功能:

from PyQt5 import QtCore, QtGui, QtWidgets

class PathSlider(QtWidgets.QAbstractSlider):
    def __init__(self, path=QtGui.QPainterPath(), *args, **kwargs):
        super(PathSlider, self).__init__(*args, **kwargs)
        self._path = path
        self.stroke_path = self._path
        self.scale_path = self._path

    def setPath(self, path):
        path.translate(-path.boundingRect().topLeft())
        self._path = path
        self.update()

    def path(self):
        return self._path

    path = QtCore.pyqtProperty(QtGui.QPainterPath, fget=path, fset=setPath)

    def paintEvent(self, event):
        border = 10
        painter = QtGui.QPainter(self)
        painter.setRenderHint(QtGui.QPainter.Antialiasing)
        sx, sy = (self.rect().width() -2*border)/self.path.boundingRect().width(), \
                 (self.rect().height() -2*border)/self.path.boundingRect().height()
        tr = QtGui.QTransform()
        tr.translate(border, border)
        tr.scale(sx, sy)
        self.scale_path = tr.map(self.path)
        stroker = QtGui.QPainterPathStroker()
        stroker.setCapStyle(QtCore.Qt.RoundCap)
        stroker.setWidth(4)
        stroke_path = stroker.createStroke(self.scale_path).simplified()
        painter.setPen(QtGui.QPen(self.palette().color(QtGui.QPalette.Shadow), 1))
        painter.setBrush(QtGui.QBrush(self.palette().color(QtGui.QPalette.Midlight)))
        painter.drawPath(stroke_path)
        stroker.setWidth(20)
        self.stroke_path = stroker.createStroke(self.scale_path).simplified()
        percentage = (self.value() - self.minimum())/(self.maximum() - self.minimum())
        highlight_path = QtGui.QPainterPath()
        highlight_path.moveTo(self.scale_path.pointAtPercent(0))
        n_p = int((self.maximum() + 1 - self.minimum())/self.singleStep())
        for i in range(n_p+1):
            d = i*percentage/n_p
            p = self.scale_path.pointAtPercent(d)
            highlight_path.lineTo(p)
        stroker.setWidth(3)
        new_phighlight_path = stroker.createStroke(highlight_path).simplified()

        activeHighlight = self.palette().color(QtGui.QPalette.Highlight)
        painter.setPen(activeHighlight)
        painter.setBrush(QtGui.QBrush(QtGui.QColor(activeHighlight)))
        painter.drawPath(new_phighlight_path)

        opt  = QtWidgets.QStyleOptionSlider()
        r = self.style().subControlRect(QtWidgets.QStyle.CC_Slider, opt, QtWidgets.QStyle.SC_SliderHandle, self)
        pixmap = QtGui.QPixmap(r.width() + 2*2, r.height() + 2*2)
        pixmap.fill(QtCore.Qt.transparent)
        r = pixmap.rect().adjusted(2, 2, -2, -2)
        pixmap_painter = QtGui.QPainter(pixmap)
        pixmap_painter.setRenderHint(QtGui.QPainter.Antialiasing)
        pixmap_painter.setPen(QtGui.QPen(self.palette().color(QtGui.QPalette.Shadow), 2))
        pixmap_painter.setBrush(self.palette().color(QtGui.QPalette.Base))
        pixmap_painter.drawRoundedRect(r, 4, 4)
        pixmap_painter.end()
        r.moveCenter(p.toPoint())
        painter.drawPixmap(r, pixmap)

    def minimumSizeHint(self):
        return QtCore.QSize(15, 15)

    def sizeHint(self):
        return QtCore.QSize(336, 336)

    def mousePressEvent(self, event):
        self.update_pos(event.pos())
        super(PathSlider, self).mousePressEvent(event)

    def mouseMoveEvent(self, event):
        self.update_pos(event.pos())
        super(PathSlider, self).mouseMoveEvent(event)

    def update_pos(self, point):
        if self.stroke_path.contains(point):
            n_p = int((self.maximum() + 1 - self.minimum())/self.singleStep())
            ls = []
            for i in range(n_p):
                p = self.scale_path.pointAtPercent(i*1.0/n_p)
                ls.append(QtCore.QLineF(point, p).length())
            j = ls.index(min(ls))
            val = int(j*(self.maximum() + 1 - self.minimum())/n_p)
            self.setValue(val)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv) 
    w = QtWidgets.QWidget()

    s1 = PathSlider(minimum=0, maximum=100)
    s2 = PathSlider(minimum=0, maximum=100)
    s = QtWidgets.QSlider(minimum=0, maximum=100)
    s.valueChanged.connect(s1.setValue)
    s.valueChanged.connect(s2.setValue)
    s1.valueChanged.connect(s.setValue)
    s2.valueChanged.connect(s.setValue)

    c1 = QtCore.QPointF(5, -15) 
    c2 = QtCore.QPointF(220, -15) 
    path = QtGui.QPainterPath(QtCore.QPointF(5, 100)) 
    path.cubicTo(c1, c2, QtCore.QPointF(235, 100))
    s1.setPath(path)

    c1 = QtCore.QPointF(5, 15) 
    c2 = QtCore.QPointF(220, 15) 
    path = QtGui.QPainterPath(QtCore.QPointF(5, -100)) 
    path.cubicTo(c1, c2, QtCore.QPointF(235, -100))
    s2.setPath(path)

    lay = QtWidgets.QHBoxLayout(w)
    lay.addWidget(s1)
    lay.addWidget(s2)
    lay.addWidget(s)
    w.show()
    sys.exit(app.exec_())

enter image description here

enter image description here