-1

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

  • 1
    Please read [ask] and [mre]. This is **way** too much code to explain the problem. – Karl Knechtel Sep 18 '22 at 07:58
  • 1
    Anyway, please think carefully about the logic. How many times will `for item in drivers_list:` run? When the item is not found, how many times should `data.append("Item not Found")` occur? Therefore, does it make sense for that `.append` to be inside the loop? If you are looking for an object in real life, do you think "nope, my overall search was unsuccessful" every time you look somewhere and it isn't there? Rather than that - when you finish looking everywhere, how do you decide whether you found the object or not? – Karl Knechtel Sep 18 '22 at 08:04
  • @ Karl Knechtel. Why did u flagged duplicated? It is not duplicated nor it is not same code – toyota Supra Sep 18 '22 at 12:25

1 Answers1

2

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")
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • 1
    This one is better. You can type Veb but not found. def check(e): if (v := entry_box.get())=='': hide_button(menu) else: data=[item for item in drivers_list if v.lower() in item.lower()] if not data: # data is empty data.append("Item not found") update(data) show_button(menu) – toyota Supra Sep 18 '22 at 12:28
  • 1
    @Safwan Khan. Your code is working by using my comment as above. When you type vettel, then search, it will display on the right side. – toyota Supra Sep 18 '22 at 12:37
  • Can you open dispute? – toyota Supra Sep 19 '22 at 03:17
  • Not too sure how to "open dispute" Also, thank you for your solution to the problem. Neat idea to use a list comprehension. Thank you all for contributing to finding a fix to this problem! – Safwan Khan Sep 19 '22 at 04:39