多线程 Python 海龟递归

Multithreading Python turtle with recursion

我做了一个 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().

  • 中的每个命令
  • 你不能在主线程之外的任何地方创建新海龟,因为它们 在创建时调用 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))

没关系,我只是想获取 运行 的代码。你需要弄清楚这是否对你有任何好处。