PyQt Radio Buttons and the Mediator Pattern
In our previous article, we showed how to derive a new class from QRadioButton that keeps the index value of that button and keeps the click event callback right in the class itself.
In this simple article we are going to look at an easy program that fills an entry field with a text string based on which radio button you select. We also introduce you to the Mediator Design Pattern to help communication between widgets.
We came up with this little trick while writing a program to create a cast list for our operetta company. Leads play named roles, but for chorus member we just store their voice part. The radio buttons are labeled S, A, T and B, and when you click on one of them, the entry field is filled with the full name of that voice part: Soprano, Alto, Tenor and Bass. Since the entry field is editable, you can add more text like “2nd Tenor” where this might be useful, and that name is stored as part of the last list.
Using a Mediator
When you click on any of these RadioButtons, the long name is copied into the entry field. The question is, how does it get there?
In effect, each of the four button instances need to communicate with the entry field. And, while that is not difficult to achieve, it doesn’t scale very well as your program grows to include more visual widgets. To simplify this problem, you use the Mediator Design Pattern and the Mediator class.
Rather than the various controls all sending information to each other, the Mediator class becomes the traffic cop that receives the various button clicks and other widget actions. Then, it knows about the other controls theymight want to communicate with and passes the click information on to them. So, when we create instances of the derived vcRadioButton, you pass it the label it displays, the long name it is to send on to the entry field and a reference to the Mediator class:
You first create the Mediator and give it a reference to the entry field (which is called QLineEdit in PyQt5):
# create the entry field
self.entry = QLineEdit()
grid.addWidget(self.entry,0,0)# Create Mediator and
# and pass it the entry reference
self.med = Mediator()
self.med.setEntry(self.entry)# Create a GroupBox for the four radio buttons
self.voiceBox = QGroupBox("Voice part")
vcGrid = QGridLayout()
Then you create the buttons and pass each of them a reference to the Mediator
# create the four radio buttons and labels
vs = VcRadioButton(“S”, “Soprano”, self.med)
va = VcRadioButton(“A”, “Alto”, self.med)
vt = VcRadioButton(“T”, “Tenor”, self.med)
vb = VcRadioButton(“B”, “Bass”, self.med)# and add them to the 2 x 2 grid
vcGrid.addWidget(vs, 0, 0)
vcGrid.addWidget(va, 0, 1)
vcGrid.addWidget(vt, 1, 0)
vcGrid.addWidget(vb, 1, 1)
Then we add the vcGrid to the GroupBox layout, and the GroupBox to the outer grid.
grid.addWidget(self.voiceBox, 1, 0)
The VcRadioButton class
Our actual VcRadioButton class is even simpler than the one we wrote for the six radio button example, because we don’t have to store anything in a class variable: we just tell the Mediator that the button has been clicked.
def __init__(self, label, title, med):
self.title = title #save the title
self.med = med # and copy the Mediator reference # connect the button clicks to the comd method
self.toggled.connect(self.comd) # call the same comd method # returns title stored in this class instance
return self.title # gets the title and puts it in the character entry field
# using the Mediator
radio = self.sender() #get the button you clicked
if radio.isChecked(): # if checked copy the title
Note that the VcRadioButton connects the click event (called toggled in Qt5) to the comd method right there in the same class. And that comd method tells the Mediator to set the voice part into the entry field.
Then finally our Mediator is simple, since we are only mediating connections between radio buttons and the entry field. When the radio button is clicked, it calls the setVoice method in the Mediator, which copies the text into the entry field.
# The Mediator saves a reference to the entry field
# and copies text into the field when setVoice is calledclass Mediator():
# save the entry field reference
def setEntry(self, entry):
self.entry = entry # copy the text into the entry field
def setVoice(self, text):
This may seem like a lot of running around to copy text into an entry field, but the Mediator quickly becomes quite important when your program needs to handle interactions among a number of widgets, such as list boxes, push buttons and check boxes. It is probably the most useful and significant of the 23 Design Patterns when you are writing user interfaces.
Code for this program can be found on Github at jameswcooper/articles/pyqt5