PyQt: ejemplo de trabajo más simple de un cuadro combinado dentro de QTableView

Antecedentes : no puedo encontrar un ejemplo completo de un cuadro combinado dentro de un QTableView . Así que escribí este código basado en varios otros ejemplos artificiales más por ahí. Sin embargo, el problema es que este ejemplo requiere que hagas doble clic en el cuadro combinado antes de que se active, luego debes hacer clic nuevamente para soltarlo. No es muy fácil de usar. Si hago lo que no es modelo / vista usando QTableWidget , el cuadro combinado se QTableWidget en el primer clic.

Pregunta : ¿Puede alguien ver esto y decirme qué se debe hacer para que responda como QTableWidget ? Además, si hay algo que esté haciendo que sea innecesario, indíquelo también. Por ejemplo, ¿es absolutamente necesario hacer referencia al estilo de la aplicación?

 import sys from PyQt4 import QtGui, QtCore rows = "ABCD" choices = ['apple', 'orange', 'banana'] class Delegate(QtGui.QItemDelegate): def __init__(self, owner, items): super(Delegate, self).__init__(owner) self.items = items def createEditor(self, parent, option, index): self.editor = QtGui.QComboBox(parent) self.editor.addItems(self.items) return self.editor def paint(self, painter, option, index): value = index.data(QtCore.Qt.DisplayRole).toString() style = QtGui.QApplication.style() opt = QtGui.QStyleOptionComboBox() opt.text = str(value) opt.rect = option.rect style.drawComplexControl(QtGui.QStyle.CC_ComboBox, opt, painter) QtGui.QItemDelegate.paint(self, painter, option, index) def setEditorData(self, editor, index): value = index.data(QtCore.Qt.DisplayRole).toString() num = self.items.index(value) editor.setCurrentIndex(num) def setModelData(self, editor, model, index): value = editor.currentText() model.setData(index, QtCore.Qt.DisplayRole, QtCore.QVariant(value)) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class Model(QtCore.QAbstractTableModel): def __init__(self): super(Model, self).__init__() self.table = [[row, choices[0]] for row in rows] def rowCount(self, index=QtCore.QModelIndex()): return len(self.table) def columnCount(self, index=QtCore.QModelIndex()): return 2 def flags(self, index): return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def data(self, index, role): if role == QtCore.Qt.DisplayRole: return self.table[index.row()][index.column()] def setData(self, index, role, value): if role == QtCore.Qt.DisplayRole: self.table[index.row()][index.column()] = value class Main(QtGui.QMainWindow): def __init__(self, parent=None): super(Main, self).__init__(parent) self.model = Model() self.table = QtGui.QTableView() self.table.setModel(self.model) self.table.setItemDelegateForColumn(1, Delegate(self, ["apple", "orange", "banana"])) self.setCentralWidget(self.table) self.setWindowTitle('Delegate Test') self.show() if __name__ == '__main__': app = QtGui.QApplication(sys.argv) main = Main() app.exec_() 

Utilizando QTableWiget.setCellWidget

 import sys from PyQt4 import QtGui app = QtGui.QApplication(sys.argv) table = QtGui.QTableWidget(1,1) combobox = QtGui.QComboBox() combobox.addItem("Combobox item") table.setCellWidget(0,0, combobox) table.show() app.exec() 

Si está intentando ajustar cuando la vista muestra el editor, necesita cambiar el activador de edición como se define en QAbstractItemView. El valor predeterminado es editar en doubleClick, pero creo que lo que está buscando es QAbstractItemView.CurrentChanged . Configúralo llamando a myView.setEditTrigger ()

Si alguien está interesado, a continuación se muestra el mismo ejemplo modificado para PyQt5 y Python 3. Las actualizaciones clave incluyen:

  • Python 3: super().__init__()
  • PyQt5: la mayoría de las clases están en QtWidgets ; QtGui no es necesario para este ejemplo
  • Model.setData : el orden de los argumentos de entrada cambió a: index, value, role y True devuelto en lugar de None
  • Las choices cuadro combinado y los contenidos de la tabla ahora se especifican en Main Esto hace que el Delegate y el Model más generales.
 from PyQt5 import QtWidgets, QtCore class Delegate(QtWidgets.QItemDelegate): def __init__(self, owner, choices): super().__init__(owner) self.items = choices def createEditor(self, parent, option, index): self.editor = QtWidgets.QComboBox(parent) self.editor.addItems(self.items) return self.editor def paint(self, painter, option, index): value = index.data(QtCore.Qt.DisplayRole) style = QtWidgets.QApplication.style() opt = QtWidgets.QStyleOptionComboBox() opt.text = str(value) opt.rect = option.rect style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter) QtWidgets.QItemDelegate.paint(self, painter, option, index) 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, QtCore.Qt.DisplayRole, QtCore.QVariant(value)) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class Model(QtCore.QAbstractTableModel): def __init__(self, table): super().__init__() self.table = table def rowCount(self, parent): return len(self.table) def columnCount(self, parent): return len(self.table[0]) def flags(self, index): return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def data(self, index, role): if role == QtCore.Qt.DisplayRole: return self.table[index.row()][index.column()] def setData(self, index, value, role): if role == QtCore.Qt.EditRole: self.table[index.row()][index.column()] = value return True class Main(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) # set combo box choices: choices = ['apple', 'orange', 'banana'] # create table data: table = [] table.append(['A', choices[0]]) table.append(['B', choices[0]]) table.append(['C', choices[0]]) table.append(['D', choices[0]]) # create table view: self.model = Model(table) self.tableView = QtWidgets.QTableView() self.tableView.setModel(self.model) self.tableView.setItemDelegateForColumn(1, Delegate(self,choices)) # make combo boxes editable with a single-click: for row in range( len(table) ): self.tableView.openPersistentEditor(self.model.index(row, 1)) # initialize self.setCentralWidget(self.tableView) self.setWindowTitle('Delegate Test') self.show() if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) main = Main() app.exec_() 

Esto debería funcionar:

 view = QTreeView() model = QStandardItemModel(view) view.setModel(model) combobox = QComboBox() child1 = QStandardItem('test1') child2 = QStandardItem('test2') child3 = QStandardItem('test3') model.appendRow([child1, child2, child3]) a = model.index(0, 2) view.setIndexWidget(a, combobox) 

Puedes probar algo como esto.

 import sys from PyQt4 import QtGui, QtCore rows = "ABCD" choices = ['apple', 'orange', 'banana'] class Delegate(QtGui.QItemDelegate): def __init__(self, owner, items): super(Delegate, self).__init__(owner) self.items = items def createEditor(self, parent, option, index): self.editor = QtGui.QComboBox(parent) self.editor.addItems(self.items) return self.editor def paint(self, painter, option, index): value = index.data(QtCore.Qt.DisplayRole).toString() style = QtGui.QApplication.style() opt = QtGui.QStyleOptionComboBox() opt.text = str(value) opt.rect = option.rect style.drawComplexControl(QtGui.QStyle.CC_ComboBox, opt, painter) QtGui.QItemDelegate.paint(self, painter, option, index) def setEditorData(self, editor, index): value = index.data(QtCore.Qt.DisplayRole).toString() num = self.items.index(value) editor.setCurrentIndex(num) if index.column() == 1: #just to be sure that we have a QCombobox editor.showPopup() def setModelData(self, editor, model, index): value = editor.currentText() model.setData(index, QtCore.Qt.DisplayRole, QtCore.QVariant(value)) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class Model(QtCore.QAbstractTableModel): def __init__(self): super(Model, self).__init__() self.table = [[row, choices[0]] for row in rows] def rowCount(self, index=QtCore.QModelIndex()): return len(self.table) def columnCount(self, index=QtCore.QModelIndex()): return 2 def flags(self, index): return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable def data(self, index, role): if role == QtCore.Qt.DisplayRole: return self.table[index.row()][index.column()] def setData(self, index, role, value): if role == QtCore.Qt.DisplayRole: self.table[index.row()][index.column()] = value return True else: return False class Main(QtGui.QMainWindow): def __init__(self, parent=None): super(Main, self).__init__(parent) self.model = Model() self.table = QtGui.QTableView() self.table.setModel(self.model) self.table.setEditTriggers(QtGui.QAbstractItemView.CurrentChanged) # this is the one that fits best to your request self.table.setItemDelegateForColumn(1, Delegate(self, ["apple", "orange", "banana"])) self.setCentralWidget(self.table) self.setWindowTitle('Delegate Test') self.show() if __name__ == '__main__': app = QtGui.QApplication(sys.argv) main = Main() app.exec_() 

Como puede ver, acabo de agregar un par de líneas a su código. La vista administra la “edición”, por lo que debe cambiar los disparadores de edición. Luego, cuando configura los datos del delegado, obligue al delegado a mostrar la ventana emergente desde el widget.

Hace algún tiempo, leí una publicación de blog en la que el autor subclasificó QAbstractItemView para trabajar ‘correctamente’ con delegates (edición, navegación, actualización de datos, etc.), pero no puedo encontrar la publicación 🙁

Espero eso ayude.