This is code from main.py. I don't know how to pass function into nested if statement, so when object from option menu is chosen, the text relevant to this object is shown in the main window(The list of movies with relevant to Mood genres). I have tried to pass function as it's said in python documentation. This is the full traceback:
Traceback (most recent call last):
File "/opt/miniconda3/lib/python3.9/tkinter/__init__.py", line 1892, in __call__
return self.func(*args)
File "/Users/nighttwinkle/Documents/Movie_mood/main.py", line 41, in find_movie
pass_genres.pass_multiple_genres(class_object.Emotional.genre)
AttributeError: 'Mood' object has no attribute 'pass_multiple_genres'
It's shown in terminal when I click to show movie list
import json
import tkinter as tk
from click import pass_context
import pandas as pd
import requests
import class_object
from class_object import Mood
pass_genres = Mood(name ='',genre='')
#main window
root = tk.Tk()
root.geometry('400x800')
root.title("Movie chooser based on mood")
#dataframe with genres
data = requests.get(
"https://api.themoviedb.org/3/genre/movie/list?api_key=25edbcd94c9f3ff7a7af1c08760aa632&language=en-US")
data = json.loads(data.text)
df = pd.DataFrame(data)
#dropdown menu with mood options
clicked = tk.StringVar()
clicked.set("What's your mood?")
genre = tk.StringVar()
options = [class_object.Happy.name, class_object.Emotional.name, class_object.Scared.name, class_object.Mysterious.name,
class_object.Fantasy.name, class_object.Curious.name, class_object.White_noise.name]
drop = tk.OptionMenu(root, clicked, *options).pack()
def find_movie():
moodtext = clicked.get()
if moodtext == class_object.Happy.name:
pass_genres.pass_multiple_genres(class_object.Happy.genre)
if moodtext == class_object.Emotional.name:
pass_genres.pass_multiple_genres(class_object.Emotional.genre)
if moodtext == class_object.Scared.name:
pass_genres.pass_multiple_genres(class_object.Scared.genre)
if moodtext == class_object.Mysterious.name:
pass_genres.pass_multiple_genres(class_object.Mysterious.genre)
if moodtext == class_object.Curious.name:
pass_genres.pass_multiple_genres(class_object.Curious.genre)
if moodtext == class_object.White_noise(class_object.White_noise.name):
pass_genres.pass_multiple_genres(class_object.White_noise.genre)
choose_button = tk.Button(root, text = "Show the list", command=find_movie)
choose_button.pack()
if __name__ == '__main__':
root.mainloop()
This is the code from class_object.py The file contains class Mood, function and based on this class objects.
from unicodedata import name
import requests
import pandas as pd
import json
import tkinter as tk
root = tk.Tk()
data = requests.get("https://api.themoviedb.org/3/genre/movie/list?api_key=25edbcd94c9f3ff7a7af1c08760aa632&language=en-US")
data = json.loads(data.text)
df = pd.DataFrame(data)
#genres
action = df.iat[0,0]['name']
adventure = df.iat[1,0]['name']
animation = df.iat[2,0]['name']
comedy = df.iat[3,0]['name']
crime = df.iat[4,0]['name']
documentary = df.iat[5,0]['name']
drama = df.iat[6,0]['name']
family = df.iat[7,0]['name']
fantasy = df.iat[8,0]['name']
history = df.iat[9,0]['name']
horor = df.iat[10,0]['name']
music = df.iat[11,0]['name']
mystery = df.iat[12,0]['name']
romance = df.iat[13,0]['name']
fiction = df.iat[14,0]['name']
tv_movie = df.iat[15,0]['name']
thriller = df.iat[16,0]['name']
war = df.iat[17,0]['name']
western = df.iat[18,0]['name']
#Class Mood
class Mood():
def __init__(self,name,genre):
self.name = name
self.genre = genre
def pass_multiple_genres(self):
movie_data1 = requests.get(f"4c9f3ffhttps://api.themoviedb.org/3/discover/movie?api_key=25edbcd97a7af1c08760aa632&language=en-US&sort_by=vote_average.desc&include_adult=false&include_video=false&page=1,2,3,4&with_genres={self.genre[0]}&with_watch_monetization_types=flatrate")
movie_data1 = json.loads(movie_data1.text)
df_movie_data1 = pd.DataFrame(movie_data1)
movie_data2 = requests.get(f"4c9f3ffhttps://api.themoviedb.org/3/discover/movie?api_key=25edbcd97a7af1c08760aa632&language=en-US&sort_by=vote_average.desc&include_adult=false&include_video=false&page=1,2,3,4&with_genres={self.genre[1]}&with_watch_monetization_types=flatrate")
movie_data2 = json.loads(movie_data2.text)
df_movie_data2 = pd.DataFrame(movie_data2)
My_label = tk.Label(root, text=df_movie_data1, text=df_movie_data2)
My_label.pack()
#objects with mood
Happy = Mood(name='Happy', genre=[comedy,adventure])
Emotional = Mood(name='Emotional',genre= [drama, romance])
Scared = Mood(name='Scared', genre = [horor, thriller])
Mysterious = Mood(name='Mysterious',genre = [mystery])
Fantasy = Mood(name='Fantasy', genre = [fantasy,fiction])
Curious = Mood(name='Curious',genre= [documentary])
White_noise = Mood(name='White noise on the background',genre = tv_movie)
CodePudding user response:
Here's a simplified reworking of your code, since it was unfortunately a bit hard to make sense of the original.
- For a simple program like this, it's really not worth it to split it to multiple modules at this point.
- The program consists of three logical sections: the API calls to talk with MovieDB, the Mood data (which maps MovieDB genre names to your moods), and the main UI code.
- The genre-determination code you had (using
df.iat
) was brittle; it would break if the API returned data in a different order than you imagined. Instead, instead of using Pandas to read that data, we just use a simple dict comprehension to map a genre name to its id. - The
get_movies
wrapper just returns a list of dicts; mangling them into a dataframe is done later. - The
Mood
class no longer contains any logic (it could be made adataclass
). - The moods are stored in a list
mood_list
, not free variables. This makes it a lot easier to enumerate them for the dropdown menu.- The
mood_map
dict makes it easy to look them up in thehandle_find_movie()
function, so noif
statements are needed at all.
- The
import tkinter as tk
import pandas as pd
import requests
API_KEY = "25edbcd..."
def get_genre_map():
"""
Get a dict mapping genre names to genre IDs.
"""
resp = requests.get(
f"https://api.themoviedb.org/3/genre/movie/list",
params={"api_key": API_KEY, "language": "en-US"},
)
resp.raise_for_status()
return {genre["name"]: genre["id"] for genre in resp.json()["genres"]}
def get_movies(genre_ids):
"""
Get movie discovery suggestions based on the genre IDs.
Returns a list of dicts.
"""
resp = requests.get(
"https://api.themoviedb.org/3/discover/movie",
params={
"api_key": API_KEY,
"with_genres": ",".join(genre_ids),
"sort_by": "popularity.desc",
"language": "en-US",
"include_adult": "false",
"include_video": "false",
"page": "1",
},
)
resp.raise_for_status()
return resp.json()["results"]
class Mood:
def __init__(self, name, genres):
self.name = name
self.genres = list(genres)
mood_list = [
Mood(name="Happy", genres=["Comedy", "Adventure"]),
Mood(name="Emotional", genres=["Drama", "Romance"]),
Mood(name="Scared", genres=["Horror", "Thriller"]),
Mood(name="Mysterious", genres=["Mystery"]),
Mood(name="Fantasy", genres=["Fantasy", "Science Fiction"]),
Mood(name="Curious", genres=["Documentary"]),
]
# To make lookups nicer later on...
mood_map = {mood.name: mood for mood in mood_list}
def handle_find_movie():
"""
Action handler for the "Show the list" button.
"""
# Look up the mood in the mood map based on the name variable.
# This could raise a KeyError (and indeed will if the user doesn't
# select anything in the list), but we ignore that for the time being.
mood = mood_map[mood_var.get()]
# Load the genre map from movie db...
genre_map = get_genre_map()
# ...and use it to find the movie genre IDs based on the strings in the mood
genre_ids = [str(genre_map[genre]) for genre in mood.genres]
# Get the movies and format into a dataframe (for easy display)
movies_df = pd.DataFrame(get_movies(genre_ids))
# Dump the dataframe as a string into a label.
result_label = tk.Label(root, text=str(movies_df))
result_label.pack()
# main window
root = tk.Tk()
root.geometry("800x800")
root.title("Movie chooser based on mood")
# dropdown menu with mood options
mood_var = tk.StringVar(value="What's your mood?")
tk.OptionMenu(root, mood_var, *mood_map).pack()
choose_button = tk.Button(root, text="Show the list", command=handle_find_movie)
choose_button.pack()
if __name__ == "__main__":
root.mainloop()