Custom style for menu items

How to use QWidgetAction to create menu items with custom background colors in PyQt6
Heads up! You've already completed this tutorial.

How can I create a menu item with a custom background color using QWidgetAction, while keeping the same behavior and style as regular QAction items?

When building menus in PyQt6, you might want to visually distinguish certain menu items — for example, by giving one a different background color. The standard QAction class doesn't directly support styling with stylesheets, so the usual approach is to use QWidgetAction, which lets you embed any widget inside a menu. The challenge is making that custom widget look and behave like a normal menu item.

Let's walk through how to do this properly.

The problem with a basic approach

A first attempt might look like embedding a QCheckBox inside a QWidgetAction and applying a stylesheet to it:

python
check_box = QCheckBox("My Item")
check_box.setStyleSheet("background-color: tomato;")

widget_action = QWidgetAction(self)
widget_action.setDefaultWidget(check_box)

This works partially — you get a checkbox in the menu with a red background. But the styling only covers the checkbox widget itself, not the full width of the menu item. The result looks awkward: the colored area doesn't stretch across the entire row, and the hover/highlight behavior doesn't match the other items.

Using a container widget for full-width styling

To get the background color to fill the entire menu item row, you need to wrap your widget inside a container (like a QFrame or QWidget) and apply the background color to that container. This way the color spans the full width of the menu.

Here's a complete working example that demonstrates the approach:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QFrame,
    QHBoxLayout,
    QMainWindow,
    QWidgetAction,
)


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("Custom Menu Item")
        self.resize(640, 480)

        # Standard actions
        action1 = QAction("QAction Item 1", self)

        action2 = QAction("QAction Item 2", self)
        action2.setCheckable(True)

        action3 = QAction("QAction Item 3", self)

        # Custom styled menu item using QWidgetAction
        widget_action = QWidgetAction(self)

        # Create a container frame for the full-width background
        container = QFrame()
        container.setStyleSheet("background-color: tomato;")

        layout = QHBoxLayout(container)
        layout.setContentsMargins(4, 2, 4, 2)

        check_box = QCheckBox("QWidgetAction Item")
        check_box.setStyleSheet("background-color: tomato;")
        layout.addWidget(check_box)

        widget_action.setDefaultWidget(container)

        # Build the menu
        menu = self.menuBar().addMenu("Menu")
        menu.addAction(action1)
        menu.addAction(action2)
        menu.addAction(widget_action)
        menu.addAction(action3)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec())

With this setup, the QFrame container stretches to fill the full width of the menu, and the tomato background color covers the entire row.

Matching hover behavior

One thing you'll notice is that QWidgetAction items don't automatically get the same hover highlight as regular QAction items. The mouse hover styling is handled by the menu's style engine for standard actions, but custom widgets are on their own.

You can restore some of that behavior by using stylesheets on the container that respond to hover:

Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

Get the book

python
container.setStyleSheet("""
    QFrame {
        background-color: tomato;
    }
    QFrame:hover {
        background-color: #ff7f6e;
    }
""")

This gives the item a slightly lighter shade when the mouse hovers over it, providing visual feedback similar to standard menu items.

A more polished example

Here's the full example with hover behavior included:

python
import sys

from PyQt6.QtCore import Qt
from PyQt6.QtGui import QAction
from PyQt6.QtWidgets import (
    QApplication,
    QCheckBox,
    QFrame,
    QHBoxLayout,
    QMainWindow,
    QWidgetAction,
)


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("Custom Menu Item")
        self.resize(640, 480)

        # Center the window on screen
        available_geometry = self.screen().availableGeometry()
        self.move(
            int((available_geometry.width() - self.width()) / 2),
            int((available_geometry.height() - self.height()) / 2),
        )

        # Standard actions
        action1 = QAction("QAction Item 1", self)
        action1.triggered.connect(lambda: print("Item 1 triggered"))

        action2 = QAction("QAction Item 2", self)
        action2.setCheckable(True)
        action2.toggled.connect(lambda checked: print(f"Item 2 toggled: {checked}"))

        action3 = QAction("QAction Item 3", self)
        action3.triggered.connect(lambda: print("Item 3 triggered"))

        # Custom styled menu item
        widget_action = QWidgetAction(self)

        container = QFrame()
        container.setStyleSheet(
            """
            QFrame {
                background-color: tomato;
            }
            QFrame:hover {
                background-color: #ff7f6e;
            }
            """
        )

        layout = QHBoxLayout(container)
        layout.setContentsMargins(4, 2, 4, 2)

        self.custom_check_box = QCheckBox("Custom Styled Item")
        self.custom_check_box.setStyleSheet(
            """
            QCheckBox {
                background-color: transparent;
                padding: 2px;
            }
            """
        )
        self.custom_check_box.toggled.connect(
            lambda checked: print(f"Custom item toggled: {checked}")
        )
        layout.addWidget(self.custom_check_box)

        widget_action.setDefaultWidget(container)

        # Build the menu
        menu = self.menuBar().addMenu("Menu")
        menu.addAction(action1)
        menu.addAction(action2)
        menu.addAction(widget_action)
        menu.addAction(action3)


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec())

Notice that the checkbox itself has background-color: transparent so it inherits the container's background color rather than painting its own solid block on top.

A note about menu closing behavior

One behavioral difference with QWidgetAction is that clicking the embedded widget does not automatically close the menu. With a standard QAction, clicking it triggers the action and closes the menu. With QWidgetAction, the menu stays open — which can actually be useful if you want users to toggle multiple checkboxes without having to reopen the menu each time.

If you do want the menu to close when the checkbox is clicked, you can connect the checkbox signal to the menu's close() method:

python
self.custom_check_box.toggled.connect(menu.close)

Add this after both the checkbox and the menu have been created. For more on how signals and connections work, see the Signals, Slots & Events tutorial.

Summary

Using QWidgetAction with a container QFrame gives you full control over the appearance of individual menu items. The container widget stretches to fill the menu width, and you can apply any stylesheet you like — background colors, fonts, borders, and more. Adding hover styles to the container keeps the visual feedback consistent with standard menu items, and connecting signals from your embedded widgets lets you respond to user interaction just like you would with regular actions.

If you're looking to further customize your application's appearance, you might also be interested in building custom widgets or arranging your UI with layouts.

Over 15,000 developers have bought Create GUI Applications with Python & Qt!
Create GUI Applications with Python & Qt6
Get the book

Downloadable ebook (PDF, ePub) & Complete Source code

[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak
Martin Fitzpatrick

Custom style for menu items was written by Martin Fitzpatrick.

Martin Fitzpatrick is the creator of Python GUIs, and has been developing Python/Qt applications for the past 12+ years. He has written a number of popular Python books and provides Python software development & consulting for teams and startups.