Search code examples
pythonuser-interfacepyqt4qcomboboxqcheckbox

Lock a choice in PyQt4 QComboBox using a QCheckBox


I am beginning to write a GUI using PyQt4. This is my first experience with GUIs (and also oo-programming is somewhat new to me). Part of that GUI will be like 4 to 5 instances of QComboBox. As many choices are to be made, I want the user to be able to lock a choice, such that is not being changed unintenionally later. For one QComboBox I can solve the problem with this code that I wrote:

import sys
from PyQt4 import QtGui, QtCore

class MyGui(QtGui.QWidget):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.resize(250, 50)

        # vertical layout for widgets
        self.vbox = QtGui.QVBoxLayout()
        self.setLayout(self.vbox)

        # Create a combo box with some choices
        self.combo_color = QtGui.QComboBox()
        self.vbox.addWidget(self.combo_color)
        items = 'Red Yellow Purple'.split()
        self.combo_color.addItems(items)
        self.connect(self.combo_color, QtCore.SIGNAL('activated(QString)'), self.use_choice)

        # add a checkbox next to the combobox which (un-)locks the the combo-choice
        self.checkbox_color = QtGui.QCheckBox('Lock Choice', self)
        self.vbox.addWidget(self.checkbox_color)
        self.connect(self.checkbox_color, QtCore.SIGNAL('stateChanged(int)'), self.lock_choice)

    def use_choice(self, text):
        # do something very useful with the choice
        print 'The current choice is: {choice}'.format(choice=text)

    def lock_choice(self):
        if self.checkbox_color.isChecked():
            self.combo_color.setEnabled(False)
            print 'Choice {choice} locked'.format(choice=self.combo_color.currentText())
        else:
            self.combo_color.setEnabled(True)
            print 'Choice unlocked'


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mygui = MyGui()
    mygui.show()
    app.exec_()

This code does what it should, but I am very unhappy with its design, because the method lock_choice is hard-coded to only lock the choice of the QComboBox combo_color. What if I now want to do the same thing for another QComboBox (say combo_name) and a second QCheckBox (say checkbox_name) which could be realized by appending the following code to the classes __init__(self) code block:

    # create second combo box with some other choices
    self.combo_name = QtGui.QComboBox()
    self.vbox.addWidget(self.combo_name)
    items = 'Bob Peter'.split()
    self.combo_name.addItems(items)
    self.connect(self.combo_name, QtCore.SIGNAL('activated(QString)'), self.use_choice)

    # add a checkbox next to the combobox which (un-)locks the the combo-choice
    self.checkbox_name = QtGui.QCheckBox('Lock Choice', self)
    self.vbox.addWidget(self.checkbox_name)
    self.connect(self.checkbox_name, QtCore.SIGNAL('stateChanged(int)'), self.lock_choice) # <-- obviously wrong, as it (un-)locks color choice at the moment

Both QComboBoxes can share the method use_choice() right now, but they cannot share the method lock_choice(), as both checkboxes lock the color-choice. I want the checkbox checkbox_name to lock the name-choice, without copy and pasting the current lock_choice()-method and switching the hardcoded combobox. I am sure there is an easy way, like passing the target-combobox to the method, which i just don't know yet. Help would be appreciated!


Solution

  • Try partial functions-

    from functools import partial
    

    with the connect signal, pass the QWidget name:

    self.connect(self.checkbox_color, QtCore.SIGNAL('stateChanged(int)'), partial(self.lock_choice, self.combo_color))
    

    And in your method, you may add an argument.

    We'll add an argument in the method like below, to handle the QWidget(QComboBox) that we'll pass through the partial function above.

    def lock_choice(self, combos):
        if combos.isEnabled(True):
            combos.setEnabled(False)
            print 'Choice {choice} locked'.format(choice=combos.currentText())