如何在 QTableView 中为单个单元格设置委托?
how to set a delegate for a single cell in QTableView?
class QTableView为setDelegate提供了3个接口:
setItemDelegate -- 为整个 QTableView 设置委托
setItemDelegateForRow -- 为给定行设置委托
setItemDelegateForColumn -- 为给定列设置委托
问题:如果我只想为一个单元格设置委托,我该怎么做?
例如,我有一个包含两列的 Qtableview,第一列设置了自定义 QCombobox 委托,其中包含人类和植物项目。第二列也是用QCombobox delegate设置的,但是可选项取决于第一列的选择。这意味着第二列中的每个单元格可能有不同的代表。
例如,如果从组合框委托中选择数据(行=1,列=1)作为人类,则单元格(行=1,列=2)有一个包含项目的组合框委托header ,body,手脚;如果从组合框委托中选择数据(行=2,列=1)作为植物,则单元格(行=2,列=2)有一个组合框委托,其中包含项目根、叶、花。
有类似的问题还没回答,Set Delegate for each cell in a QTableView?
对此没有直接的解决方案,至少(据我所知)来自 PyQt5。
问题是所有项目视图 类 使用私有(并且无法从 PyQt 访问)delegateForIndex
函数,该函数首先检查行和 returns 该行的委托如果存在,则它对列执行相同的操作(同样,如果存在),最后 returns 默认委托(内置委托或具有通用 setItemDelegate()
的一组)。
所以,如果你想确保代表总是首先基于row/column对(然后“退回到”某行或某列依赖行为),唯一的解决方案是使用unique委托,然后根据row/column位置实现相关功能。
我认为你有 XY problem。该逻辑与每个单元格的委托无关,而是基于 QModelIndex(提供行和列)实现逻辑。
import random
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QStyledItemDelegate,
QTableView,
QWidget,
)
OPTIONS = {
"human": ["header", "body", "hand", "foot"],
"plant": ["root", "leaf", "flower"],
}
class Delegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
editor.currentTextChanged.connect(self.handle_commit_close_editor)
return editor
def setEditorData(self, editor, index):
if index.column() == 0:
option = index.data()
editor.clear()
editor.addItems(list(OPTIONS.keys()))
editor.setCurrentText(option)
elif index.column() == 1:
option = index.sibling(index.row(), 0).data()
options = OPTIONS.get(option, [])
editor.clear()
editor.addItems(options)
def setModelData(self, editor, model, index):
if index.column() == 0:
option = editor.currentText()
model.setData(index, option, Qt.DisplayRole)
options = OPTIONS.get(option, [])
model.setData(
index.sibling(index.row(), 1),
options[0] if options else "",
Qt.DisplayRole,
)
elif index.column() == 1:
option = editor.currentText()
model.setData(index, option, Qt.DisplayRole)
def handle_commit_close_editor(self):
editor = self.sender()
if isinstance(editor, QWidget):
self.commitData.emit(editor)
self.closeEditor.emit(editor)
def main():
app = QApplication(sys.argv)
app.setStyle("fusion")
model = QStandardItemModel(0, 2)
for i in range(4):
option = random.choice(list(OPTIONS.keys()))
item_1 = QStandardItem(option)
item_2 = QStandardItem(random.choice(list(OPTIONS[option])))
model.appendRow([item_1, item_2])
view = QTableView()
view.setModel(model)
view.resize(640, 480)
view.show()
delegate = Delegate(view)
view.setItemDelegate(delegate)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
class QTableView为setDelegate提供了3个接口:
setItemDelegate -- 为整个 QTableView 设置委托
setItemDelegateForRow -- 为给定行设置委托
setItemDelegateForColumn -- 为给定列设置委托
问题:如果我只想为一个单元格设置委托,我该怎么做?
例如,我有一个包含两列的 Qtableview,第一列设置了自定义 QCombobox 委托,其中包含人类和植物项目。第二列也是用QCombobox delegate设置的,但是可选项取决于第一列的选择。这意味着第二列中的每个单元格可能有不同的代表。
例如,如果从组合框委托中选择数据(行=1,列=1)作为人类,则单元格(行=1,列=2)有一个包含项目的组合框委托header ,body,手脚;如果从组合框委托中选择数据(行=2,列=1)作为植物,则单元格(行=2,列=2)有一个组合框委托,其中包含项目根、叶、花。
有类似的问题还没回答,Set Delegate for each cell in a QTableView?
对此没有直接的解决方案,至少(据我所知)来自 PyQt5。
问题是所有项目视图 类 使用私有(并且无法从 PyQt 访问)delegateForIndex
函数,该函数首先检查行和 returns 该行的委托如果存在,则它对列执行相同的操作(同样,如果存在),最后 returns 默认委托(内置委托或具有通用 setItemDelegate()
的一组)。
所以,如果你想确保代表总是首先基于row/column对(然后“退回到”某行或某列依赖行为),唯一的解决方案是使用unique委托,然后根据row/column位置实现相关功能。
我认为你有 XY problem。该逻辑与每个单元格的委托无关,而是基于 QModelIndex(提供行和列)实现逻辑。
import random
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (
QApplication,
QComboBox,
QStyledItemDelegate,
QTableView,
QWidget,
)
OPTIONS = {
"human": ["header", "body", "hand", "foot"],
"plant": ["root", "leaf", "flower"],
}
class Delegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
editor = QComboBox(parent)
editor.currentTextChanged.connect(self.handle_commit_close_editor)
return editor
def setEditorData(self, editor, index):
if index.column() == 0:
option = index.data()
editor.clear()
editor.addItems(list(OPTIONS.keys()))
editor.setCurrentText(option)
elif index.column() == 1:
option = index.sibling(index.row(), 0).data()
options = OPTIONS.get(option, [])
editor.clear()
editor.addItems(options)
def setModelData(self, editor, model, index):
if index.column() == 0:
option = editor.currentText()
model.setData(index, option, Qt.DisplayRole)
options = OPTIONS.get(option, [])
model.setData(
index.sibling(index.row(), 1),
options[0] if options else "",
Qt.DisplayRole,
)
elif index.column() == 1:
option = editor.currentText()
model.setData(index, option, Qt.DisplayRole)
def handle_commit_close_editor(self):
editor = self.sender()
if isinstance(editor, QWidget):
self.commitData.emit(editor)
self.closeEditor.emit(editor)
def main():
app = QApplication(sys.argv)
app.setStyle("fusion")
model = QStandardItemModel(0, 2)
for i in range(4):
option = random.choice(list(OPTIONS.keys()))
item_1 = QStandardItem(option)
item_2 = QStandardItem(random.choice(list(OPTIONS[option])))
model.appendRow([item_1, item_2])
view = QTableView()
view.setModel(model)
view.resize(640, 480)
view.show()
delegate = Delegate(view)
view.setItemDelegate(delegate)
sys.exit(app.exec_())
if __name__ == "__main__":
main()