I'm new to GUI programming, and I'm trying to make a GUI for one of my python parsers.
I know that:
Tkinter is single threaded. Screen updates occur every time you go through the event loop. Each time you have a long command, you forbid the event loop to complete the iteration, thereby preventing the processing of events, thereby preventing redrawing.
My program calls a great function, which takes about 5 minutes to run completely. Therefore, I assume that the only solution is to use a thread for a long command. BUT, my long team is in a thread already cut, so I really don't know how to do this.
-> As soon as I click on BUT1 in the GUI, the program freezes until the function is fully executed. I would like to run this function in backgroung, so the program does not freeze.
-> I'm not looking for a complete solution, but if someone can put me on a good track, it will be great!
- Main.py → GUI
- Module_1.py → The function we call by pressing the BUT1 button
Thank you in advance!
Here is the Main.py file -> GUI
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")
self.TEXT_INFO.config(text="BUT1 FINISHED")
self.BUT1.config(text="DONE")
root = Tk()
app = App(root)
root.mainloop()
And Module_1.py -> contains a great function
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)
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
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":
elif choice == "BUT1":
run('Function1',SPIDER1(),'C:\LOL\Output1'+DATE+'.csv')
So, by pressing BUT1, we will run the main ("BUT1") function contained in the Module_1.py file, with the argument BUT1, which launches → run ("Function1", SPIDER1 (), "C: \ LOL \ Output1" '+ DATE + '. CSV ") And then the program freezes until the parser finishes work. :)