Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change checkbox state to not checked when other checkbox is checked pyqt

Tags:

checkbox

qt

pyqt

I'm using Qt Designer and pyqt code to write an app.

I have a very simple question: in my UI I have 2 checkboxes and what I need is to set the first checkbox as always unchecked when the second checkbox is unchecked.

In other words the first checkbox can be checked only when the second checkbox is checked, but the second checkbox can be checked also if the first one is not checked.

In Qt Designer I have not find an easy way to do that with the Signals/Slots function.

I had a look in the Qt API and I tried to write some code:

class CreateRIVLayerDialog(QDialog, FORM_CLASS):
    def __init__(self, iface)

    # some more code here...

    if self.addCheckDB.isChecked():
        self.addCheck.setChecked(False)

but without results.

Does anybody have some hints?

Thanks

like image 504
matteo Avatar asked Mar 29 '16 10:03

matteo


2 Answers

The simplest way is with two signal connections, one of which can be done in Qt Designer.

Your design requires that "the first checkbox can be checked only when the second checkbox is checked". This is equivalent to saying the first checkbox should be disabled when the second checkbox is unchecked. Disabling the checkbox makes it clear to the user that the option is unavailable.

So in Qt Designer, you should connect the toggled signal of checkbox2 to the setEnabled slot of checkbox1. (It may also be necessary to set the initial enabled state of checkbox1 in Qt Designer as well).

Then, in your code, you should make a second connection, like this:

    self.checkbox2.toggled.connect(
        lambda checked: not checked and self.checkbox1.setChecked(False))

This will clear the checked state of checkbox1 whenever checkbox2 is unchecked.

If you wanted to do the whole thing in code, it would look like this:

    self.checkbox1.setEnabled(False)
    self.checkbox2.toggled.connect(self.checkbox1.setEnabled)
    self.checkbox2.toggled.connect(
        lambda checked: not checked and self.checkbox1.setChecked(False))
like image 55
ekhumoro Avatar answered Oct 15 '22 09:10

ekhumoro


Just use signals. You are correct when saying that you cannot directly do that via the designer since you have to invert the checked property. The easiest and most readable way that comes to my mind is using a common slot plus an internal member variable that holds the checked state for both:

  • Add self._toggle = INITIAL_VALUE to your class - the INITIAL_VALUE holds a boolean value, which you use to check/uncheck your checkboxes

      self._toggle = True
      self.check1 = QCheckBox('Check 1', self)
      self.check1.setChecked(self._toggle)
      self.check2 = QCheckBox('Check 2', self)
      self.check2.setChecked(not self._toggle)
    
  • Add a slot:

      @pyqtSlot()
      def toggle(self):
        self._toggle = not self._toggle
        self.check1.setChecked(self._toggle)
        self.check2.setChecked(not self._toggle)
    
  • Connect the clicked signal of both checkboxes to this slot.

    Warning! Do not use the stateChanged signal here or you will start an infinite recursion. :3

      self.check1.clicked.connect(self.toggle)
      self.check2.clicked.connect(self.toggle)
    

What I'm doing here is basically taking over the change of the state of both checkboxes and do it manually using the value of self._toggle for the first checkbox and the inverted value of self._toggle for the second checkbox.

If you want less inverting inside the slot the following also works though it is less readable omho:

@pyqtSlot()
def toggle(self):
  self.check2.setChecked(self._toggle) # Old value of our check1 becomes the value of check2
  self._toggle = not self._toggle # Invert
  self.check1.setChecked(self._toggle) # New value is assigned to check1

Note: You can also use isChecked() inside the slot and do all of the above without the extra variable however I find this more readable and with much less function calls (every isChecked() and setChecked() is a function call)

like image 5
rbaleksandar Avatar answered Oct 15 '22 09:10

rbaleksandar