如何在 python 2 tkinter 中同时显示当前任务 运行 的状态并更新进度条而不冻结?
How can I show status of current task running and update the progressbar without freezing at same time in python 2 tkinter?
我的代码显示一个按钮。按下按钮时,
一个文件对话框似乎向用户 select 询问一个文件
(在消息框之后)。这里没问题。
我想更新进度条时出现问题
并显示当前正在执行的任务的状态。
GUI 冻结,进度条和任务状态仅在工作完成后更新。
或者,如果有人可以给我一个 functional/similar 示例来执行此操作,请。
这是我正在处理的实际文件 (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()
我试图找到可以帮助我解决这个问题的例子:
How to connect a progress bar to a function?
http://pythonexample.com/snippet/python/progresspy_rtogo_python
http://pythonexample.com/snippet/python/progresspy_c02t3x_python
https://www.python-forum.de/viewtopic.php?f=18&t=19150
还有更多...
但是这些对我来说似乎还很困难,因为我是 python 的新手并且
我不知道如何在没有 freezing/crashing GUI 的情况下放置 tkfiledialog。
我创建了与线程通信的队列
self.queue = Queue.Queue()
和运行 具有获取队列作为参数的函数的线程。
self.thread = threading.Thread(target=self.my_function, args=(self.queue,))
线程将运行一些长运行宁代码并使用队列向主线程发送消息。
它将不会显示任何消息框或更改小部件中的值。
我在启动线程之前请求文件 - 所以最终线程不使用任何 tkinter 的小部件或 window。
主线程使用 after()
定期 运行 函数检查队列,如果有消息,它获取消息并更新 window 中的标签。它还会更改 Progressbar
中的值。我用 mode="determinate"
不用 progressbar.start()
.
如果消息是 "TASKS FINISHED"
则函数不会再次检查队列。
代码可以满足您的需要。
我删除了你在代码中的所有评论,只有我的评论。
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()
我的代码显示一个按钮。按下按钮时, 一个文件对话框似乎向用户 select 询问一个文件 (在消息框之后)。这里没问题。
我想更新进度条时出现问题 并显示当前正在执行的任务的状态。
GUI 冻结,进度条和任务状态仅在工作完成后更新。
或者,如果有人可以给我一个 functional/similar 示例来执行此操作,请。
这是我正在处理的实际文件 (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()
我试图找到可以帮助我解决这个问题的例子:
How to connect a progress bar to a function?
http://pythonexample.com/snippet/python/progresspy_rtogo_python
http://pythonexample.com/snippet/python/progresspy_c02t3x_python
https://www.python-forum.de/viewtopic.php?f=18&t=19150
还有更多...
但是这些对我来说似乎还很困难,因为我是 python 的新手并且 我不知道如何在没有 freezing/crashing GUI 的情况下放置 tkfiledialog。
我创建了与线程通信的队列
self.queue = Queue.Queue()
和运行 具有获取队列作为参数的函数的线程。
self.thread = threading.Thread(target=self.my_function, args=(self.queue,))
线程将运行一些长运行宁代码并使用队列向主线程发送消息。
它将不会显示任何消息框或更改小部件中的值。
我在启动线程之前请求文件 - 所以最终线程不使用任何 tkinter 的小部件或 window。
主线程使用 after()
定期 运行 函数检查队列,如果有消息,它获取消息并更新 window 中的标签。它还会更改 Progressbar
中的值。我用 mode="determinate"
不用 progressbar.start()
.
如果消息是 "TASKS FINISHED"
则函数不会再次检查队列。
代码可以满足您的需要。
我删除了你在代码中的所有评论,只有我的评论。
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()