Search code examples
pyqtcontextmenuqtreeviewqstandarditem

PyQt extend existing Contextual Menu when editing QstandardItem's Text (in a QtreeView)


I would like to add an action to the "default" Contextual menu that shows up when editing QStandardItem's Text (wich is shown in a QTreeView)

This contextual menu seems to be the default contextual menu of QPlainTextEdit widget. There is the default actions : Undo, Redo, Copy, Paste, Delete, Select All. I want to add a custom action just here.

I have no clue on how to modify this menu.

Thanks in advance !


Solution

  • To customise the editor for a QTreeView cell you need to create a QItemDelegate and associate it with a column of your treeview.

    Custom delegates can be more complex (eg to change the save/restore behaviour of the editor) but in this case we just want to change which class it is using to instantiate the editor widget:

    class CustomDelegate(QItemDelegate):
        def createEditor(self, parent, option, index):
            editor = CustomLineEdit(parent)                
            return editor
    

    This will ensure the editor is now a CustomLineEdit widget, which can have any added functionality you wish (like a custom context menu). In my example below, I use a slightly modified version of the answer provided by Achayan to implement this CustomLineEdit.

    You assign a custom delegate to a column with the following code (in this case I've chosen column 0 of the treeview):

    # Create custom Delegate which instantiates our custom editor
    delegate = CustomDelegate()
    # Set this delegate for the first column only
    treeview.setItemDelegateForColumn(0, delegate)
    

    Full Code

    import sys
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    class CustomLineEdit(QLineEdit):
        def __init__(self, *args, **kwargs):
            super(CustomLineEdit, self).__init__(*args, **kwargs)
            self.setContextMenuPolicy(Qt.CustomContextMenu)
            self.customContextMenuRequested.connect(self.__contextMenu)
    
        def __contextMenu(self):
            self._normalMenu = self.createStandardContextMenu()
            self._addCustomMenuItems(self._normalMenu)
            self._normalMenu.exec_(QCursor.pos())
    
        def _addCustomMenuItems(self, menu):
            menu.addSeparator()
            menu.addAction(u'Test', self.testFunc)
    
        def testFunc(self):
            print "Call"
    
    
    class CustomDelegate(QItemDelegate):
        def createEditor(self, parent, option, index):
            editor = CustomLineEdit(parent)                
            return editor
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
    
        # Create Widgets
        window = QMainWindow()
        widget = QWidget()
        layout = QVBoxLayout(widget)
        treeview = QTreeView()
        layout.addWidget(treeview)
        window.setCentralWidget(widget)
        window.show()
    
        # Create Model
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels(['Header 1','Header 2'])
        treeview.setModel(model)
    
        # Create custom Delegate which instantiates our custom editor
        delegate = CustomDelegate()
        # Set this delegate for the first column only
        treeview.setItemDelegateForColumn(0, delegate)
    
        # Populate the model with some test data
        row1 = []
        row1.append(QStandardItem("asd"))
        row1.append(QStandardItem("fgh"))
        model.appendRow(row1)
        row2 = []
        row2.append(QStandardItem("qwe"))
        row2.append(QStandardItem("rty"))
        model.appendRow(row2)
    
        app.exec_()