How To Add Context Menu Into QTreeWidget

GOAL

Today’s goal is to add context menu to the QTreeWidget in PySide.

Environment

Windows 10
Python 3.8.7
PySide2 (5.15.2)

Method

Change the ContextMenuPolicy of the widget.

Use CustomContextMenu if you’d like to connect your function to the signal that is emitted when the context menu is requested by actions such as right-clicked. See Qt::ContextMenuPolicy in Qt Documentation for details about ContextMenuPolocy types.

self.tree_widget = QTreeWidget()
self.tree_widget.setContextMenuPolicy(Qt.CustomContextMenu)
self.tree_widget.customContextMenuRequested.connect(self._show_context_menu)

And define the function to connect. Please use exec_() instead of show() to display the menu. The mapToGlobal function is used to determines the position to display the menu.

    # the function to display context menu
    def _show_context_menu(self, position):
        display_action1 = QAction("Display Selection")
        display_action1.triggered.connect(self.display_selection)

        menu = QMenu(self.tree_widget)
        menu.addAction(display_action1)
        
        menu.exec_(self.tree_widget.mapToGlobal(position))

Complete Code

cotext_menu_test.py

import sys
from PySide2.QtWidgets import *
from PySide2.QtCore import Qt

class MyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)
        self._generate_ui()
        self._init_tree_widget()

    def _generate_ui(self):
        main_widget = QWidget()
        main_layout = QVBoxLayout()
        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)
        self.tree_widget = QTreeWidget()
        self.tree_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)

        #----------add context menu--------------------------------------
        self.tree_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree_widget.customContextMenuRequested.connect(self._show_context_menu)
        #----------------------------------------------------------------

        main_layout.addWidget(self.tree_widget)

    def _init_tree_widget(self):
        headers = ["header1", "header2"]
        self.tree_widget.setHeaderLabels(headers)
        tree_widget_item1 = QTreeWidgetItem(["group1"])
        tree_widget_item1.addChild(QTreeWidgetItem(["item1_1", "item1_2"]))
        self.tree_widget.addTopLevelItem(tree_widget_item1)
        tree_widget_item2 = QTreeWidgetItem(["group2"])
        self.tree_widget.addTopLevelItem(tree_widget_item2)
        tree_widget_item2.addChild(QTreeWidgetItem(["item2_1", "item2_2"]))
        tree_widget_item2.addChild(QTreeWidgetItem(["item3_1", "item3_2"]))

    # the function to display context menu
    def _show_context_menu(self, position):
        display_action1 = QAction("Display Selection")
        display_action1.triggered.connect(self.display_selection)

        menu = QMenu(self.tree_widget)
        menu.addAction(display_action1)
        menu.exec_(self.tree_widget.mapToGlobal(position))

    # the action executed when menu is clicked
    def display_selection(self):
        column = self.tree_widget.currentColumn()
        text = self.tree_widget.currentItem().text(column)
        print("right-clicked item is " + text)

def launch():
    app = QApplication.instance()
    if not app:
        app = QApplication(sys.argv)
    widget = MyMainWindow()
    widget.show()
    app.exec_()


launch()

Result

When the item “item1_2” is right-clicked, the menu appears.

Click the “Display Selection”, the message is displayed.