在运行时覆盖 QListView 的虚拟保护插槽

Overriding virtual protected slot of QListView at runtime

我正在使用 PyQt 5.9.1,我想在运行时将 QListView.currentChanged 连接到一个函数。但出于某种原因,我只能在调用 setModel() 之前执行此操作。如果我事先调用 setModel()(即使使用 None 参数),那么我的 currentChanged 函数将永远不会被调用。

# https://www.pythoncentral.io/pyside-pyqt-tutorial-qlistview-and-qstandarditemmodel/
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import  *

def currentChanged(current, previous):
    print(current, previous)


app = QApplication(sys.argv)

model = QStandardItemModel()
foods = [
    'Cookie dough', # Must be store-bought
    'Hummus', # Must be homemade
    'Spaghetti', # Must be saucy
    'Dal makhani', # Must be spicy
    'Chocolate whipped cream' # Must be plentiful
]
for food in foods:
    item = QStandardItem(food)
    model.appendRow(item)

list = QListView()
list.setWindowTitle('Honey-Do List')
list.setMinimumSize(600, 400)
list.currentChanged = currentChanged  # BEFORE setModel
list.setModel(model)

list.show()
app.exec_()

每当调用 setModel() 时,列表视图的新 selection model is created. Internally, the currentChanged signal of the new selection model is then connected to the protected currentChanged 插槽。因此,无论何时设置数据模型,现有的内部信号连接都会自动断开并重新连接。在您的示例中,当您在 currentChanged 调用 setModel() 之后对 currentChanged 进行猴子修补时,将不会重新建立内部信号连接,因此您的函数将永远不会打电话。

一般来说,在设置任何模型之前对视图进行更改通常是错误的,因为可能会出现无法预测的内部副作用'总是被记录下来。

您还应该注意,根本不需要猴子补丁 currentChanged。正确的做法是这样的:

list = QListView()
list.setModel(model)
list.selectionModel().currentChanged.connect(currentChanged)

这将以与您当前代码完全相同的方式工作。