Improving the RadioButtons in PyQt5

James Cooper
4 min readSep 19, 2020

Subclassing QRadioButtons gives you a slicker interface.

PyQt5 is an alternative GUI interface for Python that some choose over Tkinter. It has a number of advantages, notably built-in Tooltips. Coding for PyQt is in general as easy or easier than in tkinter, but there are some quirks.

One place where you might find it more troublesome is in the way that it handles Radiobuttons. So, in this article, we show you how to make the QRadioButton class a little friendlier.

Now, the idea of a Radiobutton is that you can only select one button, just like old car radios. This interface is now usually provided on some screen in your car, rather than by actual push buttons. But the idea is that if you pick one, any other selected button is unselected.

If you have more than one group of Radiobuttons on a page, you want to find a way to group them so that clicking on a member of one group doesn’t affect the other group. In Tkinter, you do this by associating all the members of one group with a single external variable. Then, whatever button you click changes the value of that variable. So, if you have three buttons, the variable might take on the value 0, 1 or 2.

In Qt5, you group the variables by putting them inside a frame or Groupbox. And how do you find out which on was clicked? You have to run through them all to look for which one’s isChecked() status is true. Now, if there are only two buttons this is simple: you only need to check one button. If its status is false, then the other one must be true.

But what if you have six or more buttons like in this interface for storing cast member roles in an operetta?

Figure 1: Six QRadioButtons in a display

In the tkinter approach, you just take the value of that external variable. In PyQt5, you would have to run through them individually or put them in a List and run through that.

But here is where we have a cooler solution. The QRadioButton is a first-class object and you can create derived classes from it really easily. So, all we need to do, is create a RlRadioButton derived from QRadiobutton which contains an index value for each instance of the button.

So, we could write the following , assigning an index to each button:

Lead = RlRadioButton(“Lead”, 0)
MinorLead = RlRadioButton(“Minor lead”, 1)

and so forth. We can then keep the index of the each button in an instance variable: self.index.

class RlRadioButton(QRadioButton):
clickIndex = 0 # key of last button selected stored here

def __init__(self, label, index):
super().__init__(label)
self.index= index

Note that the variable clickIndex is a class-level variable There is only one copy of this variable, shared by all six instances of the RlRadioButton class. But how does this variable get set?

It gets set when you click on that RadioButton. We connect the click event for each button to the same method within the button class.

self.toggled.connect(self.onClicked) #connect toggled to onClicked

The toggled event occurs whenever you click on a button.You get an event from the button you click on AND from the button which becomes deselected. So, you must check to see whether the button is selected. If it is selected, this method copies the index of that button in that instance into the class variable clickIndex.

#store index of selected button in class variable
def onClicked(self):
radio = self.sender()
if radio.isChecked(): #if it is checked, store that index
RlRadioButton.clickIndex = radio.getIndex()

So, what is happening is that there are six instances of RlRadiobutton, one for each button. Each instance has a different index number, and if the button for that instance is clicked, it copies its index into the class variable clickIndex they all hold in common. Then, to find out which was selected you simply check the variable RlRadioButton.clickIndex from anywhere in the program.

We illustrate these instances of the RlRadioButton below, where button 1 was selected.

Figure 2: Three RlRadioButton class instances share access to the class variable clickIndex

Figure 2 shows that while there are three instances of RlRadioButton with three different indexes, there is only one copy of clickIndex that all instances of the RlRadioButton class share.

In Figure 1, you can click on the Status button to see which Radiobutton was selected. The program then fetches that value from RlRadioButton.clickIndex and displays it in a message box using this somewhat verbose message box code:

msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Role index: "
+ str(RlRadioButton.clickIndex))
msg.setWindowTitle("Status")
msg.setStandardButtons(QMessageBox.Ok )
retval = msg.exec_()

and displays the result in that message box.

So, to conclude, the best way to query a large list of QRadioButtons is by deriving a class which can save the current index and asking the class for the index of the last selected button. And within that class you create a connection to the toggled event, which is called whenever a button is selected or deselected.

--

--

James Cooper

is the author of “Python Programming with Design Patterns” and 20 previous books.