PyQt - 如何使用 QItemDelegate 在表视图中设置 QComboBox [英] PyQt - How to set QComboBox in a table view using QItemDelegate

查看:59
本文介绍了PyQt - 如何使用 QItemDelegate 在表视图中设置 QComboBox的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to display a combo box in my table, so that I can set the selected index from the table model, as with the other cells in the table. I have pieced this together from other examples but still cannot understand how the interaction works to set the selected index of the QComboBox.

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

This is the simplest example I can come up with to demonstrate the problem. If anyone can demonstrate how to set the index automatically from model data? Also how to use the 'currentIndexChanged' signal, as this seems to fire almost continuously whenever it is repainted? Thanks.

# 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_())

推荐答案

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

You're using paint method incorrectly. It should be used when you want to change displaying behavior of the view. Also creating new widget each time you want to paint it is very expensive. But you want to change editing behavior so you need to change entire logic of your program.

请参阅修复代码.下面我将解释这些变化.

See the fixed code. Below I'll expain the changes.

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

1. First of all, we need to make the first column editable. You can do it by reimplementing QAbstractItemModel::flags:

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

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

2. By default the item editor is created when user performs a double click on item. If you want to show all comboboxes by default, you can use openPersistentEditor:

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

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

Note that you should also open editors for newly created cells (if any).

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

3. Now back to our delegate. We need to implement createEditor method that will be automatically called by the view when an editor is requested for a cell:

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 低于 append s 因为我们需要在初始化时避免 currentIndexChanged 信号.

Note that connect is below appends because we need to avoid currentIndexChanged signals on initialization.

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

4. Implement setEditorData method that will be called by the view when model data has been changed. Also it will be called once when an editor is initialized.

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

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

Again, we want to avoid signals that are not caused by the user, so we use blockSignals.

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

5. In the slot we simply emit commitData signal that will cause the view to call the setModelData of our delegate:

@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方法:

7. Your model needs to support data changing. So we should implement setData method of the model:

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

这篇关于PyQt - 如何使用 QItemDelegate 在表视图中设置 QComboBox的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆