问题
My code displays a button. When button is pressed, a filedialog appears to ask to user select a file (after a messagebox). No problem here.
My problem occurs when I want update the progressbar and show the status of current task in execution.
The GUI freezes, and the progressbar and task status are updated only after work was finished.
Or, if anyone can give me a functional/similar example to do this, please.
This is the actual file I'm working on (Python 2):
# -*- coding: utf-8 -*-
import os
import Tkinter
import ttk
import tkMessageBox
import tkFileDialog
import base64
import threading
import Queue
import subprocess
import sys
import time
#here write my tasks
class Tareas():
def __init__(self, parent, row, column, columnspan):
self.parent = parent
self.length=200
self.value=0
self.maximum=100
self.interval=10
#I changed this from the original code - progressbar
self.barra_progreso = ttk.Progressbar(parent, orient=Tkinter.HORIZONTAL,
length = self.length,
mode="determinate",
value=self.value,
maximum=self.maximum)
self.barra_progreso.grid(row=row, column=column,
columnspan=columnspan)
#creating a thread to avoid gui freezing
self.thread = threading.Thread()
# status label tite (this does not change)
self.lbl_estado = Tkinter.Label(parent, text='STATUS:')
self.lbl_estado.grid(row=9, column=0, padx = 20, pady = 5)
# creating the status variable and declaring its value
self.estado_aplicacion = Tkinter.StringVar()
self.estado_aplicacion.set("Started, waiting for a task...")
# ***HERE I WANT DISPLAY CURRENT TASK RUNNING***
self.lbl_info_estado = Tkinter.Label(parent, text=self.estado_aplicacion.get(), textvariable=self.estado_aplicacion)
self.lbl_info_estado.grid(row=10, column=0, padx = 20, pady = 5)
def extraerDatosArchivo(self):
#task 1
print 'tarea 1'
#CHANGING TASK STATUS
self.estado_aplicacion.set('Seleccionando respaldo válido... (1/6)')
#displaying a messagebox to indicate to user choose a backup
tkMessageBox.showinfo('INFORMACIÓN', 'Select file to decrypt.')
#asking for a backup
archivo_respaldo = tkFileDialog.askopenfile(initialdir="/", title="Select file", filetypes=(("All files", "*.*"), ("All files2", "*.*")) )
#getting file
print 'archivo a desencriptar: ', archivo_respaldo
#checking if a file exists
if archivo_respaldo is None or not archivo_respaldo:
tkMessageBox.showerror('ERROR', 'No seleccionó nada.')
return None #stop task without close gui
###activating progressbar
if not self.thread.isAlive():
VALUE = self.barra_progreso["value"]
self.barra_progreso.configure(mode="indeterminate",
maximum=self.maximum,
value=VALUE)
self.barra_progreso.start(self.interval)
###
#CHANGING TASK STATUS
self.estado_aplicacion.set('Copiando clave privada... (2/6)')
#simulating long task
time.sleep(4)
print '2'
#CHANGING TASK STATUS
self.estado_aplicacion.set('Creando carpeta de trabajo... (3/6)')
#simulating long task
time.sleep(4)
print '3'
#CHANGING TASK STATUS
self.estado_aplicacion.set('TASKS FINISHED')
#displaying task finished succesfully
tkMessageBox.showinfo('INFORMATION', 'Done!.')
#gui tool, buttons, bla, bla, and more...
class GUI(Tkinter.Frame):
""" class to define tkinter GUI"""
def __init__(self, parent,):
Tkinter.Frame.__init__(self, master=parent)
"""desde aca se va controlar la progressbar"""
tareas = Tareas(parent, row=8, column=0, columnspan=2) #putting prog bar
#button for task 1
btn_extraer_datos_archivo = Tkinter.Button(parent, text = 'Select file', width=24, height=2, command=tareas.extraerDatosArchivo, state='normal')
btn_extraer_datos_archivo.grid(row=2, column=0, padx = 40, pady = 5)
root = Tkinter.Tk()
root.title('Extractor de datos 1.0')#title tool
root.minsize(200, 200)#bla bla...
root.resizable(0,0)#disabling resizing
herramienta = GUI(root)
root.mainloop()
I tried to find examples that could helped me in this:
How to connect a progress bar to a function?
https://reformatcode.com/code/python/tkinter-how-to-use-threads-to-preventing-main-event-loop-from-quotfreezingquot
http://pythonexample.com/snippet/python/progresspy_rtogo_python
http://pythonexample.com/snippet/python/progresspy_c02t3x_python
https://www.reich13.tech/python-how-to-get-progressbar-start-info-from-one-window-class-to-other-5a26adfbcb90451297178f35
https://www.python-forum.de/viewtopic.php?f=18&t=19150
and more...
But these seems difficult yet for me because I'm newbie in python and I don´t have idea how to put tkfiledialog in those without freezing/crashing the GUI.
回答1:
I create queue for communication with thread
self.queue = Queue.Queue()
and run thread with function which gets queue as parameter.
self.thread = threading.Thread(target=self.my_function, args=(self.queue,))
Thread will run some long-running code and use queue to send messages to main thread.
It will NOT display any message box or change values in widgets.
I ask for file before starting thread - so finally thread doesn't use any tkinter's widget or window.
Main thread uses after()
to periodically run function which checks queue and if there is message it gets message and updates Label in window. It also changes value in Progressbar
. I use mode="determinate"
and don't use progressbar.start()
.
If message is "TASKS FINISHED"
then function doesn't check queue again.
Code works as you probably need.
I removed all your comments in code and there are only my comments.
import os
import Tkinter
import ttk
import tkMessageBox
import tkFileDialog
import threading
import Queue
#import sys
import time
class Tareas():
def __init__(self, parent, row, column, columnspan):
self.parent = parent
self.length=200
self.value=0
self.maximum=100
self.interval=10
self.barra_progreso = ttk.Progressbar(parent, orient=Tkinter.HORIZONTAL,
length = self.length,
mode="determinate",
value=self.value,
maximum=self.maximum)
self.barra_progreso.grid(row=row, column=column,
columnspan=columnspan)
self.lbl_estado = Tkinter.Label(parent, text='STATUS:')
self.lbl_estado.grid(row=9, column=0, padx = 20, pady = 5)
self.estado_aplicacion = Tkinter.StringVar()
self.estado_aplicacion.set("Started, waiting for a task...")
self.lbl_info_estado = Tkinter.Label(parent, text=self.estado_aplicacion.get(), textvariable=self.estado_aplicacion)
self.lbl_info_estado.grid(row=10, column=0, padx = 20, pady = 5)
def extraerDatosArchivo(self):
print 'tarea 1'
# do some job before you run thread
self.estado_aplicacion.set('Seleccionando respaldo válido... (1/6)')
tkMessageBox.showinfo('INFORMACIÓN', 'Select file to decrypt.')
archivo_respaldo = tkFileDialog.askopenfile(initialdir="/home/furas", title="Select file", filetypes=(("All files", "*.*"), ("All files2", "*.*")) )
print 'archivo a desencriptar: ', archivo_respaldo
if archivo_respaldo is None or not archivo_respaldo:
tkMessageBox.showerror('ERROR', 'No seleccionó nada.')
return
# --- (re)set progressbar ---
# set progressbar for 6+1 steps and `mode="determinate"`.
# because first step is already done so set value=1
self.barra_progreso.configure(#mode="indeterminate",
maximum=7,
value=1)
# don't start progresbar - I will change it manually
#self.barra_progreso.start()#self.interval)
# --- here starts thread ---
# create queue for communication with thread
self.queue = Queue.Queue()
# create thread and send queue as argument
self.thread = threading.Thread(target=self.my_function, args=(self.queue,))
# start thread
self.thread.start()
# start checking queue
self.check_queue()
def check_queue(self):
print("check queue")
# check if something in queue
# because `queue.get()` may block program when it waits for message
if not self.queue.empty():
# get message from queue
text = self.queue.get()
print("get text from queue:", text)
# change status
self.estado_aplicacion.set(text)
# TODO: you can update progressbar
self.barra_progreso['value'] += 1
# check if it is last message
if text == 'TASKS FINISHED':
# stop progersbar
self.barra_progreso.stop()
#displaying task finished succesfully
tkMessageBox.showinfo('INFORMATION', 'Done!.')
# exit without running `root.after()` again
return
# check queue after 200ms (0.2s) so mainloop will can do its job
root.after(200, self.check_queue)
def my_function(self, queue):
#CHANGING TASK STATUS
queue.put('Copiando clave privada... (2/6)')
#simulating long task
time.sleep(4)
print '2'
#CHANGING TASK STATUS
queue.put('Creando carpeta de trabajo... (3/6)')
#simulating long task
time.sleep(4)
print '3'
#CHANGING TASK STATUS
queue.put('Creando carpeta de trabajo... (4/6)')
#simulating long task
time.sleep(4)
print '4'
#CHANGING TASK STATUS
queue.put('Creando carpeta de trabajo... (5/6)')
#simulating long task
time.sleep(4)
print '5'
#CHANGING TASK STATUS
queue.put('Creando carpeta de trabajo... (6/6)')
#simulating long task
time.sleep(4)
print '6'
#CHANGING TASK STATUS
queue.put('TASKS FINISHED')
class GUI(Tkinter.Frame):
""" class to define tkinter GUI"""
def __init__(self, parent,):
Tkinter.Frame.__init__(self, master=parent)
tareas = Tareas(parent, row=8, column=0, columnspan=2)
btn_extraer_datos_archivo = Tkinter.Button(parent, text = 'Select file', width=24, height=2, command=tareas.extraerDatosArchivo, state='normal')
btn_extraer_datos_archivo.grid(row=2, column=0, padx = 40, pady = 5)
# --- main ---
root = Tkinter.Tk()
root.title('Extractor de datos 1.0')
root.minsize(200, 200)
root.resizable(0,0)
herramienta = GUI(root)
root.mainloop()
来源:https://stackoverflow.com/questions/48019622/how-can-i-show-status-of-current-task-running-and-update-the-progressbar-without