Prevent ComboBox editing in StyledItemDelegate

对着背影说爱祢 提交于 2021-02-19 08:16:16

问题


I am trying to make everything shown by the current code un-editable.

Previous searches all suggest either modifying the flags() function of the model or using the setEditTriggers of the table. I do both in this code, but neither of them work.

Looking at a widget-by-widget case, I can find readonly modes for LineEdit and others, but not for ComboBox. So I can not even modify the delegate to force the readonly constraint, not that I would necessarily like to do it this way.

EDIT: to clarify, when I say I want the user to not be able to 'edit' I mean that he shouldn't be able to change the state of the widget in any way. E.g. he won't be able to click on a ComboBox (or at least changing the current selected item/index).

from PyQt5 import QtCore, QtWidgets
import sys


class MyWindow(QtWidgets.QWidget):
    def __init__(self, *args):
        super().__init__(*args)
        tableview = TableView()
        layout = QtWidgets.QVBoxLayout(self)
        layout.addWidget(tableview)
        self.setLayout(layout)


class Delegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, model):
        super().__init__()
        self.model = model

    def createEditor(self, parent, option, index):
        widget = QtWidgets.QComboBox(parent)
        widget.addItems(['', 'Cat', 'Dog'])
        return widget

    def setModelData(self, widget, model, index):
        self.model.setData(index, widget.currentIndex())


class Model(QtCore.QAbstractTableModel):
    def __init__(self, parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent=parent)
        self.value = 0

    def flags(self, index):
        return QtCore.Qt.ItemIsEnabled

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid() or role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()
        return QtCore.QVariant(self.value)

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        self.value = value
        print("data[{}][{}] = {}".format(index.row(), index.column(), value))
        return True

    def rowCount(self, parent=QtCore.QModelIndex()):
        return 1

    def columnCount(self, parent=QtCore.QModelIndex()):
        return 1


class TableView(QtWidgets.QTableView):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.model = Model(self)
        delegate = Delegate(self.model)
        self.setItemDelegate(delegate)
        self.setModel(self.model)
        self.setEditTriggers(QtWidgets.QTableWidget.NoEditTriggers)
        for row in range(self.model.rowCount()):
            for column in range(self.model.columnCount()):
                index = self.model.index(row, column)
                self.openPersistentEditor(index)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

回答1:


Explanation:

Some concepts must be clarified:

  • For Qt to disable editing that a view (QListView, QTableView, QTreeView, etc.) or item of the view implies only that the editor will not open through user events such as clicked, double-clicked, etc.

  • The user interaction in Qt follows the following path:

    1. The user interacts through the OS with the mouse, keyboard, etc.
    2. the OS notifies Qt of that interaction.
    3. Qt creates QEvents and sends it to the widgets.
    4. The widget analyzes what you should modify regarding the QEvent you receive.

In your case, using openPersistentEditor() shows the widgets, and so the edibility from the Qt point of view is not valid for this case.

Solution:

Considering the above a possible general methodology to make a widget not editable: block some point of the user-widget interaction path. In this case, the simplest thing is to prevent the widget from receiving the QEvents through an event filter.

Considering the above, the solution is:

class DisableEventsManager(QtCore.QObject):
    def __init__(self, *, qobject, events=None, apply_childrens=False):
        if not isinstance(qobject, QtCore.QObject):
            raise TypeError(
                f"{qobject} must belong to a class that inherits from QObject"
            )
        super().__init__(qobject)

        self._qobject = qobject
        self._events = events or []

        self._qobject.installEventFilter(self)
        self._children_filter = []
        if apply_childrens:
            for child in self._qobject.findChildren(QtWidgets.QWidget):
                child_filter = DisableEventsManager(
                    qobject=child, events=events, apply_childrens=apply_childrens
                )
                self._children_filter.append(child_filter)

    @property
    def events(self):
        return self._events

    @events.setter
    def events(self, events):
        self._events = events
        for child_filter in self._children_filter:
            child_filter.events = events

    def eventFilter(self, obj, event):
        if self.events and self._qobject is obj:
            if event.type() in self.events:
                return True
        return super().eventFilter(obj, event)
def createEditor(self, parent, option, index):
    combo = QtWidgets.QComboBox(parent)
    combo.addItems(["", "Cat", "Dog"])
    combo_event_filter = DisableEventsManager(qobject=combo)
    combo_event_filter.events = [
        QtCore.QEvent.KeyPress,
        QtCore.QEvent.FocusIn,
        QtCore.QEvent.MouseButtonPress,
        QtCore.QEvent.MouseButtonDblClick,
    ]
    return combo


来源:https://stackoverflow.com/questions/58250309/prevent-combobox-editing-in-styleditemdelegate

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!