I Apologize in advance for the long code and if I break any rules as this is my first time asking a question on this site. I'm fairly new to Python but I'm currently writing a program for a school project that requests data from the OpenFEC API and then display it in a GUI that I created using TKinter. Everything works well if I print to the console but once I try to insert my data to the Text widget I get an error and not all the data is shown. The error is 'TypeError: must be str, not NoneType', I've tried converting the variable to a string using str(varName) but doesn't work. I've been working on this specific issue for a couple of days now and have searched all over the internet for possible solutions but I haven't found anything that helps me. I appreciate any help that I can receive.
import json
import urllib.request
import urllib.parse
import urllib.error
from tkinter import *
from tkinter import ttk
def inputValidation(year):
# Ask the user for a year
# year = yearEntry.get()
# Input validation
year = int(year) # Converting input to integer for comparison purposes
if year >= 2003 and year <= 2020:
message = "Here are the election dates for " + str(year) + ':' + '\n'
else:
message = "Please enter a valid year."
# Clear out any existing text from entry window
data.config(state=NORMAL)
data.delete(0.0, END)
# Set the data window
data.insert(1.0, message)
data.config(state=DISABLED)
# Convert the year back to a string
year = str(year)
return year
# Function to get API data
def apiCall(event, year, pageNumber):
""" Requests data from OpenFEC API by constructing a url using predetermined
values. """
apiKey = 'rdudHBjgS5srIohVWYyyUL64AOsqVfRkGZD4gvMU'
perPage = '90' # Number of items to print per page
electionYear = year
nullHide = 'true'
nullOnly = 'true'
sort = sortNum.get()
if sort == 1:
sort = '-election_state'
if sort == 2:
sort = '-election_date'
if sort == 3:
sort = 'office_sought'
url = ('https://api.open.fec.gov/v1/election-dates/?per_page=' + perPage +
'&api_key=' + apiKey +
'&election_year=' + electionYear +
'&page=' + pageNumber +
'&sort_hide_null=' + nullHide +
'&sort_null_only=' + nullOnly +
'&sort=' + sort)
uh = urllib.request.urlopen(url)
data = uh.read().decode()
js = json.loads(data)
print(url)
return js # We receive a dictionary with all the
info requested
# Function to print the API info
# Provide a year between 2003 - 2020
def electionDates(event):
year = yearEntry.get() # User provided year
year = inputValidation(year)
pageNumber = '1'
js = apiCall(event, year, pageNumber) # Call the API by using the first function
pages = js['pagination']['pages']
print('TOTAL PAGES: ', pages)
# print('TOTAL ITEMS: ', items)
while int(pages) >= int(pageNumber):
idx = 0
totalItems = 0
items = 0
print('PAGE', pageNumber, 'OF', pages)
for item in js['results']:
state = js['results'][idx]['election_state']
date = js['results'][idx]['election_date']
electionType = js['results'][idx]['election_type_full']
notes = js['results'][idx]['election_notes']
office = js['results'][idx]['office_sought']
# Changing initials from API to full office names
if office == 'S':
office = office.replace('S', 'Senate') # Print out the full word instead of just the initial
if office == 'H':
office = office.replace('H', 'House of Representatives') # Print out the full word, not the initial
if office == 'P':
office = office.replace('P', 'President') # Print out the full word instead of just the initial
idx = idx + 1 # idx allows us to iterate through each item in the dictionary
# Displaying Data in Text Box
data.config(state=NORMAL)
data.insert(2.0, '' +
'\n' 'Date: ' + date +
'\n' 'State: ' + state +
'\n' 'Election Type: ' + electionType +
'\n' 'Office: ' + office +
'\n' 'Notes: ' + notes +
'\n', END)
data.config(state=DISABLED)
items = items + 1
pageNumber = int(pageNumber) + 1
pageNumber = str(pageNumber)
js = apiCall(event, year, pageNumber) # Re-call the API function to print the next page
# -------- GUI CODE --------
root = Tk()
root.title('InfoLection')
frame = Frame(root)
sortNum = IntVar()
""" Create label, button, entry and text widgets into our frame. """
# --- Create instruction label ---
yearLbl = ttk.Label(root, text='Enter Year: ')
yearLbl.grid(row=0, column=1, sticky=E)
# --- Create Entry Box ---
yearEntry = ttk.Entry(root)
yearEntry.grid(row=0, column=2, columnspan=1, sticky=W)
yearEntry.delete(0, END)
yearEntry.insert(0, '')
# --- Create Submit Button ---
submitBtn = ttk.Button(root, text='Submit')
submitBtn.bind('<Button-1>', electionDates)
submitBtn.grid(row=3, column=0, columnspan=5, sticky=NSEW)
# Menu Bar
menu = Menu(root)
root.config(menu=menu)
# Submenu
subMenu = Menu(menu)
menu.add_cascade(label='About', menu=subMenu)
subMenu.add_command(label="Information")
subMenu.add_command(label='Exit')
# --- Radio Buttons to Select Sorting Method ---
# Label
sortByRB = ttk.Label(root, text='Sort by:')
sortByRB.grid(row=1, column=0, sticky=E)
# Radio Buttons
stateSortRB = ttk.Radiobutton(root, text='State', value=1, variable=sortNum)
# Sort by state
stateSortRB.grid(row=2, column=1, sticky=W)
dateSortRB = ttk.Radiobutton(root, text='Date', value=2, variable=sortNum)
# Sort by date
dateSortRB.grid(row=2, column=2, sticky=W)
officeSortRB = ttk.Radiobutton(root, text='Office', value=3,
variable=sortNum) # Sort by Office
officeSortRB.grid(row=2, column=3, sticky=W)
# --- Text Widget To Display Data ---
data = Text(root, width=50, height=25, wrap=WORD)
data.grid(row=4, column=0, columnspan=4, sticky=NSEW)
# --- Scroll Bar ---
scroll = ttk.Scrollbar(root, command=data.yview)
data['yscrollcommand'] = scroll.set
scroll.grid(row=4, column=5, pady=3, sticky=NSEW)
# Window Icon
# --- Keep Window Open ---
root.mainloop()
You're trying to concatenate "Notes: "
and notes
, but sometimes notes
is None, and you can't add None to a string. You could manually convert:
'\n' 'Notes: ' + str(notes) +
... But I think it would be easier to take advantage of Python's str.format()
method, which automatically converts its arguments to strings (in the absence of any specification in the format string telling it otherwise):
data.insert(2.0, ''
'\n' 'Date: {}'
'\n' 'State: {}'
'\n' 'Election Type: {}'
'\n' 'Office: {}'
'\n' 'Notes: {}'
'\n'.format(date, state, electionType, office, notes)
, END)
... Or you could use f-strings, although those are only available in Python 3.6 and higher:
data.insert(2.0, '' +
'\n' f'Date: {date}'
'\n' f'State: {state}'
'\n' f'Election Type: {electionType}'
'\n' f'Office: {office}'
'\n' f'Notes: {notes}'
'\n', END)