Search code examples
pythontkintermenufrontend

Generating a "No Item Found" string when user-input doesn't match entries in a list (Tkinter)


currently working on an F1 Tkinter Gui app, which lets users input a driver (either via typing or picking a driver from a Listbox) and relevant statistics get shown.

Currently, I'm trying to do a case as follows: when the user-input doesn't match the entries in drivers_list, the Listbox, menu, should show 1 string of "No Item Found" (or even better, the current user_input + "Not Found")

I've tried a couple of different ways. For example, in the code shown below, I put a separate else-statement within check, where it prints out "No Item Found", but what happens is that instead of only 1 "No Item Found" being shown in menu, multiple get shown. I think I know why multiple get shown, but I don't know a solution to fix it. It does seem that the issue lies within check, but I could be wrong

Here's a picture of the issue enter image description here

Here's the code:

#UI Design approach 4

from tkinter import *
from tkinter import ttk
from PIL import ImageTk, Image
from tkinter import messagebox
import requests
import json

root = Tk()
root.geometry("800x300")
root.title("F1 Desktop Application")
root.iconbitmap("formula1_logo.ico")




#generate 2022 drivers-list [can scale to drivers-list by changing the]
drivers_list_request = requests.get("http://ergast.com/api/f1/2022/drivers.json")
#initialize empty-list
drivers_list = []
drivers_list_object = json.loads(drivers_list_request.content)

for elements in drivers_list_object["MRData"]["DriverTable"]["Drivers"]:
    drivers_list.append(elements["givenName"] + " " + elements["familyName"])


# Update the Entry widget with the selected item in list
def check(e):
    
    v= entry_box.get()

    if v=='':
        hide_button(menu)
    
    else:
        data=[]
        for item in drivers_list:
            if v.lower() in item.lower():
                data.append(item)
                
            else:
                data.append("Item not Found")
        
        update(data)
        show_button(menu)

def update(data):
   # Clear the Combobox
   menu.delete(0, END)
   # Add values to the combobox
   for value in data:
        menu.insert(END,value)
        
def fillout(event):
    
    try:
        
        entry_box.delete(0,END)
        
        entry_box.insert(0,menu.get(menu.curselection()))
        
        hide_button(menu)
        
    #handle a complete deletion of entry-box via cursor double tap
    except:
        
        pass
        
def hide_button(widget):
    widget.grid_remove()
    
def show_button(widget):
    widget.grid()
    
    
def full_name():
    
    lower_user_input = grab_user_input()    

    response = requests.get("http://ergast.com/api/f1/drivers/{}.json".format(lower_user_input))
    
    response_object = json.loads(response.content)
    
    name = response_object["MRData"]["DriverTable"]["Drivers"][0]["givenName"] + " " + response_object["MRData"]["DriverTable"]["Drivers"][0]["familyName"]

    driver_name_label.configure(text = name)
    
    
def team():
    
    
    lower_user_input = grab_user_input()
    
    response = requests.get("http://ergast.com/api/f1/current/drivers/{}/constructors.json".format(lower_user_input))

    response_object = json.loads(response.content)

    team = response_object["MRData"]["ConstructorTable"]["Constructors"][0]["name"]
    
    team_api.configure(text = team)
    
    
def driver_code(): 
    
    lower_user_input = grab_user_input()    
    
    response = requests.get("http://ergast.com/api/f1/drivers/{}.json".format(lower_user_input))
        
    response_object = json.loads(response.content)
        
    code = response_object["MRData"]["DriverTable"]["Drivers"][0]["code"]
    
    code_api.configure(text = code)
        

    
    
def nationality(): 
    
    lower_user_input = grab_user_input()
    
    response = requests.get("http://ergast.com/api/f1/drivers/{}.json".format(lower_user_input))
        
    response_object = json.loads(response.content)
    
    nationality = response_object["MRData"]["DriverTable"]["Drivers"][0]["nationality"]
    
    nationality_api.configure(text = nationality)
        

        

def wins(): 
    
    lower_user_input = grab_user_input()

    response = requests.get("http://ergast.com/api/f1/drivers/{}/results/1.json".format(lower_user_input))
        
    response_object = json.loads(response.content)
        
    wins = response_object["MRData"]["total"]
    
    wins_api.configure(text = wins)
    
    
    
    
def poles(): 
    
    lower_user_input = grab_user_input()
    
    response = requests.get("http://ergast.com/api/f1/drivers/{}/qualifying/1.json".format(lower_user_input))

    response_object = json.loads(response.content)

    poles = response_object["MRData"]["total"]
    
    poles_api.configure(text = poles)

    

def podiums():
    
    lower_user_input = grab_user_input()

    #podiums is sum of 1st, 2nd, 3rd place finishes
    
    #####DRY principle; let's reuse the code from wins()
    ######noticing how maybe should create a separate function which "places" the widget
    #######for now, reuse the code
    
    #1st place finishes
    
    response = requests.get("http://ergast.com/api/f1/drivers/{}/results/1.json".format(lower_user_input))
        
    response_object = json.loads(response.content)
    
    #convert wins to int
        
    wins = int(response_object["MRData"]["total"])
    
    #2nd place finishes
        
    response_ii = requests.get("http://ergast.com/api/f1/drivers/{}/results/2.json".format(lower_user_input))
    
    response_ii_object = json.loads(response_ii.content)
    
    response_ii_amount = int(response_ii_object["MRData"]["total"])
    
    
    
    #3rd place finishes 
    
    response_iii = requests.get("http://ergast.com/api/f1/drivers/{}/results/3.json".format(lower_user_input))
    
    response_iii_object = json.loads(response_iii.content)
    
    response_iii_amount = int(response_iii_object["MRData"]["total"])
    
        
        
    podiums = str(wins + response_ii_amount + response_iii_amount)
    
    podiums_api.configure(text = podiums)

    
    
    
    
    
def championships():
    
    lower_user_input = grab_user_input()
    
    response = requests.get("http://ergast.com/api/f1/drivers/{}/driverStandings/1/seasons.json".format(lower_user_input))

    response_object = json.loads(response.content)
    
    championships = response_object["MRData"]["total"]
    
    championships_api.configure(text = championships)
    
    
def standing():
    
    lower_user_input = grab_user_input()
    
    response = requests.get("http://ergast.com/api/f1/current/drivers/{}/driverStandings.json".format(lower_user_input))

    response_object = json.loads(response.content)

    position = response_object["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0]["position"]

    points = response_object["MRData"]["StandingsTable"]["StandingsLists"][0]["DriverStandings"][0]["points"]

    standing = "{} ({} pts)".format(position, points)
    
    standing_api.configure(text = standing)
    
    
def grab_user_input(): 
    
    #need to find a scalable way, since what if this was an app to search any driver in history? 
    
    edge_case_inputs = ["Max Verstappen", "Kevin Magnussen", "Mick Schumacher"]
        
    user_input = entry_box.get()

    if user_input in edge_case_inputs:
        
        lower_user_input = user_input.lower().replace(" ", "_")
        
        return lower_user_input
    
    #very unique case which can't be modularsed
    
    elif user_input == "Nyck de Vries":
        
        lower_user_input = "de_vries"
    
        
    else:
        
        lower_user_input = user_input.lower().split(" ")[1]
    
        return lower_user_input
    
 
    

    
def search():
            
    #slow run-time is simply due to the multiple api calls made
    hide_button(menu)
    
    #fast-run time
    full_name()
    
    #fast-run time
    team()
    
    #fast-run time
    driver_code()
    
    #fast-run time
    nationality()
    
    #fast-run time
    wins()
    
    #fast-run time
    poles()
    
    #fast-run time
    podiums()
    
    #fast-run time
    championships()
    
    #fast-run time
    standing()
        
    show_button(main_frame)




    

    
    
    

left_frame = LabelFrame(root, width = 275, height = 300)
left_frame.grid(row = 1, column = 0, sticky = SE)
left_frame.grid_propagate(False)

search_label = Label(left_frame, text = "Search Driver", font = ("Arial bold", 12))
search_label.grid(row = 0, column = 0, pady = 10)

entry_box = Entry(left_frame, bd = 5)
entry_box.grid(row = 1, column = 0, padx = 35, pady = 40)
entry_box.bind('<KeyRelease>',check)

#pass something into the lamda
search_button = Button(left_frame, text = "search", command = search)
search_button.grid(row = 1, column = 1, padx = 15)


menu= Listbox(left_frame, height = 7)
menu.grid(row = 2, column = 0)
menu.bind("<<ListboxSelect>>",fillout)





main_frame = LabelFrame(root, width = 575, height = 300)
main_frame.grid(row = 1, column = 1, sticky = SE)
main_frame.grid_propagate(False)

driver_name_label = Label(main_frame, text = "", font = ("Arial", 15))
driver_name_label.grid(row = 0, column = 0,pady = 30)

team_label = Label(main_frame, text = "TEAM", font = ("Arial", 10))
team_label.grid(row = 1, column = 0, sticky = W)

team_api = Label(main_frame, text = "", font = ("Arial", 10))
team_api.grid(row = 1, column = 1)

nationality_label = Label(main_frame, text = "NATIONALITY", font = ("Arial", 10))
nationality_label.grid(row = 2, column = 0, sticky = W)

nationality_api = Label(main_frame, text = "", font = ("Arial", 10))
nationality_api.grid(row = 2, column = 1, sticky = W)


driver_code_label = Label(main_frame, text = "DRIVER CODE", font = ("Arial", 10))
driver_code_label.grid(row = 3, column = 0, sticky = W)

code_api = Label(main_frame, text = "", font = ("Arial", 10))
code_api.grid(row = 3, column = 1, sticky = W)

wins_label = Label(main_frame, text = "WINS", font = ("Arial", 10))
wins_label.grid(row = 4, column = 0, sticky = W)

wins_api = Label(main_frame, text = "", font = ("Arial", 10))
wins_api.grid(row = 4, column = 1, sticky = W)

podiums_label = Label(main_frame, text = "PODIUMS", font = ("Arial", 10))
podiums_label.grid(row = 1, column = 2, sticky = W, padx = 15)

podiums_api = Label(main_frame, text = "", font = ("Arial", 10))
podiums_api.grid(row = 1, column = 3, padx = 15,  sticky = W)

poles_label = Label(main_frame, text = "POLES", font = ("Arial", 10))
poles_label.grid(row = 2, column = 2, sticky = W, padx = 15)

poles_api = Label(main_frame, text = "", font = ("Arial", 10))
poles_api.grid(row = 2, column = 3, sticky = W, padx = 15)

world_championships_label = Label(main_frame, text = "WORLD CHAMPIONSHIPS", font = ("Arial", 10))
world_championships_label.grid(row = 3, column = 2, padx = 15, sticky = W)

championships_api = Label(main_frame, text = "", font = ("Arial", 10))
championships_api.grid(row = 3, column = 3, padx = 15, sticky = W)


current_standing_label = Label(main_frame, text = "F1 2022 STANDING", font = ("Arial", 10))
current_standing_label.grid(row = 4, column = 2, padx = 15, sticky = W)

standing_api = Label(main_frame, text = "", font = ("Arial", 10))
standing_api.grid(row = 4, column = 3, padx = 15, sticky = W)   


hide_button(menu)
hide_button(main_frame)

root.mainloop()

If anyone has follow-up questions for clarity, let me know!

Thanks, Safwan


Solution

  • Instead of adding "Item not found" to data in every iteration of the loop, don't add it until after the loop, if data is empty.

    if v.lower() in item.lower():
        data.append(item)
    # no else part
    
    if not data:  # data is empty
        data.append("Item not found")