2
\$\begingroup\$

I happened to delve into tkinter and created a couple of mini projects so I could get more familiar with it. I was wondering if there was any portion of my code or code structure that is sloppily written and could be more python-esque. As a beginner in both python and tkinter (been learning during this quarantine) it would be great if you fellow coders would be willing to give me some pointers about my code which will benefit me in the future (breaking bad habits now rather than later, creating good habits instead). Thanks!

from tkinter import *

window = Tk()
window.title("Basic Converter")

window.geometry("500x300+500+350")

#globally declare measurement variables
measurement1 = ""
measurement2 = ""

def convert_SI(val, unit_in, unit_out):     #based on unitconverters.net
    SI = {'Meter':1, 'Kilometer':1000, 'Centimeter':0.01, 'Millimeter':0.001,
          'Micrometer':0.000001, 'Mile':1609.35, 'Yard':0.9144, 'Foot':0.3048,
          'Inch':0.0254}
    return val*SI[unit_in]/SI[unit_out]

def helpsection():
    pass    #put helpful info text here (e.g. no entering in right entry box else error)

def selectedInput():
    global measurement1
    measurement1 = listbox.get(listbox.curselection())#whatever is currently selected

def selectedOutput():
    global measurement2
    measurement2 = listbox1.get(listbox1.curselection()) #whatever is currently selected

def converter():
    try:
        global measurement1, measurement2
        result.set(str(convert_SI(float(inputEntry.get()), measurement1, measurement2)))

    except:
        result.set("Error")

title = Label(window, text="Basic Converter", font="Calibri 16")
title.grid(columnspan=3)
result = StringVar()    #initalize dispalyed output variable
#create a top-level menu
filemenu = Menu(window)
filemenu.add_command(label='Help', command=helpsection)
window.config(menu=filemenu)    #displays menu
#input and output entry fields
inputEntry = Entry(window)
inputEntry.grid(row=1, column=0)
arrow = Label(window, text="--->", font="Calibri 20").grid(row=1, column=1)
outputEntry = Entry(window, textvariable=result).grid(row=1, column=2)

convertButton = Button(window, text='Convert!', command=converter).grid(row=2, column=1)

#if nonetype error, because .grid needs to be called on their own line since it always returns None
scrollbar = Scrollbar(window)   #left scrollbar
scrollbar.grid(row=2, column=0, sticky = NE + SE)
listbox = Listbox(window, exportselection=False)   #left listbox
#exportselection option in order to select 2 different listbox at same time
listbox.grid(row=2, column=0)

measurement_list = ['Meter', 'Kilometer', 'Centimeter', 'Millimeter',
                    'Micrometer', 'Mile', 'Yard', 'Foot', 'Inch']
for measurement in measurement_list:
    listbox.insert(END, measurement)
listbox.bind("<<ListboxSelect>>", lambda x: selectedInput())   #this instead of command= option
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)


scrollbar1 = Scrollbar(window)   #right scrollbar
scrollbar1.grid(row=2, column=2, sticky = NE + SE)   #add sticky if scrollbar not showing
listbox1 = Listbox(window, exportselection=False)   #right listbox
listbox1.grid(row=2, column=2)

for measurement in measurement_list:
    listbox1.insert(END, measurement)
listbox1.bind("<<ListboxSelect>>", lambda x: selectedOutput())
listbox1.config(yscrollcommand=scrollbar1.set)
scrollbar1.config(command=listbox1.yview)

#configure grid layout to adjust whenever window dimensions change
for i in range(3):
    window.grid_rowconfigure(i, weight=1)
    window.grid_columnconfigure(i, weight=1)


window.mainloop()


\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Don't use wildcard imports.

Import tkinter with import tkinter as tk, and then prefix all of your calls to tkinter classes with tk.. For example:

import tkinter as tk

window = tk.Tk()
...
title = tk.Label(window, text="Basic Converter", font="Calibri 16")
filemenu = tk.Menu(window)

...

PEP8 is a set of guidelines that most python programmers follow, and it recommends against wildcard imports.

Don't call grid or pack inline

In python, x().y() always returns the value of y(). In tkinter, grid, pac, and place all return None. Thus, when you do this:

arrow = Label(window, text="--->", font="Calibri 20").grid(row=1, column=1)

... all you're doing is setting arrow to None, which renders arrow useless. Plus, doing it inline (or worse, sometimes in line and sometimes now) makes the code hard to read.

Which leads us to the next point...

Separate layout from widget creation.

I see this mistake all the time: create a widget, call grid, create a widget, call grid, ... This results in code that is hard to read, and makes it hard to visualize the layout. If you have to change the layout, such as changing the order of two rows, it's easier if those two rows are together in the code rather than intermixed with other code.

As a rule of thumb, create all widgets within a given group together, and lay them out together.

For example:

title = Label(window, text="Basic Converter", font="Calibri 16")
inputEntry = Entry(window)
arrow = Label(window, text="--->", font="Calibri 20")
convertButton = Button(window, text='Convert!', command=converter)
outputEntry = Entry(window, textvariable=result)
scrollbar = Scrollbar(window)   #left scrollbar
...
title.grid(columnspan=3)
inputEntry.grid(row=1, column=0)
arrow.grid(row=1, column=1)
convertButton..grid(row=2, column=1)
outputEntry.grid(row=1, column=2)
scrollbar.grid(row=2, column=0, sticky = NE + SE)
...

In this specific example everything is in the root window so it doesn't matter quite so much, but if you used multiple frames for organizing your widgets this would make it much easier to see which widgets were grouped together.

Make your UI responsive

When I resized your window, the layout didn't respond very well. This is what I see when I resize the program; notice how the scrollbars are the wrong size, the listboxes didn't expand, etc.

screenshot

Tkinter makes this easy to do, but you have to use the options tkinter gives you. Think about what should happen when the user resizes the program. This becomes much easier when you group all of your calls to grid together.

\$\endgroup\$
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.