Home > Mobile >  How would I send data from Class A's function to Class B's function if both of them inheri
How would I send data from Class A's function to Class B's function if both of them inheri

Time:10-10

I am trying to create a simple open-sourced project for tagging Japanese Audiobooks with metadata from Audible Japan. I have a working script that grabs the title, author, and narrators, and I am trying to populate Tkinter's tree with that data. The problem arose when I tried doing it the OOP methodology, and broke everything up into different classes and functions.

import tkinter as tk
from tkinter import *
from tkinter import ttk

import requests
from bs4 import BeautifulSoup


class ResultFrame(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)

        # field options
        options = {'padx': 10, 'pady': 10}

        # result label frame
        self.result_label = ttk.LabelFrame(self, text='Results')
        self.result_label.grid(row=1, column=0, sticky=tk.W, **options)

        # result tree
        self.tree = ttk.Treeview(self.result_label)
        self.tree['columns'] = ("Title", "Author", "Narrator", "Published")
        self.tree.column("#0", width=0, stretch=NO)  # hide the first autogenerated column
        self.tree.column("Title", anchor=W)
        self.tree.column("Author", anchor=W)
        self.tree.column("Narrator", anchor=W)
        self.tree.column("Published", anchor=W)

        # result tree headings
        self.tree.heading('#0', text='')  # hide the first autogenerated column
        self.tree.heading('Title', text='Title', anchor=W)
        self.tree.heading('Author', text='Author', anchor=W)
        self.tree.heading('Narrator', text='Narrator', anchor=W)
        self.tree.heading('Published', text='Published', anchor=W)

        self.tree.pack()
        self.grid(padx=10, pady=10, sticky=tk.NSEW)

    def populate_tree(self, data):
        count = 0

        for book in data.items():
            self.tree.insert(
                parent='', index='end', iid=count, text='',
                values=(
                    book[0], book[1]['authors'], book[1]['narrators'], 'not implemented'
                )
            )
            count  = 1


class SearchFrame(ttk.Frame):
    def __init__(self, container):
        super().__init__(container)
        self.audiobooks = {}

        # field options
        options = {'padx': 5, 'pady': 5}

        # search label
        self.search_label = ttk.Label(self, text='Search')
        self.search_label.grid(row=0, column=0, sticky=tk.W, **options)

        # search entry
        self.search = tk.StringVar()
        self.search_entry = ttk.Entry(self, textvariable=self.search, width=50)
        self.search_entry.grid(row=0, column=1, **options)
        self.search_entry.focus()

        self.search_button = ttk.Button(self, text="search")
        self.search_button['command'] = self.search_audible
        self.search_button.grid(row=0, column=2, sticky=tk.W, **options)

        # # results label
        # self.result_label = ttk.Label(self)
        # self.result_label.grid(row=1, columnspan=3, **options)

        # add padding to the frame and show it
        self.grid(padx=10, pady=10, sticky=tk.NSEW)

    def search_audible(self):
        """
        Search audible for the book
        :param self:
        :return:
        """
        temp_authors = []
        temp_narrators = []

        # build the url
        base_url = "https://www.audible.co.jp/search?ipRedirectOverride=true&overrideBaseCountry=true&keywords="
        query = self.search_entry.get()

        # request the page
        page = requests.get(base_url   query)

        soup = BeautifulSoup(page.content, "html.parser")

        # grab only the content that we need
        search_results = soup.find(id="center-3")

        # scrape information

        for product_item in search_results.find_all('li', class_="bc-list-item productListItem"):
            title = product_item['aria-label']

            # get all of the authors from anchor tags
            for link in soup.find("li", class_="bc-list-item authorLabel").find_all("a"):
                if link.text not in temp_authors:
                    temp_authors.append(link.text)

            # join the results with comma separation to prepare for MP3/M4B file tags
            authors = ", ".join(temp_authors)

            for link in soup.find("li", class_="bc-list-item narratorLabel").find_all("a"):
                if link.text not in temp_narrators:
                    temp_narrators.append(link.text)

            narrators = ", ".join(temp_narrators)

            self.audiobooks[title] = {
                "authors": authors,
                "narrators": narrators
            }

        ResultFrame.populate_tree(self, data=self.audiobooks)


class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title('Audiobook Tagger')


if __name__ == '__main__':
    app = App()
    SearchFrame(app)
    ResultFrame(app)
    app.mainloop()

From my understanding reading this answer: https://stackoverflow.com/a/41513358, I would essentially have to make ResultFrame inherit from SearchFrame, so it would be ResultFrame(SearchFrame) instead of ResultFrame(ttk.Frame). But the issue is that both of my classes are inheriting from ttk.Frame, so I am unsure how to proceed.

To summarize my question, in my SearchFrame class, the function called search_audible scraped information from Audible. I would like to send that scrapped information to ResultFrame's populate_tree() so that it can show the results conveniently.

If possible, I would prefer to have the two Frames into separated classes, because it helps organize and manage the code in my opinion.

CodePudding user response:

To summarize my question, in my SearchFrame class, the function called search_audible scraped information from Audible. I would like to send that scrapped information to ResultFrame's populate_tree() so that it can show the results conveniently.

In order to do that SearchFrame needs to be aware of ResultFrame. You can do that by:

class SearchFrame(ttk.Frame):
    def __init__(self, container,reciever):
        super().__init__(container)
        self.reciever = reciever

Through that reference you can target this object by:

self.reciever.populate_tree(self, data=self.audiobooks)

since you need a reference to that object you need to swap the order of construction to have a reference you can pass:

res = ResultFrame(app)
ser = SearchFrame(app,res)

Hint

class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.res = ResultFrame(self)
        self.ser = SearchFrame(self,self.res)

        self.title('Audiobook Tagger')


if __name__ == '__main__':
    app = App()
    app.mainloop()
  • Related