2012-06-02 7 views
5

Là một lập trình GUI mới và tôi đang cố gắng tạo GUI cho một trong các trình phân tích cú pháp python của tôi.Python & Tkinter -> Giới thiệu về việc gọi một hàm chạy dài đóng băng chương trình

Tôi biết rằng:

  • Tkinter là đơn ren. Cập nhật màn hình diễn ra trên mỗi chuyến đi qua vòng lặp sự kiện. Bất cứ lúc nào bạn có một lệnh chạy dài, bạn đang ngăn chặn vòng lặp sự kiện hoàn thành một lần lặp, do đó ngăn chặn việc xử lý các sự kiện, do đó ngăn không cho vẽ lại.

  • Chương trình của tôi gọi hàm lớn mất khoảng 5 phút để chạy hoàn toàn. Vì vậy, tôi đoán giải pháp duy nhất là tu sử dụng thread cho lệnh chạy dài. NHƯNG, lệnh chạy dài của tôi đã được tạo luồng nên tôi không thực sự biết cách tiến hành.

-> Ngay sau khi tôi nhấp vào BUT1 trong GUI, chương trình sẽ đóng băng cho đến khi hàm hoàn tất. Tôi muốn chạy hàm này trong backgroung, vì vậy chương trình sẽ không bị đóng băng.

-> Tôi không tìm kiếm giải pháp hoàn chỉnh nhưng nếu ai đó có thể giúp tôi có một ca khúc tốt, nó sẽ rất tuyệt vời!

  • Main.py -> Các GUI
  • Module_1.py -> Chức năng mà chúng ta gọi bằng cách nhấp vào nút BUT1

Cảm ơn bạn trước!

Đây là 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() 

Và đây là Module_1.py -> chứa hàm lớn

#!/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') 

Vì vậy, bằng cách nhấp vào BUT1, chúng tôi chạy chức năng chính ("BUT1") chứa trong tệp Module_1.py với đối số BUT1 khởi chạy -> chạy ('Hàm1', SPIDER1(), 'C: \ LOL \ Output1' + DATE + '. csv') Và sau đó là chương trình bị đóng băng cho đến khi trình phân tích cú pháp kết thúc là công việc .. :)

Trả lời

3

Vấn đề rất đơn giản: BUT1 sẽ không trở lại cho đến khi cuộc gọi đến số main trở về. Miễn là main (và do đó, BUT1) không trả lại, GUI của bạn sẽ bị đóng băng.

Để làm việc này, bạn phải đặt main trong một chuỗi riêng biệt. Nó không đủ rằng main sinh ra các chủ đề khác nếu tất cả nó đang làm là chờ đợi cho những chủ đề.

+0

Tôi đã thử điều này nhưng không thành công, tôi khá mới trong việc phân luồng, nếu bạn chỉ có một ví dụ ngắn gọn, nó sẽ rất hữu ích;) –

+0

Bất kỳ ý tưởng nào? Tôi đã thử nhưng không thành công ... Trợ giúp sẽ rất hữu ích;) –

2

Nếu bạn gọi root.update() thỉnh thoảng từ chức năng BUT1, điều đó sẽ ngăn không cho GUI bị đóng băng. Bạn cũng có thể làm điều đó từ một chuỗi python với một khoảng thời gian cố định.

Ví dụ, cập nhật mỗi 0,1 giây:

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) 

Sau khi chức năng lớn hoàn thành bạn có thể đặt self.updateNeeded False.

+0

Rất thú vị! Tôi đang cố gắng gọi root.update() từ hàm BUT1 và quay lại để đặt chủ đề này là đóng nếu nó hoạt động! Cảm ơn bạn rất nhiều, có vẻ như rất hứa hẹn! –

+1

@NokyamaShi: Đây là * rất * nguy hiểm. Bạn đã thực sự thử điều này chưa? Tôi sẽ ngạc nhiên nếu nó làm việc và cuối cùng không gây ra một bế tắc hoặc tai nạn tại một số điểm. Gọi 'update' thường là một thực hành tồi, và làm như vậy từ một thread khác có vẻ khá nguy hiểm. Về cơ bản bạn đang tạo vòng lặp sự kiện thứ hai khi bạn gọi 'cập nhật' và nếu sự kiện xảy ra trong quá trình đó khiến mã này được gọi lại, bạn có thể gặp sự cố thực. –

+0

@BryanOakley: Tôi đã sử dụng điều này mà không có bất kỳ vấn đề bản thân mình, bạn có thể giải thích những loại 'vấn đề thực sự' bạn có nghĩa là gì? Nó sẽ không đủ để ngăn chặn các cửa sổ trở nên không phản hồi? Ngoài ra, bạn có một sự thay thế? – Junuxx