Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 使用QItemDelegate关闭或打开QCheckBox时,移除QCOMBOX_Python_Pyqt_Pyqt5 - Fatal编程技术网

Python 使用QItemDelegate关闭或打开QCheckBox时,移除QCOMBOX

Python 使用QItemDelegate关闭或打开QCheckBox时,移除QCOMBOX,python,pyqt,pyqt5,Python,Pyqt,Pyqt5,每当上述复选框的切换发生变化时,我想用组合框更新QTableView的单元格内容。我正在使用QTableView和自定义委托来绘制组合框。复选框由QTableView本身控制。目前,当我切换复选框时,下面的组合框将出现,但我无法在切换复选框时删除组合框 下面是我的代码示例 import sys import pandas as pd from pandas.api.types import is_numeric_dtype import numpy as np from PyQt5.QtCor

每当上述复选框的切换发生变化时,我想用组合框更新QTableView的单元格内容。我正在使用QTableView和自定义委托来绘制组合框。复选框由QTableView本身控制。目前,当我切换复选框时,下面的组合框将出现,但我无法在切换复选框时删除组合框

下面是我的代码示例

import sys
import pandas as pd
from pandas.api.types import is_numeric_dtype
import numpy as np

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


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

    def __init__(self, df=pd.DataFrame(), parent=None):
        super(DataFrameModel, self).__init__(parent)
        self._dataframe = df
        self.df2 = pd.DataFrame(self._dataframe.iloc[2:3, :].to_dict())

    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, 1]:
                    pass
                else:
                    return self._dataframe.index.tolist()[section - 2]
            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():
            return None

        col = index.column()
        row = index.row()
        dt = self.df2[self.df2.columns[col]].dtype
        is_numeric = is_numeric_dtype(self.df2[self.df2.columns[col]])
        if row == 0 and is_numeric:
            value = self._dataframe.iloc[row, col].text()
        else:
            value = self._dataframe.iloc[row, col]
        if role == Qt.DisplayRole:
            return value
        elif role == Qt.CheckStateRole:
            if row == 0 and is_numeric:
                if self._dataframe.iloc[row, col].isChecked():
                    return Qt.Checked
                else:
                    return Qt.Unchecked

        elif role == DataFrameModel.ValueRole:
            return value
        elif role == DataFrameModel.ValueRole:
            return value
        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=Qt.EditRole):
        if not index.isValid():
            return False
        col = index.column()
        row = index.row()
        is_numeric = is_numeric_dtype(self.df2[self.df2.columns[col]])
        if role == Qt.CheckStateRole and index.row() == 0:
            if is_numeric:
                if value == Qt.Checked:
                    self._dataframe.iloc[row, col].setChecked(True)
                    self._dataframe.iloc[row, col].setText("Grade Item")
                else:
                    self._dataframe.iloc[row, col].setChecked(False)
                    self._dataframe.iloc[row, col].setText("Not a Grade")
        elif row == 1 and role == Qt.EditRole:
            if isinstance(value, QVariant):
                value = value.value()
            if hasattr(value, 'toPyObject'):
                value = value.toPyObject()
            self._dataframe.iloc[row, col] = value
        elif row >= 2 and role == Qt.EditRole:
            try:
                value = eval(value)
                if not isinstance(
                        value,
                        self._dataframe.applymap(type).iloc[row, col]):
                    value = self._dataframe.iloc[row, col]
            except:
                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):
        if not index.isValid():
            return None

        if index.row() == 0:
            return (Qt.ItemIsEnabled | Qt.ItemIsSelectable |
                    Qt.ItemIsUserCheckable)
        else:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable

    def sort(self, column, order):
        self.layoutAboutToBeChanged.emit()
        col_name = self._dataframe.columns.tolist()[column]
        sheet1 = self._dataframe.iloc[:2, :]
        sheet2 = self._dataframe.iloc[2:, :].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, parent, choices=None):
        super().__init__(parent)
        self.items = choices

    def createEditor(self, parent, option, index):
        self.parent().model().dataChanged.emit(index, index, (Qt.DisplayRole,))
        if is_numeric_dtype(
                self.parent().model().df2[
                    self.parent().model().df2.columns[index.column()]]):
            checked = self.parent().model().dataFrame.iloc[
                0, index.column()].isChecked()
            if checked:
                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):
        editor = self.sender()
        self.commitData.emit(editor)


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

        self.table = QTableView()
        self.setCentralWidget(self.table)

        check_bx_lst = []
        is_number = [is_numeric_dtype(self.pandas_sheet[col]) for col
                     in self.pandas_sheet.columns]
        for is_numb in is_number:
            if is_numb:
                checkbox = QCheckBox('Not a Grade')
                checkbox.setChecked(False)
                check_bx_lst.append(checkbox)
            else:
                check_bx_lst.append(None)

        for i in range(2):
            self.pandas_sheet.loc[-1] = [' '] * \
                                        self.pandas_sheet.columns.size
            self.pandas_sheet.index = self.pandas_sheet.index + 1
            self.pandas_sheet = self.pandas_sheet.sort_index()

        self.pandas_sheet.loc[0] = check_bx_lst

        model = DataFrameModel(self.pandas_sheet)
        self.table.setModel(model)
        self.table.setSortingEnabled(True)

        delegate = ComboBoxDelegate(self.table,
                                    [None, 'Test', 'Quiz'])
        self.table.setItemDelegateForRow(1, delegate)
        self.table.resizeColumnsToContents()
        self.table.resizeRowsToContents()


if __name__ == '__main__':
    df = pd.DataFrame({'a': ['student ' + str(i) for i in range(5)],
                       'b': np.arange(5),
                       'c': np.random.rand(5)})
    app = QApplication(sys.argv)
    window = MainWindow(df)
    window.table.model().sort(df.columns.get_loc("a"), Qt.AscendingOrder)
    window.setFixedSize(280, 200)
    window.show()

    sys.exit(app.exec_())
当上面的复选框被关闭时,我想删除这个组合框

非常感谢您的帮助


在绘制函数中使用的是
openpersisteneditor
,这完全是错误的:代理使用的每个索引都会绘制,并且每次绘制该行中的每个单元格时,实际上都会调用
createEditor
,在滚动视图时,所有单元格都会发生这种情况,或鼠标悬停的任何单元格

按照您的逻辑,最终创建创建者可能是由于数据更改请求的绘制,因为此时createEditor中的
if
条件为True。从那时起,您将创建一个持久编辑器,如果您不删除它,它将留在那里。
显然,所有这些都不是一个好方法,主要是因为您实际上检查了从绘制函数创建编辑器是否可以,这没有多大意义

您应该连接到模型的dataChanged信号以验证选中状态,然后相应地打开或关闭编辑器

class MainWindow(QMainWindow):
    def __init__(self, pandas_sheet):
        # ...
        model.dataChanged.connect(self.dataChanged)

    def dataChanged(self, topLeft, bottomRight, roles):
        if topLeft.row() == 0:
            if topLeft.data(QtCore.Qt.CheckStateRole):
                self.table.openPersistentEditor(topLeft.sibling(1, topLeft.column()))
            else:
                self.table.closePersistentEditor(topLeft.sibling(1, topLeft.column()))

我过度简化了if条件,但我认为这个概念已经足够清楚了。

您在paint函数中使用的是
openpersisteneditor
,这完全是错误的:对于委托使用的每个索引,都经常进行绘制,每次绘制该行中的每个单元格时,您实际上都在调用
createEditor
,当视图滚动时,所有单元格都会发生这种情况,或者鼠标悬停的任何单元格都会发生这种情况

按照您的逻辑,最终创建创建者可能是由于数据更改请求的绘制,因为此时createEditor中的
if
条件为True。从那时起,您将创建一个持久编辑器,如果您不删除它,它将留在那里。
显然,所有这些都不是一个好方法,主要是因为您实际上检查了从绘制函数创建编辑器是否可以,这没有多大意义

您应该连接到模型的dataChanged信号以验证选中状态,然后相应地打开或关闭编辑器

class MainWindow(QMainWindow):
    def __init__(self, pandas_sheet):
        # ...
        model.dataChanged.connect(self.dataChanged)

    def dataChanged(self, topLeft, bottomRight, roles):
        if topLeft.row() == 0:
            if topLeft.data(QtCore.Qt.CheckStateRole):
                self.table.openPersistentEditor(topLeft.sibling(1, topLeft.column()))
            else:
                self.table.closePersistentEditor(topLeft.sibling(1, topLeft.column()))

我过度简化了if条件,但我认为这个概念已经足够清楚了。

非常感谢,它起到了很好的作用。非常感谢你的解释。我删除了
ComboBoxDelegate
中的
paint
方法。不幸的是,当我根据列对模型进行排序时。编辑器被删除了。这可能是因为在排序时实际上重置了模型,这意味着表用于持久化编辑器的索引不再存在。Qt5支持layoutAboutToBeChanged和layoutChanged的可选父索引列表参数(和更改提示),但由于基本上是重置模型,因此无法以正确的方式轻松实现它们;作为一种解决方法,您可以创建一个自定义信号,在layoutChanged之后发出已排序的列号,将其连接到主窗口中的一个函数,该函数将检查该列并重新创建持久编辑器。确实如此。非常感谢你。你的变通方法非常有效。非常感谢,它非常有效。非常感谢你的解释。我删除了
ComboBoxDelegate
中的
paint
方法。不幸的是,当我根据列对模型进行排序时。编辑器被删除了。这可能是因为在排序时实际上重置了模型,这意味着表用于持久化编辑器的索引不再存在。Qt5支持layoutAboutToBeChanged和layoutChanged的可选父索引列表参数(和更改提示),但由于基本上是重置模型,因此无法以正确的方式轻松实现它们;作为一种解决方法,您可以创建一个自定义信号,在layoutChanged之后发出已排序的列号,将其连接到主窗口中的一个函数,该函数将检查该列并重新创建持久编辑器。确实如此。非常感谢你。你的变通方法非常有效。