【问题标题】:PyQt5 Curved QSliderPyQt5 弯曲 QSlider
【发布时间】:2018-12-03 23:32:52
【问题描述】:

我一直在使用绘画事件自定义 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)

【问题讨论】:

    标签: python pyqt5 qpainter


    【解决方案1】:

    要获得深度感,只需选择正确的颜色,因为也使用了这个 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_())
    

    【讨论】:

    • 谢谢 Eyllanesc。在过去的几天里,我一直在阅读这些类和方法中的大部分内容。我在反转曲线上略有挣扎。如果我使用这些坐标:c1 = QtCore.QPointF(5, -15) c2 = QtCore.QPointF(220, -15) self._path = QtGui.QPainterPath(QtCore.QPointF(5, 100)) self._path.cubicTo(c1, c2, QtCore.QPointF(235, 100)) 我会得到一个可爱的半圆。如果我尝试反转它,它总是倾斜的,例如:c1 = QtCore.QPointF(5, 15) c2 = QtCore.QPointF(220, 15) self._path = QtGui.QPainterPath(QtCore.QPointF(5, -100)) self._path.cubicTo(c1, c2, QtCore.QPointF(235, -100))
    • @xnemesis 我在测试中考虑过 QPainterPath 属于 x 和 y 为正的象限,所以这是我的错误,请原谅。现在我解决了。另一方面,不要忘记将我的答案标记为正确,如果您不知道该怎么做,请查看tour,这是最好的感谢方式。
    • 永远是 eyllanesc,你现在是我的蟒蛇大师;)谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-05
    • 2018-10-28
    • 1970-01-01
    • 2015-01-10
    • 2021-06-18
    • 1970-01-01
    • 2021-02-21
    相关资源
    最近更新 更多