gpt4 book ai didi

python - QStyledItemDelegate 在QTableView中显示QComboBox

转载 作者:太空宇宙 更新时间:2023-11-03 15:40:05 26 4
gpt4 key购买 nike

我是 Python 和 PyQt5 的新手。我正在使用 QStyledItemDelegate 制作仅包含 ComboBox 的 QTableView 列之一。我设法显示了 ComboBox,但我在处理它的行为时遇到了问题。

问题 1:ComboBox 似乎没有提交对模型的更改,即使选择已更改。我使用导出按钮打印列表以供检查。

问题 2:当我向表中添加新行时,新行 ComboBox 选择不断恢复到第一个选择。这是为什么?

谁能帮我提点建议?谢谢。

代码:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import re

class Delegate(QStyledItemDelegate):
def __init__(self, owner, choices):
super().__init__(owner)
self.items = choices

def createEditor(self, parent, option, index):
editor = QComboBox(parent)
editor.addItems(self.items)
return editor

def paint(self, painter, option, index):
if isinstance(self.parent(), QAbstractItemView):
self.parent().openPersistentEditor(index, 1)
QStyledItemDelegate.paint(self, painter, option, index)

def setEditorData(self, editor, index):
editor.blockSignals(True)
value = index.data(Qt.DisplayRole)
num = self.items.index(value)
editor.setCurrentIndex(num)
editor.blockSignals(False)

def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, Qt.EditRole)

def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)

class Model(QAbstractTableModel):
ActiveRole = 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 == Qt.DisplayRole and orientation == Qt.Horizontal:
return QVariant(self.headerdata[section])
return QVariant()

def rowCount(self, parent):
return len(self.arraydata)

def columnCount(self, parent):
if len(self.arraydata) > 0:
return len(self.arraydata[0])
return 0

def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

def data(self, index, role):
if not index.isValid():
return QVariant()
elif role != Qt.DisplayRole:
return QVariant()
return QVariant(self.arraydata[index.row()][index.column()])

def setData(self, index, value, role):
r = re.compile(r"^[0-9]\d*(\.\d+)?$")
if role == Qt.EditRole and value != "":
if not index.column() in range(0, 1):
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, (Qt.DisplayRole, ))
return True
else:
if r.match(value):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (Qt.DisplayRole, ))
return True
elif index.column() in range(0, 1):
self.arraydata[index.row()][index.column()] = value
self.dataChanged.emit(index, index, (Qt.DisplayRole, ))
return True
return False

def print_arraydata(self):
print(self.arraydata)

class Main(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.clicked.connect(self.tv_clicked_pos)

# Set the maximum value of row to the selected row
self.selectrow = self.tableview.model().rowCount(QModelIndex())

# create buttons:
self.addbtn = QPushButton('Add')
self.addbtn.clicked.connect(self.insert_row)
self.deletebtn = QPushButton('Delete')
self.deletebtn.clicked.connect(self.remove_row)
self.exportbtn = QPushButton('Export')
self.exportbtn.clicked.connect(self.export_tv)
self.computebtn = QPushButton('Compute')
self.enablechkbox = QCheckBox('Completed')

# create label:
self.lbltitle = QLabel('Table')
self.lbltitle.setFont(QFont('Arial', 20))

# create gridlayout
self.grid_layout = QGridLayout()
self.grid_layout.addWidget(self.exportbtn, 2, 2, 1, 1)
self.grid_layout.addWidget(self.computebtn, 2, 3, 1, 1)
self.grid_layout.addWidget(self.addbtn, 2, 4, 1, 1)
self.grid_layout.addWidget(self.deletebtn, 2, 5, 1, 1)
self.grid_layout.addWidget(self.enablechkbox, 2, 6, 1, 1, Qt.AlignCenter)
self.grid_layout.addWidget(self.tableview, 1, 0, 1, 7)
self.grid_layout.addWidget(self.lbltitle, 0, 3, 1, 1, Qt.AlignCenter)

# initializing layout
self.title = 'Data Visualization Tool'
self.setWindowTitle(self.title)
self.setGeometry(0, 0, 1024, 576)
self.showMaximized()
self.centralwidget = QWidget()
self.centralwidget.setLayout(self.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 = 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(self, self.choices))

# make combo boxes editable with a single-click:
for row in range(len(self.tabledata)):
tv.openPersistentEditor(tablemodel.index(row, 1))

return tv

def export_tv(self):
self.tableview.model().print_arraydata()

def insert_row(self, position, rows=1, index=QModelIndex()):
position = self.selectrow
self.tableview.model().beginInsertRows(QModelIndex(), position, position + rows - 1)
for row in range(rows):
self.tableview.model().arraydata.append(['Name', self.choices[0], 0.0, 0.0, 0.0])
self.tableview.model().endInsertRows()
self.tableview.model().rowsInserted.connect(lambda: QTimer.singleShot(0, self.tableview.scrollToBottom))
return True

def remove_row(self, position, rows=1, index=QModelIndex()):
position = self.selectrow
self.tableview.model().beginRemoveRows(QModelIndex(), position, position + rows - 1)
self.tableview.model().arraydata = self.tableview.model().arraydata[:position] + self.tableview.model().arraydata[position + rows:]
self.tableview.model().endRemoveRows()
return True

def tv_clicked_pos(self, indexClicked):
self.selectrow = indexClicked.row()

if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
main = Main()
main.show()
app.exec_()

最佳答案

默认情况下,setModelData() 在编辑器关闭时被调用,在您使用 openPersistentEditor() 的情况下,编辑器将永远不会关闭,除非您调用 closePersistentEditor(),因此 setModelData() 将不会被调用。所以上面的解决方案是发出commitData()信号,这样我们就通知delegate保存数据了。但是它仍然没有保存数据,因为 setData() 的实现有问题,在你的代码中你使用 range(0, 1) 并且众所周知 range(0, n)[0, 1, ..., n-1] 所以在你的情况下 range(0, 1) 等于[0] 并且 QComboBox 的数据位于 1 列中,因此您必须修改该逻辑,以便它也接受 1

另一方面,我看到的错误是,如果添加一行,编辑器不会持久打开,逻辑是代码:if isinstance(self.parent(), QtWidgets.QAbstractItemView) : self.parent().openPersistentEditor (index) 做那个工作,但是委托(delegate)的父级应该是 View ,而不是 mainwidow。

利用上述,得到如下解:

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_())

关于python - QStyledItemDelegate 在QTableView中显示QComboBox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53059449/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com