Wrong action triggered when pressing enter in QMenu

The widget must accept focus, so a proper focus policy (StrongFocus or WheelFocus) must be set, but since it's a container you should also set the focus proxy to the spin, so that it will handle the focus for it.

In this way the keyboard navigation of the menu will also work properly by using arrows or Tab to focus the spinbox.

The action also has to be set as the active action of the menu in order to prevent the menu to accept events that may trigger other actions, and a way to achieve that is to install an event filter on the spin and check for events that should make the widget action as the active one, like FocusIn and Enter event.

Note that this results in some unintuitive results from the UX perspective: by default, hovering a normal QAction makes it the active one, but the user might want to move the mouse away from the spinbox in order to edit it without having the mouse on it, and by doing it another action could become active; in the meantime the spin should still keep the focus for obvious reasons. The result is that the other action might seem visually selected while the spinbox has focus. There is no obvious solution for this, and that's why widgets used for QWidgetActions should always be chosen with care.

class SpinAction(QWidgetAction):
    FocusEvents = QtCore.QEvent.FocusIn, QtCore.QEvent.Enter
    ActivateEvents = QtCore.QEvent.KeyPress, QtCore.QEvent.Wheel)
    WatchedEvents = FocusEvents + ActivateEvents
    def __init__(self, parent):
        # ...
        w.setFocusPolicy(self.spin.focusPolicy())
        w.setFocusProxy(self.spin)
        self.spin.installEventFilter(self)

    def eventFilter(self, obj, event):
        if obj == self.spin and event.type() in self.WatchedEvents:
            if isinstance(self.parent(), QtWidgets.QMenu):
                self.parent().setActiveAction(self)
            if event.type() in self.FocusEvents:
                self.spin.setFocus()
        return super().eventFilter(obj, event)

Note: the obj == self.spin condition is required since QWidgetAction already installs an event filter. A more elegant solution is to create a QObject subclass intended for that purpose only, and only overriding eventFilter().