How can I show status of current task running and update the progressbar without freezing at same time in python 2 tkinter?

前端 未结 1 2033
生来不讨喜
生来不讨喜 2021-01-25 20:55

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 wan

相关标签:
1条回答
  • 2021-01-25 21:56

    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()
    
    0 讨论(0)
提交回复
热议问题