如何在 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?
问题描述
我的代码显示一个按钮.当按下按钮时,出现一个文件对话框,要求用户选择一个文件(在消息框之后).这里没问题.
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.
GUI 冻结,只有在工作完成后才会更新进度条和任务状态.
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.
这是我正在处理的实际文件(Python 2):
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:
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 新手并且我不知道如何在不冻结/崩溃 GUI 的情况下将 tkfiledialog 放入那些中.
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.
推荐答案
我创建了与线程通信的队列
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.
我在开始线程之前请求文件 - 所以最后线程不使用任何 tkinter 的小部件或窗口.
I ask for file before starting thread - so finally thread doesn't use any tkinter's widget or window.
主线程使用 after()
定期运行检查队列的函数,如果有消息则获取消息并更新窗口中的标签.它还会更改 Progressbar
中的值.我使用 mode="determinate"
并且不使用 progressbar.start()
.
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()
.
如果消息是 "TASKS FINISHED"
那么函数不会再次检查队列.
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()
这篇关于如何在 python 2 tkinter 中显示当前任务的运行状态并更新进度条而不会同时冻结?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!