一尘不染

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

python

这是GUI编程中的新功能,我正在尝试为我的python解析器之一创建GUI。

我知道 :

  • Tkinter是单线程的。屏幕更新在整个事件循环中每次旅行时发生。每当您使用长时间运行的命令时,都在阻止事件循环完成迭代,从而阻止了事件的处理,从而阻止了重绘。

  • 我的程序调用了一个大函数,大约需要5分钟才能完全运行它。因此,我猜唯一的解决方案是针对长期运行的命令使用线程。但是,我长期运行的命令已经在线程中,所以我真的不知道如何继续。

->在GUI中单击BUT1后,程序将冻结,直到功能完全完成。我想 在backgroung中 运行此功能,因此程序不会冻结。

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

  • 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->包含big函数

#!/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’+ DATE +
‘.csv’),然后程序冻结,直到解析器完成工作为止.. :)


阅读 352

收藏
2021-01-20

共1个答案

一尘不染

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

为此,您必须放入main一个单独的线程。main如果所有其他线程正在等待这些线程,则生成其他线程是不够的。

2021-01-20