如何在同继承自ttk.Frame的Tkinter类之间传递爬取数据以填充Treeview
我完全理解你想保持两个Frame类独立、避免继承耦合的需求,其实不需要让它们互相继承,通过主App类作为中间协调者来传递组件引用,就能完美实现“搜索完成后更新Treeview”的功能,同时保持代码结构清晰。
下面是具体的修改方案和完整代码:
核心修改思路
- 让主App类管理两个Frame实例:在
App中创建ResultFrame和SearchFrame的实例,并把ResultFrame的引用传递给SearchFrame,这样SearchFrame就知道要把数据传给哪个展示组件。 - 修正爬取逻辑的bug:你当前的代码会把所有书的作者/朗读者都设为第一本书的内容,需要改为从当前遍历的
product_item中提取信息。 - 添加数据清空逻辑:每次搜索前清空旧的搜索结果和Treeview内容,避免结果叠加。
修改后的完整代码
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, result_frame): super().__init__(container) self.result_frame = result_frame # 保存ResultFrame实例引用 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) # 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: """ # 清空旧数据 self.audiobooks.clear() for item in self.result_frame.tree.get_children(): self.result_frame.tree.delete(item) # 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") if not search_results: return # scrape information for product_item in search_results.find_all('li', class_="bc-list-item productListItem"): title = product_item['aria-label'] temp_authors = [] temp_narrators = [] # 从当前product_item提取作者 author_item = product_item.find("li", class_="bc-list-item authorLabel") if author_item: for link in author_item.find_all("a"): if link.text not in temp_authors: temp_authors.append(link.text) authors = ", ".join(temp_authors) # 从当前product_item提取朗读者 narrator_item = product_item.find("li", class_="bc-list-item narratorLabel") if narrator_item: for link in narrator_item.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.result_frame.populate_tree(self.audiobooks) class App(tk.Tk): def __init__(self): super().__init__() self.title('Audiobook Tagger') # 创建两个Frame实例并建立关联 self.result_frame = ResultFrame(self) self.search_frame = SearchFrame(self, self.result_frame) if __name__ == '__main__': app = App() app.mainloop()
关键修改点说明
- 组件引用传递:在
App的初始化方法中,先创建ResultFrame,再把它作为参数传给SearchFrame,这样SearchFrame就能直接调用ResultFrame的实例方法。 - 爬取逻辑修复:将作者和朗读者的查找范围从
soup改为当前的product_item,确保每本书的信息对应正确。 - 清空旧数据:每次搜索前清空
audiobooks字典和Treeview的旧条目,避免新结果和旧结果混在一起。
这种方式既保持了两个Frame类的职责分离(一个负责搜索,一个负责展示),又实现了它们之间的交互,代码结构更清晰,也更容易后续扩展功能。
内容的提问来源于stack exchange,提问作者user12758446
相关产品推荐
相关产品推荐

