6
votes

Where are the signals itemChecked and itemUncheсked on the QTreeWidget?

Qt Signals: (quote from PyQt4 QTreeWidget documentation page)

void currentItemChanged (QTreeWidgetItem *,QTreeWidgetItem *)
void itemActivated (QTreeWidgetItem *,int)
void itemChanged (QTreeWidgetItem *,int)
void itemClicked (QTreeWidgetItem *,int)
void itemCollapsed (QTreeWidgetItem *)
void itemDoubleClicked (QTreeWidgetItem *,int)
void itemEntered (QTreeWidgetItem *,int)
void itemExpanded (QTreeWidgetItem *)
void itemPressed (QTreeWidgetItem *,int)
void itemSelectionChanged ()

At current moment I solved it like this:

self.treeWidget.itemClicked.connect (self.handle)

def handle (item, column): 
    print 'emitted!', item.text(column)
    if item.checkState(column) == QtCore.Qt.Checked:
        # there are a lot of my functions inside which work with item data
        self.handleChecked(item, column)
    elif item.checkState(column) == QtCore.Qt.Unchecked:
        self.handleUnchecked(item, column)

But it's a bad solution for me, because itemClicked emitted in a really lot of cases. It emitted in the case of Left/Right Mouse Clicks on the item text, which is absolutely unnecessary (I have heavy functions within self.handleChecked, and unnecessary calls of them on the context menu opening are pretty lousy).

Well, I also tried to use itemChanged:

self.treeWidget.itemChanged.connect (self.handle)

but this way situation is even worse! self.handle function calls himself recursively to infinity and beyond, because my functions within self.handleChecked change item data and this signal emits again and again. Also, I need signal which emits only on item checkbox toggling.

Can someone tell me, what I'm doing wrong?

2
The itemChanged signal is definitely the right one to use. What do mean by "self.handle calls recursively"? And what, exactly, is "unacceptable" about it?ekhumoro
@ekhumoro, I updated the post.Bruno Gelb
I have updated my answer with a possible solution for emitting a signal only when the checked state changes, which you may find better than blocking signals.ekhumoro

2 Answers

13
votes

To avoid problems with recursion when using the itemChanged signal, try temporarily blocking signals until the handler has completed:

def handle(self, item, column):
    self.treeWidget.blockSignals(True)
    if item.checkState(column) == QtCore.Qt.Checked:
        self.handleChecked(item, column)
    elif item.checkState(column) == QtCore.Qt.Unchecked:
        self.handleUnchecked(item, column)
    self.treeWidget.blockSignals(False)

UPDATE

The other part of your question asked about emitting a signal only when an item is checked.

One way to do this would be to subclass QTreeWidgetItem and reimplement it's setData function:

class TreeWidgetItem(QtGui.QTreeWidgetItem):
    def setData(self, column, role, value):
        state = self.checkState(column)
        QtGui.QTreeWidgetItem.setData(self, column, role, value)
        if (role == QtCore.Qt.CheckStateRole and
            state != self.checkState(column)):
            treewidget = self.treeWidget()
            if treewidget is not None:
                treewidget.itemChecked.emit(self, column)

class Window(QtGui.QTreeWidget):
    itemChecked = QtCore.pyqtSignal(object, int)

    def __init__(self, rows, columns):
        QtGui.QTreeWidget.__init__(self)
        self.itemChecked.connect(self.handleItemChecked)

    def handleItemChecked(self, item, column):
        print 'ItemChecked', int(item.checkState(column))
0
votes

To complete the answer of @ekhumoro, and to give a shorter answer (without subclassing QTreeWidgetItem): block the signal :

self.treeWidget.blockSignals(True)
self.treeWidget.blockSignals(False)

and use

self.treeWidget.itemChanged

to connect to what you want. the signal is used when the data is changed (text inside, checked state, ...) and in your function linked to this signal verify if the treeWidget has changed is checked state.