There is a QTableView()
, one of it's column is filled with QComboBox
es.
The question is how to select item in combobox that is in QTableView()
according to data taken from dictionary
I see that I should apply self.combo.setCurrentIndex(self.combo.findText( status_str))
but can't understand how to get that variable status_str
in comboBox
or place in code where to apply it.
Also I cannot understand how make comboBox
appear only after double clicking. If cell was not double clicked it must looks like any other cell.
The sample of code:
data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}
class ComboDelegate(QItemDelegate):
def __init__(self, parent):
QItemDelegate.__init__(self, parent)
def paint(self, painter, option, index):
self.combo = QComboBox(self.parent())
li = []
li.append("closed")
li.append("expired")
li.append("cancelled")
li.append("waiting")
self.combo.addItems(li)
#self.combo.setCurrentIndex(self.combo.findText( status_str ))
if not self.parent().indexWidget(index):
self.parent().setIndexWidget( index, self.combo )
class TableView(QTableView):
def __init__(self, *args, **kwargs):
QTableView.__init__(self, *args, **kwargs)
self.setItemDelegateForColumn(1, ComboDelegate(self))
class MainFrame(QWidget):
def __init__(self):
QWidget.__init__(self)
table = TableView(self)
self.model = QStandardItemModel()
table.setModel(self.model)
MainWindow = QVBoxLayout()
MainWindow.addWidget(table)
self.setLayout(MainWindow)
self.fillModel()
def fillModel(self):
for i in data:
print i
name_str = i
status_str = data[i]["status"]
name = QStandardItem(name_str)
status = QStandardItem(status_str)
items = [name, status]
self.model.appendRow(items)
if __name__ == "__main__":
app = QApplication(sys.argv)
main = MainFrame()
main.show()
main.move(app.desktop().screen().rect().center() - main.rect().center())
sys.exit(app.exec_())
Overriding QItemDelegate.paint
is not the recommended method for creating a delegate. QItemDelegate
has methods such as createEditor
and setEditorData
which you should override instead. These methods are called appropriately by Qt.
In createEditor
you should create your comboBox
, and return it. For example:
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
li = []
li.append("closed")
li.append("expired")
li.append("cancelled")
li.append("waiting")
editor.addItems(li)
return editor
In setEditorData
you query your model for the current index of the combobox. This will be called For example:
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.EditRole)
editor.setCurrentIndex(editor.findText(value))
Note that in this example, I've relied on the default implementation of QItemDelegate.setModelData()
to save the current text of the combobox
into the EditRole
. If you want to do something more complex (for example saving the combobox
index instead of the text), you can save/restore data to a different role (for example Qt.UserRole
) in which case you would modify where you get the role in the setEditorData
method as well as overriding setModelData
like so:
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.UserRole)
editor.setCurrentIndex(int(value))
def setModelData(self, editor, model, index):
model.setData(index, editor.currentIndex(), Qt.UserRole)
Here is a minimal working example of the above code! Note that I've turned off support for QVariant
using sip
so that the model returns native Python types.
import sys
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtGui import *
from PyQt4.QtCore import *
data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}
class ComboDelegate(QItemDelegate):
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
li = []
li.append("closed")
li.append("expired")
li.append("cancelled")
li.append("waiting")
editor.addItems(li)
return editor
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.EditRole)
editor.setCurrentIndex(editor.findText(value))
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.tableview = QTableView()
self.tableview.setItemDelegateForColumn(1, ComboDelegate())
self.setCentralWidget(self.tableview)
self.model = QStandardItemModel()
self.tableview.setModel(self.model)
self.fillModel()
self.show()
def fillModel(self):
for i in data:
name_str = i
status_str = data[i]["status"]
name = QStandardItem(name_str)
status = QStandardItem(status_str)
items = [name, status]
self.model.appendRow(items)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
I've just noticed your other question about automatically showing the the comboBox
after you double click. I have a hack for doing that to which I've used before. It relies on passing the view into the delegate and adding the following lines to the createEditor
method:
editor.activated.connect(lambda index, editor=editor: self._view.commitData(editor))
editor.activated.connect(lambda index, editor=editor: self._view.closeEditor(editor,QAbstractItemDelegate.NoHint))
QTimer.singleShot(10,editor.showPopup)
Full working example:
import sys
import sip
sip.setapi('QVariant', 2)
from PyQt4.QtGui import *
from PyQt4.QtCore import *
data = {"first":{"status":"closed"},"second":{"status":"expired"},"third":{ "status":"cancelled"}}
class ComboDelegate(QItemDelegate):
def __init__(self, view):
QItemDelegate.__init__(self)
self._view = view
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
li = []
li.append("closed")
li.append("expired")
li.append("cancelled")
li.append("waiting")
editor.addItems(li)
editor.activated.connect(lambda index, editor=editor: self._view.commitData(editor))
editor.activated.connect(lambda index, editor=editor: self._view.closeEditor(editor,QAbstractItemDelegate.NoHint))
QTimer.singleShot(10,editor.showPopup)
return editor
def setEditorData(self, editor, index):
value = index.model().data(index, Qt.EditRole)
editor.setCurrentIndex(editor.findText(value))
class Example(QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.tableview = QTableView()
self.tableview.setItemDelegateForColumn(1, ComboDelegate(self.tableview))
self.setCentralWidget(self.tableview)
self.model = QStandardItemModel()
self.tableview.setModel(self.model)
self.fillModel()
self.show()
def fillModel(self):
for i in data:
name_str = i
status_str = data[i]["status"]
name = QStandardItem(name_str)
status = QStandardItem(status_str)
items = [name, status]
self.model.appendRow(items)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())