当鼠标位于禁用的委托按钮时 QTableView 不滚动

QTableView not scrolling when mouse is at disabled delegate button

设置说明

问题描述
如果委托中的按钮被禁用 [setEnabled(False)],当鼠标位于禁用按钮上方时 table 的滚动不起作用。

其他

已使用 PySide6 (6.2.2.1)、Python 3.9、Windows 10、Debian

进行测试

可能是同样的问题(但没有列出解决方案):
https://forum.qt.io/topic/123495/disabled-widget-hogs-scrollevents-how-to-disable

最小功能示例:

from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout 
from PySide6.QtWidgets import  QTableView, QWidget, QStyledItemDelegate, QPushButton
from PySide6.QtCore import Qt, QModelIndex, QAbstractTableModel, QItemSelectionModel

class ButtonDelegate(QStyledItemDelegate):
    def __init__(self, parent):
        QStyledItemDelegate.__init__(self, parent)    

    def paint(self, painter, option, index):
        self.parent().openPersistentEditor(index)
        super(ButtonDelegate, self).paint(painter, option, index)

    def createEditor(self, parent, option, index):
        editor = QPushButton("Button", parent)
        editor.setEnabled(False)
        return editor

class TableModel(QAbstractTableModel):
    def __init__(self, localData=[[]], parent=None):
        super().__init__(parent)
        self.modelData = localData

    def headerData(self, section: int, orientation: Qt.Orientation, role: int):
        if role == Qt.DisplayRole:
            if orientation == Qt.Vertical:
                return "Row " + str(section)

    def columnCount(self, parent=None):
        return len(self.modelData[0])

    def rowCount(self, parent=None):
        return len(self.modelData)

    def data(self, index: QModelIndex, role: int):
        if role == Qt.DisplayRole:
            row = index.row()
            col = index.column()
            return self.modelData[row][col]

app = QApplication()

data = [['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],
    ['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],['1', '2'],
    ['1', '2'],['1', '2'],['1', '2'],['1', '2']]

model = TableModel(data)

tableView = QTableView()
tableView.setModel(model)
selectionModel = QItemSelectionModel(model)
tableView.setSelectionModel(selectionModel)
tableView.setItemDelegateForColumn(1, ButtonDelegate(tableView))


widget = QWidget()
widget.horizontalHeader = tableView.horizontalHeader()
widget.horizontalHeader.setStretchLastSection(True)
widget.mainLayout = QVBoxLayout()
widget.mainLayout.setContentsMargins(1,1,1,1)
widget.mainLayout.addWidget(tableView)
widget.setLayout(widget.mainLayout)

mainWindow = QMainWindow()
mainWindow.setCentralWidget(widget)
mainWindow.setGeometry(0, 0, 300, 300)
mainWindow.show()

exit(app.exec())

我可以用 PyQt6 重现这个,所以我可以确认它与 Qt6 有关

不同之处在于,Wheel 事件在 Qt6 中的禁用按钮上总是 accepted 而在 Qt5 中被忽略,这与 QAbstractButton 的特殊行为有关输入事件。这实际上被认为是 Qt5 的错误行为,因此不能将其视为错误(参见 QTBUG-79102 and QTBUG-67032)。

解决方案是将事件设置为 not 在委托的事件过滤器中接受,并 return True 传播事件致家长:

class ButtonDelegate(QStyledItemDelegate):
    def eventFilter(self, obj, event):
        if event.type() == event.Type.Wheel:
            event.setAccepted(False)
            return True
        return super().eventFilter(obj, event)

请注意,在 paint 函数中打开编辑器不是一个好主意:虽然该函数通常在编辑器已经打开时被“忽略”,但任何可能导致重绘的操作(并且,可能,几何变化)应该永远不会发生在绘画函数中。