Mario Camilleri
Mario Camilleri

Reputation: 1567

Detecting clicks on QTreeView icons

I have a QTreeView in which some items are decorated with an icon. The items could be in any column of the TreeView.

I wish to know how to detect mouse clicks on an icon. I can use the view's mousePressEvent() to detect a mouse press, I can check whether an icon is present in the clicked item by calling the model's data() method with Qt.DecorationRole to see whether I get an empty QVariant back, and I can query the size of the icon using the view's iconSize() method. But I have no way of knowing the co-ordinates of the icon within the item's visual rectangle.

PS. Other SO questions relating to QTreeView decorations usually refer to the tree collapse and expand icons, and have no bearing on this question.

Upvotes: 1

Views: 987

Answers (2)

eyllanesc
eyllanesc

Reputation: 244301

The logic is to have 2 data:

  • The position of the mouse where you click, and that can be obtained in the editorEvent method of delegate.
  • The position of the icon that is obtained using the StyleOptionViewItem and the QStyle used for painting.
from PyQt5 import QtCore, QtGui, QtWidgets


class Delegate(QtWidgets.QStyledItemDelegate):
    def editorEvent(self, event, model, option, index):
        ret = super().editorEvent(event, model, option, index)
        if event.type() == QtCore.QEvent.MouseButtonPress:
            self.initStyleOption(option, index)
            widget = option.widget
            style = (
                widget.style() if widget is not None else QtWidgets.QApplication.style()
            )
            icon_rect = style.subElementRect(
                QtWidgets.QStyle.SE_ItemViewItemDecoration, option, widget
            )
            if icon_rect.contains(event.pos()):
                print("icon clicked")
        return ret


def main():
    app = QtWidgets.QApplication([])
    pixmap = QtGui.QPixmap(128, 128)
    pixmap.fill(QtCore.Qt.green)
    icon = QtGui.QIcon(pixmap)
    model = QtGui.QStandardItemModel()
    for i in range(5):
        item = QtGui.QStandardItem(f"item-{i}")
        item.setIcon(icon)
        item.setEditable(False)
        model.appendRow(item)
        for j in range(6):
            child_item = QtGui.QStandardItem(f"item {i}{j}")
            child_item.setIcon(icon)
            child_item.setEditable(False)
            item.appendRow(child_item)
    w = QtWidgets.QTreeView()
    w.setModel(model)
    w.expandAll()
    delegate = Delegate(w)
    w.setItemDelegate(delegate)
    w.resize(640, 480)
    w.show()
    app.exec_()


if __name__ == "__main__":
    main()

Upvotes: 2

Summit
Summit

Reputation: 2268

In c++ QT this can be achieved through this method , must be similar in python also.

If you draw the icon in paint you must know the location.

https://doc.qt.io/qt-5/qabstractitemdelegate.html#editorEvent.

bool PixelDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    QMouseEvent* mouseEvent = (QMouseEvent*) event;
    QPoint p =  mouseEvent->pos();   // Position of mouse click.    
    
    return false;
}

Upvotes: 1

Related Questions