Python & Tkinter -> 关于调用卡住程序的长时间运行的函数

标签 python multithreading parsing tkinter

我是 GUI 编程的新手,我正在尝试为我的一个 python 解析器制作一个 GUI。

我知道:

  • Tkinter 是单线程的。屏幕更新发生在事件循环的每次行程中。任何时候你有一个长时间运行的命令,你都会阻止事件循环完成迭代,从而阻止事件的处理,从而防止重绘。

  • 我的程序调用了一个大型函数,整个运行大约需要 5 分钟。所以我想唯一的解决方案是对长时间运行的命令使用线程。 但是,我长时间运行的命令已经线程化了,所以我真的不知道如何继续。

--> 我一在 GUI 中单击 BUT1,程序就会卡住,直到功能完全完成。我想在背景中运行这个函数,这样程序就不会卡住。

--> 我不是在寻找一个完整的解决方案,但如果有人能让我走上正轨,那就太好了!

  • Main.py -> GUI
  • Module_1.py -> 我们通过点击按钮 BUT1 调用的函数

提前致谢!

这是 Main.py --> GUI

#!/usr/bin/python
# -*- coding: utf-8 -*-


from Tkinter import *
import sys
import tkMessageBox
import tkFileDialog
import Module_1
import csv

from time import strftime, gmtime
DATE = strftime("_%d_%b_%Y")


class App:

    def __init__(self, master):

        self.frame = Frame(master, borderwidth=5, relief=RIDGE)
        self.frame.grid()

        class IORedirector(object):
            def __init__(self,TEXT_INFO):
                self.TEXT_INFO = TEXT_INFO

        class StdoutRedirector(IORedirector):
            def write(self,str):
                self.TEXT_INFO.config(text=self.TEXT_INFO.cget('text') + str)


        self.TEXT_HEADER = self.text_intro = Label(self.frame, bg="lightblue",text="THIS IS \n MY SUPER PROGRAM") 
        self.TEXT_HEADER.grid(row=0, column=0, columnspan=2, sticky=W+E+N+S)

        self.MENU = Frame(self.frame, borderwidth=5, relief=RIDGE, height=12) 
        self.MENU.grid(row=1, column=0, sticky=N)

        self.button = Button(self.MENU, text="QUIT", bg="red", command=self.frame.quit)
        self.button.grid(row=4, column=0)

        self.BUT1 = Button(self.MENU, text="BUT1", command=self.BUT1)
        self.BUT1.grid(row=0, column=0,sticky=W+E)



        self.TEXT_INFO = Label(self.frame, height=12, width=40, text="SOME TEXT", bg="grey",borderwidth=5, relief=RIDGE)
        self.TEXT_INFO.grid(row=1, column=1, sticky = N+W)

        sys.stdout = StdoutRedirector(self.TEXT_INFO)


    def BUT1(self):
        self.BUT1.config(text="RUNNING") 
        self.TEXT_INFO.config(text="BUT1 LAUNCHED")

        Module_1.main("BUT1")
        ## HERE WE NEED TO RUN THE FUNCTION
        ## THE PROGRAMM FREEZE HERE UNTIL THE FUNCTION IS ENTIRELY RUN

        self.TEXT_INFO.config(text="BUT1 FINISHED")
        self.BUT1.config(text="DONE")


root = Tk()
app = App(root)

root.mainloop()

这里是 Module_1.py --> 包含大函数

#!/usr/bin/python
# -*- coding: utf-8 -*-

import Queue
import threading
import urllib2
import time
from bs4 import BeautifulSoup as soup
from urllib2 import urlopen
import re
import os
import random
import sys
import logging
import csv
from time import strftime, gmtime
import os
import random
import shutil
import sys
import re
import logging
from threading import RLock
from time import strftime, gmtime
import csv
import urllib
from urllib import urlretrieve
from grab.spider import Spider, Task

logging.basicConfig(level=logging.CRITICAL) # Loggin to DEBUG / INFO
log = logging.getLogger()

DATE = strftime("_%d_%b_%Y")


class SPIDER1(Spider):
    initial_urls = ['URL_THAT_I_NEED_TO_PARSE']

    def __init__(self):
        super(SPIDER1, self).__init__(
            thread_number=20,
            network_try_limit=20,
            task_try_limit=20
        )
        self.result = {}

    def task_initial(self, grab, task):
        for opt in grab.css_list("select[name='Template$TestCentreSearch1$SubRegionList'] option")[1:]:
            grab.set_input('Template$TestCentreSearch1$SubRegionList', opt.attrib['value'])
            grab.submit(extra_post={
                '__EVENTTARGET': 'Template$TestCentreSearch1$SubRegionList'
            }, make_request=False)
            yield Task('parse', grab=grab, country=opt.text_content())

    def task_parse(self, grab, task):
        log.info('downloaded %s' % task.country)
        city_gen = (x.text_content() for x in grab.css_list(".TestCentreSearchLabel+br+span"))
        title_gen = (x.text_content() for x in grab.css_list(".TestCentreSearchTitle"))
        id_gen = (x.attrib['href'][-36:] for x in grab.css_list(".TestCentreSearchLink"))
        for x in zip(city_gen, title_gen, id_gen):
            self.result[x[2]] = {
                'country': task.country,
                'city': x[0],
                'name': x[1],
                'id': x[2],
                'price':'',
                'currency':'',
                'fee':''
            }
            yield Task('info', 'URL_URL=%s' % x[2], id=x[2])

    def task_info(self, grab, task):
        for label in grab.css_list(".TestCentreViewLabel"):
            if label.text_content().strip()=="Test Fee:":
                fees = label.getnext().text_content().strip()
                self.result[task.id]['fee'] = fees
                price = re.findall('\d[\d\., ]+\d',fees)
                if price:
                    price = re.findall('\d[\d\., ]+\d',fees)[0]
                    self.result[task.id]['price'] = price.replace(' ','').replace(',','.')
                    currency = re.findall('[A-Z]{2,3}[$|€|£]?',fees)
                    if not currency:
                        currency = re.findall('[$|€|£]',fees)
                        if not currency:
                            currency = fees.replace(price,'').strip().replace('  ','')
                    if isinstance(currency,list):
                        currency = currency[0]
                    self.result[task.id]['currency'] = currency
                #log.info('      %(price)s   %(currency)s     -   %(fee)s ' % self.result[task.id])
                break


    def dump(self, path):
        """
        Save result as csv into the path
        """
        with open(path, 'w') as file:
            file.write("ID;Country;State;City;Name;Price;Currency;Original Fee\n")
            for test_center in sorted(self.result.values(), key=lambda x: "%(country)s%(city)s%(name)s" % x):
                file.write(("%(id)s;%(country)s;;%(country)s;%(name)s;%(price)s;%(currency)s;%(fee)s\n" % test_center).encode('utf8'))


def main(choice):
    parser, path, name = None, None, None

    def run(name,parser,path):
        log.info('Parsing %s...' % name)
        parser.run()
        parser.dump(path)
        log.info('Parsing %s completed, data was dumped into %s' % (name, path))
        log.info(parser.render_stats())


    if choice == "NONE":
        # DO NOTHING
        # HERE I'D LIKE TO HAVE ANOTHER CALL TO ANOTHER THREADED FUNCTION

    elif choice == "BUT1":
        run('Function1',SPIDER1(),'C:\LOL\Output1'+DATE+'.csv')

因此,通过单击 BUT1,我们运行包含在 Module_1.py 文件中的 main("BUT1") 函数,参数 BUT1 启动 -> run('Function1',SPIDER1(),'C:\LOL\Output1 '+日期+'.csv') 然后程序卡住,直到解析器完成工作.. :)

最佳答案

问题很简单:BUT1 不会返回,直到对 main 的调用返回。只要 main(因此,BUT1)不返回,您的 GUI 就会被卡住。

要使其工作,您必须将 main 放在一个单独的线程中。如果 main 只是在等待这些线程,那么仅仅生成其他线程是不够的。

关于Python & Tkinter -> 关于调用卡住程序的长时间运行的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10860302/

相关文章:

python - CentOS6下安装twis​​ted

python - 如何使用 OpenCV 或深度学习从平面图扫描房间轮廓数据?

c++ - 为什么只能保证std::atomic_flag是无锁的?

c# - 可以通过使用多线程来改进此代码吗?

php - 当字符串变量中有 URL 时,如何在 PHP 中使用 parse_url?

javascript - 所有脚本和页面加载完成后如何获取所有html数据? ( puppeteer 师)

python - 删除列表中的重复项

Python:递归删除空文件夹

c# - 如果等待的任务链上有 'unwrapped'任务,则Task.Result/wait(..)无限期等待,而如果使用 'async/await',则成功完成

c - 如何捕获生成的解析器中语法错误的行号