2

I have QTableWidget with CheckBoxes in some cells. I want to disable user to perform mouse click over the table cells (so he can't change checkBox state) for some time while I am using data from the table. I've tried table.setDisabled(1) but that disables whole table and I need scroll to be enabled.

Any help would be appreciated.

EDIT To be more precise: there could be up to 15x3000 cells in table, filled with text(editable), checkbox(checkable), svg graphic(opens other window when double click on it) or some custom widgets(which also have clickable or editable parts). I need to disable user to click or double click over cells(so he can't change any of them) for 1sec - 10sec time interval (solution must be something fast, not iterating through all items), but I need scroll-bar to be enabled and normal table visibility.

Aleksandar
  • 3,541
  • 4
  • 34
  • 57

3 Answers3

1

One way to achieve this is to subclass QTableWidgetItem and re-implement the setData method. That way, you can control whether items accept values for certain roles.

To control the "checkability" for all items, you could add a class attribute to the subclass which could be tested whenever a value for the check-state role was passed to setData.

Here's what the subclass might look like:

class TableWidgetItem(QtGui.QTableWidgetItem):
    _blocked = True

    @classmethod
    def blocked(cls):
        return cls._checkable

    @classmethod
    def setBlocked(cls, checkable):
        cls._checkable = bool(checkable)

    def setData(self, role, value):
        if role != QtCore.Qt.CheckStateRole or self.checkable():
            QtGui.QTableWidgetItem.setData(self, role, value)

And the "checkability" of all items would be disabled like this:

    TableWidgetItem.setCheckable(False)

UPDATE:

The above idea can be extended by adding a generic wrapper class for cell widgets.

The classes below will block changes to text and check-state for table-widget items, and also a range of keyboard and mouse events for cell widgets via an event-filter (other events can be blocked as required).

The cell-widgets would need to be created like this:

    widget = CellWidget(self.table, QtGui.QLineEdit())
    self.table.setCellWidget(row, column, widget)

and then accessed like this:

    widget = self.table.cellWidget().widget()

Blocking for the whole table would be switched on like this:

    TableWidgetItem.setBlocked(True)
    CellWidget.setBlocked(True)
    # or Blockable.setBlocked(True)

Here are the classes:

class Blockable(object):
    _blocked = False

    @classmethod
    def blocked(cls):
        return cls._blocked

    @classmethod
    def setBlocked(cls, blocked):
        cls._blocked = bool(blocked)

class TableWidgetItem(Blockable, QtGui.QTableWidgetItem):
    def setData(self, role, value):
        if (not self.blocked() or (
            role != QtCore.Qt.EditRole and
            role != QtCore.Qt.CheckStateRole)):
            QtGui.QTableWidgetItem.setData(self, role, value)

class CellWidget(Blockable, QtGui.QWidget):
    def __init__(self, parent, widget):
        QtGui.QWidget.__init__(self, parent)
        self._widget = widget
        layout = QtGui.QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(widget)
        widget.setParent(self)
        widget.installEventFilter(self)
        if hasattr(widget, 'viewport'):
            widget.viewport().installEventFilter(self)
        widget.show()

    def widget(self):
        return self._widget

    def eventFilter(self, widget, event):
        if self.blocked():
            etype = event.type()
            if (etype == QtCore.QEvent.KeyPress or
                etype == QtCore.QEvent.KeyRelease or
                etype == QtCore.QEvent.MouseButtonPress or
                etype == QtCore.QEvent.MouseButtonRelease or
                etype == QtCore.QEvent.MouseButtonDblClick or
                etype == QtCore.QEvent.ContextMenu or
                etype == QtCore.QEvent.Wheel):
                return True
        return QtGui.QWidget.eventFilter(self, widget, event)
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • hello, tnx for answering. `@classmethod` is something new I've just learned (It's like `static` in c++, right?), but I think I can't use it because I have many different cells (with text, with checkBox, with svg graphic...). or can I? – Aleksandar Jan 09 '14 at 07:56
  • 1
    @Aleksandar. The main difference between `@classmethod` and `@staticmethod` in python is that the class is passed as first argument. Since all instances have the same class, it provides a simple mechanism for sharing state (and is perhaps better than using a global variable). As to your actual problem: I did wonder whether you were using cell widgets, which obviously changes the spec quite a bit. I see you've edited your question, but it still seems underspecified. What about keyboard actions? Should it still be possible to select cells, move from cell to cell with arrow keys, etc, etc...? – ekhumoro Jan 09 '14 at 18:42
  • @Aleksandar. I have updated my answer with a solution that may work for you. – ekhumoro Jan 09 '14 at 22:05
  • Once again thank you for answering. Looking at your `eventFilter` method I have one more question: would it be simpler, if possible, to do something like that in table widget class? -To block all events except `QtCore.QEvent.Wheel` when I call `myTable.blockEvants()` – Aleksandar Jan 10 '14 at 08:10
  • 1
    @Aleksandar. Blocking events on the table (and its viewport) will completely prevent editing of cells, but it won't block events on cell widgets at all. Do you want to block all interaction other than scrolling? If so, then you could use a masking widget that covers the table's viewport and passes wheel events to the table. – ekhumoro Jan 10 '14 at 18:32
  • Yes, something like that sounds more understandable for me. Can you point me to some examples of how to use masking widget and pass desired events through it? – Aleksandar Jan 10 '14 at 19:10
  • @Aleksandar. Please start a new question for this and be clear about exactly what you want. You should also post a some code that shows that you have at least tried to come up with a solution yourself. – ekhumoro Jan 10 '14 at 19:20
0

Just iterate through all QStandardItems and change flags values for items which should not be changeable.
You can use flag: Qt::ItemIsEditable or/and Qt::ItemIsEnabled.

Marek R
  • 32,568
  • 6
  • 55
  • 140
0

You would need to disable the items themselves as opposed to the whole table if you have other items than QCheckBoxes that you would not like to disable. See the python code below for details:

'''
    Iterate through all the check boxes in the standard items
    and make sure the enabled flag is cleared so that the items are disabled
'''
for standardItem in standardItems:
    standardItem.setFlags(standardItem.flags() & ~Qt.ItemIsEnabled)

Here you can find the corresponding documentation:

void QTableWidgetItem::setFlags(Qt::ItemFlags flags)

Sets the flags for the item to the given flags. These determine whether the item can be selected or modified.

Community
  • 1
  • 1
László Papp
  • 51,870
  • 39
  • 111
  • 135
  • thank you for answer. I thought of that at first but my table can have up to 15x3000 cells, and time I want to disable user to perform click could be 1s - 10s, so iterating through all items can take more time then that (I also need to enable it). That is why I need something more like `table.setDisabled(1)`, but not to affect scrolling – Aleksandar Jan 08 '14 at 21:37