Search code examples
pythonpyqtpyqt4qtreeview

DoubleClick event on QTreeView has no result when trying to capture mouse position


I am working with Python 3.6 and pyqt 4.11. I have two QTreeViews stacked in a Widget, both of them display some batch jobs so each step can expand to show all functions. I want to be able to double click on one line of the tree view and generate a pop up dialogue where I can edit the parameters of the function I double clicked on.

If I connect the double click signal without capturing the position:

self.connect(self.QTreeView, QtCore.SIGNAL('mouseDoubleClickEvent()'),print('OK'))

it works and OK is printed.

However as soon as I try to catch the cursor position nothing happens anymore. I have tried both to connect the whole widget and the treeView to a simple test function. It doesn't work at all, not even OK gets printed.


self.connect(self.QTreeView, QtCore.SIGNAL('mouseDoubleClickEvent(const QPoint &)'),self.showDlg)

def showDlg (self, point):
        print ('OK')
        treeidx=self.treeview.indexAt(point)
        print (treeidx)

A ContextMenu is triggered by right click on the whole Widget and it works

self.QTreeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)   

self.connect(self.QTreeWidget, QtCore.SIGNAL('customContextMenuRequested(const QPoint &)'), self.customMyContextMenu)

But double clicking on the same Widget gives no result

self.connect(self.QTreeWidget, QtCore.SIGNAL('mouseDoubleClickEvent(const QPoint &)'),self.showDlg)

I would like to use the pointer position to know in what leaf of the tree view the changes have to happen, I thought treeview.indexAt(point) would be the way to do that, but since my simple function doesn't get called at all, there must be some other problem I don't see.


Solution

  • I find it strange that in your first code is printed "OK", what returns to me is an error because connect also expects a callable, but print('OK') returns None that is not a callable. Besides, mouseDoubleClickEvent is not a signal but an event which reaffirms my strangeness.

    Instead you have to use the doubleClicked signal that returns the QModelIndex associated with the item, and to get the position you have to use the QCursor::pos() next to mapFromGlobal() of the viewport() of QTreeView. You must also use the new connection syntax.

    from PyQt4 import QtCore, QtGui
    
    
    def create_model(parent):
        model = QtGui.QStandardItemModel(parent)
        for i in range(3):
            parent_item = QtGui.QStandardItem("Family {}".format(i))
            for j in range(3):
                child1 = QtGui.QStandardItem("Child {}".format(i * 3 + j))
                child2 = QtGui.QStandardItem("row: {}, col: {}".format(i, j + 1))
                child3 = QtGui.QStandardItem("row: {}, col: {}".format(i, j + 2))
            parent_item.appendRow([child1, child2, child3])
            model.appendRow(parent_item)
        return model
    
    
    class Widget(QtGui.QWidget):
        def __init__(self, parent=None):
            super(Widget, self).__init__(parent)
            self._tree_view = QtGui.QTreeView()
            self._tree_view.setModel(create_model(self))
            self._tree_view.expandAll()
            lay = QtGui.QVBoxLayout(self)
            lay.addWidget(self._tree_view)
    
            self._tree_view.doubleClicked.connect(self.on_doubleClicked)
    
        @QtCore.pyqtSlot("QModelIndex")
        def on_doubleClicked(self, ix):
            print(ix.data())
    
            gp = QtGui.QCursor.pos()
            lp = self._tree_view.viewport().mapFromGlobal(gp)
            ix_ = self._tree_view.indexAt(lp)
            if ix_.isValid():
                print(ix_.data())
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtGui.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    PySide Version:

    from PySide import QtCore, QtGui
    
    
    def create_model(parent):
        model = QtGui.QStandardItemModel(parent)
        for i in range(3):
            parent_item = QtGui.QStandardItem("Family {}".format(i))
            for j in range(3):
                child1 = QtGui.QStandardItem("Child {}".format(i * 3 + j))
                child2 = QtGui.QStandardItem("row: {}, col: {}".format(i, j + 1))
                child3 = QtGui.QStandardItem("row: {}, col: {}".format(i, j + 2))
            parent_item.appendRow([child1, child2, child3])
            model.appendRow(parent_item)
        return model
    
    
    class Widget(QtGui.QWidget):
        def __init__(self, parent=None):
            super(Widget, self).__init__(parent)
            self._tree_view = QtGui.QTreeView()
            self._tree_view.setModel(create_model(self))
            self._tree_view.expandAll()
            lay = QtGui.QVBoxLayout(self)
            lay.addWidget(self._tree_view)
    
            self._tree_view.doubleClicked.connect(self.on_doubleClicked)
    
        @QtCore.Slot("QModelIndex")
        def on_doubleClicked(self, ix):
            print(ix.data())
    
            gp = QtGui.QCursor.pos()
            lp = self._tree_view.viewport().mapFromGlobal(gp)
            ix_ = self._tree_view.indexAt(lp)
            if ix_.isValid():
                print(ix_.data())
    
    
    if __name__ == "__main__":
        import sys
    
        app = QtGui.QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())