问题
I want to add a delegate QComboBox delegate to specific cells in some of the QTableView rows. I've found several posts on how to add the delegate, but none with examples for updating a cell with the QComboBox selection.
This is what I have so far:
main.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>350</width>
<height>239</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QWidget" name="formLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>341</width>
<height>231</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="1">
<widget class="QPushButton" name="btnPopulate">
<property name="text">
<string>Populate Table</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
test.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys, os
from PyQt5 import uic, QtWidgets
from PyQt5.QtGui import QStandardItemModel
from PyQt5.QtWidgets import QDialog, QComboBox, QApplication
class GUI(QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
# button
self.btnPopulate.clicked.connect(self.populate)
# table model
self.header = ['col1', 'col2', 'col3']
self.QSModel = QStandardItemModel()
self.QSModel.setColumnCount(3)
self.QSModel.setHorizontalHeaderLabels(self.header)
self.tableView.setModel(self.QSModel)
# combobox delegate
self.cbDelegate = QComboBox()
self.cbDelegate.addItems(['choice1', 'choice2'])
def populate(self):
row = self.QSModel.rowCount()
for x in range(0, 7):
self.QSModel.insertRow(row)
self.QSModel.setData(self.QSModel.index(row, 0), 'data')
self.QSModel.item(row, 0).setEditable(True)
self.QSModel.setData(self.QSModel.index(row, 1), 'data')
self.QSModel.item(row, 1).setEditable(True)
# add combobox delegate to even rows
if x % 2 == 0:
print('Delegate added.')
index = self.tableView.model().index(row, 1)
self.tableView.setIndexWidget(index, self.cbDelegate)
self.QSModel.setData(self.QSModel.index(row, 2), 'data')
self.QSModel.item(row, 1).setEditable(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
However, when I click the Populate Table button only the second cell in the very first row is displayed with a QComboBox.
How do I need to change the code to display the QComboBox in rows 0, 2, 4 and 6 or any arbitrary row for that matter. (Whether or not the QComboBox needs to be displayed depends on the value of the first cell in reach row.)
What kind of method do I need to use to replace the cell contents with the QComboBox selection?
回答1:
How do I need to change the code to display the QComboBox in rows 0, 2, 4 and 6 or any arbitrary row for that matter. (Whether or not the QComboBox needs to be displayed depends on the value of the first cell in reach row.)
the problem so you can only see a QComboBox is caused because you have only created a QComboBox, to solve it you must create a QComboBox for each cell in which you want to establish them.
What kind of method do I need to use to replace the cell contents with the QComboBox selection?
You must connect the appropriate signal to the setData () method of the model, we could use the QModelIndex but this is dangerous because it can be changed when you delete, move or insert an item, it is appropriate to use QPersistentModelIndex, in this case you must use the signal currentIndexChanged
In this example I will use the QModelIndex
:
def populate(self):
row = self.QSModel.rowCount()
for x in range(7):
self.QSModel.insertRow(row)
self.QSModel.setData(self.QSModel.index(row, 0), 'data')
self.QSModel.item(row, 0).setEditable(True)
self.QSModel.setData(self.QSModel.index(row, 1), 'data')
self.QSModel.item(row, 1).setEditable(True)
# add combobox delegate to even rows
if x % 2 == 0:
index = self.tableView.model().index(row, 1)
cbDelegate = QComboBox()
pix = QPersistentModelIndex(index)
cbDelegate.currentIndexChanged[str].connect(lambda txt, pix=pix:self.tableView.model().setData(QModelIndex(pix), txt))
cbDelegate.addItems(['choice1', 'choice2'])
self.tableView.setIndexWidget(index, cbDelegate)
self.QSModel.setData(self.QSModel.index(row, 2), 'data')
self.QSModel.item(row, 1).setEditable(True)
In this example I will use the QStandartItem
:
def populate(self):
row = self.QSModel.rowCount()
for x in range(7):
self.QSModel.insertRow(row)
self.QSModel.setData(self.QSModel.index(row, 0), 'data')
self.QSModel.item(row, 0).setEditable(True)
self.QSModel.setData(self.QSModel.index(row, 1), 'data')
self.QSModel.item(row, 1).setEditable(True)
if x % 2 == 0:
item = self.tableView.model().item(row, 1)
cbDelegate = QComboBox()
cbDelegate.currentIndexChanged[str].connect(item.setText)
cbDelegate.addItems(['choice1', 'choice2'])
self.tableView.setIndexWidget(item.index(), cbDelegate)
self.QSModel.setData(self.QSModel.index(row, 2), 'data')
self.QSModel.item(row, 1).setEditable(True)
Another way to do something similar is to use a delegate, for that we create a class that inherits from QStyledItemDelegate
:
class ComboBoxDelegate(QStyledItemDelegate):
def paint(self, painter, option, index):
if index.row()%2 == 0:
opt = QStyleOptionComboBox()
opt.rect = option.rect
opt.currentText = index.data()
QApplication.style().drawComplexControl(QStyle.CC_ComboBox, opt, painter)
QApplication.style().drawControl(QStyle.CE_ComboBoxLabel, opt, painter)
else:
QStyledItemDelegate.paint(self, painter, option, index)
def createEditor(self, parent, option, index):
if index.row()%2 == 0:
combobox = QComboBox(parent)
options = ['choice1', 'choice2']
if index.data() not in options:
combobox.addItem(index.data())
combobox.addItems(options)
return combobox
return QStyledItemDelegate.createEditor(self, parent, option, index)
def setEditorData(self, editor, index):
if isinstance(editor, QComboBox):
text = index.data()
ix = editor.findText(text)
if ix > 0:
editor.setCurrentIndex(ix)
else:
index.model().setData(index, editor.currentText())
else:
QStyledItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
if isinstance(editor, QComboBox):
model.setData(index, editor.currentText())
else:
QStyledItemDelegate.setModelData(self, editor, model, index)
class GUI(QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
self.btnPopulate.clicked.connect(self.populate)
self.header = ['col1', 'col2', 'col3']
self.QSModel = QStandardItemModel()
self.QSModel.setColumnCount(3)
self.QSModel.setHorizontalHeaderLabels(self.header)
self.tableView.setModel(self.QSModel)
self.tableView.setItemDelegateForColumn(1, ComboBoxDelegate(self.tableView))
self.populate()
def populate(self):
row = self.QSModel.rowCount()
for x in range(7):
for i, value in enumerate(['data1', 'data2', 'data3']):
self.QSModel.setItem(row+x, i, QStandardItem(value))
self.QSModel.item(row+x, i).setEditable(True)
来源:https://stackoverflow.com/questions/48105026/how-to-update-a-qtableview-cell-with-a-qcombobox-selection