Search code examples
pythonqtpyqtqtablewidgetqtablewidgetitem

How can I block QTableWidget signals after disabling the table?


I have a python QTableWidget populated with 4 columns. Selecting any of the columns runs the qTableWidget.cellClicked() signal, which then highlights the entire row, and runs a bunch of code.

self._qTableWidget.cellClicked.connect(self.cellClicked)

def cellClicked(self, row, column):
   """
      Handle cell clicked signal.
   """
   self._qTableWidget.setDisabled(True)
   qTableWidgetItem = self._qTableWidget.item(row, column)
   if qTableWidgetItem is None:
      return
   else:
      # ***RUNS SOME CODE***
      pass
   self._qTableWidget.setEnabled(True)

In the code, I want to disable the QTableWidget from accepting any clicks until after the code runs, then enable the QTableWidget (which I have tried by using setDisabled()/setEnabled()).

However if you click a cell, the table is disabled correctly, but you can still click on cells in the table. When the table is enabled again, it runs the cellClicked() signal again on the QTableWidgetItem you clicked on while the table was disabled.

I have tried using the following:

self._qTableWidget.blockSignals(True)
self._qTableWidget.blockSignals(False)
self._qTableWidget.cellClicked.disconnect(self.cellClicked)
self._qTableWidget.cellClicked.connect(self.cellClicked)
self.setAllCellItemFlags(QtCore.Qt.NoItemFlags)

def setAllCellItemFlags(self, flag):
   """
      Sets all QTableWidgetItem flags
   """
   for row in range(self._qTableWidget.rowCount()):
      for column in range(self._qTableWidget.columnCount()):
         qTableWidgetItem = self._qTableWidget.item(row, column)
         qTableWidgetItem.setFlags(flag)

Solution

  • This is what probably happens in your code:

    1. first click arrives and it emits signal
    2. in the connected slot you disable (or disconnect) signals
    3. you start your code and while it is running it blocks the event loop in the main thread
    4. meanwhile another mouse click comes, it creates mouse click event but it is put to waiting queue waiting for the event loop to be able to process it
    5. after a while your lengthy code gets finished and you re-enable (or re-connect) the signals
    6. the event loop gets unblocked and processes the waiting mouse event
    7. as response to the click event another signal gets emitted and connected slot gets run again, i.e. going to step 2...

    As you can see, the second click gets processed AFTER the signal gets re-enabled. This is the root cause of your problem. What you need to do is to re-enable the signals AFTER the event loop processes the waiting mouse click (or mouse clicks).

    The easiest solution would be to call QApplication.processEvents(QEventLoop.AllEvents, 0) just before you re-enable the signals. This will cause all click events to be processed, i.e. ignored because the signals will still be disabled at that moment. Please read the documentation about this overload of the method which processes also posted (not yet queued) events. In contrast to the other overload without ms argument, which processes only already queued events. If value 0 is not sufficient, then experiment with a bit longer time delays, e.g. 100 ms.