Tkinter Turtles and Threads
-
27-10-2019 - |
문제
world! In turtle graphics in python, there's possible to create various Turtle objects and manipulate them with their methods, forward, backward... I wanted to experiment with threads so I wrote a threaded class called MyTurtleManipulator.
from threading import Thread
from cTurtle import Turtle
import random
class MyTurtleManipulator(Thread):
def __init__(self, turtle):
Thread.__init__(self)
self.turtle=turtle
def run(self):
actions=[Turtle.forward, Turtle.right, Turtle.left]
while True:
action=random.choice(actions)
action(self.turtle, random.randint(1,25))
turtles=[Turtle() for i in range(5)]
threads=[MyTurtleManipulator(turtle) for turtle in turtles]
for thread in threads:
print(thread)
thread.start()
With the experiment I expected to see all the turtles moving "simultaneously" and randomly but When I run the program i get these errors:
<MyTurtleManipulator(Thread-1, initial)>
<MyTurtleManipulator(Thread-2, initial)>
<MyTurtleManipulator(Thread-3, initial)>
<MyTurtleManipulator(Thread-4, initial)>
<MyTurtleManipulator(Thread-5, initial)>
>>> Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.1/threading.py", line 516, in _bootstrap_inner
self.run()
File "/home/rfrm/test.py", line 13, in run
action(self.turtle, random.randint(1,25))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1162, in forward
checkargs((int, float))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1131, in _go
ende = self._position + self._orient * distance
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2266, in _goto
(start, self._position),
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 419, in _drawline
cl.append(-y)
File "<string>", line 1, in coords
File "/usr/lib/python3.1/tkinter/__init__.py", line 2123, in coords
self.tk.call((self._w, 'coords') + args)))
RuntimeError: main thread is not in main loop
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python3.1/threading.py", line 516, in _bootstrap_inner
self.run()
File "/home/rfrm/test.py", line 13, in run
action(self.turtle, random.randint(1,25))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1203, in right
checkargs((int, float))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2300, in _rotate
self._orient = self._orient.rotate(delta)
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2085, in _update
for t in screen._turtles:
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2219, in _drawturtle
screen._drawpoly(titem, shape, fill=fc, outline=oc,
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 384, in _drawpoly
cl.append(-y)
File "<string>", line 1, in coords
File "/usr/lib/python3.1/tkinter/__init__.py", line 2123, in coords
self.tk.call((self._w, 'coords') + args)))
RuntimeError: main thread is not in main loop
Exception in thread Thread-3:
Traceback (most recent call last):
File "/usr/lib/python3.1/threading.py", line 516, in _bootstrap_inner
self.run()
File "/home/rfrm/test.py", line 13, in run
action(self.turtle, random.randint(1,25))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1162, in forward
checkargs((int, float))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1131, in _go
ende = self._position + self._orient * distance
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2270, in _goto
screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 419, in _drawline
cl.append(-y)
File "<string>", line 1, in coords
File "/usr/lib/python3.1/tkinter/__init__.py", line 2123, in coords
self.tk.call((self._w, 'coords') + args)))
RuntimeError: main thread is not in main loop
Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python3.1/threading.py", line 516, in _bootstrap_inner
self.run()
File "/home/rfrm/test.py", line 13, in run
action(self.turtle, random.randint(1,25))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1162, in forward
checkargs((int, float))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1131, in _go
ende = self._position + self._orient * distance
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2270, in _goto
screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)),
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 419, in _drawline
cl.append(-y)
File "<string>", line 1, in coords
File "/usr/lib/python3.1/tkinter/__init__.py", line 2123, in coords
self.tk.call((self._w, 'coords') + args)))
RuntimeError: main thread is not in main loop
Exception in thread Thread-5:
Traceback (most recent call last):
File "/usr/lib/python3.1/threading.py", line 516, in _bootstrap_inner
self.run()
File "/home/rfrm/test.py", line 13, in run
action(self.turtle, random.randint(1,25))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 1203, in right
checkargs((int, float))
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2300, in _rotate
self._orient = self._orient.rotate(delta)
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2085, in _update
for t in screen._turtles:
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 2219, in _drawturtle
screen._drawpoly(titem, shape, fill=fc, outline=oc,
File "/usr/local/lib/python3.1/dist-packages/cTurtle.py", line 384, in _drawpoly
cl.append(-y)
File "<string>", line 1, in coords
File "/usr/lib/python3.1/tkinter/__init__.py", line 2123, in coords
self.tk.call((self._w, 'coords') + args)))
RuntimeError: main thread is not in main loop
What does this means, what does "main thread is not in main loop" means. Thank you for your help.
해결책
this seems to be a python/tkinter limitation as described here, turtle and threads are not friends
다른 팁
This has its limitations but may still be possible. The answer is in your error messges:
RuntimeError: main thread is not in main loop
Limit your turtle/tkinter operations to the main thread. In my rework of your example below, I've used the thread safe queue data structure to communicate between the threads and turtle:
from threading import Thread, active_count
from turtle import Turtle, Screen
import queue
import random
QUEUE_SIZE = 1 # set higher the more hardware threads you have
ACTIONS = [Turtle.forward, Turtle.right, Turtle.left]
COLORS = ['red', 'black', 'blue', 'green', 'magenta']
class MyTurtleManipulator(Thread):
def __init__(self, turtle):
super().__init__()
self.turtle = turtle
def run(self):
for _ in range(100):
actions.put((self.turtle, random.choice(ACTIONS), random.randint(1, 30)))
def process_queue():
while not actions.empty():
turtle, action, argument = actions.get()
action(turtle, argument)
if active_count() > 1:
screen.ontimer(process_queue, 100)
actions = queue.Queue(QUEUE_SIZE)
for color in COLORS:
turtle = Turtle('turtle')
turtle.color(color)
turtle.setheading(random.randint(0, 360))
MyTurtleManipulator(turtle).start()
screen = Screen()
process_queue()
screen.mainloop()
I set the QUEUE_SIZE to 1 as I'm on a machine with only two threads! I'm curious to know if everything works correctly on a machine with more threads and a QUEUE_SIZE ~= #threads - 1