【问题标题】:PyQt - How to set QComboBox in a table view using QItemDelegatePyQt - 如何使用 QItemDelegate 在表格视图中设置 QComboBox
【发布时间】:2013-07-11 01:26:33
【问题描述】:

我试图在我的表格中显示一个组合框,以便我可以设置表格模型中的选定索引,就像表格中的其他单元格一样。我已经从其他示例中将其拼凑在一起,但仍然无法理解交互如何设置 QComboBox 的选定索引。

这是我能想出的最简单的例子来演示这个问题。如果有人可以演示如何从模型数据中自动设置索引?另外如何使用'currentIndexChanged'信号,因为它似乎在重新绘制时几乎连续触发?谢谢。

# The following tells SIP (the system that binds Qt's C++ to Python)
# to return Python native types rather than QString and QVariant
import sip
sip.setapi('QString', 2)
sip.setapi('QVariant', 2)


from PyQt4 import QtCore, QtGui

class TableModel(QtCore.QAbstractTableModel):
    """
    A simple 5x4 table model to demonstrate the delegates
    """
    def rowCount(self, parent=QtCore.QModelIndex()): return 5
    def columnCount(self, parent=QtCore.QModelIndex()): return 4

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid(): return None
        if not role==QtCore.Qt.DisplayRole: return None
        return "{0:02d}".format(index.row())


class ComboDelegate(QtGui.QItemDelegate):
    """
    A delegate that places a fully functioning QComboBox in every
    cell of the column to which it's applied
    """
    def __init__(self, parent):

        QtGui.QItemDelegate.__init__(self, parent)

    def paint(self, painter, option, index):

        self.combo = QtGui.QComboBox(self.parent())
        self.connect(self.combo, QtCore.SIGNAL("currentIndexChanged(int)"), self.parent().currentIndexChanged)

        li = []
        li.append("Zero")
        li.append("One")
        li.append("Two")
        li.append("Three")
        li.append("Four")
        li.append("Five")

        self.combo.addItems(li)

        if not self.parent().indexWidget(index):
            self.parent().setIndexWidget(
                index, 
                self.combo
            )

class TableView(QtGui.QTableView):
    """
    A simple table to demonstrate the QComboBox delegate.
    """
    def __init__(self, *args, **kwargs):
        QtGui.QTableView.__init__(self, *args, **kwargs)

        # Set the delegate for column 0 of our table
        # self.setItemDelegateForColumn(0, ButtonDelegate(self))
        self.setItemDelegateForColumn(0, ComboDelegate(self))

    @QtCore.pyqtSlot()
    def currentIndexChanged(self, ind):
        print "Combo Index changed {0} {1} : {2}".format(ind, self.sender().currentIndex(), self.sender().currentText())

if __name__=="__main__":
    from sys import argv, exit

    class Widget(QtGui.QWidget):
        """
        A simple test widget to contain and own the model and table.
        """
        def __init__(self, parent=None):
            QtGui.QWidget.__init__(self, parent)

            l=QtGui.QVBoxLayout(self)
            self._tm=TableModel(self)
            self._tv=TableView(self)
            self._tv.setModel(self._tm)
            l.addWidget(self._tv)

    a=QtGui.QApplication(argv)
    w=Widget()
    w.show()
    w.raise_()
    exit(a.exec_())

【问题讨论】:

    标签: python qt pyqt


    【解决方案1】:

    您错误地使用了paint 方法。当您想要更改视图的显示行为时,应该使用它。每次你想画它时创建新的小部件也是非常昂贵的。但是您想更改编辑行为,因此您需要更改程序的整个逻辑。

    请参阅fixed code。下面我将解释这些变化。

    1. 首先,我们需要使第一列可编辑。你可以通过重新实现QAbstractItemModel::flags来做到这一点:

    def flags(self, index):
        if (index.column() == 0):
            return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
        else:
            return QtCore.Qt.ItemIsEnabled
    

    2. 默认情况下,当用户双击项目时会创建项目编辑器。如果要默认显示所有组合框,可以使用openPersistentEditor

    for row in range(0, self._tm.rowCount()):
        self._tv.openPersistentEditor(self._tm.index(row, 0))
    

    请注意,您还应该为新创建的单元格(如果有)打开编辑器。

    3. 现在回到我们的代表。我们需要实现createEditor 方法,当单元格请求编辑器时,视图会自动调用该方法:

    def createEditor(self, parent, option, index):
        combo = QtGui.QComboBox(parent)
        li = []
        li.append("Zero")
        li.append("One")
        li.append("Two")
        li.append("Three")
        li.append("Four")
        li.append("Five")
        combo.addItems(li)
        self.connect(combo, QtCore.SIGNAL("currentIndexChanged(int)"), 
                     self, QtCore.SLOT("currentIndexChanged()"))
        return combo
    

    注意connect 低于appends,因为我们需要在初始化时避免currentIndexChanged 信号。

    4. 实现setEditorData 方法,当模型数据更改时视图将调用该方法。当编辑器初始化时,它也会被调用一次。

    def setEditorData(self, editor, index):
        editor.blockSignals(True)
        editor.setCurrentIndex(int(index.model().data(index)))
        editor.blockSignals(False)
    

    再次,我们想避免不是由用户引起的信号,所以我们使用blockSignals

    5. 在插槽中,我们只需发出commitData 信号,这将导致视图调用我们委托的setModelData

    @QtCore.pyqtSlot()
    def currentIndexChanged(self):
        self.commitData.emit(self.sender())
    

    6. 实现setModelData 方法:

    def setModelData(self, editor, model, index):
        model.setData(index, editor.currentIndex())
    

    7.您的模型需要支持数据更改。所以我们应该实现模型的setData方法:

    def setData(self, index, value, role=QtCore.Qt.DisplayRole):
        print "setData", index.row(), index.column(), value
        # todo: remember the data
    

    【讨论】:

    • 我添加了一个关于数据模型here 的后续问题,而不是过多地扩展这个问题。
    • 还有另一个后续问题here,关于在视图中添加一列复选框。
    • 我知道这已经很长时间了,但我在实施您的解决方案时遇到了麻烦。我已经发布了一个后续问题here
    • 谢谢,这对我有用!在调用openPersistentEditor() 方面,除了循环之外,可以做的是在委托的paint() 函数中调用它。
    猜你喜欢
    • 1970-01-01
    • 2012-06-21
    • 2013-12-26
    • 2018-03-19
    • 1970-01-01
    • 2014-05-12
    • 2012-06-30
    • 2019-09-03
    • 1970-01-01
    相关资源
    最近更新 更多