python - Cómo usar QComboBox como delegado con QTableView
pyqt (2)
El código siguiente crea un solo QTableView. Al hacer doble clic en su elemento se establecerá con un QComboBox
delegado.
Problema : cuando se hace clic en el ComboBox, su menú desplegable aparece momentáneamente y luego vuelve a su estado desenrollado.
Si comboBox se configurara para ser editable usando:
combo.setEditable(True)
el menú desplegable permanecería abierto según lo deseado. Pero luego los elementos del combobox se vuelven editables. Y no es lo que necesitaba Dado que los elementos del combobox solo se pueden seleccionar.
¿Cómo corregir el comportamiento de la caja combinada autocolapsable?
Ps He notado que cuando ComboBox se configura para ser editable y se desenrolla su menú desplegable, los data()
del modelo data()
se llaman constantemente ... ¿Es probable que el comportamiento de auto colapso pueda ser desencadenado por el modelo?
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class ComboDelegate(QItemDelegate):
comboItems=[''Combo_Zero'', ''Combo_One'',''Combo_Two'']
def createEditor(self, parent, option, proxyModelIndex):
combo = QComboBox(parent)
combo.addItems(self.comboItems)
# combo.setEditable(True)
self.connect(combo, SIGNAL("currentIndexChanged(int)"), self, SLOT("currentIndexChanged()"))
return combo
def setModelData(self, combo, model, index):
comboIndex=combo.currentIndex()
text=self.comboItems[comboIndex]
model.setData(index, text)
print ''/t/t/t ...setModelData() 1'', text
@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())
class MyModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items=[''Data_Item01'',''Data_Item02'',''Data_Item03'']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
row=index.row()
item=self.items[row]
if row>len(self.items): return QVariant()
if role == Qt.DisplayRole:
print '' << >> MyModel.data() returning ...'', item
return QVariant(item)
def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled
def setData(self, index, text):
self.items[index.row()]=text
if __name__ == ''__main__'':
app = QApplication(sys.argv)
model = MyModel()
tableView = QTableView()
tableView.setModel(model)
delegate = ComboDelegate()
tableView.setItemDelegate(delegate)
tableView.resizeRowsToContents()
tableView.show()
sys.exit(app.exec_())
Aquí está el intento de sustituir un QComboBox
con QToolButton
vinculado a QMenu
y QAction
s.
La apariencia es casi lo mismo que QComboBox con una característica adicional de tener la capacidad de configurar múltiples QActions comprobadas (actualmente no es posible tener múltiples elementos de ComboBoxes marcados).
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class ComboDelegate(QItemDelegate):
comboItems=[''Combo_Zero'', ''Combo_One'',''Combo_Two'']
def __init__(self, parent):
QItemDelegate.__init__(self, parent=None)
self.actionEmitted=None
def createEditor(self, parent, option, index):
if not index.isValid(): return
model=index.model()
itemName=model.data(index, Qt.DisplayRole)
toolButton=QToolButton(parent)
toolButton.setText( itemName.toString() )
toolButton.setPopupMode(QToolButton.InstantPopup)
menu=QMenu(parent)
action1=QAction(''Action 01'', menu, checkable=True)
action2=QAction(''Action 02'', menu, checkable=True)
action3=QAction(''Action 03'', menu, checkable=True)
action1.setObjectName(''Action 01'')
action2.setObjectName(''Action 02'')
action3.setObjectName(''Action 03'')
action1.setChecked(True)
action3.setChecked(True)
self.connect(action1, SIGNAL("triggered(bool)"), self, SLOT("actionTriggered()"))
self.connect(action2, SIGNAL("triggered(bool)"), self, SLOT("actionTriggered()"))
self.connect(action3, SIGNAL("triggered(bool)"), self, SLOT("actionTriggered()"))
menu.addAction(action1)
menu.addAction(action2)
menu.addAction(action3)
toolButton.setMenu(menu)
return toolButton
def setModelData(self, toolButton, model, index):
print ''/t/t/t ...setModelData() 1'', toolButton, model, index
if not self.actionEmitted: return
menu=toolButton.menu()
for action in menu.actions():
actionName=action.objectName()
actionStatus=action.isChecked()
if actionStatus:
model.setData(index, actionName)
print ''####'', actionName, actionStatus
@pyqtSlot()
def actionTriggered(self):
self.actionEmitted=self.sender()
self.commitData.emit( self.actionEmitted )
print ''actionTriggered.....'', self.actionEmitted
class MyModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items=[''Data_Item01'',''Data_Item02'',''Data_Item03'']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
row=index.row()
item=self.items[row]
if row>len(self.items): return QVariant()
if role == Qt.DisplayRole:
print '' << >> MyModel.data() returning ...'', item
return QVariant(item)
def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled
def setData(self, index, itemName):
self.items[index.row()]=itemName
if __name__ == ''__main__'':
app = QApplication(sys.argv)
combo=QComboBox()
combo.addItems([''Combo_Zero'', ''Combo_One'',''Combo_Two''])
model = MyModel()
tableView = QTableView()
tableView.setModel(model)
delegate = ComboDelegate(tableView)
tableView.setItemDelegate(delegate)
tableView.resizeRowsToContents()
tableView.show()
sys.exit(app.exec_())
Su código original está funcionando en PyQt5, el cuadro combinado permanece abierto. Pero el usuario tiene que hacer clic para abrir el editor y luego hacer clic para abrir el cuadro combinado. Para evitar esto, reemplacé QComboBox por QlistWidget en tu código. Además, establecí editorGeometry:
import sys
# from PyQt4.QtCore import *
# from PyQt4.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ComboDelegate(QItemDelegate):
editorItems=[''Combo_Zero'', ''Combo_One'',''Combo_Two'']
height = 25
width = 200
def createEditor(self, parent, option, index):
editor = QListWidget(parent)
# editor.addItems(self.editorItems)
# editor.setEditable(True)
editor.currentItemChanged.connect(self.currentItemChanged)
return editor
def setEditorData(self,editor,index):
z = 0
for item in self.editorItems:
ai = QListWidgetItem(item)
editor.addItem(ai)
if item == index.data():
editor.setCurrentItem(editor.item(z))
z += 1
editor.setGeometry(0,index.row()*self.height,self.width,self.height*len(self.editorItems))
def setModelData(self, editor, model, index):
editorIndex=editor.currentIndex()
text=editor.currentItem().text()
model.setData(index, text)
# print ''/t/t/t ...setModelData() 1'', text
@pyqtSlot()
def currentItemChanged(self):
self.commitData.emit(self.sender())
class MyModel(QAbstractTableModel):
def __init__(self, parent=None, *args):
QAbstractTableModel.__init__(self, parent, *args)
self.items=[''Data_Item01'',''Data_Item02'',''Data_Item03'']
def rowCount(self, parent=QModelIndex()):
return len(self.items)
def columnCount(self, parent=QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return QVariant()
row=index.row()
item=self.items[row]
if row>len(self.items): return QVariant()
if role == Qt.DisplayRole:
# print '' << >> MyModel.data() returning ...'', item
return QVariant(item)
def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled
def setData(self, index, text):
self.items[index.row()]=text
if __name__ == ''__main__'':
app = QApplication(sys.argv)
model = MyModel()
tableView = QTableView()
tableView.setModel(model)
delegate = ComboDelegate()
tableView.setItemDelegate(delegate)
for i in range(0,tableView.model().rowCount()):
tableView.setRowHeight(i,tableView.itemDelegate().height)
for i in range(0,tableView.model().columnCount()):
tableView.setColumnWidth(i,tableView.itemDelegate().width)
tableView.show()
sys.exit(app.exec_())