Обновление Label из цикла в tkinter

632 просмотра
0
0 Комментариев

Есть мой ужасный код и есть цикл, результат которого должен постоянно обновляться в tkinter.Label. Опыта в программировании мало, не могу никак до конца разобраться в классах и областях видимости.

В общем, суть в том, что лейбл, созданный внутри create_widgets(), должен обновляться из результата цикла for _ in range(30). Функции внутри start_reading() используются только там, поэтому мне казалось приемлемой идеей поместить функции внутрь другой функции (метода?). Нашел вот этот вопрос, но он мне не особо помог, к сожалению.

Теперь я конкретно запутался и прошу помощи. Как исправить код, чтобы решить проблему?

import tkinter as tk
 
class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.create_widgets()
 
    def create_widgets(self):
        ...  # Здесь должен быть лейбл
 
    def get_resolution(self, handle, client=True):
        ...
 
    def configuration(self):
        ...
 
    def start_reading(self, arg):
        a = ...
        b = ...
 
        def get_area(self, resolution, area, foo=None):
            ...
 
        spam = get_area(...)
        eggs = get_area(...)
 
        def get_hash(self, img):
            ...
 
        for _ in range(30):  # Лейбл должен изменятся каждый раз
            ...              # при изменении result, т.е. 30 раз
            result = ...
 
        del a  # После цикла должны быть удалены a и b.
        del b  # Только там не переменные на самом деле, а
               # .DeleteDC, .ReleaseDC и подобная ересь
 
if __name__ == '__main__':
    root = tk.Tk()
    app = Application(master=root)
    app.configuration()
    app.mainloop()


Добавить комментарий

2 Answers

Python Опубликовано 14.12.2018
0

import tkinter as tk
 
class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__()
        self.create_widgets()
 
    def create_widgets(self):
        self.label = tk.Label(text='None')  # лейбл
        self.label.pack()
 
    def start_reading(self, arg):
        if arg:
            self.label['text'] = arg  # Лейбл должен изменятся каждый раз
            self.label.update()  # обновить Лейбл
            root.after(500, self.start_reading, arg - 1)  # repeat the call
 
 
if __name__ == '__main__':
    root = tk.Tk()
    app = Application(master=root)
    root.after(1000, app.start_reading, 30)  # запустить start_reading после 1000ms
    app.mainloop()

Добавить комментарий
0

Идиоматичный способ организовать цикл обновления GUI элемента—это root.after():

#!/usr/bin/env python3
import tkinter
 
def loop(n):
    label['text'] = str(n)
    root.after(500, loop, n+1) # call loop(n+1) in 0.5 seconds
 
root = tkinter.Tk()
label = tkinter.Label(font=(None, 100))
label.pack()
root.after_idle(loop, 0)  # start loop
root.after(5000, root.destroy)  # quit in 5 seconds
# center window
root.eval('tk::PlaceWindow %s center' % root.winfo_pathname(root.winfo_id()))
root.mainloop()

Весь цикл в функции loop(). Ещё пример: countdown() функция.

Обратите внимание, что НЕ следует явный цикл с time.sleep() использовать, иначе GUI подвешивается/замораживается:

#XXX DO NOT DO IT
for n in range(10):
    label['text'] = str(n)
    time.sleep(0.5)

Обработчики событий (таких как нажатие клавиш, движение мыши, обратные вызовы (callback), запланированные программно с помощью root.after()) выполняются в одном GUI потоке, поэтому если поток спит на time.sleep() строчке или крутится в долгом/вечном цикле, то никакие другие события не обрабатываются—эффект «подвисания» GUI.

Это код можно попытаться спасти, используя label.update() и root.update_idletasks(), но вариант с root.after() является более гибким и надёжным.

Связанный вопрос: Мультизадачность на Python: выполнить две долгие функции одновременно, не блокируя GUI.

Добавить комментарий
Напишите свой ответ на данный вопрос.
Scroll Up