如何使用 QComboBox 作为 QTableView 的委托
How to use QComboBox as delegate with QTableView
下面的代码创建了一个 QTableView
。 Double-clicking 其项目将使用委托 QComboBox
.
设置它
问题:
单击 ComboBox
时,它的 pull-down 菜单会暂时显示,然后折叠回展开状态。
如果 comboBox
设置为可使用 combox.setEditable(True)
编辑,
pull-down 菜单将根据需要保持打开状态。但随后 combobox
的项目变为可编辑。这不是所需要的。由于 combobox
的项目应该只能选择。
如何修复 self-collapsing combobox
的行为?
P.s。我注意到当 ComboBox
设置为可编辑且其 pull-down 菜单展开时,模型的 data()
不断被调用... self-collapsing 行为可能由以下原因触发型号?
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_())
这里尝试用链接到 QMenu
和 QAction
的 QToolButton
替换 QComboBox
。
外观与 QComboBox
大致相同,具有设置多个 QActions
选中的附加功能(目前无法选中多个组合框的项目) .
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_())
您的原始代码在 PyQt5 中运行,combobox
保持打开状态。但是用户必须点击打开编辑器,然后点击打开combobox
。为了避免这种情况,我在您的代码中将 QComboBox
替换为 QlistWidget
。另外我设置 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_())
下面的代码创建了一个 QTableView
。 Double-clicking 其项目将使用委托 QComboBox
.
问题:
单击 ComboBox
时,它的 pull-down 菜单会暂时显示,然后折叠回展开状态。
如果 comboBox
设置为可使用 combox.setEditable(True)
编辑,
pull-down 菜单将根据需要保持打开状态。但随后 combobox
的项目变为可编辑。这不是所需要的。由于 combobox
的项目应该只能选择。
如何修复 self-collapsing combobox
的行为?
P.s。我注意到当 ComboBox
设置为可编辑且其 pull-down 菜单展开时,模型的 data()
不断被调用... self-collapsing 行为可能由以下原因触发型号?
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_())
这里尝试用链接到 QMenu
和 QAction
的 QToolButton
替换 QComboBox
。
外观与 QComboBox
大致相同,具有设置多个 QActions
选中的附加功能(目前无法选中多个组合框的项目) .
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_())
您的原始代码在 PyQt5 中运行,combobox
保持打开状态。但是用户必须点击打开编辑器,然后点击打开combobox
。为了避免这种情况,我在您的代码中将 QComboBox
替换为 QlistWidget
。另外我设置 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_())