我是Python和PyQt5的新手。我正在使用k
作为仅由ComboBox组成的QStyledItemDelegate
列之一。我设法显示了ComboBox,但是它的行为遇到了麻烦。
问题1:即使选择已更改,ComboBox似乎也未提交对模型的更改。我使用导出按钮将列表打印出来以进行检查。
问题2:当我向表中添加新行时,新行的ComboBox选择会一直恢复到第一个选择。为什么会这样?
有人可以帮助我一些建议吗?谢谢。
代码:
QTableView
答案 0 :(得分:3)
默认情况下,关闭编辑器时会调用setModelData()
,在您使用openPersistentEditor()
的情况下,除非您调用closePersistentEditor()
,否则编辑器将永远不会关闭,因此setModelData()
不会被调用。因此,上述解决方案是发出commitData()
信号,因此我们通知委托保存数据。但是仍然无法保存数据,因为setData()
的实现存在问题,在您的代码中使用range(0, 1)
,并且知道range(0, n)
是[0, 1, ..., n-1]
,因此在您的情况下range(0, 1)
等于[0]
,并且QComboBox
的数据在1
列中,因此您必须修改该逻辑,以便它也接受1
。 / p>
另一方面,我看到的错误是,如果添加行,则不会持久打开编辑器,其逻辑是该代码:if isinstance(self.parent(), QtWidgets.QAbstractItemView): self.parent().openPersistentEditor (index)
可以执行此操作,但是希望委托的父级执行是视图,而不是主要的窗口。
使用上述方法,可获得以下解决方案:
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_())