CheckBox in qactions not working

I am trying to have a QAction in context menu, where I toggle the state of the check box using the following code. But, it is not working. I have also tried checking the box in the beginning (so it does show checked) but it doesn’t get unchecked when I click the updateOn action.

Any suggestions would be greatly appreciated. Thanks in advance

# author = copied from somewhere
from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu
import sys


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Context Menu"
        self.top = 200
        self.left = 500
        self.width = 400
        self.height = 300
        self.InitWindow()


    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon("icon.png"))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show()

    def contextMenuEvent(self, event):
        contextMenu = QMenu(self)
        updateOn = contextMenu.addAction("Update On")
        updateOn.setCheckable(True)
        updateOn.setChecked(True)
        quitAct = contextMenu.addAction("Quit")
        action = contextMenu.exec_(self.mapToGlobal(event.pos()))
        if action == updateOn:
            print("action=", action)
            if updateOn.isChecked():
                updateOn.setChecked(False)
            else:
                updateOn.setChecked(True)
        if action == quitAct:
            self.close()


App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

Hi @userk182 welcome the forum!

The problem is your updateOn object is recreated each time the contentMenuEvent method is called. When you change the checked state at the end, this isn’t stored anywhere and the updateOn object is deleted and cleared up on exiting the method.

One way to do this would be to store the update on state on the window. This way you can correctly populate the state each time the menu is recreated.

from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu
import sys


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Context Menu"
        self.top = 200
        self.left = 500
        self.width = 400
        self.height = 300
        self.InitWindow()
        self._update_on_state = False


    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon("icon.png"))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show()

    def contextMenuEvent(self, event):
        contextMenu = QMenu(self)
        updateOn = contextMenu.addAction("Update On")
        updateOn.setCheckable(True)
        print(self._update_on_state)
        updateOn.setChecked(self._update_on_state)
        quitAct = contextMenu.addAction("Quit")
        action = contextMenu.exec_(self.mapToGlobal(event.pos()))
        if action == updateOn:
            self._update_on_state = updateOn.isChecked()

        if action == quitAct:
            self.close()


App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

Alternatively, if you want to persist the state in the menu object (& create it only once) you need to keep a reference to the menu and objects somewhere so they are not cleared up.

from PyQt5 import QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QMenu
import sys


class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.title = "PyQt5 Context Menu"
        self.top = 200
        self.left = 500
        self.width = 400
        self.height = 300
        self.InitWindow()
        
        self.context_menu = QMenu(self)
        self.context_menu_updateOn = self.context_menu.addAction("Update On")
        self.context_menu_updateOn.setCheckable(True)
        self.context_menu_updateOn.setChecked(True)
        self.context_menu_quitAct = self.context_menu.addAction("Quit")
        
    def InitWindow(self):
        self.setWindowIcon(QtGui.QIcon("icon.png"))
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.show()

    def contextMenuEvent(self, event):
        action = self.context_menu.exec_(self.mapToGlobal(event.pos()))
        if action == self.context_menu_updateOn:
            pass # is persisted anyway.

        if action == self.context_menu_quitAct:
            self.close()


App = QApplication(sys.argv)
window = Window()
sys.exit(App.exec())

Hope that helps!