6

GUI プログラミングの初心者で、Python パーサーの 1 つの 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 で実行されます。 '.csv') そして、パーサーが終了するまでプログラムがフリーズします.. :)

4

2 に答える 2

3

問題は単純です:BUT1への呼び出しが戻るまで戻りませんmainmain(したがって、BUT1) が戻らない限り、GUI はフリーズします。

mainこれを機能させるには、別のスレッドを作成する必要があります。main他のスレッドを生成するだけでは、それらのスレッドを待機するだけでは十分ではありません。

于 2012-06-02T13:50:24.513 に答える
2

root.update()BUT1関数から時々呼び出す場合は、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に設定できます。

于 2012-06-02T11:59:55.060 に答える