我的代码显示一个按钮。按下按钮时,会出现一个提示用户选择文件的文件(在消息框之后)。这里没问题。
当我想要更新进度条并显示当前任务的执行状态时,我的问题就出现了。
GUI冻结,只有在工作完成后才更新进度条和任务状态。
或者,如果有人能给我一个功能/类似的例子来做这件事,请。
这是我正在处理的实际文件(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中的新手,我不知道如何将tkfiledialog放在那些没有冻结/崩溃GUI的人身上。
我创建了与线程进行通信的队列
self.queue = Queue.Queue()
并使用将队列作为参数的函数运行线程。
self.thread = threading.Thread(target=self.my_function, args=(self.queue,))
线程将运行一些长时间运行的代码并使用队列将消息发送到主线程。 它不会在窗口小部件中显示任何消息框或更改值。
我在启动线程之前要求文件 - 所以最后线程不使用任何tkinter的小部件或窗口。
主线程使用after()
定期运行检查队列的功能,如果有消息则获取消息并在窗口中更新Label。它也改变了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()