在我的应用程序中,我使用了一个QTableView,QStandardItemModel和一个QSortFilterProxyModel来进行过滤。
内容是通过一个方法更新的列1 & 2,我希望有一个第三列供用户选择选项。我希望使用QComboBox。
我已经把所有的东西都弄好了,只是当我在第3列的任何一个单元格中选择QComboBox中的项目时,它没有被填充。这是否与我的 setModelData()
方法?
我也有一个清除按钮,我想把所有的QComboBoxes重置为第一项,也就是一个空条目。我不知道如何解决这个问题,我发现了一些方法,比如使用 deleteLater()
或设置QTableView的 setItemDelegateForColumn()
为 "无",然后重新申请。
显然这些都不是最有效的。我缺少什么?
工作的例子:在我的应用程序中,我使用了一个QTableView,QStandardItemModel和一个QSortFilterProxy。
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的功能,那么你就不必验证列。考虑到以上所有情况,我将你的委托人简化为。
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 user作为更新的参数,在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, "")