Using QComboBox in QTableView properly - issues with data being set and clearing QComboBoxes

家住魔仙堡 提交于 2021-02-05 07:39:57

问题


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

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.

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?

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?

Working example:

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

回答1:


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

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

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, "")


来源:https://stackoverflow.com/questions/61921388/using-qcombobox-in-qtableview-properly-issues-with-data-being-set-and-clearing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!