Tkinter mainloop, после и многопоточность путаницы с трудоемким кодом - PullRequest
0 голосов
/ 07 октября 2019

У меня есть рабочий код для обучения нейронной сети. Теперь я хочу создать графический интерфейс TkInter, чтобы иметь возможность, например, изменять параметры на лету. Я представляю, что это похоже на пульт дистанционного управления. Но я изо всех сил пытаюсь получить отзывчивый интерфейс. Обучение одной эпохи займет пару минут, и я не смогу перехватить.

Я видел много примеров использования метода .after () для обновления часов в графическом интерфейсе, которыйхорошо, потому что обновление не занимает минут. Я не могу заставить его работать, когда обратный вызов занимает минуты.

Я воссоздал свою проблему с минимальным количеством кода:

from time import sleep
import tkinter as tk

def trainEpoch(i):
    print ("Training", i)
    sleep(1) #in reality more like sleep(300)

def clickBtn():
    print ("Button pressed")

root = tk.Tk()
btn = tk.Button(root, text="Click Me", command=clickBtn)
btn.grid(column=0, row=0)
for i in range (5):
    trainEpoch(i)
print ("finished" )
root.mainloop()

Этот код сначала "обучает" сеть,затем создает отзывчивое окно TkInter (которое я полностью понимаю). Просто чтобы прояснить, я хочу следующее поведение: я хочу, чтобы графический интерфейс открывался до начала тренировки и был активным, когда тренировка делает свое дело. Мне все равно, если он будет уничтожен впоследствии.

Нужно ли мне многопоточность для такого рода проблемы?

С уважением, Лев

1 Ответ

0 голосов
/ 07 октября 2019

Вы не можете использовать sleep в том же потоке, что и tkinter. Поскольку tkinter является однопоточным, вы заблокируете mainloop от выполнения каких-либо действий до тех пор, пока не будет завершен спящий режим.

Вот пример использования after() для выполнения той же задачи без блокировки вашего приложения tkinter.

import tkinter as tk


def train_epoch(i):
    if i <= 5:
        print("Training", i)
        i += 1
        root.after(2000, lambda i=i: train_epoch(i))
    else:
        print("finished")


def click_btn():
    print("Button pressed")


root = tk.Tk()
tk.Button(root, text="Click Me", command=click_btn).grid(column=0, row=0)
train_epoch(1)
root.mainloop()

Результаты:

enter image description here

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

См. Этот пример:

import tkinter as tk
import threading
import time


def train_epoch():
    global some_var
    while some_var <= 5:
        print(some_var)
        time.sleep(0.5)


def click_btn():
    global some_var
    some_var += 1
    print("Button pressed")


root = tk.Tk()
some_var = 0
tk.Button(root, text="Click Me", command=click_btn).pack()
thread = threading.Thread(target=train_epoch)
thread.start()

root.mainloop()
...