Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python&;Tkinter->;关于调用冻结程序的长时间运行函数_Python_Multithreading_Parsing_Tkinter - Fatal编程技术网

Python&;Tkinter->;关于调用冻结程序的长时间运行函数

Python&;Tkinter->;关于调用冻结程序的长时间运行函数,python,multithreading,parsing,tkinter,Python,Multithreading,Parsing,Tkinter,我是GUI编程方面的新手,我正在尝试为我的python解析器之一制作GUI 我知道: Tkinter是单螺纹的。通过事件循环的每次行程都会发生屏幕更新。任何时候,只要有一个长时间运行的命令,就会阻止事件循环完成迭代,从而阻止事件的处理,从而防止重画 我的程序调用一个大函数,整个运行大约需要5分钟。因此,我想唯一的解决方案是对长时间运行的命令使用线程。 但是,我在中长时间运行的命令已经线程化,所以我真的不知道如何继续 -->只要我在GUI中单击BUT1,程序就会冻结,直到功能完全完成。我想在后

我是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()
下面是模块_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启动->运行('Function1',SPIDER1(),'C:\LOL\Output1'+DATE+'.csv'))
然后程序冻结,直到解析器完成工作:)

如果偶尔从BUT1函数调用
root.update()
,应该可以防止GUI冻结。您也可以通过一个固定间隔的python线程来实现这一点

例如,每0.1秒更新一次:

from threading import Thread
from time import sleep

self.updateGUIThread = Thread(target=self.updateGUI)

def updateGUI(self):
    while self.updateNeeded
        root.update()
        sleep(0.1)

大函数完成后,您可以将
self.updateNeeded
设置为False。

问题很简单:
BUT1
在调用
main
返回之前不会返回。只要
main
(因此,
BUT1
)没有返回,GUI就会被冻结


要使其工作,必须将
main
放在单独的线程中。如果
main
所做的只是等待其他线程,那么它产生其他线程是不够的。

非常有趣!我正在尝试从BUT1函数调用root.update(),然后返回,如果这个线程有效,则将其设置为关闭!非常感谢,看起来很有希望@野间町:这是非常危险的。你真的试过这个吗?如果它起作用了,并且最终没有在某个时候导致死锁或崩溃,我会感到惊讶。调用
update
通常是一种不好的做法,从另一个线程调用似乎也相当危险。本质上,当您调用
update
时,您正在创建第二个事件循环,如果在此过程中发生了导致再次调用此代码的事件,您可能会遇到真正的问题。@BryanOakley:我自己使用此循环时没有任何问题,您能解释一下您所指的“真正的问题”是什么吗?这难道不足以防止窗口失去响应吗?还有,你还有别的选择吗?@Junuxx:calling
update
是危险的。实际上,它启动了一个新的事件循环。如果在处理事件的过程中重新调用了相同的代码,则最终会出现嵌套的事件循环。每个事件循环都将阻塞,直到其嵌套子项完成。另外,因为Tkinter不是线程安全的,所以从另一个线程调用任何Tkinter命令都是有风险的。另一种选择很简单,在另一个线程中运行耗时的过程,或者在另一个进程中运行可能更好的过程。但是,如果您确保更新间隔足够长,以便在再次调用之前有效地确定更新已完成,那么这将是安全的?我已经尝试过这个方法,但没有成功,我在线程方面非常新,如果您只有一个简单的示例,这将非常有用;)有什么想法吗?我试过了,但没有成功。。。帮助会很有帮助;)