Reputation: 442
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_())
Upvotes: 1
Views: 284
Reputation: 244311
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:
In your case, using openPersistentEditor()
shows the widgets, and so the edibility from the Qt point of view is not valid for this case.
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 QEvent
s 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
Upvotes: 1