首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在PySide2和QTableView中,如何使用pandas模型在表视图中获得多个委托?

在PySide2和QTableView中,如何使用pandas模型在表视图中获得多个委托?
EN

Stack Overflow用户
提问于 2020-05-08 00:57:07
回答 1查看 697关注 0票数 1

嗨,我已经尝试了所有我能想到的,并且已经看了成百上千个关于表和委托的堆栈溢出问题,并且花了几个小时看文档试图理解c++语言,我还没有读到任何清楚地说明表视图可以接受和不可以接受的委托数量限制的东西,现在我希望我可以说我已经对pyside2和pyqt5中的基本知识有了很好的理解,特别是对于表和模型,但是委托有点令人难以置信。我已经基于人们的问题走到了这一步,主要是因为堆栈溢出,所以这是我第一次尝试寻求帮助。

代码语言:javascript
复制
import pandas as pd
from PySide2 import QtWidgets

from PySide2.QtCore import (Qt, QAbstractTableModel, QModelIndex, QEvent, QPersistentModelIndex, 
                            QSortFilterProxyModel,
                            QTimer, Slot)
from PySide2.QtWidgets import QTableView, QAbstractItemView, QComboBox, QItemDelegate


class ScheduleModel(QAbstractTableModel):

    def __init__(self, schedules_list=None, parent=None):
        super(ScheduleModel, self).__init__(parent)

        if schedules_list is None:
            self.schedules_list = []
        else:
            self.schedules_list = schedules_list

    def rowCount(self, index=QModelIndex()):
        return self.schedules_list.shape[0]

    def columnCount(self, index=QModelIndex()):
        return self.schedules_list.shape[1]

    def data(self, index, role=Qt.DisplayRole):
        col = index.column()
        if index.isValid():
            if role == Qt.DisplayRole:
                value = self.schedules_list.iloc[index.row(), index.column()]
                return str(self.schedules_list.iloc[index.row(), index.column()])
        return None

    def headerData(self, section, orientation, role):
        # section is the index of the column/row.
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.schedules_list.columns[section]

        if orientation == Qt.Vertical:
            return str(self.schedules_list.index[section])

    def setData(self, index, value, role=Qt.EditRole):
        if role != Qt.EditRole:
            return False

        if index.isValid() and 0 <= index.row() < len(self.schedules_list):
            self.schedules_list.iloc[index.row(), index.column()] = value
            if self.data(index, Qt.DisplayRole) == value:
                self.dataChanged.emit(index, index, (Qt.EditRole,))
                return True
        return False

    def flags(self, index):
        if 1 <= index.column() <= 7:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
        if index.column() == 5:
            return Qt.ItemIsEditable | Qt.ItemIsUserCheckable | Qt.ItemIsSelectable
        elif index.column() == 1 and index.column() == 7:
            return Qt.DecorationRole
        else:
            return Qt.ItemIsSelectable


class ClickDelegate(QtWidgets.QStyledItemDelegate):
    blankText = '<Click here to add path>'

    def openFileDialog(self, lineEdit):
        if not self.blankText.startswith(lineEdit.text()):
            currentPath = lineEdit.text()
        else:
            currentPath = ''
        path, _ = QtWidgets.QFileDialog.getOpenFileName(lineEdit.window(),
                                                        'Select file', currentPath)
        if path:
            lineEdit.setText(path)

    def createEditor(self, parent, option, index):
        editor = QtWidgets.QWidget(parent)

        layout = QtWidgets.QHBoxLayout(editor)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        editor.lineEdit = QtWidgets.QLineEdit(self.blankText)
        layout.addWidget(editor.lineEdit)
        editor.setFocusProxy(editor.lineEdit)
        editor.lineEdit.installEventFilter(self)

        button = QtWidgets.QToolButton(text='...')
        layout.addWidget(button)
        button.setFocusPolicy(Qt.NoFocus)
        button.clicked.connect(lambda: self.openFileDialog(editor.lineEdit))
        return editor

    def setEditorData(self, editor, index):
        if index.data():
            editor.lineEdit.setText(str(index.data()))
        editor.lineEdit.selectAll()

    def setModelData(self, editor, model, index):
        if not editor.lineEdit.text():
            model.setData(index, None)
        elif not self.blankText.startswith(editor.lineEdit.text()):
            model.setData(index, editor.lineEdit.text())

    def initStyleOption(self, option, index):
        super(ClickDelegate, self).initStyleOption(option, index)

        if not option.text:
            option.text = self.blankText

    def eventFilter(self, source, event):
        if isinstance(source, QtWidgets.QLineEdit):
            if (event.type() == QEvent.MouseButtonPress and
                    source.hasSelectedText() and
                    self.blankText.startswith(source.text())):
                res = super(ClickDelegate, self).eventFilter(source, event)
                source.clear()
                return res
            elif event.type() == QEvent.KeyPress and event.key() in (
                    Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab):
                return False
        return super(ClickDelegate, self).eventFilter(source, event)

    def checkIndex(self, table, index):
        if index in table.selectedIndexes() and index == table.currentIndex():
            table.edit(index)

    def editorEvent(self, event, model, option, index):
        if (event.type() == QEvent.MouseButtonPress and
                event.button() == Qt.LeftButton and
                index in option.widget.selectedIndexes()):
            table = option.widget
            QTimer.singleShot(0, lambda: self.checkIndex(table, index))
        return super(ClickDelegate, self).editorEvent(event, model, option, index)


class CheckBoxDelegate(QtWidgets.QItemDelegate):
    """
    A delegate that places a fully functioning QCheckBox cell of the column to which it's applied.
    """

    def __init__(self, parent):
        QtWidgets.QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        """
        Important, otherwise an editor is created if the user clicks in this cell.
        """
        return None

    def paint(self, painter, option, index):
        """
        Paint a checkbox without the label.
        """
        self.drawCheck(painter, option, option.rect,
                       Qt.Unchecked if int(index.data()) == 0 else Qt.Checked)

    def editorEvent(self, event, model, option, index):
        '''
        Change the data in the model and the state of the checkbox
        if the user presses the left mousebutton and this cell is editable. Otherwise do nothing.
        '''
        if not int(index.flags() and Qt.ItemIsEditable) > 0:
            return False

        if event.type() == QEvent.MouseButtonRelease and event.button() == Qt.LeftButton:
            # Change the checkbox-state
            self.setModelData(None, model, index)
            return True

        return False

    def setModelData(self, editor, model, index):
        '''
        The user wanted to change the old state in the opposite.
        '''
        model.setData(index, 1 if int(index.data()) == 0 else 0, Qt.EditRole)


class DoubleSpinBoxDelegate(QtWidgets.QStyledItemDelegate):
    """A delegate class displaying a double spin box."""

    def __init__(self, parent=None, minimum=0.0, maximum=100.0, step=0.01):
        QtWidgets.QStyledItemDelegate.__init__(self, parent)
        self._min = minimum
        self._max = maximum
        self._step = step

    def createEditor(self, parent, option, index):
        editor = QtWidgets.QDoubleSpinBox(parent)
        editor.setMinimum(self._min)
        editor.setMaximum(self._max)
        editor.setSingleStep(self._step)
        editor.setAccelerated(True)
        editor.installEventFilter(self)
        return editor

    def setEditorData(self, spinBox, index):
        value = float(index.model().data(index, Qt.DisplayRole))
        spinBox.setValue(value)

    def setModelData(self, spinBox, model, index):
        value = spinBox.value()
        model.setData(index, value)

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


class ComboBoxDelegate(QItemDelegate):
    def __init__(self, parent=None):
        super(ComboBoxDelegate, self).__init__(parent)
        self.items = []

    def setItems(self, items):
        self.items = items

    def createEditor(self, parent, option, index):
        combo = QComboBox(parent)
        li = []
        for item in self.items:
            li.append(item)
        combo.addItems(li)
        combo.currentIndexChanged.connect(self.currentIndexChanged)
        return combo

    def setEditorData(self, editor, index):
        editor.blockSignals(True)
        text = index.model().data(index, Qt.DisplayRole)
        try:
            i = self.items.index(text)
        except ValueError:
            i = 0
        editor.setCurrentIndex(i)

    def setModelData(self, editor, model, index):
        # model.setData(index, editor.currentIndex(), Qt.EditRole)
        model.setData(index, editor.currentText())

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

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


class SchedulesViewer(QTableView):
    # selectionChanged = Signal(QItemSelection)
    # data_changed = Signal(QModelIndex, QModelIndex)

    def __init__(self, parent=None):
        QTableView.__init__(self, parent)

        # self.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.customContextMenuRequested.connect(self.schedule_context_menu)
        address = {'idx': '1',
                   'presets': 'presets',
                   'selected_source': 'get_source',
                   'selected_destinations': 'selected_destinations',
                   'interval': '0400',
                   'active': '1',
                   'priority': 'high',
                   'categories': 'programming',
                   'last_total': '222',
                   }

        self.schedule_model = ScheduleModel(pd.DataFrame([address]))

        self.proxyModel = QSortFilterProxyModel(self)
        self.proxyModel.setSourceModel(self.schedule_model)
        self.proxyModel.setDynamicSortFilter(True)

        self.setModel(self.proxyModel)

        **"""
        HAVING LIMITS TO THE AMOUNT OF WIDGETS TABLE VIEW CAN HANDEL
        """
        dialog_delegate = ClickDelegate(self)
        self.setItemDelegateForColumn(2, dialog_delegate)
        self.setItemDelegateForColumn(3, dialog_delegate)

        # spin_delegate = DoubleSpinBoxDelegate()
        # self.setItemDelegateForColumn(4, spin_delegate)

        # CheckBox = CheckBoxDelegate(None)
        # self.setItemDelegateForColumn(5, CheckBox)

        data = ['programming', 'game_build', 'other']
        combo_delegate = ComboBoxDelegate()
        combo_delegate.setItems([str(row) for row in data])
        self.setItemDelegateForColumn(6, combo_delegate)**

        self.setSortingEnabled(True)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.horizontalHeader().setStretchLastSection(True)
        self.verticalHeader().hide()
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.proxyModel.sort(0, Qt.AscendingOrder)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)

        self.setSelectionMode(QAbstractItemView.SingleSelection)

        # self.selectionModel().selectionChanged.connect(self.selectionChanged)

        self.show()


if __name__ == "__main__":
    import sys
    from PySide2.QtWidgets import QApplication

    app = QApplication(sys.argv)
    addressWidget = SchedulesViewer()
    addressWidget.show()
    sys.exit(app.exec_())

所以请有人帮我理解我遗漏或不理解的是什么,我想要实现的是添加已经被散列出来的代表,并使它成为一个可编辑的表,但是如果我添加旋转框或复选框代表,应用程序冻结和崩溃,所以有没有限制有多少代表的表视图可以处理或我做错了什么?如有任何帮助,我们将不胜感激,并提前向您表示感谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-05-09 00:26:22

多亏了musicamante如此自由地指出了我的简单错误,我忽略了明显的自我缺失,使所有的委托都成为实例的成员,我已经测试过了,它是有效的,所以这是代码。

代码语言:javascript
复制
import pandas as pd
from PySide2 import QtWidgets

from PySide2.QtCore import (Qt, QAbstractTableModel, QModelIndex, QEvent, 
                            QPersistentModelIndex,
                            QSortFilterProxyModel,
                            QTimer, Slot)
from PySide2.QtWidgets import QTableView, QAbstractItemView, QComboBox, QItemDelegate


class ScheduleModel(QAbstractTableModel):

    def __init__(self, schedules_list=None, parent=None):
        super(ScheduleModel, self).__init__(parent)

        if schedules_list is None:
            self.schedules_list = []
        else:
            self.schedules_list = schedules_list

    def rowCount(self, index=QModelIndex()):
        return self.schedules_list.shape[0]

    def columnCount(self, index=QModelIndex()):
        return self.schedules_list.shape[1]

    def data(self, index, role=Qt.DisplayRole):
        col = index.column()
        if index.isValid():
            if role == Qt.DisplayRole:
                value = self.schedules_list.iloc[index.row(), index.column()]
                return str(self.schedules_list.iloc[index.row(), index.column()])
        return None

    def headerData(self, section, orientation, role):
        # section is the index of the column/row.
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.schedules_list.columns[section]

        if orientation == Qt.Vertical:
            return str(self.schedules_list.index[section])

    def setData(self, index, value, role=Qt.EditRole):
        if role != Qt.EditRole:
            return False

        if index.isValid() and 0 <= index.row() < len(self.schedules_list):
            self.schedules_list.iloc[index.row(), index.column()] = value
            if self.data(index, Qt.DisplayRole) == value:
                self.dataChanged.emit(index, index, (Qt.EditRole,))
                return True
        return False

    def flags(self, index):
        if 1 <= index.column() <= 7:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
        if index.column() == 5:
            return Qt.ItemIsEditable | Qt.ItemIsUserCheckable | Qt.ItemIsSelectable
        elif index.column() == 1 and index.column() == 7:
            return Qt.DecorationRole
        else:
            return Qt.ItemIsSelectable


class ClickDelegate(QtWidgets.QStyledItemDelegate):
    blankText = '<Click here to add path>'

    def openFileDialog(self, lineEdit):
        if not self.blankText.startswith(lineEdit.text()):
            currentPath = lineEdit.text()
        else:
            currentPath = ''
        path, _ = QtWidgets.QFileDialog.getOpenFileName(lineEdit.window(),
                                                        'Select file', currentPath)
        if path:
            lineEdit.setText(path)

    def createEditor(self, parent, option, index):
        editor = QtWidgets.QWidget(parent)

        layout = QtWidgets.QHBoxLayout(editor)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        editor.lineEdit = QtWidgets.QLineEdit(self.blankText)
        layout.addWidget(editor.lineEdit)
        editor.setFocusProxy(editor.lineEdit)
        editor.lineEdit.installEventFilter(self)

        button = QtWidgets.QToolButton(text='...')
        layout.addWidget(button)
        button.setFocusPolicy(Qt.NoFocus)
        button.clicked.connect(lambda: self.openFileDialog(editor.lineEdit))
        return editor

    def setEditorData(self, editor, index):
        if index.data():
            editor.lineEdit.setText(str(index.data()))
        editor.lineEdit.selectAll()

    def setModelData(self, editor, model, index):
        if not editor.lineEdit.text():
            model.setData(index, None)
        elif not self.blankText.startswith(editor.lineEdit.text()):
            model.setData(index, editor.lineEdit.text())

    def initStyleOption(self, option, index):
        super(ClickDelegate, self).initStyleOption(option, index)

        if not option.text:
            option.text = self.blankText

    def eventFilter(self, source, event):
        if isinstance(source, QtWidgets.QLineEdit):
            if (event.type() == QEvent.MouseButtonPress and
                    source.hasSelectedText() and
                    self.blankText.startswith(source.text())):
                res = super(ClickDelegate, self).eventFilter(source, event)
                source.clear()
                return res
            elif event.type() == QEvent.KeyPress and event.key() in (
                    Qt.Key_Escape, Qt.Key_Tab, Qt.Key_Backtab):
                return False
        return super(ClickDelegate, self).eventFilter(source, event)

    def checkIndex(self, table, index):
        if index in table.selectedIndexes() and index == table.currentIndex():
            table.edit(index)

    def editorEvent(self, event, model, option, index):
        if (event.type() == QEvent.MouseButtonPress and
                event.button() == Qt.LeftButton and
                index in option.widget.selectedIndexes()):
            table = option.widget
            QTimer.singleShot(0, lambda: self.checkIndex(table, index))
        return super(ClickDelegate, self).editorEvent(event, model, option, index)


class CheckBoxDelegate(QtWidgets.QItemDelegate):
    """
    A delegate that places a fully functioning QCheckBox cell of the column to which 
    it's applied.
    """

    def __init__(self, parent):
        QtWidgets.QItemDelegate.__init__(self, parent)

    def createEditor(self, parent, option, index):
        """
        Important, otherwise an editor is created if the user clicks in this cell.
        """
        return None

    def paint(self, painter, option, index):
        """
        Paint a checkbox without the label.
        """
        self.drawCheck(painter, option, option.rect,
                       Qt.Unchecked if int(index.data()) == 0 else Qt.Checked)

    def editorEvent(self, event, model, option, index):
        '''
        Change the data in the model and the state of the checkbox
        if the user presses the left mousebutton and this cell is editable. Otherwise 
        do nothing.
        '''
        if not int(index.flags() and Qt.ItemIsEditable) > 0:
            return False

        if event.type() == QEvent.MouseButtonRelease and event.button() == 
            Qt.LeftButton:
            # Change the checkbox-state
            self.setModelData(None, model, index)
            return True

        return False

    def setModelData(self, editor, model, index):
        '''
        The user wanted to change the old state in the opposite.
        '''
        model.setData(index, 1 if int(index.data()) == 0 else 0, Qt.EditRole)


class DoubleSpinBoxDelegate(QtWidgets.QStyledItemDelegate):
    """A delegate class displaying a double spin box."""

    def __init__(self, parent=None, minimum=0.0, maximum=100.0, step=0.01):
        QtWidgets.QStyledItemDelegate.__init__(self, parent)
        self._min = minimum
        self._max = maximum
        self._step = step

    def createEditor(self, parent, option, index):
        editor = QtWidgets.QDoubleSpinBox(parent)
        editor.setMinimum(self._min)
        editor.setMaximum(self._max)
        editor.setSingleStep(self._step)
        editor.setAccelerated(True)
        editor.installEventFilter(self)
        return editor

    def setEditorData(self, spinBox, index):
        value = float(index.model().data(index, Qt.DisplayRole))
        spinBox.setValue(value)

    def setModelData(self, spinBox, model, index):
        value = spinBox.value()
        model.setData(index, value)

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


class ComboBoxDelegate(QItemDelegate):
    def __init__(self, parent=None):
        super(ComboBoxDelegate, self).__init__(parent)
        self.items = []

    def setItems(self, items):
        self.items = items

    def createEditor(self, parent, option, index):
        combo = QComboBox(parent)
        li = []
        for item in self.items:
            li.append(item)
        combo.addItems(li)
        combo.currentIndexChanged.connect(self.currentIndexChanged)
        return combo

    def setEditorData(self, editor, index):
        editor.blockSignals(True)
        text = index.model().data(index, Qt.DisplayRole)
        try:
            i = self.items.index(text)
        except ValueError:
            i = 0
        editor.setCurrentIndex(i)

    def setModelData(self, editor, model, index):
        # model.setData(index, editor.currentIndex(), Qt.EditRole)
        model.setData(index, editor.currentText())

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

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


class SchedulesViewer(QTableView):
    # selectionChanged = Signal(QItemSelection)
    # data_changed = Signal(QModelIndex, QModelIndex)

    def __init__(self, parent=None):
        QTableView.__init__(self, parent)

        # self.setContextMenuPolicy(Qt.CustomContextMenu)
        # self.customContextMenuRequested.connect(self.schedule_context_menu)
        address = {'idx': '1',
                   'presets': 'presets',
                   'selected_source': 'get_source',
                   'selected_destinations': 'selected_destinations',
                   'interval': '0400',
                   'active': '1',
                   'priority': 'high',
                   'categories': 'programming',
                   'last_total': '222',
                   }

        self.schedule_model = ScheduleModel(pd.DataFrame([address]))

        self.proxyModel = QSortFilterProxyModel(self)
        self.proxyModel.setSourceModel(self.schedule_model)
        self.proxyModel.setDynamicSortFilter(True)

        self.setModel(self.proxyModel)

        """
        HAVING LIMITS TO THE AMOUNT OF WIDGETS TABLE VIEW CAN HANDEL
        """

        self.setItemDelegateForColumn(2, ClickDelegate(self))
        self.setItemDelegateForColumn(3, ClickDelegate(self))

        self.setItemDelegateForColumn(4, DoubleSpinBoxDelegate(self))

        self.setItemDelegateForColumn(5, CheckBoxDelegate(self))

        data = ['programming', 'game_build', 'other']
        combo_delegate = ComboBoxDelegate(self)
        combo_delegate.setItems([str(row) for row in data])
        self.setItemDelegateForColumn(6, combo_delegate)

        self.setSortingEnabled(True)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.horizontalHeader().setStretchLastSection(True)
        self.verticalHeader().hide()
        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.proxyModel.sort(0, Qt.AscendingOrder)
        self.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.setEditTriggers(QAbstractItemView.DoubleClicked)

        self.setSelectionMode(QAbstractItemView.SingleSelection)

        # self.selectionModel().selectionChanged.connect(self.selectionChanged)

        self.show()


if __name__ == "__main__":
    import sys
    from PySide2.QtWidgets import QApplication

    app = QApplication(sys.argv)
    addressWidget = SchedulesViewer()
    addressWidget.show()
    sys.exit(app.exec_())
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61663446

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档