Вопрос-Ответ

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()

def task():
print("hello")
root.after(2000, task) # reschedule event in 2 seconds

root.after(2000, task)
root.mainloop()

Вот объявление и документация для after метода:

def after(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.

Исправленный код выглядит следующим образом:

# Run tkinter code in another thread

import tkinter as tk
import threading

class App(threading.Thread):

def __init__(self):
threading.Thread.__init__(self)
self.start()

def callback(self):
self.root.quit()

def run(self):
self.root = tk.Tk()
self.root.protocol("WM_DELETE_WINDOW", self.callback)

label = tk.Label(self.root, text="Hello World")
label.pack()

self.root.mainloop()


app = App()
print('Now we can continue running code while mainloop runs!')

for i in range(100000):
print(i)
Ответ 3

При написании собственного цикла, как при моделировании (я полагаю), вам нужно вызвать update функцию, которая делает то, что делает mainloop: обновляет окно вашими изменениями, но вы делаете это в своем цикле.

def task():
# do something
root.update()

while 1:
task()
Ответ 4

Другой вариант - позволить tkinter выполняться в отдельном потоке. Один из способов сделать это так:

import Tkinter
import threading

class MyTkApp(threading.Thread):
def __init__(self):
self.root=Tkinter.Tk()
self.s = Tkinter.StringVar()
self.s.set('Foo')
l = Tkinter.Label(self.root,textvariable=self.s)
l.pack()
threading.Thread.__init__(self)

def run(self):
self.root.mainloop()


app = MyTkApp()
app.start()

# 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.

python tkinter