pyqt 是否支持具有两个值的堆叠式进度条?

Does pyqt support a stacked progress bar with two values?

类似于this question, but for pyqt. I have an application that has two threads, one of which processes some data (time consuming), and the second thread that presents the results and asks for verification on the results. I want to show the number of objects processed in a progress bar. However, I also want to show the number of objects verified by user. Number processed will always be equal or greater than the number of objects verified (since you can't verify what hasn't been verified). In essence, it's kind of like the loading bar of a youtube video or something, showing a grey part that is "loaded" and red part that is "watched." Is this something that can be supported in pyqt? The documentation for QProgressBar似乎没有暗示有任何支持。使用 PyQt5 和 Python 3.6.

它看起来应该类似于:

这是一个最小的可行代码,它有两个独立的进度条,一个用于处理的对象数量,另一个用于验证的数量,但我希望它们重叠...

import sys

from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar, QPushButton)

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Progress Bar')
        self.objectsToProcess = 100
        self.objectsProcessed = 0
        self.objectsVerified = 0

        self.processProgress = QProgressBar(self)
        self.processProgress.setGeometry(5, 5, 300, 25)
        self.processProgress.setMaximum(self.objectsToProcess)

        self.verifyProgress = QProgressBar(self)
        self.verifyProgress.setGeometry(5, 35, 300, 25)
        self.verifyProgress.setMaximum(self.objectsToProcess)

        self.processButton = QPushButton('Process', self)
        self.processButton.move(5, 75)

        self.verifyButton = QPushButton('Verify', self)
        self.verifyButton.move(90, 75)

        self.show()

        self.processButton.clicked.connect(self.process)
        self.verifyButton.clicked.connect(self.verify)

    def process(self):
        if self.objectsProcessed + 1 < self.objectsToProcess:
            self.objectsProcessed += 1
            self.processProgress.setValue(self.objectsProcessed)

    def verify(self):
        if self.objectsVerified < self.objectsProcessed:
            self.objectsVerified += 1
            self.verifyProgress.setValue(self.objectsVerified)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

以上代码的结果:

一个可能的解决方案是在 QProgressBar 中创建一个新的属性来显示备选进度,并且我们可以使用 QProxyStyle 进行绘制:

from PyQt5 import QtCore, QtGui, QtWidgets

class ProxyStyle(QtWidgets.QProxyStyle):
    def drawControl(self, element, option, painter, widget):
        if element == QtWidgets.QStyle.CE_ProgressBar:
            super(ProxyStyle, self).drawControl(element, option, painter, widget)
            if hasattr(option, 'alternative'):
                alternative = option.alternative

                last_value = option.progress
                last_pal = option.palette
                last_rect = option.rect

                option.progress = alternative
                pal = QtGui.QPalette()
                # alternative color
                pal.setColor(QtGui.QPalette.Highlight, QtCore.Qt.red)
                option.palette = pal
                option.rect = self.subElementRect(QtWidgets.QStyle.SE_ProgressBarContents, option, widget)
                self.proxy().drawControl(QtWidgets.QStyle.CE_ProgressBarContents, option, painter, widget)

                option.progress = last_value 
                option.palette = last_pal
                option.rect = last_rect
            return
        super(ProxyStyle, self).drawControl(element, option, painter, widget)

class ProgressBar(QtWidgets.QProgressBar):
    def paintEvent(self, event):
        painter =  QtWidgets.QStylePainter(self)
        opt = QtWidgets.QStyleOptionProgressBar()
        if hasattr(self, 'alternative'):
            opt.alternative = self.alternative()
        self.initStyleOption(opt)
        painter.drawControl(QtWidgets.QStyle.CE_ProgressBar, opt)

    @QtCore.pyqtSlot(int)
    def setAlternative(self, value):
        self._alternative = value
        self.update()

    def alternative(self):
        if not hasattr(self, '_alternative'):
            self._alternative = 0
        return self._alternative

class Actions(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Progress Bar')

        self.objectsToProcess = 100
        self.objectsProcessed = 0
        self.objectsVerified = 0

        self.progress_bar = ProgressBar(maximum=self.objectsToProcess)
        self.process_btn = QtWidgets.QPushButton('Process')
        self.verify_btn = QtWidgets.QPushButton('Verify')

        self.process_btn.clicked.connect(self.process)
        self.verify_btn.clicked.connect(self.verify)

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(self.progress_bar, 0, 0, 1, 2)
        lay.addWidget(self.process_btn, 1, 0)
        lay.addWidget(self.verify_btn, 1, 1)

    def process(self):
        if self.objectsProcessed + 1 < self.objectsToProcess:
            self.objectsProcessed += 1
            self.progress_bar.setValue(self.objectsProcessed)

    def verify(self):
        if self.objectsVerified < self.objectsProcessed:
            self.objectsVerified += 1
            self.progress_bar.setAlternative(self.objectsVerified)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(ProxyStyle(app.style()))
    w = Actions()
    w.show()
    sys.exit(app.exec_())

感谢@eyllanesc 提供强大的解决方案。我选择了一个更轻的(公认的 hackish)解决方案,即重叠两个进度条并使用 QGraphicsOpacityEffect 使顶部条稍微透明。

    # Opaque prog bar
    self.verifyProgress = QProgressBar(self)
    self.verifyProgress.setGeometry(5, 5, 300, 25)
    self.verifyProgress.setMaximum(self.objectsToProcess)
    self.verifyProgress.setFormat('%p% /                 ')
    self.verifyProgress.setAlignment(Qt.AlignCenter)

    # Must set the transparent prog bar second to overlay on top of opaque prog bar
    self.processProgress = QProgressBar(self)
    self.processProgress.setGeometry(5, 5, 300, 25)
    self.processProgress.setMaximum(self.objectsToProcess)
    self.processProgress.setFormat('   %p%')
    self.processProgress.setAlignment(Qt.AlignCenter)
    op = QGraphicsOpacityEffect(self.processProgress)
    op.setOpacity(0.5)
    self.processProgress.setGraphicsEffect(op)

结果:

我最近需要做一些类似的事情并选择对进度条块使用渐变颜色,因为我也需要使用样式表。

    def set_pb_value(self, pb, value_1, value_2):
        if value_2 > value_1:
            pb.setValue(value_2)
            pb.setFormat("{} / {}".format(value_1, value_2))
            pb.setStyleSheet('QProgressBar::chunk {' +
                         'background-color: qlineargradient(spread:pad, x1:' + str(value_1/pb.maximum()) + ', y1:0, x2:' +
                         str(value_1/value_2) + ', y2:0, stop:' + str(value_1/value_2) + ' rgba(0, 255, 0, 255), stop:1 '
                                            'rgba(255, 0, 0, 255)); width: -1px; margin: -1px;}')
        else:
            pb.setValue(value_1)
            pb.setFormat("%v")

我的价值观是整数,所以 value_1/pb.maximum() 对我来说是必要的,但可以根据您的需要进行更改。 我还遇到了其他样式表和进度条边距的一些问题,这就是为什么它们现在设置为 -1,您可能不需要包括它。