I am writing a program in python that will process a big amount of data it reads from some excel file. I've build a GUI for this program using Tkinter. I know Tkinter is single threaded, hence to open the file and make some processes I've used a thread not to block the GUI. One of the thread tasks is to fill a list (called columnList in my code) and use its elements as options in an optionmenu, so before the thread finishes the option menu is empty, hence I've used join () to let the main thread wait for the worker thread. And, here comes the problem, as long as the worker thread is executing the GUI will be not responding (around 7 seconds), but after that it will work normally.
I want to use some graphical indicator that indicates that something is being loaded, and at the same time blocks the the GUI window so the user can't click on it. After the thread stops, the indicator should disappear and the GUI should be enabled again. I searched for such a concept, but I didn't find such a thing on the web, this question here, Python Tkinter: loading screen is very similar to my case, but it has no answer.
This is a part of my code where I need to apply the concept:
(working code example)
__author__ = 'Dania'
import threading
from Tkinter import *
from tkFileDialog import askopenfilename
import numpy as np
import xlrd
global x
global v
x = np.ones(5)
v= np.ones(5)
global columnList
columnList=""
def open_file (file_name):
try:
workbook = xlrd.open_workbook(file_name)
sheet=workbook.sheet_by_index(0)
global columns
columns = [] #this is a list, in each index we will store a numpy array of a column values.
for i in range (0,sheet.ncols-1):
columns.append(np.array (sheet.col_values(i,1))) # make a list, each index has a numpy array that represnts a column. 1 means start from row 1 (leave the label)
if (i!=0):
columns[i]= columns[i].astype(np.float)
#Preprocessing columns[0]:
m= columns [0]
for i in range (m.shape[0]):
m[i]= m[i]*2 +1
m=m.astype(np.int)
columns[0]=m
global columnList
columnList= np.array(sheet.row_values(0)) #I was using sheet.row(0), but this is better since it doesn't return a 'u'
columnList=columnList.astype(np.str)
# removing nans:
index=input("enter the column index to interpolate: ") #this should be user input
n= columns [index]
for i in range (n.shape[0]-1, -1, -1):
if (np.isnan(n[i])):
n=np.delete(n,i)
columns[0]=np.delete(columns[0],i)
columns [index]= np.delete(columns[index],i)
except IOError:
print ("The specified file was not found")
global x
np.resize(x, m.shape[0])
x=columns[0]
global v
np.resize(v,n.shape[0])
v=columns[index]
#return columns [0], columns [index]
class Interface:
def __init__(self, master):
self.title= Label(master,text="Kriging Missing data Imputation", fg="blue", font=("Helvetica", 18))
self.select_file= Label (master, text="Select the file that contains the data (must be an excel file): ", font=("Helvetica", 12))
self.title.grid (row=1, column=5, columnspan= 4, pady= (20,0))
self.select_file.grid (row=3, column=1, sticky=W, pady=(20,0), padx=(5,2))
self.browse_button= Button (master, text="Browse", command=self.browser, font=("Helvetica", 12), width=12)
self.browse_button.grid (row=3, column=3, pady=(20,0))
self.varLoc= StringVar(master)
self.varLoc.set("status")
self.varColumn= StringVar(master)
self.varColumn.set("")
self.locationColumn= Label(master,text="Select a column as a location indicator", font=("Helvetica", 12))
self.columnLabel= Label(master,text="Select a column to process", font=("Helvetica", 12))
global locationOption
global columnOption
columnOption= OptionMenu (master, self.varColumn,"",*columnList)
locationOption= OptionMenu (master, self.varLoc,"",*columnList)
self.locationColumn.grid (row=5, column=1, pady=(20,0), sticky=W, padx=(5,0))
locationOption.grid (row=5, column=3, pady=(20,0))
self.columnLabel.grid (row=7, column=1, pady=(20,0), sticky=W, padx=(5,0))
columnOption.grid(row=7, column= 3, pady= (20,0))
self.missing_label= Label(master, text="Select missing data indicator: ", font=("Helvetica", 12))
self.var = StringVar (master)
self.var.set("nan")
self.menu= OptionMenu (master, self.var,"nan", "?", "*")
self.missing_label.grid (row=9, column=1, padx=(5,2), pady= (20,0), sticky=W)
self.menu.grid(row=9, column=3, pady= (20,0))
self.extrapolate= Label (master, text="Select a range for extrapolation (max=800): ", font=("Helvetica", 12))
self.max_extra= Entry (master)
self.extrapolate.grid (row=11, column=1, padx=(5,2), pady= (20,0), sticky=W)
self.max_extra.grid (row=11, column=3, pady=(20,0))
self.a_label= Label (master, text="enter the value of a (range): ", font=("Helvetica", 12))
self.a_value= Entry (master)
self.a_label.grid (row=13, column=1, padx=(5,2), pady=(20,0), sticky=W)
self.a_value.grid (row=13, column=3, pady=(20,0))
self.start_button= Button (master, text="Start", font=("Helvetica", 12), width=12)
self.pause_button= Button (master, text= "Pause", font=("Helvetica", 12),width=12)
self.stop_button= Button (master, text="stop", font=("Helvetica", 12),width=12)
self.start_button.grid (row=15, column=1, pady=(30,0) )
self.pause_button.grid (row=15, column=2, pady=(30,0))
self.stop_button.grid (row=15, column=3, pady=(30,0))
def browser (self):
filename = askopenfilename()
#indicator should start here.
t=threading.Thread (target=open_file, args=(filename, ))
t.start()
t.join() #I use join because if I didn't,next lines will execute before open_file is completed, this will make columnList empty and the code will not execute.
#indicator should end here.
opt=columnOption.children ['menu']
optLoc= locationOption.children ['menu']
optLoc.entryconfig (0,label= columnList [0], command=self.justamethod)
opt.entryconfig (0, label= columnList [0], command=self.justamethod)
for i in range(1,len (columnList)):
opt.add_command (label=columnList[i], command=self.justamethod)
optLoc.add_command (label=columnList[i], command=self.justamethod)
def justamethod (self):
print("method is called")
print(self.varLoc.get())
window= Tk () #main window.
starter= Interface (window)
window.mainloop() #keep the window open until the user decides to close it.
I've tried to add some progress bar inside the method browser like this,
def browser (self):
filename = askopenfilename()
progressbar = ttk.Progressbar(orient=HORIZONTAL, length=200, mode='determinate')
progressbar.pack(side="bottom")
progressbar.start()
t=threading.Thread (target=open_file, args=(filename, ))
t.start()
t.join() #I use join because if I didn't,next lines will execute before open_file is completed, this will make columnList empty and the code will not execute.
progressbar.stop()
opt=columnOption.children ['menu']
opt.entryconfig (0, label= columnList [0], command=self.justamethod)
for i in range(1,len (columnList)):
opt.add_command (label=columnList[i], command=self.justamethod)
optLoc.add_command (label=columnList[i], command=self.justamethod)
def justamethod (self):
print("method is called")
window= Tk () #main window.
starter= Interface (window)
window.mainloop() #keep the window open until the user decides to close it.
But, the code above doesn't even show the progress bar, and it's not what I really need.