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()
1 Answer 1
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.
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.
-
\$\begingroup\$ thanks! I was just looking at your prior post on OOP structuring for tkinter: stackoverflow.com/questions/17466561/… \$\endgroup\$Justin– Justin2020年04月02日 21:23:55 +00:00Commented Apr 2, 2020 at 21:23