1

enter image description here

Summary of problem: Two of the three checkboxes above have checkmarks. In one, the value was set with Python code self.setwithPythoncode.setChecked(True)whereas the other was set by the user clicking on the box while the app was running.

Actual Results: The background in the portion of the checkbox widget that is where the check goes is blue in the box checked by the user but it is plain (or white) on the one that was set in python code.

Desired Results: How can I change the code to make the background look the same when I set it programmatically as when it is set by the user, i.e., has the blue background in the actual box.

Discussion: BTW, if I check the "set with Python code" button twice, once uncheck it and again to check it, then the blue background appears.

What I've Tried: I haven't found a property of a QCheckBox or QAbstractButton that controls the background of just the checkable square. I couldn't find anything obvious in the Designer properties list for a checkbox.

Here is the Python code.

from PyQt5.QtWidgets import QApplication
from PyQt5 import QtWidgets, uic


class X(QtWidgets.QTableWidget):
    def __init__(self, ui_file):
        super(X, self).__init__()
        uic.loadUi(ui_file, self)
        self.setwithPythoncode.setChecked(True)


if __name__== '__main__':
    app = QApplication([''])
    window = X("test_check_boxes.ui")
    window.show()
    app.exec_()

Here is test_check_boxes.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>217</width>
    <height>201</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QCheckBox" name="setbyuserclicking">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>122</y>
     <width>161</width>
     <height>18</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="minimumSize">
    <size>
     <width>30</width>
     <height>0</height>
    </size>
   </property>
   <property name="focusPolicy">
    <enum>Qt::NoFocus</enum>
   </property>
   <property name="autoFillBackground">
    <bool>false</bool>
   </property>
   <property name="text">
    <string>Set by user clicking</string>
   </property>
   <property name="checked">
    <bool>false</bool>
   </property>
  </widget>
  <widget class="QCheckBox" name="notsetanywhere">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>18</y>
     <width>141</width>
     <height>18</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="minimumSize">
    <size>
     <width>30</width>
     <height>0</height>
    </size>
   </property>
   <property name="focusPolicy">
    <enum>Qt::NoFocus</enum>
   </property>
   <property name="text">
    <string>Not set anywhere</string>
   </property>
  </widget>
  <widget class="QCheckBox" name="setwithPythoncode">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>70</y>
     <width>181</width>
     <height>18</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="minimumSize">
    <size>
     <width>30</width>
     <height>0</height>
    </size>
   </property>
   <property name="focusPolicy">
    <enum>Qt::NoFocus</enum>
   </property>
   <property name="text">
    <string>Set with Python code</string>
   </property>
   <property name="checked">
    <bool>false</bool>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
WesR
  • 1,292
  • 1
  • 18
  • 30
  • I suspect that this blue color is given in the last QCheckBox since it has the focus. If you click on "Not set anywhere" the third QCheckBox still has the color blue? – eyllanesc Dec 09 '19 at 02:26
  • Yes. Once the color is set by check in the box it stays set after I check on another box. – WesR Dec 10 '19 at 04:55
  • Thanks to @eyllanesc for adding the import statements I forgot to copy. If someone is looking at this they should be sure to see his edit. – WesR Dec 10 '19 at 05:01

1 Answers1

1

After some test I've figured out this is probably something related to the fact that when you call setChecked(True) the QApplication is still not active.

You can check that by calling setChecked(True) within stateChanged slot. In this case, the app is already active, therefore this works as intended.

class CheckDemo(QWidget):

    def __init__(self, parent = None):
        super(CheckDemo, self).__init__(parent)

        self.layout = QHBoxLayout()
        self.b1 = QCheckBox("Button1", self)
        self.b2 = QCheckBox("Button2", self)
        self.b3 = QCheckBox("Button3", self)

        self.b1.setChecked(False)
        self.b2.setChecked(False)
        self.b3.setChecked(False)

        self.layout.addWidget(self.b1)
        self.layout.addWidget(self.b2)
        self.layout.addWidget(self.b3)
        self.setLayout(self.layout)
        self.setWindowTitle("checkbox demo")

        self.b2.stateChanged.connect(self.stateSlot)

        self.show()

    def stateSlot(self, state):
        self.b3.setChecked(True)

I've found three ways to work around this issue though.

1. Changing PyQt version

If You are using the most recent version which is 5.13.2, you can rollback it to 5.10.1 and your code will work just fine.

2. Using QTimer

By doing that you're delaying the setChecked call. Which gives time to the QApplication to be active. The problem is that depending on your computer, code, etc. It may not work and give you a hard time.

class CheckDemo(QWidget):

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

        self.layout = QHBoxLayout()
        self.b1 = QCheckBox("Button1", self)
        self.b2 = QCheckBox("Button2", self)
        self.b3 = QCheckBox("Button3", self)

        self.layout.addWidget(self.b1)
        self.layout.addWidget(self.b2)
        self.layout.addWidget(self.b3)

        self.b1.setChecked(False)
        self.b2.setChecked(False)
        self.b3.setChecked(False)

        self.setLayout(self.layout)
        self.setWindowTitle("checkbox demo")
        QTimer.singleShot(500, lambda: self.b2.setChecked(True)) #Here


def main():
    app = QApplication(sys.argv)
    customWidget = CheckDemo()
    app.setTargetObject(ex)
    customWidget.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

3. Custom QApplication subclass

You can capture the QEvent.ApplicationActivate within the event method and an call initialize method from your class.

class MyApp(QApplication):

    def __init__(self, args):
        super().__init__(args)
        self.application_first_time_active = True# You need to know when is the first time application was activated
        self.target_object = None

    def setTargetObject(self, obj):
        self.target_object = obj

    def event(self, event):
         if event.type() == QEvent.ApplicationActivated and self.application_first_time_active:
             self.target_object.initialize()
             self.application_first_time_active = False
         return super().event(event)


class CheckDemo(QWidget):

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

        self.layout = QHBoxLayout()
        self.b1 = QCheckBox("Button1", self)
        self.b2 = QCheckBox("Button2", self)
        self.b3 = QCheckBox("Button3", self)

        self.layout.addWidget(self.b1)
        self.layout.addWidget(self.b2)
        self.layout.addWidget(self.b3)

        self.b1.setChecked(False)
        self.b2.setChecked(False)
        self.b3.setChecked(False)

        self.setLayout(self.layout)
        self.setWindowTitle("checkbox demo")

    def initialize(self):
        self.b2.setChecked(True)


def main():
    app = MyApp(sys.argv)
    customWidget = CheckDemo()
    app.setTargetObject(customWidget)# Fixed
    customWidget.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

I'd say this is probably a bug on the latest qt versions. I would recommend you opening an issue to the QtCompany.

Eliakin Costa
  • 930
  • 6
  • 16
  • great reply. I did report it to Qt and am using your QTimer workaround. In your third solution, what is the definition of 'ex' in "app.setTargetObject(ex)"? – WesR Dec 11 '19 at 22:32
  • It should be `customWidget`. I've fixed it now. @WesR – Eliakin Costa Dec 12 '19 at 11:46