NewPlayerX
NewPlayerX

Reputation: 47

Python2 PyQt4 Connect a bool Variable to a QCheckBox item

I have a problem with QCheckBox.

I am trying to connect a boolean variable to a QCheckBox so that when I change the boolean variable, the QCheckBox will be automatically checked or unchecked.

My Question is similar to the Question below but in opposite way.

question: Python3 PyQt4 Creating a simple QCheckBox and changing a Boolean variable

I just copy one solution from that question to here.

import sys

from PyQt4.QtGui import *
from PyQt4.QtCore import *

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

        self.ILCheck = False

        ILCheckbox = QCheckBox(self)
        ILCheckbox.setCheckState(Qt.Unchecked)

        ILCheckbox.stateChanged.connect(self.ILCheckbox_changed)

        MainLayout = QGridLayout()
        MainLayout.addWidget(ILCheckbox, 0, 0, 1, 1)

        self.setLayout(MainLayout)

    def ILCheckbox_changed(self, state):
        self.ILCheck = (state == Qt.Checked)

        print(self.ILCheck)


if __name__ == '__main__':
  app = QApplication(sys.argv)
  window = SelectionWindow()

  window.show()
  window.ILCheck = True
  sys.exit(app.exec_())

In this case, once I set ILCheck to True, QCheckBox will be checked.

Any help would be appreciated!!!

Thanks!!!!


Update:

I am using MVC on my project, the code above just a example show what I need. The bool value ILCheck will be use in other place, and I don't want call ILCheckBox in my model.

I expect that if I modify the value of ILCheck, ILCheckBox will react correctlly.


Update:

Thanks for all your reply and help. All your solution is great!!! The way I need is more like a Modeling-View solution so that I can separate modeling part from gui part. When I want to update something, I just need update modeling, and don't need pay attention to what gui looks like. I can't set this Bool property in View Class so that I can't use this solution.

I am not sure MVC is suitable in PyQT. I have a close solution like below with a problem.

from PyQt4 import QtGui, QtCore, uic
import sys
class CellList(QtGui.QStandardItemModel):
    def __init__(self, cells = [], parent = None):
        QtGui.QStandardItemModel.__init__(self, parent)
        self.__cells = cells
        self.add(cells)

    def headerData(self, section, orientation, role):
        if role == QtCore.Qt.DisplayRole:
            return QtCore.QString("Cell id List")
    def flags(self, index):
        return QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable

    def add(self, cells):
        for i in xrange(0, len(cells)):
            item = QtGui.QStandardItem('Cell %s' % cells[i][0])
            if (cells[i][1]):
                item.setCheckState(QtCore.Qt.Checked)
            else:
                item.setCheckState(QtCore.Qt.Unchecked)

            item.setCheckable(True)
            self.appendRow(item)
    def update(self, cells = None):
        # TODO: Making this working with out clean all old Cell
        self.clear()
        if cells is None: 
            cells = self.__cells
        else:
            print "hi"
            self.__cells = cells
        print cells
        self.add(cells)

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    listView = QtGui.QListView()
    listView.show()

    data = [[85, True], (105, True), (123, False)]
    model = CellList(data)
    listView.setModel(model)

    data[0][1] = False
    model.update(data)

    sys.exit(app.exec_())

There is a problem comes with this solution and I can't solve. I think only a view can set a Model. I am not sure if I can set a model to a single QCheckBox. .

Upvotes: 0

Views: 3034

Answers (3)

Raydel Miranda
Raydel Miranda

Reputation: 14360

As Avaris shows in his/her answer, emulating the overload of operator = is a good start for solving the question. But yet still the problem of the code being added to the SelectionWindow class.

But since we are using Qt, lets implement a custom QObject that represents our "smart" boolean variable which will emit a signal when change its value.

class SmartBool(QObject):

    valueChanged = pyqtSignal(bool)         # Signal to be emitted when value changes.

    def __init__(self):
        super(SmartBool, self).__init__()   # Call QObject contructor.
        self.__value = False                # False initialized by default.

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, value):
        if self.__value != value:
            self.valueChanged.emit(value)   # If value change emit signal.
            self.__value = value

Now your code needs only a couple of changes:

replace the line:

self.ILCheck = False

by:

self.ILCheck = SmartBool()

and connect the signal and the slot, add the line to the _ _ init _ _ some where after the line above. IMPORTANT, you are not bind to make the connection from within the SelectionWindow class

self.connect(self.ILCheck, SIGNAL("valueChanged(bool)"), ILCheckbox, SLOT("setChecked(bool)"))

for testing the result just add:

 window.ILCheck.value = True

to your "main" and you will see the checkbox checked next time you run the example.

The full code example was added to the end for stetical reasons

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class SmartBool(QObject):

    valueChanged = pyqtSignal(bool)         # Signal to be emitted when value changes.

    def __init__(self, value=False):
        super(SmartBool, self).__init__()   # Call QObject contructor.
        self.__value = value                # False initialized by default.

    @property
    def value(self):
        return self.__value

    @value.setter
    def value(self, value):
        if self.__value != value:
            self.valueChanged.emit(value)   # If value change emit signal.
            self.__value = value


class SelectionWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ILCheck = SmartBool()          # Your steroides bool variable.
        ILCheckbox = QCheckBox(self)
        self.connect(self.ILCheck, SIGNAL("valueChanged(bool)"), ILCheckbox, SLOT("setChecked(bool)"))
        ILCheckbox.setCheckState(Qt.Unchecked)
        ILCheckbox.stateChanged.connect(self.ILCheckbox_changed)
        MainLayout = QGridLayout()
        MainLayout.addWidget(ILCheckbox, 0, 0, 1, 1)
        self.setLayout(MainLayout)

    def ILCheckbox_changed(self, state):
        self.ILCheck = (state == Qt.Checked)
        print(self.ILCheck)


if __name__ == '__main__':
  app = QApplication(sys.argv)
  window = SelectionWindow()
  window.show()
  window.ILCheck.value = True
  sys.exit(app.exec_())

Upvotes: 3

OBu
OBu

Reputation: 5177

just use ILCheckbox.setCheckState(Qt.Checked) after calling ILCheck.

You don't neet signals here since you can call a slot sirectly.

If you want to do use this feature more than once, you should consider writing a setter which changes the state of self.ILCheck and emits a signal.

Edit after your clarification:

  • You can use the setter approach, but instead of setting the value of ILCheckbox, you should call your_properly_named_anddefine_signal.emit(). For more information about signal definition see e.g. http://www.pythoncentral.io/pysidepyqt-tutorial-creating-your-own-signals-and-slots/.
  • You'll have to connect your signal to a slot which will set the checkbox correctly. This connection could be made in the __init__() of your controller class.

Upvotes: 0

Avaris
Avaris

Reputation: 36715

property is the way to define a variable that does additional work upon assigning/accessing. Below is the code modified for that purpose. It changes ILCheck to a property such that it'll also update the checkbox upon assigning. Proper error checking for .setter is left out but most probably needed.

import sys

from PyQt4.QtGui import *
from PyQt4.QtCore import *

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

        self._ILCheck = False

        self.ILCheckbox = QCheckBox(self)
        self.ILCheckbox.setCheckState(Qt.Unchecked)

        self.ILCheckbox.stateChanged.connect(self.ILCheckbox_changed)

        MainLayout = QGridLayout()
        MainLayout.addWidget(self.ILCheckbox, 0, 0, 1, 1)

        self.setLayout(MainLayout)

    def ILCheckbox_changed(self, state):
        self._ILCheck = (state == Qt.Checked)

        print(self.ILCheck)

    @property
    def ILCheck(self):
        return self._ILCheck

    @ILCheck.setter
    def ILCheck(self, value):
        self._ILCheck = value
        self.ILCheckbox.setChecked(value)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = SelectionWindow()

    window.show()
    window.ILCheck = True
    sys.exit(app.exec_())

Upvotes: 1

Related Questions