防止在 StyledItemDelegate 中编辑 ComboBox [英] Prevent ComboBox editing in StyledItemDelegate

查看:43
本文介绍了防止在 StyledItemDelegate 中编辑 ComboBox的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使当前代码显示的所有内容都不可编辑.

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

以前的搜索都建议修改模型的 flags() 函数或使用表的 setEditTriggers.我在这段代码中都做了,但它们都不起作用.

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.

查看逐个小部件的案例,我可以找到 LineEdit 和其他的只读模式,但不能找到 ComboBox.所以我什至不能修改委托来强制只读约束,并不是说我一定想这样做.

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.

澄清一下,当我说我希望用户不能编辑"时,我的意思是他不应该以任何方式更改小部件的状态.例如.他将无法单击组合框(或至少更改当前选定的项目/索引).

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_())

推荐答案

说明:

必须澄清一些概念:

Explanation:

Some concepts must be clarified:

  • 对于 Qt 禁用编辑视图(QListView、QTableView、QTreeView 等)或视图项仅意味着编辑器不会通过用户事件(例如单击、双击等)打开.

  • 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.

Qt 中的用户交互遵循以下路径:

The user interaction in Qt follows the following path:

  1. 用户通过操作系统与鼠标、键盘等进行交互.
  2. 操作系统将这种交互通知 Qt.
  3. Qt 创建 QEvent 并发送到小部件.
  4. 小部件分析您应该修改关于 QEvent 的内容 你收到了.

在您的情况下,使用 openPersistentEditor() 显示小部件,因此从 Qt 的角度来看,可食用性在这种情况下无效.

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

考虑到上述可能是使小部件不可编辑的通用方法:阻止用户-小部件交互路径的某些点.在这种情况下,最简单的方法是阻止小部件接收 QEvent通过事件过滤器.

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

这篇关于防止在 StyledItemDelegate 中编辑 ComboBox的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆