繁体   English   中英

如何在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?

我的代码显示一个按钮。 当按下按钮时,出现文件对话框,要求用户选择文件(在消息框后)。 没问题

我想更新进度条并显示正在执行的当前任务的状态时,会发生我的问题。

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()

我试图找到可以帮助我解决这个问题的示例:

如何将进度条连接到功能?

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

和更多...

但是这些对我来说似乎仍然很困难,因为我是python的新手,而且我不知道如何在不冻结/崩溃GUI的情况下将tkfiledialog放入其中。

我创建与线程通信的队列

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()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM