Класс PyQt6 Delegate для отображения полей со списком в ячейках tableView не обновляет модель при выборе. Его нужно нажаPython

Программы на Python
Anonymous
Класс PyQt6 Delegate для отображения полей со списком в ячейках tableView не обновляет модель при выборе. Его нужно нажа

Сообщение Anonymous »

Я отредактировал этот пост в соответствии с полученными с благодарностью комментариями.
Я прилагаю упрощенную программу для демонстрации проблемы, и по пути ее разработки я нашел ответ на свою первоначальную проблему, которая оказалась ошибка в следующей строке метода Delegate:setModelData.

Код: Выделить всё

model.setData(index, QtCore.QVariant(value), QtCore.Qt.ItemDataRole.DisplayRole)
Нужное мне значение не должно было быть QVariant.
Другая проблема, которую я обнаружил, заключается в том, что начальное содержимое ячеек 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

Вернуться в «Python»