Python QStyledItemDelegate在QTableView中显示QComboBox
我不熟悉Python和PyQt5。我正在使用Python QStyledItemDelegate在QTableView中显示QComboBox,python,python-3.x,pyqt,pyqt5,qstyleditemdelegate,Python,Python 3.x,Pyqt,Pyqt5,Qstyleditemdelegate,我不熟悉Python和PyQt5。我正在使用QStyledItemDelegate创建一个只包含组合框的QTableView列。我成功地显示了组合框,但它的行为出现了问题 问题1:即使选择已更改,组合框似乎也不会提交对模型的更改。我使用“导出”按钮打印列表以供检查 问题2:当我向表中添加新行时,新行组合框选择会一直恢复到第一个选择。为什么呢 有人能帮我提些建议吗?多谢各位 代码: 从PyQt5.QtWidgets导入* 从PyQt5.QtGui导入* 从PyQt5.QtCore导入* 进口稀土
QStyledItemDelegate
创建一个只包含组合框的QTableView
列。我成功地显示了组合框,但它的行为出现了问题
问题1:即使选择已更改,组合框似乎也不会提交对模型的更改。我使用“导出”按钮打印列表以供检查
问题2:当我向表中添加新行时,新行组合框选择会一直恢复到第一个选择。为什么呢
有人能帮我提些建议吗?多谢各位
代码:
从PyQt5.QtWidgets导入*
从PyQt5.QtGui导入*
从PyQt5.QtCore导入*
进口稀土
类委托(QStyledItemDelegate):
定义初始化(自我、所有者、选项):
super()。\uuuu init\uuuuu(所有者)
self.items=选项
def createEditor(自身、父项、选项、索引):
编辑器=QComboBox(父级)
editor.addItems(self.items)
返回编辑器
def油漆(自身、油漆工、选项、索引):
如果isinstance(self.parent(),QAbstractItemView):
self.parent().openPersistentEditor(索引,1)
绘制(自绘制、绘制、选项、索引)
def setEditorData(自身、编辑器、索引):
编者:blockSignals(真)
value=index.data(Qt.DisplayRole)
num=self.items.index(值)
editor.setCurrentIndex(num)
编者:blockSignals(假)
def setModelData(自身、编辑器、模型、索引):
value=editor.currentText()
model.setData(索引、值、Qt.EditRole)
def updateEditorGeometry(自身、编辑器、选项、索引):
编辑器.setGeometry(option.rect)
类模型(QAbstractTableModel):
ActiveRole=Qt.UserRole+1
定义初始化(self、datain、headerdata、parent=None):
"""
Args:
数据输入:列表的列表\n
headerdata:字符串列表
"""
super()。\uuuu init\uuuuu()
self.arraydata=datain
self.headerdata=headerdata
def headerData(自身、部门、方向、角色):
如果角色==Qt.DisplayRole且方向==Qt.Horizontal:
返回QVariant(self.headerdata[节])
返回QVariant()
def行数(自身、父级):
返回len(self.arraydata)
def列数(自身、父项):
如果len(self.arraydata)>0:
返回len(self.arraydata[0])
返回0
def标志(自、索引):
返回Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.itemisselect
def数据(自身、索引、角色):
如果不是index.isValid():
返回QVariant()
elif角色!=Qt.DisplayRole:
返回QVariant()
返回QVariant(self.arraydata[index.row()][index.column()]
def setData(自身、索引、值、角色):
r=重新编译(r“^[0-9]\d*(\。\d+?$”)
如果角色==Qt.EditRole和值!="":
如果索引.column()不在范围(0,1)内:
如果index.column()==2:
如果默认情况下r.match(value)和(0setModelData()
,在使用openpersisteneditor()时,除非调用closepersisteneditor()
,否则编辑器将永远不会关闭,因此setModelData()
将不会被调用。因此,上述解决方案是发出commitData()
信号,因此我们通知代理保存数据。但它仍然不保存数据,因为setData()
的实现有问题,在您的代码中使用range(0,1)
并且已知range(0,n)
是[0,1,…,n-1]
因此在您的情况下范围(0,1)
等于[0]
,并且QComboBox
的数据在1
列中,因此您必须修改该逻辑,以便它也接受1
另一方面,我看到的错误是,如果添加了一行,则编辑器不会持久打开,逻辑是代码:if-isinstance(self.parent(),qtwidts.qabstratemview):self.parent().openpersisteneditor(index)
执行该任务,但委托的父对象应该是视图,而不是mainwidown
使用上述方法,可获得以下溶液:
from PyQt5 import QtCore, QtGui, QtWidgets
import re
class Delegate(QtWidgets.QStyledItemDelegate):
def __init__(self, owner, choices):
super().__init__(owner)
self.items = choices
def paint(self, painter, option, index):
if isinstance(self.parent(), QtWidgets.QAbstractItemView):
self.parent().openPersistentEditor(index)
super(Delegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
editor = QtWidgets.QComboBox(parent)
editor.currentIndexChanged.connect(self.commit_editor)
editor.addItems(self.items)
return editor
def commit_editor(self):
editor = self.sender()
self.commitData.emit(editor)
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.DisplayRole)
num = self.items.index(value)
editor.setCurrentIndex(num)
def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class Model(QtCore.QAbstractTableModel):
ActiveRole = QtCore.Qt.UserRole + 1
def __init__(self, datain, headerdata, parent=None):
"""
Args:
datain: a list of lists\n
headerdata: a list of strings
"""
super().__init__()
self.arraydata = datain
self.headerdata = headerdata
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
return QtCore.QVariant(self.headerdata[section])
return QtCore.QVariant()
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
return len(self.arraydata)
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
if len(self.arraydata) > 0:
return len(self.arraydata[0])
return 0
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
return QtCore.QVariant(self.arraydata[index.row()][index.column()])
def setData(self, index, value, role=QtCore.Qt.EditRole):
r = re.compile(r"^[0-9]\d*(\.\d+)?$")
if role == QtCore.Qt.EditRole and value != "" and 0 < index.column() < self.columnCount():
if index.column() in (0, 1):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
else:
if index.column() == 2:
if r.match(value) and (0 < float(value) <= 1):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
else:
if r.match(value):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
return False
def print_arraydata(self):
print(self.arraydata)
def insert_row(self, data, position, rows=1):
self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
for i, e in enumerate(data):
self.arraydata.insert(i+position, e[:])
self.endInsertRows()
return True
def remove_row(self, position, rows=1):
self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
self.arraydata = self.arraydata[:position] + self.arraydata[position + rows:]
self.endRemoveRows()
return True
def append_row(self, data):
self.insert_row([data], self.rowCount())
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table view:
self.get_choices_data()
self.get_table_data()
self.tableview = self.createTable()
self.tableview.model().rowsInserted.connect(lambda: QtCore.QTimer.singleShot(0, self.tableview.scrollToBottom))
# Set the maximum value of row to the selected row
self.selectrow = self.tableview.model().rowCount()
# create buttons:
self.addbtn = QtWidgets.QPushButton('Add')
self.addbtn.clicked.connect(self.insert_row)
self.deletebtn = QtWidgets.QPushButton('Delete')
self.deletebtn.clicked.connect(self.remove_row)
self.exportbtn = QtWidgets.QPushButton('Export')
self.exportbtn.clicked.connect(self.export_tv)
self.computebtn = QtWidgets.QPushButton('Compute')
self.enablechkbox = QtWidgets.QCheckBox('Completed')
# create label:
self.lbltitle = QtWidgets.QLabel('Table')
self.lbltitle.setFont(QtGui.QFont('Arial', 20))
# create gridlayout
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(self.exportbtn, 2, 2, 1, 1)
grid_layout.addWidget(self.computebtn, 2, 3, 1, 1)
grid_layout.addWidget(self.addbtn, 2, 4, 1, 1)
grid_layout.addWidget(self.deletebtn, 2, 5, 1, 1)
grid_layout.addWidget(self.enablechkbox, 2, 6, 1, 1, QtCore.Qt.AlignCenter)
grid_layout.addWidget(self.tableview, 1, 0, 1, 7)
grid_layout.addWidget(self.lbltitle, 0, 3, 1, 1, QtCore.Qt.AlignCenter)
# initializing layout
self.title = 'Data Visualization Tool'
self.setWindowTitle(self.title)
self.setGeometry(0, 0, 1024, 576)
self.showMaximized()
self.centralwidget = QtWidgets.QWidget()
self.centralwidget.setLayout(grid_layout)
self.setCentralWidget(self.centralwidget)
def get_table_data(self):
# set initial table values:
self.tabledata = [['Name', self.choices[0], 0.0, 0.0, 0.0]]
def get_choices_data(self):
# set combo box choices:
self.choices = ['type_1', 'type_2', 'type_3', 'type_4', 'type_5']
def createTable(self):
tv = QtWidgets.QTableView()
# set header for columns:
header = ['Name', 'Type', 'var1', 'var2', 'var3']
tablemodel = Model(self.tabledata, header, self)
tv.setModel(tablemodel)
hh = tv.horizontalHeader()
tv.resizeRowsToContents()
# ItemDelegate for combo boxes
tv.setItemDelegateForColumn(1, Delegate(tv, self.choices))
return tv
def export_tv(self):
self.tableview.model().print_arraydata()
def remove_row(self):
r = self.tableview.currentIndex().row()
self.tableview.model().remove_row(r)
def insert_row(self):
self.tableview.model().append_row(['Name', self.choices[0], 0.0, 0.0, 0.0])
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
从PyQt5导入QtCore、QtGui、qtwidget
进口稀土
类委托(QtWidgets.QStyledItemDelegate):
定义初始化(自我、所有者、选项):
super()。\uuuu init\uuuuu(所有者)
self.items=选项
def油漆(自身、油漆工、选项、索引):
如果isinstance(self.parent(),qtwidts.QAbstractItemView):
self.parent().openPersistentEditor(索引)
超级(代理,自我)。绘制(画师,选项,索引)
def createEditor(自身、父项、选项、索引):
编辑器=QtWidgets.QComboBox(父级)
editor.currentIndexChanged.connect(self.commit\u编辑器)
editor.addItems(self.items)
返回编辑器
def提交编辑器(自):
editor=self.sender()
self.commitData.emit(编辑器)
def setEditorData(自身、编辑器、索引):
value=index.data(QtCore.Qt.DisplayRole)
num=self.items.index(值)
editor.setCurrentIndex(num)
def setModelData(自身、编辑器、模型、索引):
value=editor.currentText()
model.setData(索引、值、QtCore.Qt.EditRole)
def updateEditorGeometry(自身、编辑器、选项、索引):
编辑器.setGeometry(option.rect)
类模型(QtCore.QAbstractTableModel):
ActiveRole=QtCore.Qt.UserRole+1
定义初始化(self、datain、headerdata、parent=None):
"""
Args:
数据输入:列表的列表\n
headerdata:字符串列表
"""
super()。\uuuu init\uuuuu()
self.arraydata=datain
self.headerdata=headerdata
def headerData(自身、部门、方向、角色):
如果角色==QtCore.Qt.display角色和方向==QtCore
from PyQt5 import QtCore, QtGui, QtWidgets
import re
class Delegate(QtWidgets.QStyledItemDelegate):
def __init__(self, owner, choices):
super().__init__(owner)
self.items = choices
def paint(self, painter, option, index):
if isinstance(self.parent(), QtWidgets.QAbstractItemView):
self.parent().openPersistentEditor(index)
super(Delegate, self).paint(painter, option, index)
def createEditor(self, parent, option, index):
editor = QtWidgets.QComboBox(parent)
editor.currentIndexChanged.connect(self.commit_editor)
editor.addItems(self.items)
return editor
def commit_editor(self):
editor = self.sender()
self.commitData.emit(editor)
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.DisplayRole)
num = self.items.index(value)
editor.setCurrentIndex(num)
def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class Model(QtCore.QAbstractTableModel):
ActiveRole = QtCore.Qt.UserRole + 1
def __init__(self, datain, headerdata, parent=None):
"""
Args:
datain: a list of lists\n
headerdata: a list of strings
"""
super().__init__()
self.arraydata = datain
self.headerdata = headerdata
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole and orientation == QtCore.Qt.Horizontal:
return QtCore.QVariant(self.headerdata[section])
return QtCore.QVariant()
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
return len(self.arraydata)
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid(): return 0
if len(self.arraydata) > 0:
return len(self.arraydata[0])
return 0
def flags(self, index):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
elif role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
return QtCore.QVariant(self.arraydata[index.row()][index.column()])
def setData(self, index, value, role=QtCore.Qt.EditRole):
r = re.compile(r"^[0-9]\d*(\.\d+)?$")
if role == QtCore.Qt.EditRole and value != "" and 0 < index.column() < self.columnCount():
if index.column() in (0, 1):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
else:
if index.column() == 2:
if r.match(value) and (0 < float(value) <= 1):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
else:
if r.match(value):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (QtCore.Qt.DisplayRole, ))
return True
return False
def print_arraydata(self):
print(self.arraydata)
def insert_row(self, data, position, rows=1):
self.beginInsertRows(QtCore.QModelIndex(), position, position + rows - 1)
for i, e in enumerate(data):
self.arraydata.insert(i+position, e[:])
self.endInsertRows()
return True
def remove_row(self, position, rows=1):
self.beginRemoveRows(QtCore.QModelIndex(), position, position + rows - 1)
self.arraydata = self.arraydata[:position] + self.arraydata[position + rows:]
self.endRemoveRows()
return True
def append_row(self, data):
self.insert_row([data], self.rowCount())
class Main(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# create table view:
self.get_choices_data()
self.get_table_data()
self.tableview = self.createTable()
self.tableview.model().rowsInserted.connect(lambda: QtCore.QTimer.singleShot(0, self.tableview.scrollToBottom))
# Set the maximum value of row to the selected row
self.selectrow = self.tableview.model().rowCount()
# create buttons:
self.addbtn = QtWidgets.QPushButton('Add')
self.addbtn.clicked.connect(self.insert_row)
self.deletebtn = QtWidgets.QPushButton('Delete')
self.deletebtn.clicked.connect(self.remove_row)
self.exportbtn = QtWidgets.QPushButton('Export')
self.exportbtn.clicked.connect(self.export_tv)
self.computebtn = QtWidgets.QPushButton('Compute')
self.enablechkbox = QtWidgets.QCheckBox('Completed')
# create label:
self.lbltitle = QtWidgets.QLabel('Table')
self.lbltitle.setFont(QtGui.QFont('Arial', 20))
# create gridlayout
grid_layout = QtWidgets.QGridLayout()
grid_layout.addWidget(self.exportbtn, 2, 2, 1, 1)
grid_layout.addWidget(self.computebtn, 2, 3, 1, 1)
grid_layout.addWidget(self.addbtn, 2, 4, 1, 1)
grid_layout.addWidget(self.deletebtn, 2, 5, 1, 1)
grid_layout.addWidget(self.enablechkbox, 2, 6, 1, 1, QtCore.Qt.AlignCenter)
grid_layout.addWidget(self.tableview, 1, 0, 1, 7)
grid_layout.addWidget(self.lbltitle, 0, 3, 1, 1, QtCore.Qt.AlignCenter)
# initializing layout
self.title = 'Data Visualization Tool'
self.setWindowTitle(self.title)
self.setGeometry(0, 0, 1024, 576)
self.showMaximized()
self.centralwidget = QtWidgets.QWidget()
self.centralwidget.setLayout(grid_layout)
self.setCentralWidget(self.centralwidget)
def get_table_data(self):
# set initial table values:
self.tabledata = [['Name', self.choices[0], 0.0, 0.0, 0.0]]
def get_choices_data(self):
# set combo box choices:
self.choices = ['type_1', 'type_2', 'type_3', 'type_4', 'type_5']
def createTable(self):
tv = QtWidgets.QTableView()
# set header for columns:
header = ['Name', 'Type', 'var1', 'var2', 'var3']
tablemodel = Model(self.tabledata, header, self)
tv.setModel(tablemodel)
hh = tv.horizontalHeader()
tv.resizeRowsToContents()
# ItemDelegate for combo boxes
tv.setItemDelegateForColumn(1, Delegate(tv, self.choices))
return tv
def export_tv(self):
self.tableview.model().print_arraydata()
def remove_row(self):
r = self.tableview.currentIndex().row()
self.tableview.model().remove_row(r)
def insert_row(self):
self.tableview.model().append_row(['Name', self.choices[0], 0.0, 0.0, 0.0])
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())