背景:我找不到QTableView
内组合框的完整工作示例。所以我根据其他几个更人为的例子编写了这段代码。但问题是,此示例要求您在组合框启用之前双击组合框,然后您必须再次单击以将其删除。它不是非常用户友好。如果我使用QTableWidget
进行非模型/视图操作,组合框会在第一次单击时下降。
问题:有人可以看一下这个并告诉我需要做些什么才能让它像QTableWidget
那样做出回应?此外,如果有任何我正在做的事情是不必要的,请同时说明。例如,是否绝对需要引用应用程序样式?
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_()
答案 0 :(得分:5)
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()
答案 1 :(得分:1)
如果您在视图显示编辑器时尝试调整,则需要更改QAbstractItemView中定义的编辑触发器。默认是在doubleClick上编辑,但我认为你所追求的是QAbstractItemView.CurrentChanged。通过调用myView.setEditTrigger()
进行设置答案 2 :(得分:0)
如果有人有兴趣,下面是针对PyQt5和Python 3修改的相同示例。主要更新包括:
super().__init__()
QtWidgets
;此示例不需要QtGui
Model.setData
:输入参数订单已更改为:index, value, role
,而True
已返回None
choices
和现在在Main
内指定的表格内容;这会使Delegate
和Model
更加通用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_()
答案 3 :(得分:0)
这应该有效:
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)
答案 4 :(得分:0)
你可以尝试这样的事情。
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_()
如您所见,我刚刚为您的代码添加了几行代码。 View管理"版本"所以你必须改变版本触发器。然后,当您设置委托数据时,强制委托显示窗口小部件的弹出窗口。
前段时间,我读了一篇博客文章,其中作者将QAbstractItemView子类化,以便正确地使用'与代表(版本,导航,更新数据等),但我找不到帖子:(
希望它有所帮助。
答案 5 :(得分:0)
这是我的PyQt5版本。它基于ToddP的工作(this answer),并解决了其他一些问题:
Qt.EditRole
更新模型总而言之,我发现这种方法的用户体验不好。当combox框获得焦点时,它开始吞噬所有类型的事件。没有明确的迹象表明它有重点。当弹出窗口可见时,您将无法再跳到下一个单元格。与QTableWidget内部的真实QComboBox小部件相比,它感觉笨拙。
由于我只有一个短表(<100行),所以我切换到了QTableWidget。现在,我可以自己创建小部件,然后使用setCellWidget()
将其放入表中。这样可以更轻松地添加特殊行为(例如,在QLineEdit
中,在文本的开头/结尾处按左/右时,转到下一个/上一个单元格)。
class ComboBoxDelegate(QtWidgets.QItemDelegate):
def __init__(self, choices, parent=None):
super().__init__(parent)
self.choices = choices
self.valueIndex = {
self.choices[i][1]: i
for i in range(len(self.choices))
}
def createEditor(self, parent, option, index):
self.editor = QtWidgets.QComboBox(parent)
for text, value in self.choices:
self.editor.addItem(text, value)
QTimer.singleShot(0, self.showPopup)
return self.editor
@QtCore.pyqtSlot()
def showPopup(self):
self.editor.showPopup()
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)
style.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, opt, painter)
QtWidgets.QItemDelegate.paint(self, painter, option, index)
def setEditorData(self, editor, index):
value = index.data(QtCore.Qt.EditRole)
num = self.valueIndex[value]
editor.setCurrentIndex(num)
def setModelData(self, editor, model, index):
value = editor.currentData()
model.setData(index, value, QtCore.Qt.EditRole)
def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)
您将要使用
table.setEditTriggers(QAbstractItemView.CurrentChanged) # Edit on first click
(请参阅此answer)
另请参阅: