我正在使用一个listView和一个自定义委托。通过paint函数,我绘制了一组控制元素,这样列表中的每一行行为就像它是一个小部件,而不是实际的小部件。这对于性能至关重要,因为列表由数十万个元素组成,如指向的这里和这里。唯一的问题是QStyleOptionSlider复杂控件:如果我请求一个CC.ScrollBar,该控件将呈现在视图的左上角,而不是我想要的位置。如果在QApplication.style().drawComplexControl(QStyle.CC_ScrollBar, self.scrollOptions, painter)中,我请求一个CC_Slider (而不是CC_ScrollBar),则控件将按预期的方式呈现。我也尝试从一个真正的滚动小部件初始化样式,但是没有什么改变。
我想知道我是否做错了什么,或者它是否是库的问题,因为我绘制的所有其他控件都工作得很完美。我注意到的唯一不同是,当滚动条与滑块类合并时,其他元素(例如帧、标签、按钮)有自己的QStyleOption类,但是引用文档
QStyleOptionSlider包含QStyle函数绘制QSlider和QScrollBar所需的所有信息。
调试信息:Python3.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在这两种情况下发生的情况的图片:

发布于 2021-01-29 19:19:31
这似乎是subControlRect在QCommonStyle中的一个可能不完整的实现,因为为CC_Slider返回的矩形总是位于左上角(参见来源)。
一个可能的解决方案可以是使用代理样式并返回一个转换后的矩形,如果subrect不包含在选项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())https://stackoverflow.com/questions/65957575
复制相似问题