簡體   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