【问题标题】:Tkinter stopping big loop functionsTkinter 停止大循环功能
【发布时间】:2021-01-11 08:33:31
【问题描述】:

我已经阅读了一些关于线程、tkinter 停止按钮等的其他文章和问答。大部分想法都是关于如何停止一些小循环。但我的问题是我试图用我的 Tkinter GUI 包装我一个月前创建的现有函数。而且现有的功能相当庞大,所以我不知道我应该如何停止循环。这是代码。 Reader 对象读取输入文本文件并从网络抓取结果中输出 csvfile。它从输入的购物中心 url 中提取所有评论。你可以忽略所有的韩国人,这并不重要。还有另一个名为“SmartStoreReviewScraper”的对象,但功能非常简单。它基本上请求 GET 一个 json 文件将其返回给 Reader,以便它可以转换为 csvfile。

class Reader:
def __init__(self, filename, limit=None, delay_time=0):
    self.filename = filename
    self.limit = limit
    self.delay_time = delay_time
    self.target_variable = ['평점', '아이디', '시간', '구매옵션', '리뷰내용']
    self.read_input_file()
    self.extract_file()

def read_input_file(self):
    request_df = pd.read_csv(self.filename, names=['names', 'link'], sep='*')
    request_df = request_df.set_index('names')
    request_df.index = request_df.index + request_df.groupby(level=0).cumcount().astype(str).replace('0','')
    request_df.to_csv('output/wd.csv', encoding='utf-8', header=False)

def extract_file(self):
    df = pd.read_csv('output/wd.csv', encoding='utf-8', names=['names', 'link'])
    for i in range(len(df.index)):
        
        file_name = list(df['names'])[i]
        store_link = list(df['link'])[i]

        print(f"###################{file_name} 수집 시작###################")

        app = SmartStoreReviewScraper()
        REVIEWS = app.scraped_reviews

        store_data = app.get_store_data(store_link) #스토어 정보 
        json_review = app.get_review_json(store_data['merchant_no'], store_data['product_no'], 1) #리뷰 정보 리퀘스트 
        
        review_data = app.get_review_data(json_review) #해당 아이템 리뷰 (총 아이템수 + 총 페이지수) 정보 
        total_element = review_data['totalElements'] #총 아이템수
        total_pages = review_data['totalPages'] #총 페이지수
        print(f'총 아이템 수: {total_element}\n총 페이지 수: {total_pages}')

        review_content = app.get_review_content(json_review) #목표 데이터
        app.scrape_review_contents(REVIEWS, review_content) #첫 페이지 크롤링

        if self.limit >= total_element or self.limit == 0:
            self.start_scraper(app, REVIEWS, total_element, total_pages, store_data, file_name)
        else:
            self.start_scraper(app, REVIEWS, self.limit, total_pages, store_data, file_name)
            
            

def start_scraper(self, app, REVIEWS, LIMIT, PAGES, store_data, file_name):
    print('목표 데이터 양:'+str(LIMIT)) 

    DF = pd.DataFrame([], columns=self.target_variable)

    while len(REVIEWS) < LIMIT:
        for page in trange(2, PAGES+1, desc="크롤링 진행도"): 
            #첫 페이지는 이미 크롤링 완료하였으니 두번째 페이지부터 시작
            json = app.get_review_json(store_data['merchant_no'], store_data['product_no'], page)
            content = app.get_review_content(json)
            app.scrape_review_contents(REVIEWS, content)
            time.sleep(self.delay_time) 
            if len(REVIEWS) >= LIMIT:
                break

    for i in trange(len(REVIEWS), desc='데이터 변환 중'):
        row = pd.DataFrame([REVIEWS[i]], columns=self.target_variable)
        DF = DF.append(row, ignore_index=True)

    DF.insert(0, column='번호', value=DF.index+1)
    print("<데이터 프레임 샘플>")
    print(DF.head())
    print('데이터 수집 완료! 크롤링된 아이템 수:'+str(len(DF))+'\n')
    DF.to_csv(f'output/data/{file_name}.csv', encoding='utf-8-sig', index=False)

这是进入我的 tkinter 根按钮的代码。我必须停止 Reader 对象,但我不知道该怎么做。我是线程新手,所以我不了解背后的整个机制。我每次都在阅读文档。但是有人可以建议我快速解决这个问题吗?因为我有点没时间了。

class Controller(object):
def __init__(self):
    self.thread = None
    self.stop_thread = Event()

def scraping(self):
    while not self.stop_thread.is_set():
        Reader(FILENAME[-1], limit=limit.get(), delay_time=delay_time.get())
        messagebox.showinfo('info', 'finished crawling')
        break
        if self.stop_thread.is_set():
            break

def start(self):
    self.stop_thread.clear()
    self.thread = Thread(target = self.scraping)
    self.thread.start()

def stop(self):
    self.stop_thread.set()
    self.thread.join()
    self.thread = None

control = Controller()
search_btn.grid(row=0, column=2)
start_btn = tk.Button(root, text='시작', command=control.combine)
start_btn.grid(row=2, column=2)
stop_btn = tk.Button(root, text='중지', command=control.stop)
stop_btn.grid(row=3, column=2)

我知道我必须在阅读器中插入某种停止功能。但我不知道我应该怎么做。任何形式的帮助都会非常有帮助,谢谢。

【问题讨论】:

    标签: python multithreading user-interface tkinter process


    【解决方案1】:

    尝试将事件传递给阅读器对象。

    def __init__(self, filename, limit=None, delay_time=0, stop_thread):
         self.stop_thread = stop_thread   # store event reference
         ........
    
    
    def extract_file(self):
        df = pd.read_csv('output/wd.csv', encoding='utf-8', names=['names', 'link'])
        for i in range(len(df.index)):
            if self.stop_thread.is_set(): return  # check event
            ..................
    
    
    Reader(FILENAME[-1], limit=limit.get(), delay_time=delay_time.get(), self.stop_thread) # pass event to reader
    

    【讨论】:

    • 感谢您的建议,我会尝试并告诉您
    • 如何让 Reader 每次都检查 stop_thread?因为如果你传入self.stop_thread,它是稳定的。
    • '每次'?检查每个循环中的事件,如果设置了事件,则退出循环。
    • 我对代码进行了一些编辑。我让 extract_file 函数接收 stop_thread 而不是整个对象。我认为需要一个包装函数的条件语句。我在下面发布了一个附加答案,你能检查一下吗?谢谢,我认为这是一个好的开始。
    • 确实如此,逐项。但问题是在我单击停止按钮后,程序就冻结了。停止按钮不会弹出备份。所以基本上感觉和点击退出按钮是一样的。我想让停止按钮重新弹出,所以我可以再次单击开始按钮。你有什么想法让这项工作发挥作用吗?
    【解决方案2】:
        def scraping(self):
        file_reader = Reader(FILENAME[-1], limit=limit.get(), delay_time=delay_time.get())
        file_reader.extract_file(self.stop_thread)
        messagebox.showinfo('info', 'finished crawling')
    

    感谢@Mike67,我编辑了我的代码更改。我认为应该有一个条件语句来包装file_reader.extract_file(self.stop_thread) 以检查stop_thread 的每个输入项。任何其他建议都会真正有帮助。谢谢!

    【讨论】:

    • 尝试在代码中添加打印语句以找出代码停止的位置。您还可以使用 IDE (PyCharm\Spyder\VSCode) 对其进行调试并逐步执行代码。
    • 我调试了一下,发现点击停止按钮后,程序在返回line3(file_reader.extract_file)后立即停止。程序在应该返回消息框时冻结。
    • MessageBox 可能会阻塞,因为它在后台线程中运行,您无法单击“确定”。尝试改用print
    • 我错过了!我怎么能错过!我真的很感激,伙计。谢谢!!!
    猜你喜欢
    • 1970-01-01
    • 2017-01-26
    • 2013-12-25
    • 1970-01-01
    • 2015-03-18
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多