Question

Is there a way to use two turtles at the same time to draw two circles at the same time in one window? I tried this code but two turtles draw in separated windows

from multiprocessing import Process
import turtle

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1():
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2():
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

if __name__ == '__main__':
  p1 = Process(target=tes1)
  p1.start()
  p2 = Process(target=tes2)
  p2.start()
  p1.join()
  p2.join()

but somebody told me try multithreading but this code has a bad semantic error!!

import threading
import turtle

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1():
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2():
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

t = threading.Thread(target=tes1)
t.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
t.start()

t3 = threading.Thread(target=tes2)
t3.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
t3.start()

And what is the best suggestion multiprocessing or multithreading?

Était-ce utile?

La solution 2

Is it really necessary that the turtles are in different threads? What about this?

import turtle

t1 = turtle.Turtle()
t2 = turtle.Turtle()

t1.speed(0)
t2.speed(0)
for i in range(360):
  t1.forward(1)
  t1.left(1)
  t2.forward(1)
  t2.right(1)

Autres conseils

... I want multithreading or multiprocessing answer and I'm insisting on it.

The turtle module can be used with threading if we carefully walk a tightrope where only the main thread issues turtle commands:

import queue
import threading
import turtle

def tes1():
    for _ in range(360):
        graphics.put(turtle1.forward)
        graphics.put(turtle1.left)

def tes2():
    for _ in range(360):
        graphics.put(turtle2.forward)
        graphics.put(turtle2.right)

def process_queue():
    while not graphics.empty():
        (graphics.get())(1)

    if threading.active_count() > 1:
        turtle.ontimer(process_queue, 100)

graphics = queue.Queue(1)  # size = number of hardware threads you have - 1

turtle1 = turtle.Turtle('turtle')
turtle1.speed('fastest')
thread1 = threading.Thread(target=tes1)
thread1.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread1.start()

turtle2 = turtle.Turtle('turtle')
turtle2.speed('fastest')
thread2 = threading.Thread(target=tes2)
thread2.daemon = True  # thread dies when main thread (only non-daemon thread) exits.
thread2.start()

process_queue()

turtle.exitonclick()

We're using the queue module for thread-safe communications.

enter image description here

8 turtles at a time is no problem either

import turtle
turtle.delay(0)

t = [turtle.Turtle() for i in range(8)]

for i, j in enumerate(t):
    j.right(i*45)
    j.speed(0)

for i in range(360):
    for j in t:
        j.forward(1)
        j.right(1)

I've created a threaded_turtle package, which utilises functionality of queue.Queue to seamlessly execute all turtle instructions in the main thread, while the code is still written as if the turtles were running in different threads.

threaded_turtle is on GitLab: https://gitlab.com/zvone/threaded_turtle

With that package, the code from the question needs only minor modifications to work:

import turtle
from threaded_turtle import ThreadSerializer, TurtleThread

ctrl = ThreadSerializer()                        ## <-- create a serializer

t1=turtle.Turtle()
t2=turtle.Turtle()

def tes1(t1):                                    ## <-- additional argument
  t1.speed(0)
  i=0
  while i < 360:
    t1.forward(1)
    t1.left(1)
    i+=1

def tes2(t2):                                    ## <-- additional argument
  t2.speed(0)
  i=0
  while i < 360:
    t2.forward(1)
    t2.right(1)
    i+=1

t = TurtleThread(ctrl, t1, target=tes1)          ## <-- additional arguments
t.daemon = True
t.start()

t3 = TurtleThread(ctrl, t2, target=tes2)         ## <-- additional arguments
t3.daemon = True
t3.start()

ctrl.run_forever(1)                              ## <-- run the serializer

The result:

Screenshot of two turtles circling simultaneously

I think the coroutines and generators that Beazley preaches (on p. 447) are really more logical here:

Note: the deque from the collections module is more reliable too.

import turtle
from collections import deque


def move1():
    for _ in range(360):
        turtle1.forward(1)
        turtle1.left(1)
        yield


def move2():
    for _ in range(360):
        turtle2.forward(1)
        turtle2.right(1)
        yield


# Create turtles
turtle1 = turtle.Turtle('turtle')
turtle1.speed('fastest')
turtle2 = turtle.Turtle('turtle')
turtle2.speed('fastest')

# Create and populate a task queue

taskqueue = deque()
taskqueue.append(move1())  # Add tasks (generators)
taskqueue.append(move2())

while taskqueue:   # Run all of the tasks
    # Get the next task
    task = taskqueue.pop()
    try:
        # Run it to the next yield and enqueue
        next(task)
        taskqueue.appendleft(task)
    except StopIteration:
        # Task is done
        pass

turtle.done()

The turtle module does not support multithreading. I think the only thing you can do here is what others have already suggested: Make a bunch of turtles. Alternatively, you could use something like mtTkinter, which is exactly like tkinter, but supports threading.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top