Any PyQt circular progress bar?

前端 未结 3 1306
故里飘歌
故里飘歌 2021-01-07 15:07

Does anybody know how I can implement circular progress bar on PyQt?

Also, I found an existing code: http://sourceforge.net/projects/qroundprogressbar/

But,

相关标签:
3条回答
  • 2021-01-07 15:49

    I have ported QRoundProgressBar on PyQt (and fixed some minor bugs):

    from PyQt4 import QtCore, QtGui, Qt
    
    class QRoundProgressBar(QtGui.QWidget):
    
        StyleDonut = 1
        StylePie = 2
        StyleLine = 3
    
        PositionLeft = 180
        PositionTop = 90
        PositionRight = 0
        PositionBottom = -90
    
        UF_VALUE = 1
        UF_PERCENT = 2
        UF_MAX = 4
    
        def __init__(self):
            super().__init__()
            self.min = 0
            self.max = 100
            self.value = 25
    
            self.nullPosition = self.PositionTop
            self.barStyle = self.StyleDonut
            self.outlinePenWidth =1
            self.dataPenWidth = 1
            self.rebuildBrush = False
            self.format = "%p%"
            self.decimals = 1
            self.updateFlags = self.UF_PERCENT
            self.gradientData = []
            self.donutThicknessRatio = 0.75
    
        def setRange(self, min, max):
            self.min = min
            self.max = max
    
            if self.max < self.min:
                self.max, self.min = self.min, self.max
    
            if self.value < self.min:
                self.value = self.min
            elif self.value > self.max:
                self.value = self.max
    
            if not self.gradientData:
                self.rebuildBrush = True
            self.update()
    
        def setMinimun(self, min):
            self.setRange(min, self.max)
    
        def setMaximun(self, max):
            self.setRange(self.min, max)
    
        def setValue(self, val):
            if self.value != val:
                if val < self.min:
                    self.value = self.min
                elif val > self.max:
                    self.value = self.max
                else:
                    self.value = val
                self.update()
    
        def setNullPosition(self, position):
            if position != self.nullPosition:
                self.nullPosition = position
                if not self.gradientData:
                    self.rebuildBrush = True
                self.update()
    
        def setBarStyle(self, style):
            if style != self.barStyle:
                self.barStyle = style
                self.update()
    
        def setOutlinePenWidth(self, penWidth):
            if penWidth != self.outlinePenWidth:
                self.outlinePenWidth = penWidth
                self.update()
    
        def setDataPenWidth(self, penWidth):
            if penWidth != self.dataPenWidth:
                self.dataPenWidth = penWidth
                self.update()
    
        def setDataColors(self, stopPoints):
            if stopPoints != self.gradientData:
                self.gradientData = stopPoints
                self.rebuildBrush = True
                self.update()
    
        def setFormat(self, format):
            if format != self.format:
                self.format = format
                self.valueFormatChanged()
    
        def resetFormat(self):
            self.format = ''
            self.valueFormatChanged()
    
        def setDecimals(self, count):
            if count >= 0 and count != self.decimals:
                self.decimals = count
                self.valueFormatChanged()
    
        def setDonutThicknessRatio(self, val):
            self.donutThicknessRatio = max(0., min(val, 1.))
            self.update()
    
        def paintEvent(self, event):
            outerRadius = min(self.width(), self.height())
            baseRect = QtCore.QRectF(1, 1, outerRadius-2, outerRadius-2)
    
            buffer = QtGui.QImage(outerRadius, outerRadius, QtGui.QImage.Format_ARGB32)
            buffer.fill(0)
    
            p = QtGui.QPainter(buffer)
            p.setRenderHint(QtGui.QPainter.Antialiasing)
    
            # data brush
            self.rebuildDataBrushIfNeeded()
    
            # background
            self.drawBackground(p, buffer.rect())
    
            # base circle
            self.drawBase(p, baseRect)
    
            # data circle
            arcStep = 360.0 / (self.max - self.min) * self.value
            self.drawValue(p, baseRect, self.value, arcStep)
    
            # center circle
            innerRect, innerRadius = self.calculateInnerRect(baseRect, outerRadius)
            self.drawInnerBackground(p, innerRect)
    
            # text
            self.drawText(p, innerRect, innerRadius, self.value)
    
            # finally draw the bar
            p.end()
    
            painter = QtGui.QPainter(self)
            painter.drawImage(0, 0, buffer)
    
        def drawBackground(self, p, baseRect):
            p.fillRect(baseRect, self.palette().background())
    
        def drawBase(self, p, baseRect):
            bs = self.barStyle
            if bs == self.StyleDonut:
                p.setPen(QtGui.QPen(self.palette().shadow().color(), self.outlinePenWidth))
                p.setBrush(self.palette().base())
                p.drawEllipse(baseRect)
            elif bs == self.StylePie:
                p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
                p.setBrush(self.palette().base())
                p.drawEllipse(baseRect)
            elif bs == self.StyleLine:
                p.setPen(QtGui.QPen(self.palette().base().color(), self.outlinePenWidth))
                p.setBrush(Qt.Qt.NoBrush)
                p.drawEllipse(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2))
    
        def drawValue(self, p, baseRect, value, arcLength):
            # nothing to draw
            if value == self.min:
                return
    
            # for Line style
            if self.barStyle == self.StyleLine:
                p.setPen(QtGui.QPen(self.palette().highlight().color(), self.dataPenWidth))
                p.setBrush(Qt.Qt.NoBrush)
                p.drawArc(baseRect.adjusted(self.outlinePenWidth/2, self.outlinePenWidth/2, -self.outlinePenWidth/2, -self.outlinePenWidth/2),
                          self.nullPosition * 16,
                          -arcLength * 16)
                return
    
            # for Pie and Donut styles
            dataPath = QtGui.QPainterPath()
            dataPath.setFillRule(Qt.Qt.WindingFill)
    
            # pie segment outer
            dataPath.moveTo(baseRect.center())
            dataPath.arcTo(baseRect, self.nullPosition, -arcLength)
            dataPath.lineTo(baseRect.center())
    
            p.setBrush(self.palette().highlight())
            p.setPen(QtGui.QPen(self.palette().shadow().color(), self.dataPenWidth))
            p.drawPath(dataPath)
    
        def calculateInnerRect(self, baseRect, outerRadius):
            # for Line style
            if self.barStyle == self.StyleLine:
                innerRadius = outerRadius - self.outlinePenWidth
            else:    # for Pie and Donut styles
                innerRadius = outerRadius * self.donutThicknessRatio
    
            delta = (outerRadius - innerRadius) / 2.
            innerRect = QtCore.QRectF(delta, delta, innerRadius, innerRadius)
            return innerRect, innerRadius
    
        def drawInnerBackground(self, p, innerRect):
            if self.barStyle == self.StyleDonut:
                p.setBrush(self.palette().alternateBase())
    
                cmod = p.compositionMode()
                p.setCompositionMode(QtGui.QPainter.CompositionMode_Source)
    
                p.drawEllipse(innerRect)
    
                p.setCompositionMode(cmod)
    
        def drawText(self, p, innerRect, innerRadius, value):
            if not self.format:
                return
    
            text = self.valueToText(value)
    
            # !!! to revise
            f = self.font()
            # f.setPixelSize(innerRadius * max(0.05, (0.35 - self.decimals * 0.08)))
            f.setPixelSize(innerRadius * 1.8 / len(text))
            p.setFont(f)
    
            textRect = innerRect
            p.setPen(self.palette().text().color())
            p.drawText(textRect, Qt.Qt.AlignCenter, text)
    
        def valueToText(self, value):
            textToDraw = self.format
    
            format_string = '{' + ':.{}f'.format(self.decimals) + '}'
    
            if self.updateFlags & self.UF_VALUE:
                textToDraw = textToDraw.replace("%v", format_string.format(value))
    
            if self.updateFlags & self.UF_PERCENT:
                percent = (value - self.min) / (self.max - self.min) * 100.0
                textToDraw = textToDraw.replace("%p", format_string.format(percent))
    
            if self.updateFlags & self.UF_MAX:
                m = self.max - self.min + 1
                textToDraw = textToDraw.replace("%m", format_string.format(m))
    
            return textToDraw
    
        def valueFormatChanged(self):
            self.updateFlags = 0;
    
            if "%v" in self.format:
                self.updateFlags |= self.UF_VALUE
    
            if "%p" in self.format:
                self.updateFlags |= self.UF_PERCENT
    
            if "%m" in self.format:
                self.updateFlags |= self.UF_MAX
    
            self.update()
    
        def rebuildDataBrushIfNeeded(self):
            if self.rebuildBrush:
                self.rebuildBrush = False
    
                dataBrush = QtGui.QConicalGradient()
                dataBrush.setCenter(0.5,0.5)
                dataBrush.setCoordinateMode(QtGui.QGradient.StretchToDeviceMode)
    
                for pos, color in self.gradientData:
                    dataBrush.setColorAt(1.0 - pos, color)
    
                # angle
                dataBrush.setAngle(self.nullPosition)
    
                p = self.palette()
                p.setBrush(QtGui.QPalette.Highlight, dataBrush)
                self.setPalette(p)
    

    Usage example:

    class TstWidget(QtGui.QWidget):
        def __init__(self):
            super(type(self), self).__init__()
    
            self.bar = QRoundProgressBar()
            self.bar.setFixedSize(300, 300)
    
            self.bar.setDataPenWidth(3)
            self.bar.setOutlinePenWidth(3)
            self.bar.setDonutThicknessRatio(0.85)
            self.bar.setDecimals(1)
            self.bar.setFormat('%v | %p %')
            # self.bar.resetFormat()
            self.bar.setNullPosition(90)
            self.bar.setBarStyle(QRoundProgressBar.StyleDonut)
            self.bar.setDataColors([(0., QtGui.QColor.fromRgb(255,0,0)), (0.5, QtGui.QColor.fromRgb(255,255,0)), (1., QtGui.QColor.fromRgb(0,255,0))])
    
            self.bar.setRange(0, 300)
            self.bar.setValue(260)
    
            lay = QtGui.QVBoxLayout()
            lay.addWidget(self.bar)
            self.setLayout(lay)
    
    0 讨论(0)
  • 2021-01-07 15:51

    I have write this

    class RoundProgress(QProgressBar):
    def __init__(self,parent):
        QProgressBar.__init__(self)
        self.values = self.value()
        self.values = (self.values*360)/100
        self.parent = parent
        self.setParent(parent)
        self.n = self.value()
        self.label = QLabel("<center>100%<center>")
        self.label.setStyleSheet("color:red;")
        self.label.setFont(QFont("courrier",math.sqrt(self.width())))
        self.v = QVBoxLayout(self)
        self.setLayout(self.v)
        self.v.addWidget(self.label)
    def setValue(self,n):
        self.n = n
        self.values = ((n*5650)/100)*(-1)
        self.label.setText("<center>"+str(self.n)+"</center>")
    def setNvalue(self,n):
        self.n = n
        self.values = ((n*5650)/100)*(-1)
        self.label.setText("<center>"+str(self.n)+"</center>")
    def paintEvent(self,event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        pen = QPen()
        pen.setWidth(2)
        pen.setColor(QColor("darkblue"))
        painter.setPen(pen)
        pen = QPen()
        pen.setWidth(9)
        pen.setColor(QColor("lightgrey"))
        painter.setPen(pen)
        painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,-5650)
        #painter.drawEllipse(0,0,100,100)
        painter.setBrush(QColor("lightblue"))
        pen = QPen()
        pen.setWidth(10)
        pen.setColor(QColor("red"))
        painter.setPen(pen)
        painter.drawArc(5.1,5.1,self.width()-10,self.height()-10,1450,self.values)
        self.update()
    
    0 讨论(0)
  • 2021-01-07 16:00

    @Alexandro, thank you for the code. It works nicely.

    I found that it's best to set the min and max values as floats instead of int

    self.min = 0.
    self.max = 100.
    self.value = 25.
    

    Otherwise, the values won't update properly in python3 if the input value to valueToText() is also an integer, due to integer division

    0 讨论(0)
提交回复
热议问题