I have an example code, here just for BMI index. I would like to clear the input and output fields in the GUI. It seems that i can clear the entries, but the BMI calculation is not being removed (row 79 does not seem to have an effect) (# self.text.delete(0, 'end'))
Thanks
Emin
import math
from Tkinter import *
class Application(Frame):
"""A GUI application with three buttons"""
def __init__(self, master):
"""Initialize the Frame"""
Frame.__init__(self,master)
self.grid()
self.create_widgets()
self.title = Label(self, text = "BMI index calculation")
self.title.grid(row = 0, column = 0, columnspan = 2 , sticky =W)
def create_widgets(self):
"""Create button, text and entry widgets"""
self.name = Label(self, text = "What is your name?")
self.name.grid(row = 1, column = 0, columnspan = 2 , sticky =W)
self.name_io = Entry(self)
self.name_io.grid(row = 1, column =2, sticky = W)
self.age = Label(self, text = "How old are you?")
self.age.grid(row = 2, column = 0, columnspan = 2 , sticky =W)
self.age_io = Entry(self)
self.age_io.grid(row = 2, column =2, sticky = W)
self.height = Label(self, text = "How tall are you?")
self.height.grid(row = 3, column = 0, columnspan = 2 , sticky =W)
self.height_io = Entry(self)
self.height_io.grid(row = 3, column =2, sticky = W)
self.weight = Label(self, text = "How much do you weigh in kg?")
self.weight.grid(row = 4, column = 0, columnspan = 2 , sticky =W)
self.weight_io = Entry(self)
self.weight_io.grid(row = 4, column =2, sticky = W)
self.submit_button = Button(self, text = "Calculate", command = self.reveal)
self.submit_button.grid(row = 5, column = 0, sticky = W)
self.text = Text(self, width = 40, height = 5, wrap = WORD)
self.text.grid(row = 6, column = 0, columnspan = 3, sticky = W)
self.clear_button = Button(self, text = "Clear", command = self.clear_text)
self.clear_button.grid(row = 7, column = 0, sticky = W)
def reveal(self):
"""Display message based on the password typed in"""
content_name = self.name_io.get()
content_age = float(self.age_io.get())
content_height = float(self.height_io.get())
content_weight = float(self.weight_io.get())
BMI = round((content_weight/(content_height/100)**2.),1)
underBMI = 18.5
NormalBMI = 24.9
OverweightBMI = 29.9
ObesityBMI = 30
if BMI <= underBMI:
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are underweight, so you need to eat!"
elif (BMI > underBMI) and (BMI <= NormalBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "your BMI is Normal"
elif (BMI > NormalBMI) and (BMI <= OverweightBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are Overweight - need to exercise!"
elif (BMI > OverweightBMI):
message = content_name + ", " + "your BMI index is" + " " + str(BMI) + ", " + "you are in Obesity"
self.text.insert(0.0, message)
def clear_text(self):
self.name_io.delete(0, 'end')
self.age_io.delete(0, 'end')
self.height_io.delete(0, 'end')
self.weight_io.delete(0, 'end')
# self.text.delete(0, 'end')
root = Tk()
root.title("BMI Index")
root.geometry("600x350")
app = Application(root)
root.mainloop ()
The problem is that you're giving an index that is 0.0. Text widget indexes are a string of the form line.column but you're giving it a floating point number.
The proper index for the first character is the string "1.0".
self.text.delete("1.0", 'end')
Simply
your_entry.delete(0,END)
Related
Im very new to python and im currently trying to create gui for a program that calculates the cost of parking. When running the code there are no errors but the program doesn't produce a label with the calculated cost. I also want it to produce a message asking for a valid input code if the letters C,D or V are not entered into the entry boxes. Any help in where im going wrong would be appreciated! I know the layout doesn't look good at the moment i'm going to go back and fix the placement of labels afterwards its more getting core basics of the code working that i'm struggling with.
-- coding: cp1252 --
import Tkinter as tk
import re
from functools import partial
def Vehicle(label_result, n1, n2):
Vehicle = Vehicletypeentrytext.get()
Vehicle_upper = Vehicle.upper()
if not re.match("^[C,V,D]*$", Vehicle_upper):
label_result.config(text="Please input valid vehicle code")
return
elif Vehicle_upper=='C':
def carcost(label_result, n1, n2):
num1 = (n1.get())
num2 = (n2.get())
result = (int(num2)-int(num1))+1
label_result.config(text="Cost for Parking is %d" % result)
return
elif Vehicle_upper=='V':
def vancost(label_result, n1, n2):
num1 = (n1.get())
num2 = (n2.get())
result = (int(num1)-int(num2))+2
label_result.config(text="Cost for Parking is%d" % result)
return
elif Vehicle_upper=='D':
def disabledcost(label_result, n1, n2):
num1 = (n1.get())
num2 = (n2.get())
result = (int(num1)-int(num2))
label_result.config(text="Cost for Parking is%d" % result)
return
root = tk.Tk()
root.geometry('800x600')
root.title('Simple Calculator')
number1 = tk.StringVar()
number2 = tk.StringVar()
Title = tk.Label(root, text="SHORTSTAY CARPARK PAYMENT").grid(row=0, column=2)
Price = tk.Label(root, text = "1 hour = $2.00 / $3.00 / $1.00\n2 hour = $3.00 / $4.00 / $2.00\n3 hour = $4.00 / $5.00 / $3.00\n4 hour = $5.00 / $6.00 / $4.00\n").grid(row=1, column=0)
Vehicletypeentrytext = tk.StringVar()
Vehicletypelabel = tk.Label(root, text = 'Enter your Vehicle type:\nC for Car\tV for Van\tD for Disabled driver').grid(row=2, column=3)
Vehicletypeentry = tk.Entry(root, textvariable=Vehicletypeentrytext).grid(row=4, column=3)
labelNum1 = tk.Label(root, text="Enter Time in:").grid(row=5, column=0)
labelNum2 = tk.Label(root, text="Enter Time Out:").grid(row=6, column=0)
entryNum1 = tk.Entry(root, textvariable=number1).grid(row=5, column=2)
entryNum2 = tk.Entry(root, textvariable=number2).grid(row=6, column=2)
labelResult = tk.Label(root)
labelResult.grid(row=7, column=2)
Vehicle = partial(Vehicle, labelResult, number1, number2)
buttonCal = tk.Button(root, text="Calculate", command=Vehicle).grid(row=7, column=0)
root.mainloop()
You were calling functions inside def Vehicle(label_result, n1, n2) which isn't adding anything to your script so it preventing the result to display as a Label.No need to create function inside your if elif statement.check my edit on your function def Vehicle(label_result, n1, n2)
import Tkinter as tk
import re
from functools import partial
def Vehicle(label_result, n1, n2):
Vehicle = Vehicletypeentrytext.get()
Vehicle_upper = Vehicle.upper()
if not re.match("^[C,V,D]*$", Vehicle_upper):
label_result.config(text="Please input valid vehicle code")
return
elif Vehicle_upper == 'C':
num1 = (n1.get())
num2 = (n2.get())
result = (int(num2) - int(num1)) + 1
label_result.config(text="Cost for Parking is %d" % result)
return
elif Vehicle_upper == 'V':
num1 = (n1.get())
num2 = (n2.get())
result = (int(num1) - int(num2)) + 2
label_result.config(text="Cost for Parking is%d" % result)
return
elif Vehicle_upper == 'D':
num1 = (n1.get())
num2 = (n2.get())
result = (int(num1) - int(num2))
label_result.config(text="Cost for Parking is%d" % result)
return
root = tk.Tk()
root.geometry('800x600')
root.title('Simple Calculator')
number1 = tk.StringVar()
number2 = tk.StringVar()
Title = tk.Label(root, text="SHORTSTAY CARPARK PAYMENT").grid(row=0, column=2)
Price = tk.Label(root, text = "1 hour = $2.00 / $3.00 / $1.00\n2 hour = $3.00 / $4.00 / $2.00\n3 hour = $4.00 / $5.00 / $3.00\n4 hour = $5.00 / $6.00 / $4.00\n").grid(row=1, column=0)
Vehicletypeentrytext = tk.StringVar()
Vehicletypelabel = tk.Label(root, text = 'Enter your Vehicle type:\nC for Car\tV for Van\tD for Disabled driver').grid(row=2, column=3)
Vehicletypeentry = tk.Entry(root, textvariable=Vehicletypeentrytext).grid(row=4, column=3)
labelNum1 = tk.Label(root, text="Enter Time in:").grid(row=5, column=0)
labelNum2 = tk.Label(root, text="Enter Time Out:").grid(row=6, column=0)
entryNum1 = tk.Entry(root, textvariable=number1).grid(row=5, column=2)
entryNum2 = tk.Entry(root, textvariable=number2).grid(row=6, column=2)
labelResult = tk.Label(root)
labelResult.grid(row=7, column=2)
Vehicle = partial(Vehicle, labelResult, number1, number2)
buttonCal = tk.Button(root, text="Calculate", command=Vehicle).grid(row=7,
column=0)
root.mainloop()
Hi I'm trying to build a user interface and having problem with column and row positions. What I expect to see is some distance between buttons and entry widgets since I left two empty column between them. So why are they standing just next to the entry widgets and changing the distances between entry areas? Could anyone give me some help about this?
Here is the code...
from Tkinter import*
HMCC=Tk()
HMCC.title(" GUI v1.0 ")
HMCC.geometry("500x300")
entry_1 = Entry(HMCC)
entry_2 = Entry(HMCC)
entry_3 = Entry(HMCC)
entry_4 = Entry(HMCC)
entry_5 = Entry(HMCC)
entry_6 = Entry(HMCC)
entry_7 = Entry(HMCC)
entry_8 = Entry(HMCC)
entry_1.grid(row=2,column=1)
entry_2.grid(row=3,column=1)
entry_3.grid(row=4,column=1)
entry_4.grid(row=5,column=1)
entry_5.grid(row=6,column=1)
entry_6.grid(row=7,column=1)
entry_7.grid(row=8,column=1)
entry_8.grid(row=9,column=1)
Channel_1 = Label(HMCC, text = "Channel 1 : ")
Channel_2 = Label(HMCC, text = "Channel 2 : ")
Channel_3 = Label(HMCC, text = "Channel 3 : ")
Channel_4 = Label(HMCC, text = "Channel 4 : ")
Channel_5 = Label(HMCC, text = "Channel 5 : ")
Channel_6 = Label(HMCC, text = "Channel 6 : ")
Channel_7 = Label(HMCC, text = "Channel 7 : ")
Channel_8 = Label(HMCC, text = "Channel 8 : ")
Channel_1.grid( row = 2, column = 0, sticky = E)
Channel_2.grid( row = 3, column = 0, sticky = E)
Channel_3.grid( row = 4, column = 0, sticky = E)
Channel_4.grid( row = 5, column = 0, sticky = E)
Channel_5.grid( row = 6, column = 0, sticky = E)
Channel_6.grid( row = 7, column = 0, sticky = E)
Channel_7.grid( row = 8, column = 0, sticky = E)
Channel_8.grid( row = 9, column = 0, sticky = E)
#button1 = Button(text=" START " , fg="red" )
#button2 = Button(text=" PAUSE " , fg="blue" )
#button3 = Button(text=" STOP ", fg="green")
#button4 = Button(text="QUIT" , fg="black",command=HMCC.quit)
#button1.grid( row = 1, column = 3)
#button2.grid( row = 2, column = 3)
#button3.grid( row = 3, column = 3)
#button4.grid( row = 4, column = 3)
HMCC.mainloop()
Current view
Thanks in advance
If there is nothing in column 2, then tkinter will ignore it.
In addition to the comment posted above which contains the answer to your question, you can clean up your code significantly by just using a loop:
num_rows = 8
entries = [None]*num_rows
channels = [None]*num_rows
for i in range(num_rows):
channels[i] = Label(HMCC, text = "Channel {0} : ".format(i+1))
channels[i].grid(row=i+2,column=0,sticky=E)
entries[i] = Entry(HMCC)
entries[i].grid(row=i+2, column=1)
Better yet, use list comprehension:
num_rows = 8
entries = [Entry(HMCC).grid(row=i+2, column=1) for i in range(num_rows)]
channels = [Label(HMCC, text = "Channel {0} : ".format(i)).grid(row=i+2,column=0,sticky=E) for i in range(num_rows)]
My problem at the moment is I am trying to change a label(label 16) to the first value of entry_values[0] which isn't working I have tried passing it in as a variable and many other things, after about an hour of research I couldn't find a solution.I think the main problem is that it sets the label before the code with the entry is run so that it wont change. when I set it to a textvariable it produces an empty string (I think) but when I use just text it puts in a 0 where I expect my number.
def sub_menu(root):
global subpage
subpage = Frame(root)
button5 = Button(subpage, text="Save Generation Data",
command = lambda: save_entries())
button5.grid(row = 1, column = 6, sticky = E)
button6 = Button(subpage, text="Return To Main Page",
command = lambda: switch_page("main"))
button6.grid(row = 0, column = 6, sticky = W)
juveniles_label0 = Label(subpage,text="Juveniles")
adults_label1 = Label(subpage,text="Adults")
seniles_label2 = Label(subpage,text="Seniles")
population_label3 = Label(subpage,text="Population (Thousands)")
survival_rate_label4 = Label(subpage,text="Survival Rate (Between 0 and 1)")
birth_rate_label5 = Label(subpage,text="Birth Rate")
number_of_gens_label6 = Label(subpage,text="Number of Generations")
disease_trigger_label7 = Label(subpage,text="Disease Trigger Point")
global entry0
entry0 = Entry(subpage)
global entry1
entry1 = Entry(subpage)
global entry2
entry2 = Entry(subpage)
global entry3
entry3 = Entry(subpage)
global entry4
entry4 = Entry(subpage)
global entry5
entry5 = Entry(subpage)
global entry6
entry6 = Entry(subpage)
global entry7
entry7 = Entry(subpage)
global entry8
entry8 = Entry(subpage)
juveniles_label0.grid(row = 0, column = 1)
adults_label1.grid(row = 0, column = 2)
seniles_label2.grid(row = 0, column = 3)
population_label3.grid(row = 1, column = 0)
survival_rate_label4.grid(row = 2, column = 0)
birth_rate_label5.grid(row = 3, column = 0)
number_of_gens_label6.grid(row = 3, column = 2)
disease_trigger_label7.grid(row = 4, column = 0)
entry0.grid(row = 1, column = 1)
entry1.grid(row = 1, column = 2)
entry2.grid(row = 1, column = 3)
entry3.grid(row = 2, column = 1)
entry4.grid(row = 2, column = 2)
entry5.grid(row = 2, column = 3)
entry6.grid(row = 3, column = 1)
entry7.grid(row = 3, column = 3)
entry8.grid(row = 4, column = 1)
return subpage
def save_entries(): #entry recieve point
save_page = Frame(root)
""" if e0 < 0:
make a check to check if value is < 0 dont accept and if a value is inputed or not using if type(string_name) == str """
e0 = entry0.get()
if e0 >= 0:
entry_values[0] = (e0)
e1 = entry1.get()
if e0 >= 0:
entry_values[1] = (e1)
e2 = entry2.get()
if e0 >= 0:
entry_values[2] = (e2)
e3 = entry3.get()
if e0 >= 0:
entry_values[3] = (e3)
e4 = entry4.get()
if e0 >= 0:
entry_values[4] = (e4)
e5 = entry5.get()
if e0 >= 0:
entry_values[5] = (e5)
e6 = entry6.get()
if e0 >= 0:
entry_values[6] = (e6)
e7 = entry7.get()
if e0 >= 0:
entry_values[7] = (e7)
e8 = entry8.get()
if e0 >= 0:
entry_values[8] = (e8)
print entry_values
return save_page
def display_values(root):
sub2 = Frame(root)
global entry_values
label8 = Label(sub2, text = "Juveniles")
label9 = Label(sub2, text = "Adults")
label10 = Label(sub2, text = "Seniles")
label11 = Label(sub2, text = "Population(Thousands)")
label12 = Label(sub2, text = "Survival Rate(Between 1 and 0)")
label13 = Label(sub2, text = "Birth Rate")
label14 = Label(sub2, text = "Number of Generations")
label15 = Label(sub2, text = "Disase Trigger Point")
label16 = Label(sub2, text = entry_values[0])
label17 = Label(sub2, textvariable = entry_values[1])
label18 = Label(sub2, textvariable = "")
label19 = Label(sub2, textvariable = "")
label20 = Label(sub2, textvariable = "")
label21 = Label(sub2, textvariable = "")
label22 = Label(sub2, textvariable = "")
label23 = Label(sub2, textvariable = "")
label24 = Label(sub2, textvariable = "")
button7 = Button(sub2, text="Return To Main Page",
command = lambda: switch_page("main"))
label8.grid(row = 0, column = 1)
label9.grid(row = 0, column = 2)
label10.grid(row = 0, column = 3)
label11.grid(row = 1, column = 0)
label12.grid(row = 2, column = 0)
label13.grid(row = 3, column = 0)
label14.grid(row = 3, column = 3)
label15.grid(row = 4, column = 0)
label16.grid(row = 1, column = 1)
label17.grid(row = 1, column = 2)
label18.grid(row = 1, column = 3)
label19.grid(row = 2, column = 1)
label20.grid(row = 2, column = 2)
label21.grid(row = 2, column = 3)
label22.grid(row = 3, column = 1)
label23.grid(row = 3, column = 3)
label24.grid(row = 4, column = 1)
button7.grid(row = 0, column = 0)
return sub2
In order to change the text of a label you can do:
label["text"] = textVar
or
label.config(text=textVar)
So in your above code, when the entry changes, reconfigure the label using one of the above options.
I have 2 listboxes (which are connected so that items can move from one to the other) and at the end I would like to get all the entries in the second listbox by using a 'Ok' button (or simply closing the frame). I could add/remove values to a list every time an item is selected (as shown in the commented section of the code below) but I would rather have a single line of code along the lines of [master.selected.get(idx) for idx in master.selected.curselection()] in the close function but I am unable to get it working.
Code:
def measurementPopup(self,master):
self.chargeCarrier = StringVar()
self.massModifiers = StringVar()
self.chargeCarrier.set("[M+xH]")
def onselect1(evt):
w = evt.widget
index = int(w.curselection()[0])
value = w.get(index)
# My Dirty fix -> Here I could enter the selected value to a buffer list (to be returned in the ok function).
master.selected.insert(END,value)
master.avail.delete(index)
def onselect2(evt):
w = evt.widget
index = int(w.curselection()[0])
value = w.get(index)
# My Dirty fix -> Here I could remove the selected value from a buffer list (to be returned in the ok function).
master.selected.delete(index)
master.avail.insert(END,value)
def close(self):
# Here I would return the buffer list and close the window
master.measurementWindow = 0
top.destroy()
if master.measurementWindow == 1:
return
master.measurementWindow = 1
top = self.top = Toplevel()
top.protocol( "WM_DELETE_WINDOW", lambda: close(self))
self.charge = Label(top, text = "Charge", width = 10)
self.charge.grid(row = 0, column = 0, sticky = W)
self.min = Label(top, text = "Min", width = 5)
self.min.grid(row=0, column = 1, sticky = W)
self.minCharge = Spinbox(top, from_= 1, to = 3, width = 5)
self.minCharge.grid(row = 0, column = 2, sticky = W)
self.max = Label(top, text = "Max", width = 5)
self.max.grid(row = 0, column = 3, sticky = W)
self.maxCharge = Spinbox(top, from_ = 1, to=3, width=5)
self.maxCharge.grid(row = 0, column = 4, sticky = W)
self.chargeCarrier = OptionMenu(top, self.chargeCarrier, "[M+xH]", "[M+xNa]")
self.chargeCarrier.grid(row = 0, column = 5, sticky = W)
self.availMass = Label(top, text = "Available")
self.availMass.grid(row = 1, column = 1, sticky = W)
self.selectMass = Label(top, text = "Selected")
self.selectMass.grid(row = 1, column = 3, sticky = W)
self.massMod = Label(top, text = "Mass Mods")
self.massMod.grid(row = 2, column = 0, sticky = W)
self.avail = Listbox(top)
for i in UNITS:
if BLOCKS[i]['available'] == 1:
self.avail.insert(END,BLOCKS[i]['human_readable_name'])
self.avail.grid(row = 2, column = 1, columnspan = 2, sticky = W)
self.avail.bind('<<ListboxSelect>>',onselect1)
self.selected = Listbox(top)
self.selected.grid(row = 2, column = 3, columnspan = 2, sticky = W)
self.selected.bind('<<ListboxSelect>>',onselect2)
self.ok = Button(top,text = 'Ok',command = lambda: close(self))
self.ok.grid(row = 3, column = 0, sticky = W)
I have tried to use the following small snippet in the close function:
values = [master.selected.get(idx) for idx in master.selected.curselection()]
print ', '.join(values)
However, the for segment doesn't return anything. I would expect that this is due to the fact that nothing is actually selected but that I would need something opposite, along the lines of master.selected.allitems() (if it exists and if I understand it correctly).
Summary
How would one get all the items in 1 specific listbox?
The .get() function for the Listbox widget allows you to specify a range of items, which can be specified as 0 to END to return a tuple of all the items.
Example:
from Tkinter import *
root = Tk()
l = Listbox(root, width = 15)
l.pack()
l.insert(END, "Hello")
l.insert(END, "world")
l.insert(END, "here")
l.insert(END, "is")
l.insert(END, "an")
l.insert(END, "example")
def close():
global l, root
items = l.get(0, END)
print(items)
root.destroy()
b = Button(root, text = "OK", command = close).pack()
root.mainloop()
I hope this helps, if it's not what you were looking for let me know in a comment and I can try expand my answer.
I have no idea what is wrong but I keep getting this
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/lib-tk/Tkinter.py", line 1410, in call
return self.func(*args)
File "/Users/Zane/Desktop/Factorial GUI.py", line 72, in reveal2
self.text2.insert(0.0, message)
File "/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/lib-tk/Tkinter.py", line 2986, in insert
self.tk.call((self._w, 'insert', index, chars) + args)
TclError: wrong # args: should be ".22186144.22187184 insert index chars ?tagList chars tagList ...?"
here is my code:`
from Tkinter import*
class App(Frame):
def fac(self, n):
if n >= 0:
if n == 1 or n == 0:
return 1
else:
return n*self.fac(n-1)
else:
print('Error')
def per(self, n, r):
y = (self.fac(n)) / self.fac(n - r)
print (y)
def __init__(self, master):
Frame.__init__(self,master)
self.grid()
self.create_widgets()
def create_widgets(self):
self.instruction1 = Label(self, text = "Factorial:")
self.instruction1.grid(row = 0, column = 0, columnspan = 1, sticky = W)
self.password1 = Entry(self)
self.password1.grid(row = 0, column = 1, sticky = W)
self.submit_button1 = Button(self, text ="Enter", command = self.reveal1)
self.submit_button1.grid(row = 2, column = 0, sticky = W)
self.text1 = Text(self, width = 30, height = 1, wrap = WORD)
self.text1.grid(row = 3, column = 0, columnspan = 2, sticky = W)
self.instruction2 = Label(self, text = "Permutation:")
self.instruction2.grid(row = 4, column = 0, columnspan = 1, sticky = W)
self.password2 = Entry(self)
self.password2.grid(row = 4, column = 1, sticky = W)
self.password3 = Entry(self)
self.password3.grid(row = 6, column = 1, sticky = W)
self.submit_button2 = Button(self, text ="Enter", command = self.reveal2)
self.submit_button2.grid(row = 7, column = 0, sticky = W)
self.text2 = Text(self, width = 30, height = 1, wrap = WORD)
self.text2.grid(row = 8, column = 0, columnspan = 2, sticky = W)
def reveal1(self):
y = int(self.password1.get())
message = self.fac(y)
self.text1.delete(0.0, END)
self.text1.insert(0.0, message)
def reveal2(self):
y = int(self.password2.get())
z = int(self.password3.get())
message = self.per(y, z)
self.text2.delete(0.0, END)
self.text2.insert(0.0, message)
root = Tk()
root.title('Factorial')
root.geometry("340x300")
app = App(root)
root.mainloop()
`
Almost the only way to get the error you say you get with the code you posted, is if the insert method is called when the data to insert is None. message comes from the result of per, but per returns None because you don't explicitly return anything else.
One of the first things to try when trying to debug is to check that the data you're sending to the failing function is what you think it is. You can do this in a very low-tech way by simply printing out the values being passed to the insert message. This instantly told me that message was None. Once I learned that, it's pretty simple to answer the question "why was it None?".