我正在为 QTableView 实现一个委托,其中两列应该是下拉列表,用户可以从枚举中选择一个值。
下面是一个例子:
from PyQt4 import QtGui, QtCore
import enum
Color = enum.Enum("Color", ("RED", "BLUE"))
Shape = enum.Enum("Shape", ("TRIANGLE", "CIRCLE"))
class EnumComboBoxDelegate(QtGui.QItemDelegate):
def __init__(self, enum_cls, parent=None):
super(EnumComboBoxDelegate, self).__init__(parent)
self.enum_cls = enum_cls
self.enum_objects = list(enum_cls)
self.enum_names = [enum_obj.name for enum_obj in self.enum_objects]
self.enum_values = [enum_obj.value for enum_obj in self.enum_objects]
def createEditor(self, widget, option, index):
editor = QtGui.QComboBox(widget)
for user_friendly_name in self.enum_names:
editor.addItem(user_friendly_name)
return editor
def setEditorData(self, editor, index):
combobox_index, is_int = index.model().data(index, QtCore.Qt.EditRole).toInt()
if is_int:
editor.setCurrentIndex(combobox_index)
else:
editor.setCurrentIndex(0)
def setModelData(self, editor, model, index):
combobox_index = editor.currentIndex()
if not combobox_index:
combobox_index = 0
model.setData(index, combobox_index, QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class CentralWidget(QtGui.QWidget):
def __init__(self, *args, **kwargs):
super(CentralWidget, self).__init__(*args, **kwargs)
main_layout = QtGui.QVBoxLayout()
table_view = QtGui.QTableView()
table_view.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
table_view.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
table_model = QtGui.QStandardItemModel(2, 2, None)
table_view.setModel(table_model)
color_combo_delegate = EnumComboBoxDelegate(Color)
table_view.setItemDelegateForColumn(0, color_combo_delegate)
shape_combo_delegate = EnumComboBoxDelegate(Shape)
table_view.setItemDelegateForColumn(1, shape_combo_delegate)
main_layout.addWidget(table_view)
self.setLayout(main_layout)
def run_self_contained_widget():
import sys
app = QtGui.QApplication(sys.argv)
main_window = QtGui.QMainWindow()
main_window.setCentralWidget(CentralWidget())
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
run_self_contained_widget()
现在,如果运行,代码会在启动时崩溃而没有回溯:
Process finished with exit code -1073741819 (0xC0000005)
如果我更改委托,使它们都使用相同的
EnumComboBoxDelegate
实例,我不会收到任何错误:
color_combo_delegate = EnumComboBoxDelegate(Color)
table_view.setItemDelegateForColumn(0, color_combo_delegate)
shape_combo_delegate = EnumComboBoxDelegate(Shape)
table_view.setItemDelegateForColumn(1, color_combo_delegate)
造成这种情况的原因是什么以及如何解决?该代码使用 PyQt4 和 Python 2.7,但在 Python 3.x 中似乎是相同的
我可以提供部分解决方案,但我还没有弄清楚到底为什么。看起来 color_combo_delegate 在
CentralWidget.__init__()
结束时被垃圾收集。如果将以下内容添加到EnumComboBoxDelegate
:
def __del__(self):
print("Oh no! I am about to be destroyed! Enum: {}".format(self.enum_cls.__name__))
你得到输出
Oh no! I am about to be destroyed! Enum: Color
Process finished with exit code -1073741819 (0xC0000005)
固定代码为:
self.color_combo_delegate = EnumComboBoxDelegate(Color)
table_view.setItemDelegateForColumn(0, self.color_combo_delegate)
self.shape_combo_delegate = EnumComboBoxDelegate(Shape)
table_view.setItemDelegateForColumn(1, self.shape_combo_delegate)
我不清楚为什么存在对 shape_combo_delegate 的引用,而不存在对 color_combo_delegate 的引用,但这似乎解决了问题。