为什么我的 QStyledItemDelegate 会在新的 window 中弹出,而不是在我的表视图中弹出?

Why does my QStyleItemDelegate pop up in a new window instead of in my TableView?

我正在尝试使用 Pyside2 创建一个非常简单的应用程序,它在 table 中显示用户可以编辑的一些数据。我希望 table 的单元格根据 table 中的其他值自动完成,所以我在 this tutorial 之后实现了一个自定义委托。这非常简单,代表按预期工作 除了 由于某种原因,代表弹出一个新的 window 而不是被附加到 table 中的单元格,如本示例所示(自定义委托在末尾定义):

import pandas as pd
from PySide2 import QtWidgets, QtCore
from PySide2.QtCore import Qt


class TableView(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setObjectName(u"TableView")
        self.centralwidget = QtWidgets.QWidget(self)
        self.centralwidget.setObjectName(u"centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.frame = QtWidgets.QFrame(self.centralwidget)
        self.frame.setObjectName(u"frame")
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame)
        self.verticalLayout_2.setObjectName(u"verticalLayout_2")
        
        self.tableView = QtWidgets.QTableView(self.frame)
        self.tableView.setObjectName(u"tableView")
        delegate = QAutoCompleteDelegate(self.tableView)
        self.tableView.setItemDelegate(delegate)
        self.tableModel = TableModel(self.tableView)
        self.tableView.setModel(self.tableModel)

        self.verticalLayout_2.addWidget(self.tableView)
        self.verticalLayout.addWidget(self.frame)

        self.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(self)
        self.statusbar.setObjectName(u"statusbar")
        self.setStatusBar(self.statusbar)

        QtCore.QMetaObject.connectSlotsByName(self)
        
        
class TableModel(QtCore.QAbstractTableModel):

    def __init__(self, parent=None):
        super(TableModel, self).__init__(parent=parent)
        self._data = pd.DataFrame([["A0", "B0", "C0"], ["A1", "B1", "C1"], ["A2", "B2", "C2"]], columns=["A", "B", "C"])

    def data(self, index, role):
        # Should only ever refer to the data by iloc in this method, unless you
        # go specifically fetch the correct loc based on the iloc
        row = index.row()
        col = index.column()
        if role == Qt.DisplayRole or role == Qt.EditRole:
            return self._data.iloc[row, col]

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data.columns)

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[col]
        return None
    
    def unique_vals(self, index):
        """ Identify the values to include in an autocompletion delegate for the given index """
        column = index.column()
        col_name = self._data.columns[column]
        return list(self._data[col_name].unique())

    def setData(self, index, value, role):
        if role != Qt.EditRole:
            return False
        row = self._data.index[index.row()]
        column = self._data.columns[index.column()]
        self._data.loc[row, column] = value
        self.dataChanged.emit(index, index)
        return True

    def flags(self, index):
        flags = super(self.__class__, self).flags(index)
        flags |= Qt.ItemIsEditable
        flags |= Qt.ItemIsSelectable
        flags |= Qt.ItemIsEnabled
        flags |= Qt.ItemIsDragEnabled
        flags |= Qt.ItemIsDropEnabled
        return flags


class QAutoCompleteDelegate(QtWidgets.QStyledItemDelegate):

    def createEditor(self, parent: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex) -> QtWidgets.QWidget:
        le = QtWidgets.QLineEdit()
        test = ["option1", "option2", "option3"]
        complete = QtWidgets.QCompleter(test)
        le.setCompleter(complete)
        return le

    def setEditorData(self, editor:QtWidgets.QLineEdit, index:QtCore.QModelIndex):
        val = index.model().data(index, Qt.EditRole)
        options = index.model().unique_vals(index)
        editor.setText(val)
        completer = QtWidgets.QCompleter(options)
        editor.setCompleter(completer)

    def updateEditorGeometry(self, editor: QtWidgets.QWidget, option: QtWidgets.QStyleOptionViewItem, index: QtCore.QModelIndex):
        editor.setGeometry(option.rect)


app = QtWidgets.QApplication()
tv = TableView()
tv.show()
app.exec_()

图像显示 table 单元格处于活动状态时的行为

从我读过的所有内容来看,自定义委托上的 updateEditorGeometry 方法应该将其锚定到活动单元格,但在我的情况下显然没有按预期工作。而且我还在学习 Qt,所以我不能声称了解它在做什么。有没有人可以解释这里发生了什么?非常感谢。

任何没有父级的小部件都是 window,并且由于 QLineEdit 没有父级,因此您会观察到 OP 指示的行为,因此 createEditor 方法将“parent”作为参数。

另一方面,最好创建一个自定义 QLineEdit,它可以通过模型轻松实现向 QCompleter 添加建议的处理。

class AutoCompleteLineEdit(QtWidgets.QLineEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._completer_model = QtGui.QStandardItemModel()
        completer = QtWidgets.QCompleter()
        completer.setModel(self._completer_model)
        self.setCompleter(completer)

    @property
    def suggestions(self):
        return [
            self._completer_model.item(i).text()
            for i in range(self._completer_model.rowCount())
        ]

    @suggestions.setter
    def suggestions(self, suggestions):
        self._completer_model.clear()
        for suggestion in suggestions:
            item = QtGui.QStandardItem(suggestion)
            self._completer_model.appendRow(item)


class QAutoCompleteDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(
        self,
        parent: QtWidgets.QWidget,
        option: QtWidgets.QStyleOptionViewItem,
        index: QtCore.QModelIndex,
    ) -> QtWidgets.QWidget:
        le = AutoCompleteLineEdit(parent)
        return le

    def setEditorData(self, editor: QtWidgets.QWidget, index: QtCore.QModelIndex):
        val = index.model().data(index, Qt.EditRole)
        options = index.model().unique_vals(index)
        editor.setText(val)
        editor.suggestions = options