gpt4 book ai didi

python - 使用组合委托(delegate)对 QTableView 进行排序会删除除双击之外的委托(delegate)

转载 作者:行者123 更新时间:2023-12-01 00:19:25 32 4
gpt4 key购买 nike

我有 QTableView,它可以查看 pandas 表。第一行有 QComboBox 代表。当我按列对表格进行排序时,代表消失了。

下面是我的代码的工作示例。

import sys
import pandas as pd
import numpy as np

from PyQt5.QtCore import (QAbstractTableModel, Qt, pyqtProperty, pyqtSlot,
QVariant, QModelIndex)
from PyQt5.QtWidgets import (QItemDelegate, QComboBox, QMainWindow, QTableView,
QApplication)


class DataFrameModel(QAbstractTableModel):
DtypeRole = Qt.UserRole + 1000
ValueRole = Qt.UserRole + 1001
ActiveRole = Qt.UserRole + 1

def __init__(self, df=pd.DataFrame(), parent=None):
super(DataFrameModel, self).__init__(parent)
self._dataframe = df

def setDataFrame(self, dataframe):
self.beginResetModel()
self._dataframe = dataframe.copy()
self.endResetModel()

def dataFrame(self):
return self._dataframe

dataFrame = pyqtProperty(pd.DataFrame, fget=dataFrame, fset=setDataFrame)

@pyqtSlot(int, Qt.Orientation, result=str)
def headerData(self, section, orientation, role=Qt.DisplayRole):
if role != Qt.DisplayRole:
return QVariant()

if orientation == Qt.Horizontal:
try:
return self._dataframe.columns.tolist()[section]
except (IndexError, ):
return QVariant()
elif orientation == Qt.Vertical:
try:
if section in [0]:
pass
else:
return self._dataframe.index.tolist()[section - 1]
except (IndexError, ):
return QVariant()

def rowCount(self, parent=QModelIndex()):
if parent.isValid():
return 0
return len(self._dataframe.index)

def columnCount(self, parent=QModelIndex()):

if parent.isValid():
return 0
return self._dataframe.columns.size

def data(self, index, role=Qt.DisplayRole):
if not index.isValid() or not (0 <= index.row() < self.rowCount()
and 0 <= index.column() <
self.columnCount()):
return QVariant()
row = self._dataframe.index[index.row()]
col = self._dataframe.columns[index.column()]
dt = self._dataframe[col].dtype

val = self._dataframe.iloc[row][col]

if role == Qt.DisplayRole:
return str(val)
elif role == DataFrameModel.ValueRole:
return val
if role == DataFrameModel.DtypeRole:
return dt
return QVariant()

def roleNames(self):
roles = {
Qt.DisplayRole: b'display',
DataFrameModel.DtypeRole: b'dtype',
DataFrameModel.ValueRole: b'value'
}
return roles

def setData(self, index, value, role):
col = index.column()
row = index.row()
if index.row() == 0:
if isinstance(value, QVariant):
value = value.value()
if hasattr(value, 'toPyObject'):
value = value.toPyObject()
self._dataframe.iloc[row, col] = value
self.dataChanged.emit(index, index, (Qt.DisplayRole,))
else:
try:
value = eval(value)
if not isinstance(
value,
self._dataframe.applymap(type).iloc[row, col]):
value = self._dataframe.iloc[row, col]
except Exception as e:
value = self._dataframe.iloc[row, col]
self._dataframe.iloc[row, col] = value
self.dataChanged.emit(index, index, (Qt.DisplayRole,))
return True

def flags(self, index):
return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

def sort(self, column, order):
self.layoutAboutToBeChanged.emit()
col_name = self._dataframe.columns.tolist()[column]
sheet1 = self._dataframe.iloc[:1, :]
sheet2 = self._dataframe.iloc[1:, :].sort_values(
col_name, ascending=order == Qt.AscendingOrder, inplace=False)

sheet2.reset_index(drop=True, inplace=True)
sheet3 = pd.concat([sheet1, sheet2], ignore_index=True)
self.setDataFrame(sheet3)
self.layoutChanged.emit()


class ComboBoxDelegate(QItemDelegate):
def __init__(self, owner, choices):
super().__init__(owner)
self.items = choices

def createEditor(self, parent, option, index):
editor = QComboBox(parent)
editor.addItems(self.items)
editor.currentIndexChanged.connect(self.currentIndexChanged)
return editor

def paint(self, painter, option, index):
if isinstance(self.parent(), QItemDelegate):
self.parent().openPersistentEditor(0, index)
QItemDelegate.paint(self, painter, option, index)

def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, Qt.EditRole)

def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)

@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())


class MainWindow(QMainWindow):
def __init__(self, pandas_sheet):
super().__init__()
self.pandas_sheet = pandas_sheet

for i in range(1):
self.pandas_sheet.loc[-1] = [''] * len(self.pandas_sheet.columns.values)
self.pandas_sheet.index = self.pandas_sheet.index + 1
self.pandas_sheet = self.pandas_sheet.sort_index()
self.table = QTableView()
self.setCentralWidget(self.table)
delegate = ComboBoxDelegate(self.table,
['m1', 'm2', 'm3'])
model = DataFrameModel(self.pandas_sheet, self)
self.table.setModel(model)
self.table.setSortingEnabled(True)
self.table.setItemDelegateForRow(0, delegate)
for i in range(model.columnCount()):
ix = model.index(0, i)
self.table.openPersistentEditor(ix)

self.table.resizeColumnsToContents()
self.table.resizeRowsToContents()


if __name__ == '__main__':
df = pd.DataFrame({'a': ['col0'] * 5,
'b': np.arange(5),
'c': np.random.rand(5)})
app = QApplication(sys.argv)
window = MainWindow(df)
window.show()
sys.exit(app.exec_())

下图显示了排序前的表格。

enter image description here

按其中一列对表格进行排序后的下图。

enter image description here

我希望第一行的样式在按任何列排序之前和之后都相同。这可能吗?

最佳答案

默认情况下,除非用户使用分配给 editTriggers 的标志中指示的事件与项目进行交互,或者如果您使用 openPersistentEditor() 强制它们打开它们,否则不会显示编辑器。

考虑到最后一个选项,您可以自动执行显示的任务,但为此, View 必须可以从委托(delegate)的绘制方法访问,因为它总是由解决方案将其作为父级传递的方式调用(似乎您正在尝试实现)并使用 openPersistentEditor() 如果它是 View ,在您的情况下会出现错误,因为父级不是 QItemDelegate 而是继承自 QAbstractItemView,此外您必须传递 QModelIndex。

考虑到上述情况,解决方案是:

def paint(self, painter, option, index):
if isinstance(self.parent(), <b>QAbstractItemView</b>):
<b>self.parent().openPersistentEditor(index)</b>

因此,每次重新绘制委托(delegate)时(例如在排序之后),将调用 openPersistentEditor() 以使编辑器可见。

更新:

编辑器必须通过 setModelData 将信息保存在 QModelIndex 角色中,并使用 setEditorData 检索它们,在您的情况下,您没有实现第二个角色,因此编辑器在再次创建编辑器时将无法获取信息。此外,setModelData 将信息保存在 Qt::EditRole 中,但在您的模型中,它不处理该角色,因此您必须使用 Qt::DisplayRole。

考虑到上述情况,解决方案是:

class ComboBoxDelegate(QItemDelegate):
def __init__(self, parent, choices):
super().__init__(parent)
self.items = choices

def createEditor(self, parent, option, index):
editor = QComboBox(parent)
editor.addItems(self.items)
editor.currentIndexChanged.connect(self.currentIndexChanged)
return editor

def paint(self, painter, option, index):
if isinstance(self.parent(), QAbstractItemView):
self.parent().openPersistentEditor(index)

def setModelData(self, editor, model, index):
value = editor.currentText()
model.setData(index, value, Qt.DisplayRole)

def setEditorData(self, editor, index):
text = index.data(Qt.DisplayRole) or ""
editor.setCurrentText(text)

def updateEditorGeometry(self, editor, option, index):
editor.setGeometry(option.rect)

@pyqtSlot()
def currentIndexChanged(self):
self.commitData.emit(self.sender())

关于python - 使用组合委托(delegate)对 QTableView 进行排序会删除除双击之外的委托(delegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59056031/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com