Tag: GUI

Entering data in Python: console or GUI

Entering data in Python: console or GUI

Suppose you want the user to enter some data for a Python program to work on. For this, Python provides the input statement, which can print out a prompting string and wait for keyboard input. 

name = input("What is your name? ")
print("Hi "+name +" boy!")

This little program asks for your name and waits for you to type a string and press Enter. The input statement accepts keyboard input and returns a string. So, the resulting console results might look like this:

What is your name? Jim
Hi Jim boy!

Heinlein fans may recognize the reference.

You can paste that 2-line program into any development environment, such as Thonny, PyCharm or even Jupyter Notebook and see it work right away.

Adding two numbers

Of course, you can enter numbers as well, but you must be sure to convert the resulting strings into an int or a float. Here we use the more general float.

x = float(input("Enter x: "))
y = float(input("Enter y: "))
print("The sum is: ", x+y)

The resulting output is:

Enter x: 23.45|
Enter y: 41.46
The sum is:  64.91

Of course, this naïve program expects only legal input. If you enter, say, “qq” instead of 22, you will get a Python error:

File "C:\Users\James\PycharmProjects\input\inputdemo.py", line 7, in <module>    
y = float(input("Enter y: "))
ValueError: could not convert string to float: 'qq'

There are ways to check for this, of course, such as catching Exceptions, and we will explain those in the following example.

However, you will only find the input statement in rudimentary examples. Most programs that need user input get it from a windowing interface. We’ll show these same examples next using the tkinter GUI library.

Making a windowing example program

You can do much the same thing by using the Entry field in the tkinter GUI (graphical user interface). Here we repeat those examples in simple tkinter windows.  To use tkinter, you have to import that library into your program:

from tkinter import *
import tkinter as tk

You also create an abbreviation for tkinter, naming it just tk.

Then, most of the setup code creates the visual widgets and their arrangement. In the following example, we use an Entry field, a Button and two Labels. You start by getting the window system pointer tk.Tk() and use it to attach the widgets to. After you create this objects, you run the mainloop() function which displays the window and receives mouse and keyboard input. The window keeps running until you close it by clicking on the “X” in the upper right corner or selects some widget that causes the window to close.

Our first example above was one where you enter your name and it says Hello back, again giving homage to Heinlein in the process. The tkinter program does the same thing

The code for this program creates the two labels, the Entry field and the OK button:

def build(self):
    root = tk.Tk()
    # top label
    Label(root,
         text="""What is your name?""",
         justify=LEFT, fg='blue', pady=10, padx=20).pack()

    # create entry field
    self.nmEntry = Entry(root)
    self.nmEntry.pack()   # and put it into the window

    # OK button calls getName when clicked
    self.okButton = Button(root, text="OK",
                            command=self.getName )
    self.okButton.pack()

    # This is the label whose text changes
    self.cLabel = Label(root, text='name', fg='blue')
    self.cLabel.pack()
    mainloop()      #run loop until window closes

When you click on the OK button, it calls the getName method, which fetches the text from the entry field and inserts it into the bottom label text.

# gets the entry field text
# places it on the cLabel text field
def getName(self):
   newName = self.nmEntry.get()
   self.cLabel.configure(text=“Hi “+newName+” boy!”)

Adding two numbers

And our second example rewritten from above reads two Entry fields, converts them to floats and puts the sum in the lower label.

 The code for this window is much the same, except that we fetch and add two numbers. Here is the function the OK button calls.

xval= float(self.xEntry.get())
yval = float(self.yEntry.get())
self.cLabel.configure(text="Sum = "+str(xval+yval))

Catching the error

However, if you enter some illegal non-numeric value, you can catch the exception and issue a error message:

try:
    xval= float(self.xEntry.get())
    yval = float(self.yEntry.get())
    self.cLabel.configure(text="Sum = "+str(xval+yval))
except:
    messagebox.showerror("Conversion error",
                              "Not numbers")

If you enter non-numeric strings, the program displays this message box:

And that’s all there is to creating a program with a simple graphical user interface.

PyQt Radio Buttons and the Mediator Pattern

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 the 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 you might 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
# 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 voiceBox layout, and the voiceBox to the outer grid.

self.voiceBox.setLayout(vcGrid)
grid.addWidget(self.voiceBox, 1, 0)
self.setLayout(grid)

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.

class VcRadioButton(QRadioButton):
    def __init__(self, label, title, med):
        super().__init__(label)
        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)

    # returns title stored in this class instance
    def getTitle(self):
        return self.title
    # gets the title and puts it in the character entry field
    # using the Mediator
    def comd(self):
        radio = self.sender()   #get the button you clicked
        if radio.isChecked():   # if checked copy the title
            self.med.setVoice(radio.getTitle())

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 called

class Mediator():
    # save the entry field reference
    def setEntry(self, entry):
        self.entry = entry
    # copy the text into the entry field
    def setVoice(self, text):
        self.entry.clear()
        self.entry.insert(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.