2

Goal

I want the user to be able to click on a QIcon to do something. That is, I want to connect a mouse click event, within a QIcon, to a slot.

What I have tried

In my example (below) I am using a tree view, and when the user clicks one of the icons, I want to add a child to that node.

I have tried doing this by reimplementing mousePressEvent for the icon, but I'm not sure how to do so for icons (which by default serve a purely decorator role) within a treeviewmodel.

The code, in simplified form, is as below:

from PySide import QtGui, QtCore
import icons_rc

class Node(object):
    def __init__(self, name, parentNode=None):
        self.name = name
        self.childNodes = []  
        self.parentNode = parentNode
        if parentNode is not None:  #if self has a parent, then add self node to parent in tree
            parentNode.addChild(self)

    def addChild(self, childNode):
        self.childNodes.append(childNode)  #

    def setName(self, name):
        self.name=name

    def data(self):
        return self.name

    def child(self, row):
        return self.childNodes[row]

    def childCount(self):
        return len(self.childNodes)

    def parent(self):
        return self.parentNode

    def row(self):
        if self.parentNode is not None: #if self has a parent, then return  row of self
            return self.parentNode.childNodes.index(self)


class TreeModel(QtCore.QAbstractItemModel):
    #Inputs: node, Qobject
    def __init__(self, root, parent=None):
        QtCore.QAbstractItemModel.__init__(self, parent)
        self.rootNode = root

    def rowCount(self, parentIndex=QtCore.QModelIndex()):
        if not parentIndex.isValid():
            parentNode = self.rootNode
        else:
            parentNode = parentIndex.internalPointer()  #picks out parent node of tree
        return parentNode.childCount()

    def columnCount(self, parentIndex=QtCore.QModelIndex()):
        return 1

    def flags(self,index):
        return QtCore.Qt.ItemIsEnabled

    def data(self, index, role):
        if not index.isValid():
            return None

        node=index.internalPointer()
        #role--editrole here so text doesn't disappear when you double click
        if role == QtCore.Qt.DisplayRole:
            if index.column() == 0:
                return node.data()  #node.data method defined above (string)

        #icons are shown by using a decoration role
        if role==QtCore.Qt.DecorationRole:
            if index.column() == 0:
                return QtGui.QIcon(QtGui.QPixmap(":/images/add.png"))

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if index.isValid():
            return  True
        else:
            return False

    def getNode(self,index):
        if index.isValid():
            node=index.internalPointer()
            if node:
                return node
        return self.rootNode

    def parent(self, childIndex):
        if not childIndex.isValid(): 
            return QtCore.QModelIndex()
        childNode = self.getNode(childIndex) 
        parentNode = childNode.parent()  
        if parentNode == self.rootNode:
            return QtCore.QModelIndex()
        return self.createIndex(parentNode.row(), 0, parentNode)

    def index(self, row, column, parentIndex):
        if not self.hasIndex(row, column, parentIndex):
            return QtCore.QModelIndex()
        parentNode=self.getNode(parentIndex)
        childNode = parentNode.child(row)  #implemented above
        if childNode:
            return self.createIndex(row, 0, childNode) #row/col/object yields index
        else:
            return QtCore.QModelIndex()


def main():
    import sys
    qtApp=QtGui.QApplication(sys.argv)

    rootNode = Node("Hips")  #for some reason, by design, this will not show
    childNode0 = Node("LeftLeg", rootNode)
    childNode1 = Node("RightLeg", rootNode)
    childNode2 = Node("RightAnkle", childNode1)
    childNode3 = Node("Middle toe", childNode2)
    childNode4 = Node("Pirate flank toe", childNode2)
    childNode5 = Node("Pinky toe", childNode2)

    model=TreeModel(rootNode)
    treeView = QtGui.QTreeView()
    treeView.show()  #opens empty window
    treeView.setModel(model)

    sys.exit(qtApp.exec_())

if __name__ == "__main__":
    main()

Basically I want to build an event handler that adds a child when I click an icon, an icon which is given in add.png. Is there some sneaky route to reimplement mouseClickEvent or other functions for icons? I have made zero headway on this problem with Google.

I tried subclassing QIcon, and reimplementing mousePressEvent. The goal is to use this revised icon class within TreeModel.data(). So far I have had no luck getting the class to work. Here's what I tried:

class ClickableQIcon(QtGui.QIcon):

    def __init__(self, iconPixmap, parent):
        QtGui.QIcon.__init__(self, iconPixmap, parent)      

    def mousePressEvent(self, event):
        print "This time you pressed in an icon"   

Where I would feed it something like iconPixmap=QtGui.QIcon(QtGui.QPixmap(":/images/add.png")) as one of the inputs in the calling function. It doesn't work, I only include it to show the kind of solution I was hoping to find.

Potentially relevant posts:

Button Icon Change on Click

QIcon inside combobox

Community
  • 1
  • 1
eric
  • 7,142
  • 12
  • 72
  • 138

1 Answers1

1

You cannot reimplement event handlers for QIcon because it's a simple container for an image and has nothing to do with event handling. That's why your extended QIcon class didn't do anything.

Also QTreeView by default only draws icons for decorations.

You could use a custom item delegate in your tree view. QTreeView inherits from QAbstractItemView which has several setItemDelegate methods (documentation). Then either extend QItemDelegate or implement QAbstractItemDelegate.

A bit faster might be to set custom widgets with setIndexWidget().

In case the icon is always the same you might even get away with a stylesheet on the QTreeView. (documentation)

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
  • a bit similar is http://stackoverflow.com/questions/7175333/howto-create-delegate-for-qtreewidget – NoDataDumpNoContribution Jul 22 '14 at 08:52
  • setIndexWidget should work: probably no need to mess with custom delegates if you don't want to. Asked about setting custom widgets in a list here: http://stackoverflow.com/questions/26199374/add-qwidget-to-qlistwidget . It should be similar for a tree. – eric Oct 14 '14 at 12:32
  • is it possible to change it from its default decorator role to another role, and then make it clickable? – eric Oct 14 '14 at 14:10