在 QTableView 中正确使用 QComboBox - 设置数据和清除 QComboBoxes 的问题 [英] Using QComboBox in QTableView properly - issues with data being set and clearing QComboBoxes

查看:92
本文介绍了在 QTableView 中正确使用 QComboBox - 设置数据和清除 QComboBoxes 的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我使用 QTableView、QStandardItemModel 和 QSortFilterProxyModel 进行过滤.

In my application im using a QTableView, QStandardItemModel and a QSortFilterProxyModel in between for filtering.

通过第 1 列和第 1 列的方法更新内容.2,我希望有第三列供用户选择选项.我更喜欢使用 QComboBox.

The content is updated via a method for columns 1 & 2, and I want there to be a 3rd column for user to select options. I would prefer to use a QComboBox.

我已经完成了所有工作,除了当我从第 3 列的任何单元格中的 QComboBox 中选择项目时,它不会填充.它与我的 setModelData() 方法有关吗?

I've got everything pretty much working, except that when I select the item from the QComboBox in any of the cells in column 3, it doesn't populate. Does it have something to do with my setModelData() method?

我还有一个清除按钮,我想将所有 QComboBoxes 重置为第一个项目,这是一个空条目.我不知道如何解决这个问题,我发现诸如使用 deleteLater() 或将 QTableView 的 setItemDelegateForColumn() 设置为 None 并重新应用之类的东西.

I also have a clear button that I would like to reset all of the QComboBoxes to the first item which is an empty entry. I am not sure how to tackle this, i've found such things as using deleteLater() or setting the QTableView's setItemDelegateForColumn() to None and re-apply.

显然这些都不是最有效的.我错过了什么?

Obviously these are not the most efficient. What am I missing?

工作示例:

import win32com.client
from PyQt5 import QtCore, QtGui, QtWidgets

outApp = win32com.client.gencache.EnsureDispatch("Outlook.Application")
outGAL = outApp.Session.GetGlobalAddressList()
entries = outGAL.AddressEntries

class ComboDelegate(QtWidgets.QItemDelegate):

    def __init__(self,parent=None):
        super().__init__(parent)
        self.items = ['','To', 'CC']

    def createEditor(self, widget, option, index):
        editor = QtWidgets.QComboBox(widget)
        editor.addItems(self.items)
        return editor

    def setEditorData(self, editor, index):
        if index.column() == 2:
            editor.blockSignals(True)
            text = index.model().data(index, QtCore.Qt.EditRole)
            try:
                i = self.items.index(text)
            except ValueError:
                i = 0
            editor.setCurrentIndex(i)
            editor.blockSignals(False)
        else:
            QtWidgets.QItemDelegate.setModelData(editor,model,index)

    def setModelData(self, editor, model, index):
        if index.column() == 2:
            model.setData(index, editor.currentText())
        else:
            QtWidgets.QItemDelegate.setModelData(editor,model,index)

    def updateEditorGeometry(self, editor, option, index):
        editor.setGeometry(option.rect)

    def paint(self, painter, option, index):
        QtWidgets.QApplication.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter)

class App(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        """This method creates our GUI"""
        self.centralwidget = QtWidgets.QWidget()
        self.setCentralWidget(self.centralwidget)
        self.lay = QtWidgets.QVBoxLayout(self.centralwidget)
        self.filterEdit = QtWidgets.QLineEdit()
        self.filterEdit.setPlaceholderText("Type to filter name.")
        self.label = QtWidgets.QLabel("Select an option for each person:")
        self.button = QtWidgets.QPushButton("Test Button")
        self.button.clicked.connect(self.runButton)
        self.resetbutton = QtWidgets.QPushButton("Clear")
        self.resetbutton.clicked.connect(self.clear)
        self.lay.addWidget(self.filterEdit)
        self.lay.addWidget(self.label)

        self.tableview=QtWidgets.QTableView(self.centralwidget)
        self.model=QtGui.QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['Name','Address','Option'])
        self.tableview.verticalHeader().hide()
        self.tableview.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
        self.tableview.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers)
        self.proxyModel = QtCore.QSortFilterProxyModel(self)
        self.proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxyModel.setSourceModel(self.model)
        self.proxyModel.sort(0,QtCore.Qt.AscendingOrder)
        self.proxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.tableview.setModel(self.proxyModel)

        self.model.insertRow(self.model.rowCount(QtCore.QModelIndex()))
        #self.fillModel(self.model) #uncomment if you have outlook

        self.tableview.resizeColumnsToContents()
        self.tableview.verticalHeader().setDefaultSectionSize(10)
        self.filterEdit.textChanged.connect(self.onTextChanged)
        self.lay.addWidget(self.tableview)
        self.delegate = ComboDelegate()
        self.tableview.setItemDelegateForColumn(2, self.delegate)
        self.lay.addWidget(self.button)
        self.lay.addWidget(self.resetbutton)

        self.setMinimumSize(450, 200)
        self.setMaximumSize(1500, 200)
        self.setWindowTitle('Application')

    def clear(self):
        ###clear tableview comboboxes in column 3
        print("clear")

    def runButton(self,index):
        print("Do stuff")

    def fillModel(self,model):
        """Fills model from outlook address book """
        nameList = []
        addressList = []
        for row,entry in enumerate(entries):
            if entry.Type == "EX":
                user = entry.GetExchangeUser()
                if user is not None:
                    if len(user.FirstName) > 0 and len(user.LastName) > 0:
                        nameItem = QtGui.QStandardItem(str(user.Name))
                        emailItem = QtGui.QStandardItem(str(user.PrimarySmtpAddress))
                        nameItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
                        emailItem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
                        model.appendRow([nameItem,emailItem])

    @QtCore.pyqtSlot(str)
    def onTextChanged(self, text):
        self.proxyModel.setFilterRegExp(text)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = App()
    w.show()
    sys.exit(app.exec_())

推荐答案

问题是你不必要地覆盖了paint方法,因为你不想自定义任何东西.在覆盖之前,我建议您了解它的作用,为此您可以使用文档或源代码.但总而言之,在 QItemDelegate 的情况下,paint 方法在选项"中建立角色的信息,然后只是绘制,在该信息中是文本.但在您的情况下,没有必要,因此无需覆盖.另一方面,如果您的委托具有建立 QComboBox 的唯一功能,则您不必验证列.考虑到以上所有因素,我已将您的委托简化为:

The problem is that you override the paint method unnecessarily since you don't want to customize anything. Before override I recommend you understand what it does and for this you can use the docs or the source code. But to summarize, in the case of the QItemDelegate the paint method establishes the information of the roles in the "option" and then just paints, and within that information is the text. But in your case it is not necessary so there is no need to override. On the other hand, if your delegate has the sole function of establishing a QComboBox then you don't have to verify the columns. Considering all of the above, I have simplified your delegate to:

class ComboDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.items = ["", "To", "CC"]

    def createEditor(self, widget, option, index):
        editor = QtWidgets.QComboBox(widget)
        editor.addItems(self.items)
        return editor

    def setEditorData(self, editor, index):
        editor.blockSignals(True)
        text = index.model().data(index, QtCore.Qt.EditRole)
        try:
            i = self.items.index(text)
        except ValueError:
            i = 0
        editor.setCurrentIndex(i)
        editor.blockSignals(False)

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

另一方面,QItemEditorFactory 使用 qproperty 用户作为更新参数,在 QComboBox 的情况下,它是currentText",因此可以使用该信息进一步简化:

On the other hand, the QItemEditorFactory uses the qproperty user as the parameter for the update, and in the case of the QComboBox it is the "currentText", so it can be further simplified using that information:

class ComboDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.items = ["", "To", "CC"]

    def createEditor(self, widget, option, index):
        editor = QtWidgets.QComboBox(widget)
        editor.addItems(self.items)
        return editor

对于clear方法很简单:遍历第三列的所有行并设置空文本:

For the clear method is simple: Iterate over all the rows of the third column and set the empty text:

def clear(self):
    # clear tableview comboboxes in column 3
    for i in range(self.model.rowCount()):
        index = self.model.index(i, 2)
        self.model.setData(index, "")

这篇关于在 QTableView 中正确使用 QComboBox - 设置数据和清除 QComboBoxes 的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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