Search code examples
pythonmodel-view-controllerpyqtmodel-viewqcheckbox

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. .


Solution

  • 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_())