Я прилагаю упрощенную программу для демонстрации проблемы, и по пути ее разработки я нашел ответ на свою первоначальную проблему, которая оказалась ошибка в следующей строке метода Delegate:setModelData.
Код: Выделить всё
model.setData(index, QtCore.QVariant(value), QtCore.Qt.ItemDataRole.DisplayRole)
Другая проблема, которую я обнаружил, заключается в том, что начальное содержимое ячеек tableView должно существовать в список комбобоксов. В моем исходном коде, где TableView заполняется из кадра данных pandas, исходными значениями таблицы были панды «NaN», которых не было в моем списке категорий, заполняющих поле со списком.
Здесь это упрощенный код:
Код: Выделить всё
import sys
from PyQt6 import QtCore
from PyQt6.QtWidgets import QApplication, QWidget, QDialog, QDialogButtonBox, QTableView, QComboBox, QItemDelegate, QStyleOptionComboBox, QStyle, QVBoxLayout
class Delegate(QItemDelegate): #combo box delegate
def __init__(self, choices):
super(Delegate, self).__init__()
self.items = choices
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
self.editor.setFocus()
self.editor.setEditable(True)
self.editor.addItems(self.items)
return self.editor
def paint(self, painter, option, index):
value = index.data(QtCore.Qt.ItemDataRole.DisplayRole)
style = QApplication.style()
opt = QStyleOptionComboBox()
opt.text = str(value)
opt.rect = option.rect
style.drawComplexControl(QStyle.ComplexControl.CC_ComboBox, opt, painter)
QItemDelegate.paint(self, painter, option, index)
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.ItemDataRole.DisplayRole)
num = self.items.index(value)
editor.setCurrentIndex(num)
def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, QtCore.Qt.ItemDataRole.DisplayRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
class CategoriesModel(QtCore.QAbstractTableModel):
def __init__(self):
super().__init__()
self.categories = ['Invertebrate','Fish','Amphibian','Reptile','Bird','Mammal', 'N/A']
self.uncategorised = [ ['Stickleback','N/A' ], ['Glow Worm','N/A' ], ['Python', 'N/A'], ['Black Widow', 'N/A'], ['Hedgehog', 'N/A'], ['Whale Shark','N/A' ] ]
def rowCount(self, parent=None):
return len(self.uncategorised)
def columnCount(self, parent=None):
return 2
def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
if role == QtCore.Qt.ItemDataRole.DisplayRole or role == QtCore.Qt.ItemDataRole.EditRole:
item = self.uncategorised[index.row()][index.column()]
return item
else:
return None
def flags(self, index):
return (QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsSelectable)
def setData(self, index, value, role):
if role == QtCore.Qt.ItemDataRole.DisplayRole or role == QtCore.Qt.ItemDataRole.EditRole:
self.uncategorised[index.row()][index.column()] = value
else:
print(f'Bugger it! - role is {role} but should be {QtCore.Qt.ItemDataRole.DisplayRole} or {QtCore.Qt.ItemDataRole.EditRole}')
return True
def showModel(self):
print(self.uncategorised)
# Form implementation generated from reading ui file 'form.ui'
# Created by: PyQt6 UI code generator 6.6.1
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
#Dialog.resize(300, 300)
self.verticalLayoutWidget = QWidget(parent=Dialog)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(5, 5,240, 295))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(5, 5, 5, 5)
self.verticalLayout.setObjectName("verticalLayout")
self.tableView = QTableView(parent=self.verticalLayoutWidget)
self.tableView.setObjectName("tableView")
self.verticalLayout.addWidget(self.tableView)
self.buttonBox = QDialogButtonBox(parent=self.verticalLayoutWidget)
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QtWidgets.QDialogButtonBox.StandardButton.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
from PyQt6.QtWidgets import QApplication, QDialog
from delegate import Delegate
from model import CategoriesModel
# Important:
# You need to run the following command to generate the ui_form.py file
# pyqt6-uic form.ui -o ui_form.py
from ui_form import Ui_Dialog
class Dialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.model = CategoriesModel() # Create a data model for Categories
self.ui.tableView.setModel(self.model) # Attach the model to the tableview in the dialog
self.ui.tableView.setItemDelegateForColumn(1, Delegate(self.model.categories))
self.ui.buttonBox.rejected.connect(self.close)
self.ui.buttonBox.accepted.connect(self.model.showModel)
# for row in range( self.model.rowCount()):
# self.ui.tableView.openPersistentEditor(self.model.index(row, 1))
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = Dialog()
widget.show()
sys.exit(app.exec())
Добавлено редактирование 03052024:
Я понимаю, что это tableView, который запускает обновление модели, а не выбор поля со списком. Когда выбран элемент со списком, tableview не видит изменения данных базовой ячейки до тех пор, пока комбо не потеряет фокус на какую-либо другую часть tableView или другое поле со списком в другой ячейке.
Мне нужен сигнал от комбо для ' текущее значение изменено», чтобы вызвать слот в tableView, «текущее значение ячейки изменено»
Подробнее здесь: https://stackoverflow.com/questions/784 ... pdate-mode