Home > Back-end >  I can't pass function of a class from another file into tkinter if statement. AttributeError: &
I can't pass function of a class from another file into tkinter if statement. AttributeError: &

Time:07-13

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 a dataclass).
  • 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 the handle_find_movie() function, so no if statements are needed at all.
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()
  • Related