python-3.x - 在 QTableView 中正确使用 QComboBox - 设置数据和清除 QComboBox 的问题

标签 python-3.x pyqt5 qtableview qcombobox

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

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

我已经一切正常,除了当我从第 3 列任何单元格中的 QComboBox 中选择项目时,它不会填充。它与我的 setModelData() 方法有关吗?

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

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

工作示例:

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 的情况下,绘制方法会在“选项”中建立角色信息,然后进行绘制,该信息中包含文本。但在你的情况下这是没有必要的,所以没有必要重写。另一方面,如果您的委托(delegate)具有建立 QComboBox 的唯一功能,那么您不必验证列。考虑到上述所有内容,我已将您的委托(delegate)简化为:

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”,因此可以使用该信息进一步简化:

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

关于python-3.x - 在 QTableView 中正确使用 QComboBox - 设置数据和清除 QComboBox 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61921388/

相关文章:

python - 如何将函数应用于 pandas 数据框中列中的每个值?

python - 在 PyQt 中绘制多边形

python - 在 PyQt5 中获取滚动条拇指的大小/宽度?

python - 每次单击按钮时移动标签 PyQt5

python - 如何获得 QTableView 标题中单击的右键单击上下文菜单?

Python 有时不会将类实例作为第一个参数传递 - 理解 __new__

python - 在单独的线程中运行 asyncio 循环,信号来自和到循环

python - PyQt QTableView非英文字符排序问题

Qt 模型/ View 与标准小部件

python - 如何捕获 OpenCV 写入 stderr 的消息?