QTableWidget... Does changing a header value trigger the cellChanged signal?



  • I have a QTableWidget where I do some processing when the user changes some data in a cell using the cellChanged signal, i.e.:

    table.cellChanged.connect(self.shiftTagsCellChanged)
    

    But, I also conditionally change my vertical header (row header) to a different image based on some logic, using the following code in my :

    # row header
    hitem = QTableWidgetItem()
    hitem.setData(Qt.UserRole, '+')
    hitem.setData(Qt.DecorationRole, QPixmap(self._sesTagShiftAddRowImg))
    table.setVerticalHeaderItem(row, hitem)
    

    I store custom data on the image item (Qt.UserRole) as a key.

    Ever since I took this approach, I was surprised to see my cellChanged slot callback getting called twice?! Does changing the header item count as a row cell change? It didn't seem to when I was just changing its text label. If so, is there a way I can turn this off? My current hack is to check the change against the previous (row, col) cellChanged call... which, you know, seems like a hack to me. :/

    Thanks for any insight.



  • @David-Jacobson

    Most likely is that you have accidently connect twice the same signal or that you have connected two signals to the same slot and the signals are triggered both with your action.

    You should inspect carefully the signals you are connecting. You can use also dumpObjectInfo to check for connected signals of an item.



  • @koahnig
    Wow, I didn't know of https://doc.qt.io/qt-5/qobject.html#dumpObjectInfo, that could be really useful!

    While on that subject, is there something which will walk all QObjects and dump them/some of them? For debugging I use http://doc.qt.io/qt-5/qapplication.html#allWidgets to see what widgets there are, is there similar-ish for all QObjects?



  • @JonB

    Frankly, I do not know.

    I came across the dumpObject routines a while ago here in the forum and use them occasionally.


  • Qt Champions 2017

    @David-Jacobson said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    If so, is there a way I can turn this off?

    Expected behaviour (I think) as you're calling setData twice.

    @JonB said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    is there similar-ish for all QObjects?

    You can use findChildren<QObject *>() if you have a common root object, but not in the general case, no.



  • @koahnig Thanks for the dumpObject suggestion. Do you need to be using a debug compiled version of the Qt libs for that to work? And, for your other excellent suggestion... in my case, there is only one connect for the cellChanged signal in my code, but, I'll keep debugging.

    @kshegunov I'm suspicious of the setData's being the culprit as well. But, these are setData's on a stand-alone QTableWidgetItem object, that hasn't been associated with any QTableWidget (yet), until the table.setVerticalHeaderItem(...) function is called. ? Or are you referring to the setVerticalHeaderItem() call itself and the user changing the cell data (the original cellChanged trigger), as both being implied setData's? That's the premise of my question... whether setVerticalHeaderItem causes a cellChanged signal.

    Well, just for general interest, below is the code that I was describing as a "hack" that I put at the beginning of my cellChanged slot function, which actually works in my use case. :)

    # Redundancy safety check:
    # Header changes currently trigger cellChanged signal? :(
    if self._sesPrevChangedTagShiftCell:
    	(prevchrow, prevchcol) = self._sesPrevChangedTagShiftCell
    	if row == prevchrow and col == prevchcol:
    		# Can not change self cell twice
    		return
    self._sesPrevChangedTagShiftCell = (row, col)


  • @David-Jacobson said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    @koahnig Thanks for the dumpObject suggestion. Do you need to be using a debug compiled version of the Qt libs for that to work? And, for your other excellent suggestion... in my case, there is only one connect for the cellChanged signal in my code, but, I'll keep debugging.

    Yes, it shows only when my application is in debug mode compiled.
    Glad you consider it as useful


  • Qt Champions 2017

    @David-Jacobson said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    But, these are setData's on a stand-alone QTableWidgetItem object, that hasn't been associated with any QTableWidget (yet), until the table.setVerticalHeaderItem(...) function is called. ?

    Well, yes, but I imagine setData is called onto the model whenever you associate it.

    Note: I haven't looked at the source, but seems like a reasonable assumption.

    That's the premise of my question... whether setVerticalHeaderItem causes a cellChanged signal.

    I would say so, yes. You can probably check that though, by breaking at your signal handler and inspecting the stack trace.



  • If this is the case, where setVerticalHeaderItem causes a cellChanged signal, then I would complain that the (row, col) params passed to the slot function are misleading. It would seem that if cell (0, 0) changed, and then I changed it's header cell, Qt signals (0, 0) changed again. I would prefer something indicating the header cell; like (0, -1) perhaps. :)


  • Qt Champions 2017

    @David-Jacobson said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    If this is the case, where setVerticalHeaderItem causes a cellChanged signal, then I would complain that the (row, col) params passed to the slot function are misleading.

    I had a quick glance of the source. It comes through dataChanged and headerDataChanged. Long story short, you should choose one of the two to handle the use case properly. cellChanged loses from which data structure (header or table contents) the signal originated.

    Or subscribe directly to the header view ...



  • @kshegunov said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    dataChanged

    Thank you! I am definitely learning from the insight in this thread.

    I believe this is the a more appropriate approach. But, I think there may still be a trade-off, i.e.: using the dataChanged slot on specific table items... it seems you gain visibility of the exact item emitting the signal, but lose visibility of the table (row, col) where the item is. Hmm.


  • Qt Champions 2017

    @David-Jacobson said in QTableWidget... Does changing a header value trigger the cellChanged signal?:

    but lose visibility of the table (row, col) where the item is.

    I don't follow. You can get the row and column from the model index that's given to you as an argument.



  • Ok, I have some crow to eat after further debugging and totally commenting out sections of my code, process of elimination, etc. ... my cellChanged slot function was not getting called redundantly on the same cell because of ANY of the multiple setData's I was doing on anything, header or otherwise. It was simply because I was trying to dynamically right justify the user's cell input with the following call in the cellChanged slot function!:

    changedItem = table.item(row, col)
    changedItem.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
    

    I apologize for the wild goose chase. :)

    But, that's kind of interesting... setData does not cause a cellChanged signal, but setTextAlignment does?

    Related, I found this possible interesting approach to setting the default justification of my table items:

    https://stackoverflow.com/questions/15827886/set-default-alignment-for-cells-in-qtablewidget

    So, I basically have learned to use setItemPrototype during my table init, rather than changing that cell stuff during the cellChanged event, with the following simple code:

    rowitem = QTableWidgetItem()
    rowitem.setTextAlignment(Qt.AlignVCenter | Qt.AlignRight)
    table.setItemPrototype(rowitem)
    

    This is pretty much solved for me. Thank you all for your wise advice! :)



  • I would like to note another solution that should have been obvious to me once I figured out all the potential API calls that count as changes to table items. If, in the middle of your itemsChanged or cellChanged slot function, you'd like to make all kinds of cosmetic changes to your table without those changes retriggering calls to the *changed functions, you could simply turn off signals for the table at the beginning of it, and turn them back on at the end of it:

    @pyqtSlot(QTableWidgetItem)
    def mytableItemChanged(self, changedItem):
    	table = self.tblMyTable
    	table.itemChanged.disconnect()
    	#
    	# ...do all kinds of changes to the table (i.e.: validation colors, etc.)
    	#
    	table.itemChanged.connect(self.mytableItemChanged)
    

    Is this a fairly common practice?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.