简体   繁体   English

如何使用龟在python中同时绘制项目?

[英]How to make items draw at the same time in python using turtle?

I have a homework assignment, and I have to make four different turtles move like they are planets around the sun. 我有一个家庭作业,我必须让四只不同的海龟移动,就像它们是围绕太阳的行星一样。 I have it all written, its just a matter of making the turtles draw at the same time. 我写完了所有内容,这只是让乌龟同时画画的问题。 I was wondering if there was a relatively easy way to make them start around the same time (within reason)? 我想知道是否有一种相对简单的方法可以让它们在同一时间内开始(在合理范围内)? Anyway, here's the code: 无论如何,这是代码:

def planets():
    """simulates motion of Mercury, Venus, Earth, and Mars"""
    import turtle

    mercury = turtle.Turtle()
    venus = turtle.Turtle()
    earth = turtle.Turtle()
    mars = turtle.Turtle()
    mercury.shape('circle')
    venus.shape('circle')
    earth.shape('circle')
    mars.shape('circle')
    mercury.pu()
    venus.pu()
    earth.pu()
    mars.pu()
    mercury.sety(-58)
    venus.sety(-108)
    earth.sety(-150)
    mars.sety(-228)
    mercury.pd()
    venus.pd()
    earth.pd()
    mars.pd()
    mars.speed(7.5)
    venus.speed(3)
    earth.speed(2)
    mars.speed(1)
    mercury.circle(58)
    venus.circle(108)
    earth.circle(150)
    mars.circle(228)

Thanks in advance! 提前致谢!

In general, if you want to do multiple things at the same time, there are two options: 一般来说,如果你想同时做多件事,有两种选择:

  • Preemptive multithreading, where you just create a thread for each thing and they all try to work at full speed and the computer figures out how to interleave that work. 抢占式多线程,您只需为每个事物创建一个线程,它们都会尝试全速工作,计算机会计算出如何交错。
  • Cooperative scheduling: you do a small piece of work for one thing, then a small piece for the next, and so on, then come back to the first one. 合作调度:你为一件事做一小部分工作,然后为下一部分做一小部分,依此类推,然后再回到第一件事。

In this case, it's the second one that you want. 在这种情况下,它是您想要的第二个。 (Well, you might want the first, but you can't have it; tkinter , and therefore turtle , can only run on the main thread.) Draw, say, the first 1° of each circle, then the next 1° of each circle, and so on. (好吧,你可能想要第一个,但你不能拥有它; tkinter ,因此turtle ,只能在主线程上运行。)绘制,比如说,每个圆圈的第一个1°,然后是下一个1°每个圆圈,依此类推。

So, how do you do that? 那么,你是怎么做到的? The circle method has an optional extent parameter, which is an angle (in degrees) to draw. circle方法有一个可选的extent参数,它是要绘制的角度(以度为单位)。 So, you can do this: 所以,你可以这样做:

for i in range(360):
    mercury.circle(58, 1)
    venus.circle(108, 1)
    earth.circle(150, 1)
    mars.circle(228, 1)

Of course the smaller you make that extent value, the more "steps" each turtle is taking, so the slower they will take to orbit. 当然,你获得该extent值越小,每只乌龟所采取的“步数”越多,因此它们进入轨道的速度就越慢。

Also, I'm not sure you really want to use speed the way you're using it. 另外,我不确定你真的想以你使用它的方式使用speed That causes each move to animate more slowly. 这导致每个动作的动画更慢。 It doesn't affect how quickly they orbit around the sun, it just affects how long each step takes to draw. 它不会影响它们围绕太阳运行的速度,它只影响每个步骤绘制的时间。 So I think what you really want to do here is leave all the speeds at 0 (no animation delay), but move the faster planets by a larger extent each step: 所以我认为你真正想做的是将所有速度保持在0(没有动画延迟),但是每一步都要移动更快的行星:

mercury.speed(0)
venus.speed(0)
earth.speed(0)
mars.speed(0)
for i in range(360):
    mercury.circle(58, 7.5)
    venus.circle(108, 3)
    earth.circle(150, 2)
    mars.circle(228, 1)

Of course this means Mercury will end up orbiting the sun 7.5 times, while Mars will only orbit once… but that's exactly what you want, right? 当然这意味着水星将最终绕太阳运行7.5次,而火星只能运行一次......但这正是你想要的,对吧?

Since none of the previous answers mention turtle's own ontimer() for running the simulation, I decided to do an implementation that uses it and makes the whole problem more data-oriented: 由于以前的答案都没有提到turtle自己的ontimer()来运行模拟,我决定做一个使用它的实现,并使整个问题更加面向数据:

from turtle import Turtle, Screen

""" Simulate motion of Mercury, Venus, Earth, and Mars """

planets = {
    'mercury': {'diameter': 0.383, 'orbit': 58, 'speed': 7.5, 'color': 'gray'},
    'venus': {'diameter': 0.949, 'orbit': 108, 'speed': 3, 'color': 'yellow'},
    'earth': {'diameter': 1.0, 'orbit': 150, 'speed': 2, 'color': 'blue'},
    'mars': {'diameter': 0.532, 'orbit': 228, 'speed': 1, 'color': 'red'},
}

def setup_planets(planets):
    for planet in planets:
        dictionary = planets[planet]
        turtle = Turtle(shape='circle')

        turtle.speed("fastest")  # speed controlled elsewhere, disable here
        turtle.shapesize(dictionary['diameter'])
        turtle.color(dictionary['color'])
        turtle.pu()
        turtle.sety(-dictionary['orbit'])
        turtle.pd()

        dictionary['turtle'] = turtle

    screen.ontimer(revolve, 50)

def revolve():
    for planet in planets:
        dictionary = planets[planet]
        dictionary['turtle'].circle(dictionary['orbit'], dictionary['speed'])

    screen.ontimer(revolve, 50)

screen = Screen()

setup_planets(planets)

screen.exitonclick()

OUTPUT SNAPSHOT IN TIME 输出快照及时

在此输入图像描述

In my other answer, I said that you have to do some kind of cooperative scheduling, because tkinter isn't thread-safe. 在我的另一个答案中,我说你必须做某种协作调度,因为tkinter不是线程安全的。 But that isn't quite true. 但事实并非如此。 tkinter is thread-safe, it just doesn't have any kind of dispatching mechanism to post events on the main loop from a background thread, so you have to add a queue or some other way to do it. tkinter是线程安全的,它没有任何类型的调度机制来从后台线程在主循环上发布事件,因此你必须添加一个队列或其他一些方法来执行它。

I'd definitely not recommend using threads here, but it's worth seeing how it would work. 我绝对不建议在这里使用线程,但值得看看它是如何工作的。

Allen B. Taylor's clever mtTkinter library wraps all of the magic up for you. Allen B. Taylor的聪明的mtTkinter库为你包装了所有的魔法。 It doesn't work with Python 3, but I've ported it, and you can get it as mttkinter on GitHub. 它不适用于Python 3,但我移植了它,你可以在GitHub mttkinter它作为mttkinter The module have any installer; 该模块有任何安装程序; you'll have to copy it into the same directory as planets.py . 你必须将它复制到与planets.py相同的目录中。 But then you can do this: 但是你可以这样做:

import threading
import turtle
import mttkinter

def planets():
    """simulates motion of Mercury, Venus, Earth, and Mars"""
    # Use your existing code, up to...
    mars.speed(1)

    # Now create a thread for each planet and start them
    mercury_thread = threading.Thread(target=lambda: mercury.circle(58))
    venus_thread = threading.Thread(target=lambda: venus.circle(108))
    earth_thread = threading.Thread(target=lambda: earth.circle(150))
    mars_thread = threading.Thread(target=lambda: mars.circle(228))
    mercury_thread.start()
    venus_thread.start()
    earth_thread.start()
    mars_thread.start()

    # Unfortunately, if we just exit the function here, the main thread
    # will try to exit, which means it'll wait on all the background threads.
    # But since they're all posting events and waiting on the main thread to
    # reply, they'll deadlock. So, we need to do something tkinter-related
    # here, like:
    turtle.Screen().exitonclick()

planets()

turtle.py ends with two test demos. turtle.py以两个测试演示结束。 The second one ends with one turtle 'tri' chasing another 'turtle'. 第二个结尾是一只海龟'三'追逐另一只'海龟'。 It does what abarnet suggests in the first post -- increments within a loop. 它执行abarnet在循环中的第一个后增量中建议的内容。

while tri.distance(turtle) > 4:
    turtle.fd(3.5)
    turtle.lt(0.6)
    tri.setheading(tri.towards(turtle))
    tri.fd(4)

The turtledemo package has multiple examples to learn from. turtledemo包有多个例子可供学习。 python -m turtledemo is the easy way to start the viewer. python -m turtledemo是启动查看器的简便方法。 There are a couple of bugs that have been fixed for future releases. 为将来的版本修复了一些错误。

Methods posted by other users work well. 其他用户发布的方法效果很好。 However, I did a similar model of a solar system with object oriented design and what I did was create a class called System where I create a system with a desired height and width and created a stepAll function which has a list of agents and advances all agents in that list one 'step': 然而,我做了一个类似面向对象设计的太阳系模型,我所做的是创建一个名为System的类,我创建一个具有所需高度和宽度的系统,并创建一个stepAll函数,其中包含一个代理列表并推进所有该列表中的代理商有一个“步骤”:

class System:
"""A two-dimensional world class."""

    def __init__(self, width, height):
        """Construct a new flat world with the given dimensions."""

        self._width = width
        self._height = height
        self._agents = { }
        self.message = None

    def getWidth(self):
        """Return the width of self."""

        return self._width

    def getHeight(self):
        """Return the height of self."""

        return self._height

    def stepAll(self):
        """All agents advance one step in the simulation."""

        agents = list(self._agents.values())
        for agent in agents:
            agent.step()

Then, I created a Planet class, and made the planets agents, and defined what their step would be in a Step function. 然后,我创建了一个Planet类,并制作了行星代理,并定义了它们在Step函数中的步骤。

class Planet:
"""A planet object"""

    def __init__(self, mySystem, distance, size, color, velocity, real_size, real_mass, name):
        self = self
        self._system = mySystem
        mySystem._agents[self] = self #make planet an agent
        self._velocity = velocity
        self._color = color
        self._distance = distance
        self._size = size
        self._position = [distance, distance - distance]
        self._angle = 90
        self._real_size = real_size
        self._real_mass = real_mass
        self._name = name

        #MAKE PLANET
        self._turtle = turtle.Turtle(shape = 'circle')
        self._turtle.hideturtle()

        #INITIALIZE PLANET
        self._turtle.speed('fastest')
        self._turtle.fillcolor(color)
        self._turtle.penup()
        self._turtle.goto(self._position)
        self._turtle.turtlesize(size,size,size)
        self._turtle.showturtle()


    def getmySystem(self):
        """Returns the system the planet is in"""
        return self._mySystem

    def getdistance(self):
        """Returns the distance the planet is from the sun"""
        return self._distance

    def getposition(self):
        """Returns the position of the planet"""
        return self._position

    def getvelocity(self):
        """Returns the velocity of the planet"""
        return self._velocity

    def step(self):
        """Moves the planet one step on its orbit according to its velocity and previous position"""
        xvar = self._position[0]
        yvar = self._position[1]

        newx = int(self._distance*math.cos(math.radians(90-self._angle)))
        newy = int(self._distance*math.sin(math.radians(90-self._angle)))

        self._turtle.goto(newx, newy)

        self._angle = self._angle - self._velocity

Then in my main() function I initialized all the planets with their respective values and said: 然后在我的main()函数中,我用各自的值初始化了所有行星并说:

while True:
    space.stepAll()

This accomplishes your goal and also makes it easier to add another planet later with the specific parameters you want it to contain by just calling the Planet Class, instead of drawing a completely new planet and trying to individually move it along with the others. 这样就完成了你的目标,并且通过调用Planet Class,而不是绘制一个全新的行星并尝试单独移动它与其他行星一起,可以更容易地在以后添加另一个行星,并使用您希望它包含的特定参数。

Hope this helps someone! 希望这有助于某人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM