【问题标题】:PyQt5: Qstyle.CC_ScrollBar rendered in the wrong placePyQt5:Qstyle.CC_ScrollBar 呈现在错误的位置
【发布时间】:2021-01-29 15:47:18
【问题描述】:

我正在使用 listView 和自定义委托。通过paint 函数,我绘制了一组控制元素,这样列表的每一行就好像它是一个小部件,而不是一个小部件。这对于性能至关重要,因为该列表由数十万个元素组成,如 herehere 所指出的。 唯一的问题是 QStyleOptionSlider 复杂控件:如果我要求 CC.ScrollBar 控件呈现在视图的左上角而不是我想要的位置。如果在QApplication.style().drawComplexControl(QStyle.CC_ScrollBar, self.scrollOptions, painter) 中我要求CC_Slider(而不是CC_ScrollBar),则控件将呈现在预期的位置。 我还尝试从真正的滚动小部件初始化样式,但没有任何改变。

我想知道是我做错了什么还是库有问题,因为我绘制的所有其他控件都可以正常工作。我注意到的唯一区别是其他元素(例如框架、标签、按钮)有自己的 QStyleOption 类,而滚动条与滑块类合并,但引用docs

QStyleOptionSlider 包含了 QStyle 函数绘制 QSlider 和 QScrollBar 所需的所有信息。

调试信息:Python 3.8.6 / PyQt 5.15.1 / Pyqt-tools 5.15.1.2 / Windows 10

小例子

from PyQt5.QtCore import QSize, Qt, QRect
from PyQt5.QtGui import QColor
from PyQt5.QtGui import QStandardItem
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QStyle
from PyQt5.QtWidgets import QStyledItemDelegate, QApplication, QStyleOptionFrame, \
    QStyleOptionSlider
from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setObjectName("gridLayout")
        self.inferenceListView = QtWidgets.QListView(self.centralwidget)
        self.inferenceListView.setGridSize(QtCore.QSize(0, 200))
        self.inferenceListView.setObjectName("inferenceListView")
        self.gridLayout.addWidget(self.inferenceListView, 0, 1, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        self.setupProposals()

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

    def setupProposals(self):
        self.delegate = MyDelegate()
        # self.delegate.initScroll(self.horizontalScrollBar)
        model = QStandardItemModel(0, 0)
        for index in range(0, 5000):
            model.appendRow(QStandardItem(str(index)))
            self.inferenceListView.setItemDelegateForRow(index, self.delegate)
        self.inferenceListView.setModel(model)


class MyDelegate(QStyledItemDelegate):
    def __init__(self, parent=None):
        QStyledItemDelegate.__init__(self, parent)
        self.frame = QStyleOptionFrame()
        # ---------------

        self.scrollOptions = QStyleOptionSlider()
        self.scrollOptions.orientation = Qt.Vertical
        self.scrollOptions.LayoutDirectionAuto = Qt.LayoutDirectionAuto
        self.scrollOptions.orientation = Qt.Vertical
        self.scrollOptions.state = QStyle.State_Enabled
        self.scrollOptions.maximum = 10
        self.scrollOptions.minimum = 0
        self.scrollOptions.sliderValue = 0

    def initScroll(self, scroll):
        self.scrollOptions.initFrom(scroll)

    def sizeHint(self, option, index):
        return QSize(150, 200)

    def paint(self, painter, option, index):
        optX = option.rect.x()
        optY = option.rect.y()
        optH = option.rect.height()
        optW = option.rect.width()

        painter.fillRect(option.rect, QColor(100, 100, 100, 100))
        painter.drawLine(optX, optY + optH, optX + optW, optY + optH)
        QApplication.style().drawControl(QStyle.CE_ShapedFrame, self.frame, painter)

        self.scrollOptions.rect = QRect(optX + 100, optY + 100, 50, 80)

        # OK WITH CC_SLIDER
        #QApplication.style().drawComplexControl(QStyle.CC_Slider, self.scrollOptions, painter)

        # WRONG PLACE WITH CC_SCROLLBAR
        QApplication.style().drawComplexControl(QStyle.CC_ScrollBar, self.scrollOptions, painter)

    def editorEvent(self, event, model, option, index):
        return False


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

编辑

目前似乎是与 Windows 相关的问题。我有一个使用 Mac 的同事运行上面的代码,滚动条被绘制在正确的位置。我附上了在两种情况下在 Windows 上发生的情况的图像:

【问题讨论】:

    标签: python-3.x windows qt pyqt5


    【解决方案1】:

    这似乎是 QCommonStyle 中 subControlRect 的不完整实现,因为 CC_Slider 返回的矩形始终位于左上角(请参阅 source)。

    如果子矩形不包含在选项 rect 中,则可能的解决方案是使用代理样式并返回翻译后的矩形:

    class ProxyStyle(QtWidgets.QProxyStyle):
        def subControlRect(self, cc, opt, sc, widget=None):
            r = super().subControlRect(cc, opt, sc, widget)
            if cc == self.CC_ScrollBar and not opt.rect.contains(r):
                r.translate(opt.rect.topLeft())
            return r
    
    # ...
    app = QtWidgets.QApplication(sys.argv)
    app.setStyle(ProxyStyle())
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-22
      • 2020-06-22
      • 2020-10-12
      • 1970-01-01
      相关资源
      最近更新 更多