带递归的多线程Python乌龟

我制作了一个递归生成分形树的Python乌龟程序,但由于它经常需要几个小时才能完全绘制,我想尝试使用多线程来让多个乌龟一起工作。

我能够同时获得两只乌龟,但在这种更加复杂的情况下,一切似乎都崩溃了。 我尝试了许多不同的方式,并认为最终的解决方案将是唯一的解决方案,但它只是抛出了一堆错误。

这是我的代码:

import turtle
import threading
from queue import Queue


class Location:
    def __init__(self, xpos=0, ypos=0, heading=90):
        self.xpos = xpos
        self.ypos = ypos
        self.heading = heading

    def getx(self):
        return self.xpos

    def gety(self):
        return self.ypos

    def geth(self):
        return self.heading


class Turtle(turtle.Turtle):
    def tolocation(self, location):
        self.penup()
        self.setx(location.getx())
        self.sety(location.gety())
        self.setheading(location.geth())
        self.pendown()

    def get_location(self):
        return Location(self.xcor(), self.ycor(), self.heading())

    def draw_tree(self, startpos=Location(), size=100):
        tm.q.put(self.tolocation(startpos))

        for _ in range(size):
            tm.q.put(self.forward(1))
        for _ in range(45):
            tm.q.put(self.right(1))

        t2 = Turtle()
        t2.speed(0)
        tm.new_thread(t2.draw_tree, self.get_location(), size / 2)


        for _ in range(90):
            tm.q.put(self.left(1))
        tm.new_thread(self.draw_tree, self.get_location(), size / 2)


class ThreadManager:
    def __init__(self):
        self.q = Queue()
        self.threads = []

    def new_thread(self, func, *args):
        self.threads.append(threading.Thread(target=func, args=(args,)))
        self.threads[-1].daemon = True
        self.threads[-1].start()

    def process_queue(self, scr):
        while not self.q.empty():
            (self.q.get())(1)

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


tm = ThreadManager()

scr = turtle.Screen()

t1 = Turtle()
t1.speed(0)
tm.new_thread(t1.draw_tree)

tm.process_queue(scr)

scr.exitonclick()

任何人都可以给我一个我在哪里出错的想法吗? 当process_queue自己调用时,错误消息会说递归的线条太深。 我使用scr.ontimer()错误吗?


你的代码有几个问题:

  • draw_tree()没有基本的情况来阻止它的(概念)递归,它只是不断创建新的线程。

  • 每次调用draw_tree()使用浮动除法将size分成两半,因此range(size)将失败,因为range()只能取整数。

  • 您对self.get_location()调用无效,因为它会告诉您乌龟在哪里,而不是在主线程完成处理未完成的图形命令后它将在何处。 你必须计算你的位置,而不是看你在哪里。

  • 这个调用tm.q.put(self.tolocation(startpos))是无效的 - 你可能需要对tm.q.put(self.tolocation, startpos)执行tm.q.put(self.tolocation, startpos)或调用tm.q.put() self.tolocation()命令。

  • 你不能在主线程的任何地方创建新的乌龟,因为它们在创建时调用tkinter,并且会在错误的(不是主线程)线程上。 在我下面的返工中,我只是预先分配他们。

  • args=(args,)不正确 - 应该是args=args因为args已经处于正确的格式。

  • 您不需要在每个分支点创建两个新线程,只需一个。 新的乌龟走一条路,老乌龟继续另一条路。

  • 以下是我对代码的返工,以解决上述问题和其他问题:

    import math
    import threading
    from queue import Queue
    from turtle import Turtle, Screen
    
    class Location:
        def __init__(self, xpos=0, ypos=0, heading=90):
            self.xpos = xpos
            self.ypos = ypos
            self.heading = heading
    
        def clone(self):
            return Location(self.xpos, self.ypos, self.heading)
    
    class Terrapin(Turtle):
        def tolocation(self, location):
            tm.q.put((self.penup,))
            tm.q.put((self.setx, location.xpos))
            tm.q.put((self.sety, location.ypos))
            tm.q.put((self.setheading, location.heading))
            tm.q.put((self.pendown,))
    
        def draw_tree(self, startpos, size=100):
            if size < 1:
                return
    
            self.tolocation(startpos)
    
            tm.q.put((self.forward, size))
    
            angle = math.radians(startpos.heading)
            startpos.xpos += size * math.cos(angle)
            startpos.ypos += size * math.sin(angle)
    
            tm.q.put((self.right, 45))
            startpos.heading -= 45
    
            tm.new_thread(pond.get().draw_tree, startpos.clone(), size / 2)
    
            tm.q.put((self.left, 90))
            startpos.heading += 90
    
            self.draw_tree(startpos, size / 2)
    
            pond.put(self)  # finished with this turtle, return it to pond
    
    class ThreadManager:
        def __init__(self):
            self.q = Queue()
            self.threads = Queue()
    
        def new_thread(self, method, *arguments):
            thread = threading.Thread(target=method, args=arguments)
            thread.daemon = True
            thread.start()
            self.threads.put(thread)
    
        def process_queue(self):
            while not self.q.empty():
                command, *arguments = self.q.get()
                command(*arguments)
    
            if threading.active_count() > 1:
                screen.ontimer(self.process_queue, 100)
    
    screen = Screen()
    
    # Allocate all the turtles we'll need ahead as turtle creation inside
    # threads calls into Tk which fails if not running in the main thread
    pond = Queue()
    
    for _ in range(100):
        turtle = Terrapin(visible=False)
        turtle.speed('fastest')
        pond.put(turtle)
    
    tm = ThreadManager()
    
    tm.new_thread(pond.get().draw_tree, Location())
    
    tm.process_queue()
    
    screen.exitonclick()
    

    如果不是大步骤:

    tm.q.put((self.right, 45))
    

    您想要将图形命令分解为几个小步骤:

    for _ in range(45):
        tm.q.put((self.right, 1))
    

    没关系,我只是想让代码运行。 你需要弄清楚这是否会给你带来任何好处。

    链接地址: http://www.djcxy.com/p/80715.html

    上一篇: Multithreading Python turtle with recursion

    下一篇: Python turtle won't create multiple squares