How do you run your own code alongside Tkinter's event loop?
Как вы запускаете свой собственный код вместе с циклом событий Tkinter?
Мой младший брат только начинает заниматься программированием, и для своего проекта Science Fair он моделирует стаю птиц в небе. Он написал большую часть своего кода, и это прекрасно работает, но птицам нужно двигаться каждый момент.
Однако Tkinter тратит время на свой собственный цикл событий, и поэтому его код не запускается. Выполнение root.mainloop() выполняется, выполняется и продолжает выполняться, и единственное, что оно запускает, - это обработчики событий.
Есть ли способ заставить его код запускаться вместе с основным циклом (без многопоточности, это сбивает с толку, и это должно быть простым), и если да, то что это?
Прямо сейчас он придумал уродливый взлом, привязав свою move() функцию к <b1-motion>, так что пока он удерживает кнопку нажатой и двигает мышью, это работает. Но должен быть способ получше.
Переведено автоматически
Ответ 1
Используйте after метод для Tk объекта:
from tkinter import *
root = Tk()
deftask(): print("hello") root.after(2000, task) # reschedule event in 2 seconds
root.after(2000, task) root.mainloop()
Вот объявление и документация для after метода:
defafter(self, ms, func=None, *args): """Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the function which shall be called. Additional parameters are given as parameters to the function call. Return identifier to cancel scheduling with after_cancel."""
Ответ 2
Решение, опубликованное Бьорном, приводит к появлению сообщения "RuntimeError: вызов Tcl из другой квартиры" на моем компьютере (RedHat Enterprise 5, python 2.6.1). Бьорн, возможно, не получил это сообщение, поскольку, согласно одному месту, которое я проверил, неправильное выполнение потоков с помощью Tkinter непредсказуемо и зависит от платформы.
Проблема, похоже, в том, что app.start() считается ссылкой на Tk, поскольку приложение содержит элементы Tk. Я исправил это, заменив app.start() на self.start() inside __init__. Я также сделал так, что все ссылки на Tk находятся либо внутри функции, которая вызывает mainloop(), либо внутри функций, которые вызываются функцией, которая вызывает mainloop() (по-видимому, это важно, чтобы избежать ошибки "different apartment").
Наконец, я добавил обработчик протокола с обратным вызовом, поскольку без этого программа завершается с ошибкой при закрытии пользователем окна Tk.
app = App() print('Now we can continue running code while mainloop runs!')
for i inrange(100000): print(i)
Ответ 3
При написании собственного цикла, как при моделировании (я полагаю), вам нужно вызвать update функцию, которая делает то, что делает mainloop: обновляет окно вашими изменениями, но вы делаете это в своем цикле.
deftask(): # do something root.update()
while1: task()
Ответ 4
Другой вариант - позволить tkinter выполняться в отдельном потоке. Один из способов сделать это так:
# Now the app should be running and the value shown on the label # can be changed by changing the member variable s. # Like this: # app.s.set('Bar')
Однако будьте осторожны, многопоточное программирование сложно, и действительно легко прострелить себе ногу. Например, вы должны быть осторожны при изменении переменных-членов приведенного выше примера класса, чтобы не прерывать цикл событий Tkinter.