Csci 1020 January 18, 2023 Program Assignment 2

Write a Graphical User Interface (GUI) program in Python which performs a simple mathematical feat. You can repeat the numerical problem in the P1 assignment, or create a new short mathematical problem, but here you will use a GUI.

  1. Read this entire page first.
  2. Use the template tk.py below in the discussion. Modify it for your own problem--not the current problem which finds area from entered length and width. Feel free to copy and paste pieces of code. There is a piece of sample code for each of the following padding, cursor, and image options.
  3. Create padding within entry boxes and labels to make a more pleasing. layout.
  4. Change the cursor over at least one button.
  5. Create an image to replace the text in at least one button.
  6. Create an image to display somewhere on the window, top or bottom.
  7. You can, optionally, move the buttons to the bottom of the window grid. In fact, you can move the widgets anywhere you wish on the grid. Draw a grid and decide where they should go, row and column.
  8. Show me your Python GUI p2 program(s), or email them to me at: ted@fdltcc.edu

Source code and images for all samples are here: src/

NOTE: You are creating one program, p2.py, with all the GUI options. It can be helpful to put in one option at a time, and run the program from IDLE after each change.

Tkinter

This is the best online reference available for Tkinter:

https://docs.python.org/3/library/tk.html

Python is often said to come with "batteries included". Compared to other languages, the standard Python installation includes much more, and enough to build interesting applications. Tkinter comes stock in most Python installations--and yours.

You are probably more used to GUI applications than anything else. When you start a GUI application, it waits for you to do something. This is an "event style" application, and events are button presses, text entries, click and drag a slider ... things like that.

The downside of GUI applications is that the code is lengthier, and it looks more complicated at first glance. Yet, it doesn't get much more complicated than what you see in this first program, and most items in a GUI can be copied and pasted along with a few minor modifications to get the kind of look and behavior needed.

The upsides of GUIs are high, though, so I decided it is a good idea to bring them in early. It is interesting to experiment with GUIs and tweak the appearance.

Your strategy with using Tkinter and writing GUIs should be the same as that used by experienced programmers. Hardly anyone can remember everything there is to know about GUIs, nor even Python. Experienced people use templates. Templates are working examples of code. Start with a template, in this case, with tk.py which you can copy to you own machine, open in the IDLE editor, and run. Here it is below:

Tkinter template for p2: tk.py

tk.py

Clicking on the link above should display tk.py as plain text in your browser window, and you can save it directly to your disk as a file, or you can copy the text from the browser and paste it within the IDLE editor then save it as tk.py or another file name ending with .py .

tk.py contains things which you will have in every Tkinter GUI, so we will look at it in detail. Here is the source code below (colors are for the discussion, not from IDLE editor syntax highlighting):


# simple Tkinter GUI - Wetherbee - January 18, 2023
# This program calculates the area of a rectangle.

from tkinter import *
# -------------------------------------------------------------
W = Tk() # create the window where everything is placed

# create and place a quit button
def quitter():  # our quit function to call by pressing B_quit
   W.destroy()
   sys.exit()
   
B_quit = Button(W,text='Quit',command=quitter,bg="red") # create a button
B_quit.grid(row=0,column=0)                             # place Button at 0,0

# entry for the length
L1 = Label(W,text="Length")    # create a Label
L1.grid(row=1,column=0)        # place label at 1,0
v1 = StringVar()               # entry str variable
E1 = Entry(W, textvariable=v1) # create an Entry box
E1.grid(row=1,column=1)        # place Entry at 1,1

# entry for the width
L2 = Label(W,text="Width")     # create a Label
L2.grid(row=2,column=0)        # place label at 2,0
v2 = StringVar()               # entry str variable
E2 = Entry(W, textvariable=v2) # create an Entry box
E2.grid(row=2,column=1)        # place Entry at 2,1

# entry for the area
L3 = Label(W,text="Area")      # create a Label
L3.grid(row=3,column=0)        # place label at 3,0
v3 = StringVar()               # entry str variable
E3 = Entry(W, textvariable=v3) # create an Entry box
E3.grid(row=3,column=1)        # place Entry at 3,1

# create and place a calculate button
def calculate():  # our quit function to call by pressing B_quit
   length = float( v1.get() ) # get the length v1 in the E1 Entry box, convert to a float
   width  = float( v2.get() ) # get the width v2 in the E2 Entry box, convert to a float
   area = length * width      # calculate area
   v3.set( str(area) )        # place area value in the E3 Entry box using v3
   
B_calc = Button(W,text='Calc',command=calculate,bg="green") # create a button
B_calc.grid(row=0,column=1)                                 # place Button at 0,1

# --------------------------------------------------------------
mainloop() # hangs here forever, or until Quit button is pressed

If you run it from IDLE, the GUI starts and looks like this:

You might have to hunt for it because it is a tiny window which appears somewhere on your desktop. You must keep the IDLE windows open while tk.py is running. You enter numbers to the right of Length and Width. When the Calc button is pressed, the area is calculated (area = length*width) then displayed to the right of Area. To exit this GUI, click the Quit button.

Type numbers in the entry boxes after Length and Width, then press Calc to calculate the result--which should show up after Area. (You will see errors displayed in the IDLE interactive console if the Lentry boxes are blank or do not contain numbers.)

There are 8 widgets on this example: B_quit, L1, E1, L2, E2, L3, E3, B_calc. Each of these widgets is placed on the window W. This GUI code is almost entirely about defining each widget and placing each of them within the window W.

Your Tkinter GUI programs will always look something like this:


    from tkinter import * # load Tkinter library
    W = Tk()              #  create the window W
     -- code for the GUI goes within here -- 
    mainloop()            # hangs here forever

Detailed Discussion of tk.py

from tkinter import *
Every Tkinter GUI will have this line. This loads the Tkinter library code, and These lines should usually be placed at beginning of the program.
W = Tk()
This creates a window W. Each widget is created for a specific window, and that window is W in this case.
def quitter():
    W.destroy()
    sys.exit()
This is a function definition. We will cover functions in more detail next week. The function name is quitter right after def, and the function name is always followed by ():. There can be function arguments between the parentheses, but there are none for quitter in this case. Note that the statements within the function are indented. Statements indented after the : are executed by calling the function quitter.
B_quit = Button(W,text='Quit',command=quitter,bg="red")
B_quit is a type Button widget, the text 'Quit' shows on the button, the function quitter is called when the button is clicked, and the background (bg) is set to be red.
B_quit.grid(row=0,column=0)
The button B_quit is placed on the window W at row=0 and column=0 by using grid geometry placement.

Here is how the grid placement looks.

There are 8 widgets to place on window W using grid: B_quit, L1, E1, L2, E2, L3, E3, B_calc. We simply decide where they should go, then define row and column by number to place it there using the .grid(row=__,column=__) command tasked onto the widget name. Note in the code that each of the 8 widgets is placed on the window W by using a row and column number.

L1 = Label(W,text="Length")
L1 is a Label, just plain text "Length".
L1.grid(row=1,column=0)
L1 is placed at row=1 and column=0.
v1 = StringVar()
For Entry boxes, we need a special StringVar() variable to hold the values. We can get the value in the entry box with v1.get(), and set the value in the entry box with v1.set(Value) where Value is some string.
E1 = Entry(W, textvariable=v1)
E1 is an Entry widget, and the contents of E1 are set and gotten using the variable v1 defined beforehand.
E1.grid(row=1,column=1)
We place E1 on the window W as we did for the label L1.

Labels and entry boxes L2, E2, L3, and E3 are created almost identically as for L1 and E1, just labeled differently, using different variables, and placed in different locations.

def calculate():
   length = float( v1.get() ) # get the length v1 in the E1 Entry box, convert to a float
   width  = float( v2.get() ) # get the width v2 in the E2 Entry box, convert to a float
   area = length * width      # calculate area
   v3.set( str(area) )        # place area value in the E3 Entry box using v3
When the B_calc button is pressed, this calculate function is called. The variables length and width above are local. v1.get() is the string value in the E1 entry box. float( v1.get() ) converts the string v1.get() to a number, a float type number in this case allowing a decimal point. The local variables length and width are each assigned to a float type. The area is calculated: area = length * width. The result, area, is converted to a str type with str(area), so v3.set( str(area) ) puts the result into the entry box E3 so we see it.
B_calc = Button(W,text='Calc',command=calculate,bg="green")
The B_calc button is defined, displays 'Calc' with a gree background, and it calls the calculate unction when clicked.
B_calc.grid(row=0,column=1)
We place the B_calc button on the window W using grid. Note that the widgets do not need to be placed on the window in any particular order using grid placement. There are other placement methods, such as pack, but grid is probably easier to use.
mainloop()
This is the last statement in a Tkinter GUI. Program execution of statements during loading stops with this one. Further action by the GUI program depends on user events; these events are button clicks and entering data in entry boxes for this GUI.

Sample: tk_grid_padx_pady.py

You can create padding around a widget by entering x and y padding values in pixels.

     L1.grid(row=1,column=0,padx=10,pady=10)

Every widget (L1,L2,L3,E1,E2,E3,B_calc,B_quit) is modified with padx=10,pady=10 arguments when the widget is created, this making a 10 pixel pad around each in the x and y directions.

Sample: tk_grid_ipadx_ipady.py

You can make a button larger with ipadx and ipady arguments.

     B_quit.grid(row=0,column=0,ipadx=10,ipady=10)

The two buttons, B_calc and B_quit, are modified in this sample code.

Sample: tk_window_bg.py

You can make a color background for the window.

     W.configure(bg="#CCCCFF")

The CC99FF is a red-green-blue hexadecimal number (base 16). The smallest possible value is 00, and the largest possible value is FF. CC=red, 99=green, and FF=blue in this "#CC99FF" color. You can also use well known color names like "green" and "red".

Sample: tk_cursors.py

You can change the cursor when it appears over a button and some other widgets.

     B_quit = Button(W,text='Quit',command=quitter,bg="red",cursor="gobbler")

A tcl/tk manual page shows many options: cursors

Use the name to the right of the cursor image. Above it is gobbler, a turkey which the cursor becomes when it is over the quit button.

Sample: tk_image.py

You can place images in a window by putting an image into a Label instead of text. The image must be type .gif or .ppm, so likely .gif . You may have to experiment with sizes in pixels of the image, Width x Height in pixels.

     L4_image = PhotoImage(file="logo.gif")
     L4 = Label(W,image=L4_image)
     L4.grid(row=4,column=0,columnspan=2)

The L4_image is the tkinter image, and logo.gif is the file name. The label L4 is create with the L4_image after the image is loaded, then the label is placed on the grid. Note the columnspan=2 argument which makes the label span column 0 and 1.

Sample: tk_button_image.py

You can also put an image on a button instead of plain text. Here is the .gif image placed on the button.

     B_calc = Button(W,command=calculate,bg="green") # create a button
     imageCalc = PhotoImage(file="calculate.gif")    # load image
     B_calc.config(image=imageCalc)                  # put image on button
     B_calc.grid(row=0,column=1)                     # place Button at 0,1

Content is neither approved nor reviewed by FDLTCC.