问题
I'm currently creating a Tkinter Gui for Python 2.7 and having trouble working the progress bar. I need to load largish files into my program which takes some time, so I wanted to get a progress bar to show the user the program isn't frozen loading the files. Unfortunately my progress bar does not seem to update while loading files :( I've tried creating a new thread for the progress bar with no luck. So I'm wondering what do I need to do to get an indeterminate progress bar to run during a heavy function call?
What the relevant parts of my code looks like are:
import Tkinter as tk
import ttk as ttk
import pandas as pd
import tkFileDialog as tfgiag
self.pb = ttk.Progressbar(frame, orient=tk.VERTICAL, mode='indeterminate')
mynewdata = tfgiag.askopenfilenames(parent=root,title='Choose a file',filetypes=[('CSV files', '.csv')])
self.t = threading.Thread(target = self.pb.start)
self.t.start()
#read in each CSV file selected by the user
for myfile in root.tk.splitlist(mynewdata):
foo = pd.read_csv(myfile)
self.data.appendMainData(foo)
self.pb.stop()
回答1:
Python "threads" are all still sort of locked together sequentially by what's called the GIL, global interpreter lock. It basically means that threads spawned from the same python process won't run in parallel like you want them to. Instead, they all fight for time on the main python process.
In your case, if there's an intensive process you're trying to monitor with once process, its probably hogging the GIL, and not releasing it to the thread.
One option: Try using a readline method, so it splits up the file input work enough to insert a progress bar update line.
openfile = open(filename, 'r')
for eachline in openfile.readlines():
append_line(eachline)
update_progressBar()
Another option that may be easier is to offload the csv opening to another process using python's multiprocessing module. This emulates the threads you're probably more used to. I'd kick off a new process that reads in the csv, and appends the lines to a queue. When it's done, append a sentinel value to the queue signalling its done, so the main process knows when to stop updating the progress bar and join the spawned process. Something like:
import Tkinter as tk
import ttk as ttk
import pandas as pd
import tkFileDialog as tfgiag
from multiprocessing import Process, Queue
self.pb = ttk.Progressbar(frame, orient=tk.VERTICAL, mode='indeterminate')
mynewdata = tfgiag.askopenfilenames(parent=root,title='Choose a file',filetypes=[('CSV files', '.csv')])
csvqueue=Queue(1) #A mp-enabled queue with one slot to results.
#read in each CSV file selected by the user
offloadedProcess=Process(target=csvread, args=(filename, outputqueue))
offloadedProcess.start()
procNotDone=False
while procNotDone:
result = getNewResultFromQueue(outputqueue) #pesudo code
update_ProgressBar() #<--- this should get hit more often now
if result.isLastValue:
offloadedProcess.join() #Join the process, since its now done
else:
csvresults.append(result)
def csvreadOffload(filename, outputqueue):
for myfile in root.tk.splitlist(mynewdata):
foo = pd.read_csv(myfile)
if foo is not END:#Pesudo code here
outputqueue.append(foo)
else:
outputqueue.append('Empty results')#pseudo code
回答2:
use self.frame.update_idletasks()
after every self.pb.step(x)
statement,where 'x' stands for the value by which progressbar's value increases
来源:https://stackoverflow.com/questions/18899440/tkinter-indeterminate-progress-bar-not-running