为什么我的 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,所以我不能声称了解它在做什么。有没有人可以解释这里发生了什么?非常感谢。
- OS: Ubuntu 20.04
- Python 3.8.5
- PySide2 5.15.0
- Qt 5.12.9
任何没有父级的小部件都是 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
我正在尝试使用 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,所以我不能声称了解它在做什么。有没有人可以解释这里发生了什么?非常感谢。
- OS: Ubuntu 20.04
- Python 3.8.5
- PySide2 5.15.0
- Qt 5.12.9
任何没有父级的小部件都是 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