No. 3
The above program simply allows you to enter two numbers, and when you click the Add button, it adds them together and displays the sum on the bottom line. While this is admittedly still a simple program, it illustrates a number of things that you can use in more elaborate programs.
First of all, it is essentially a five-row display. The top informational label, the button and the Sum label are single elements in a row. And the two entry field rows consist of two elements: a label and an Entry field.
This is an ideal place to use the grid layout in tkinter. There are 5 rows, and in two cases the rows are divided into two columns, a label and an entry field. So what we do is create a 5 row by two column grid, and for the single element rows we tell the layout manager that the rows span two columns. For example:
topLabel = BlueLabel(root,
"Enter 2 values:"
)
topLabel.grid(row=0, column=0, columnspan=2
)
And the BlueLabel class is derived from the Label class and makes blue labels, left justified. It is merely
class BlueLabel(Label):
def __init__(self,root, lbtext, **kwargs):
super().__init__(root, text=lbtext, justify=LEFT, fg='blue' )
So the entire layout section of this program is just five lines of grid entries. We’ll come back to this in a minute.
When you consider what the program does, you enter numbers in the two entry fields and click on the Add button. The program then fetches the text from the two fields, converts them to numbers, adds then, and places message and the sum in the label on the bottom line. This is an interaction between four different visual widgets: the button, two entry fields, and a label. Some part of the program needs to know about those widgets and get or set data from them or to them.
That part of the program is a Mediator class. The individual widgets should not have to know about all these interactions. Instead, they simply tell the Mediator they exist and have the Mediator deal with the interactions. This greatly simplifies program flow.
The Grid Layout
So when we create the grid, we create the Mediator and tell it about the entry fields and bottom label. When we create the button, we pass it a reference to the Mediator, so it can tell it when it is clicked. The whole thing is just a few lines of code.
topLabel = BlueLabel(root, "Enter 2 values:")
topLabel.grid(row=0, column=0, columnspan=2)
# create entry field
xlabel = BlueLabel(root, "Num1:").grid(row=1, column=0)
xEntry = Entry(root)
xEntry.grid(row=1, column=1, padx=10) # and put in window
ylabel = BlueLabel(root, "Num2:").grid(row=2, column=0)
yEntry = Entry(root)
yEntry.grid(row=2, column=1, padx=10, pady=10)
med = Mediator()
# OK button calls getName when clicked
self.addButton = AddButton(root, med)
self.addButton.grid(row=3, column=0, columnspan=2)
# This is the label whose text changes
botLabel = BlueLabel(root, 'Sum')
botLabel.grid(row=4, column=0, columnspan=2)
med.setLabels(topLabel, botLabel)
med.setFields(xEntry, yEntry)
The Add Button
You will note that we create an AddButton. This simple class is derived from the DButton class in the previous article and directs a button click to its own comd method, which just tells the Mediator that it has been clicked:
# Button displays OK
class AddButton(DButton):
def __init__(self, master, med, **kwargs):
super().__init__(master, text="Add", **kwargs)
self.med = med
def comd(self):
self.med.addClicked()
The Mediator
So what does the mediator do? It does the addition when it gets the addClicked call. It sort of looks like this:
class Mediator():
def setFields(self, xentry, yentry):
self.xEntry = xentry
self.yEntry = yentry
def setLabels(self, top, bottom):
self.topLabel = top
self.botLabel = bottom
def addClicked(self):
xval = float(self.xEntry.get())
yval = float(self.yEntry.get())
self.botLabel.configure(text="Sum = " + str(xval + yval))
In fact, it looks almost exactly like that, except that then we ask. “Oh, gosh, what happens if we didn’t enter a number in one of those entry fields?”
In that case, the conversion to a float will fail and Python will throw a ValueError exception. So, what we really have to do is catch that exception and issue an error message in a Messagebox. The addClicked method then is just a little longer:
def addClicked(self):
try:
xval = float(self.xEntry.get())
yval = float(self.yEntry.get())
self.botLabel.configure(text="Sum = " + str(xval + yval))
except ValueError:
messagebox.showerror("Conversion error",
"Not numbers")
This is the messagebox we display when you enter illegal characters.
Conclusion
And that is the whole program. We have an elegant little interface, and a program that won’t crash. And, as before
All code is available in GitHub under jameswcooper/newsletter