Related
I am currently trying to make a sierpinski triangle which uses a recursive function to create threads that draw individual triangles simultaneously using the turtle library(cant change that). The thing is it keeps telling me that RuntimeError: main thread is not in main loop.
Here is the code I use for thread creating and execution, as well as some of my attempts at fixing it
def triangle_thread(x, y, size, invert=False):
global turtles
turtles.append(turtle.Turtle("turtle"))
turtles[-1].speed("fastest")
t = threading.Thread(target=partial(triangle, x, y, size, turtles[-1], invert=invert))
t.daemon = True
t.start()
This is supposed to create and start a new thread that draws a triangle
It apparently works.
I tried multiple things, as well as some queue trickery but it wouldnt draw them simultaneously.
here is my latest attempt at fixing it :
thread_sierpinski_recursive = threading.Thread(target=partial(sierpinski, -700, -500, 3, 1000))
thread_sierpinski_recursive.start()
turtle.mainloop()
I tried to run the entire sierpinski triangle generation in a separate thread so the main thread would be running turtle.mainloop
this works with sierpinski with up to 4 generations, but as soon as you try more it returns the same error:
sierpinski(-700, -500, 3, 1000)
turtle.mainloop()
here is the whole code I use, after trying cdlane's solution :
import turtle
import math
from functools import partial
import threading
threads = []
turtle.speed("fastest")
def triangle(x, y, size, t, invert=False):
y_offset = size*(math.sqrt(3)/2)
t.hideturtle()
t.penup()
partial(t.goto, x, y)()
t.pendown()
partial(t.goto, x+size, y)()
partial(t.goto, x+size/2, y-y_offset if invert else y+y_offset)()
partial(t.goto, x, y)()
def inner_sierpinski(x, y, size, iteration, thread_in):
global threads
height = size * (math.sqrt(3) / 2)
triangle(x + (size / 2), y + height, size, thread_in.Turtle, invert=True)
if iteration > 3:
for nx, ny in ((x, y), (x+(size/2), y+height), (x+size, y)):
threads.append(threading.Thread(target=partial(sierpinski, x, y, iteration-1, size, is_first=False)))
threads[-1].Turtle = turtle.Turtle("turtle", visible=False)
threads[-1].Turtle.speed("fastest")
elif iteration > 1:
for nx, ny in ((x, y), (x+(size/2), y+height), (x+size, y)):
sierpinski(nx, ny, iteration-1, size, is_first=False)
#this exists for the sole purpose of drawing the outer triangle
#this function calls inner_sierpinski, whoch calls this thrice.
def sierpinski(x, y, iterations, total_size, is_first=True):
global threads
if is_first:
triangle(x, y, total_size, turtle)
threading.main_thread().Turtle = turtle
thread_out = threading.main_thread()
else:
thread_out = threads[-1]
inner_sierpinski(x, y, total_size/2, iterations, thread_out)
sierpinski(-700, -500, 6, 1000)
for thread in threads:
thread.start()
turtle.mainloop()
I would need to see more of your code to understand specifically, but generally, drawing an individual triangle seems too small a step to thread as the overhead overwhelms the process. If each triangle was a complex calculation that took significant time, that would be fine. Similarly, if you break up your Sierpinski triangle into large sections that could be drawn in parallel, that would also seem reasonable.
Below is code I just pulled together from my other examples that uses threading to recursively draw the three large components of a Koch snowflake in parallel:
from queue import Queue
from functools import partial
from turtle import Screen, Turtle
from threading import Thread, active_count
STEPS = 3
LENGTH = 300
def koch_curve(turtle, steps, length):
if steps == 0:
actions.put((turtle.forward, length))
else:
for angle in [60, -120, 60, 0]:
koch_curve(turtle, steps - 1, length / 3)
actions.put((turtle.left, angle))
def process_queue():
while not actions.empty():
action, *arguments = actions.get()
action(*arguments)
if active_count() > 1:
screen.ontimer(process_queue, 1)
screen = Screen()
actions = Queue(1) # size = number of hardware threads you have - 1
turtle1 = Turtle('turtle')
turtle1.hideturtle()
turtle1.speed('fastest')
turtle1.color('red')
turtle1.penup()
turtle1.right(30)
turtle1.backward(3**0.5 * LENGTH / 3)
turtle1.left(30)
turtle1.pendown()
turtle2 = turtle1.clone()
turtle2.color('green')
turtle2.penup()
turtle2.forward(LENGTH)
turtle2.right(120)
turtle2.pendown()
turtle3 = turtle1.clone()
turtle3.speed('fastest')
turtle3.color('blue')
turtle3.penup()
turtle3.right(240)
turtle3.backward(LENGTH)
turtle3.pendown()
thread1 = Thread(target=partial(koch_curve, turtle1, STEPS, LENGTH))
thread1.daemon = True # thread dies when main thread (only non-daemon thread) exits.
thread1.start()
thread2 = Thread(target=partial(koch_curve, turtle2, STEPS, LENGTH))
thread2.daemon = True
thread2.start()
thread3 = Thread(target=partial(koch_curve, turtle3, STEPS, LENGTH))
thread3.daemon = True
thread3.start()
process_queue()
screen.exitonclick()
Since the amount of drawing is the same (and the limiting factor on the speed) I don't expect to gain any performance over simply drawing the fractal the usual way with the turtle speed turned up or tracing turned off. It's just for visual effect.
1/16 Update: Reviewing your attempt to incorporate my example into your code it's clear you don't understand what the role of partial() is, nor, for that matter, the role of the global statement in Python. And you're missing a key concept: all turtle graphics commands must happen in the main thread, not any of the ones you create. (I.e. turtle's underpinning code is not thread safe.)
Let's begin by turning your code back into a non-threaded implemenation of the Sierpinski triangle. But with a slight change in that we will make the triangles on the corners of the top level triangle three different colors, to show we know when those major segments begin:
from turtle import Screen, Turtle
def triangle(x, y, size, turtle, invert=False):
y_offset = size * 3**0.5/2
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
turtle.goto(x + size, y)
turtle.goto(x + size/2, y + (-y_offset if invert else y_offset))
turtle.goto(x, y)
def inner_sierpinski(x, y, size, turtle, iterations, iteration):
height = size * 3**0.5/2
triangle(x + size/2, y + height, size, turtle, invert=True)
if iteration:
for nx, ny in ((x, y), (x + size/2, y + height), (x + size, y)):
sierpinski(nx, ny, size, turtle, iterations, iteration-1)
def sierpinski(x, y, size, turtle, iterations, iteration=-1):
if iteration < 0:
triangle(x, y, size, turtle)
iteration = iterations
elif iteration == iterations - 1:
turtle.color(colors.pop())
inner_sierpinski(x, y, size/2, turtle, iterations, iteration)
colors = ['black', 'red', 'green', 'blue']
screen = Screen()
turtle = Turtle(visible=False)
turtle.speed('fastest')
turtle.color(colors.pop(0))
sierpinski(-350, -250, 700, turtle, 6)
screen.exitonclick()
Having done that, let's now incorporate the threading from my Koch snowflake example:
from queue import Queue
from functools import partial
from turtle import Screen, Turtle
from threading import Thread, active_count
def triangle(x, y, size, turtle, invert=False):
y_offset = size * 3**0.5/2
actions.put((turtle.penup,))
actions.put((turtle.goto, x, y))
actions.put((turtle.pendown,))
actions.put((turtle.goto, x + size, y))
actions.put((turtle.goto, x + size/2, y + (-y_offset if invert else y_offset)))
actions.put((turtle.goto, x, y))
def inner_sierpinski(x, y, size, turtle, iterations, iteration):
# Surprisingly, this doesn't change from my
# previous example so just copy it over!
def sierpinski(x, y, size, turtle, iterations, iteration=-1):
if iteration < 0:
triangle(x, y, size, turtle)
iteration = iterations
if iteration == iterations - 1:
turtle = turtles.get()
thread = Thread(target=partial(inner_sierpinski, x, y, size/2, turtle, iterations, iteration))
thread.daemon = True
thread.start()
else:
inner_sierpinski(x, y, size/2, turtle, iterations, iteration)
def process_queue():
# Copy this function from my Koch snowflake example
COLORS = ['black', 'red', 'green', 'blue']
turtles = Queue(len(COLORS))
screen = Screen()
for color in COLORS:
turtle = Turtle(visible=False)
turtle.speed('fastest')
turtle.color(color)
turtles.put(turtle)
actions = Queue(1)
thread = Thread(target=partial(sierpinski, -350, -250, 700, turtles.get(), 6)) # must be a thread as it calls triangle()
thread.daemon = True # thread dies when main thread (only non-daemon thread) exits.
thread.start()
process_queue()
screen.exitonclick()
When this runs, you should see the black outer triangle created followed by independent threads working on the three color triangles:
So my assignement is to:
make a blue rectangle
write a function that makes the turle move in a random direction within an interval of 90 degrees and move forward in a random interval of 0-25
create a blue square
Move the turle to a random point in the square
Code so the turtle moves back inside the square if it leaves it
Create an additonal turle (both should have different colors)
Use the same statement to move both turtles (with the move_random function) 500 times
if the turtles are closer than 50 units - print a string that counts the number of times they are 50 units close.
This is what it should look like:
enter image description here
I've added some comments to explain my thought process
Any and all help is appreciated
The code:
EDIT: fixed the indentations, now i get the error message on the last line that the name "meet" is not defined. Also if i run the code without the last line which is supposed to print the amount of close encounters, nothing happens, no errors, but no turtles either.
import turtle
import random
#makes the jump function
def jump(t, x, y):
t.penup()
t.goto(x, y)
t.pendown()
#creares a turtle at a defined place
def make_turtle(x, y):
t = turtle.Turtle()
jump(t, x, y) # Use of the function defined above
return t
#function to create a rectangle and fill it with a color
def rectangle(x, y, width, height, color):
t = make_turtle(x, y)
t.speed(0)
t.hideturtle()
t.fillcolor(color)
t.begin_fill()
for dist in [width, height, width, height]:
t.forward(dist)
t.left(90)
t.end_fill()
#function to move turtle in a random heading (90 degree interval) between 0--25 units forward
#While also making it turn around if it is outside of the square
def move_random(t):
if abs(t.pos()[0]) >= 250 or abs(t.pos()[1]) >= 250:
target = (0, 0)
d = (0,0)
t.setheading(d)
else:
ini = t.heading()
new = rd.randint(ini - 45, ini + 45)
t.setheading(new)
t.forward(rd.randint(0, 25))
#creates the square and both turtles
t = make_turtle(0 , 0)
t.color("green")
t2 = make_turtle(0 , 0)
t2.color("black")
rectangle(-250, -250, 500, 500, "lightblue")
jump(t, rd.randint(-250, 250), rd.randint(-250, 250))
jump(t2, rd.randint(-250, 250), rd.randint(-250, 250)) #jumps the turles randomly in the square
meet = 0
for i in range(1, 501): #makes the turtles move randomly as specified above
move_random(t)
move_random(t2)
if t.distance(t2) < 50:
t.write("close")
meet += 1
print(str(meet), "close encounter") #prints the amount of times they are close to each other
if abs(t.pos()[0]) >= 250 or abs(t.pos()[1]) >= 250:
target = (0, 0)
d = (0,0)
t.setheading(d)
else:
See that function before the "else:"? You missed a tab there.
Link to the Exercises can be accessed here - Case Study: Interface Design, Exercise Section 4.3
Quoting the question, it seems I have to implement an arc() function:
Make a more general version of circle called arc that takes an additional parameter angle, which determines what fraction of a circle to draw. angle is in units of degrees, so when angle=360, arc should draw a complete circle.
The code I've written so far:
import turtle
import math
bob = turtle.Turtle()
def polygon(t, n, length):
for i in range(n):
t.fd(length)
t.lt(360/n)
def circle(t, r):
circumference = 2 * math.pi * r
n = int(circumference/3) + 1
length = circumference/n
polygon(t, n, length)
def arc(t, r, angle):
arc_length = 2 * math.pi * r * (angle/360)
n = (arc_length/4) + 1
arc(bob, 1000, 45)
turtle.mainloop()
I'm intending to call the circle() function within arc() just as polygon() was called within circle(), but I'm confused on how I should do that. Apart from that, the arc() function does not draw anything, rather just shows me a stationary Turtle.
I believe that the Turtle object bob isn't receiving any of the movement instructions assigned within polygon(). Thus all that it's doing is displaying the Turtle object!
I could be wrong, and this where I need clarification. Should I call circle() within arc() as well as make the Turtle object move? Are there easier alternatives? Calling functions within a function is still confusing for me, so more resources to learn about them would be great as well!
import turtle
bob=turtle.Turtle()
import math
def arc(t,radius,angle):
circumference = 2.0*math.pi*radius
frac = angle/360.0
arclength = circumference*frac
n = 50 # pick a number
len = arclength/n;
turnang = angle/n
for i in range(n):
t.fd(len)
t.lt(turnang)
arc(bob, 130,360)
turtle.done()
I'm trying to ... call the circle() function
within arc() just as polygon() was called within circle()
You've got this backward. The problem states:
Make a more general version of circle called arc
Just as you could draw a circle with the more general function polygon(), you should be able to draw a circle with the more general function arc(). Here's a skeletal program for thinking about this:
from turtle import Screen, Turtle
from math import pi
def polygon(turtle, sides, length):
outside_angle = 360 / sides
for _ in range(sides):
turtle.forward(length)
turtle.left(outside_angle)
def circle_using_polygon(turtle, radius):
circumference = 2 * pi * radius
sides = min(60, int(circumference / 3))
length = circumference / sides
polygon(turtle, sides, length)
def arc(turtle, radius, angle):
# implement arc not by calling *circle() nor by
# calling polygon() but rather by borrowing code
# from both and adding one more step to reduce
# the number of sides based on the arc angle
def circle_using_arc(turtle, radius):
arc(turtle, radius, 360)
bob = Turtle(visible=False)
# Draw overlapping circles three different ways:
bob.color("green")
circle_using_polygon(bob, 100)
for color in ['cyan', 'magenta', 'yellow', 'black']:
bob.color(color)
arc(bob, 100, 90)
bob.color("blue")
circle_using_arc(bob, 100)
screen = Screen()
screen.mainloop()
import tkinter
import swampy
from swampy.TurtleWorld import *
def polygon(n, t, length, angle):
print(t)
k= angle/360
for i in range(0,int(n*k)):
fd(t, length)
p= 360
lt(t,p/n)
t.delay
world = TurtleWorld()
bob = Turtle()
#def circle(r):
#l1= 2*3.14*r
#l= l1/60
#polygon(30, bob, l)
polygon(60, bob, 10, 180)
I am new to Python. I need to write a program to move my ball or circle when I click the mouse. How do I achieve this? I have the below code that I got started with:
from graphics import *
import time
def MouseTracker():
win = GraphWin("MyWindow", 500, 500)
win.setBackground("blue")
cir = Circle(Point(250,250) ,20)
cir.setFill("red")
cir.draw(win)
while(win.getMouse() != None):
xincr = 0
yincr = 0
for i in range(7):
cir.move(xincr, yincr)
time.sleep(.2)
win.getMouse()
Assuming you are not bound to some specific tools or implementation, you may find matplotlib useful. You can plot a circle onto the drawing area using a circle patch (http://matplotlib.org/api/patches_api.html) and then move it around when there is mouse-click in the graph axes. You will need to connect to the event-click listener and define a callback function which handles the drawing update - see http://matplotlib.org/users/event_handling.html for examples of how to do this. You can get the coordinates of the mouse press using the xdata and ydata methods.
This worked for me in python 2.7:
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
fig = plt.figure()
ax = fig.add_subplot(111)
circ = Circle((0.5,0.5), 0.1)
ax.add_patch(circ)
def update_circle(event):
ax.cla()
circ = Circle((event.xdata, event.ydata), 0.1)
ax.add_patch(circ)
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', update_circle)
plt.show()
Assuming you want to stick with the graphics package you started with, you can do it but you're missing code to save the mouse position and compare it to the circle's center position:
from graphics import *
WIDTH, HEIGHT = 500, 500
POSITION = Point(250, 250)
RADIUS = 20
STEPS = 7
def MouseTracker(window, shape):
while True:
position = window.getMouse()
if position != None: # in case we want to use checkMouse() later
center = shape.getCenter()
xincr = (position.getX() - center.getX()) / STEPS
yincr = (position.getY() - center.getY()) / STEPS
for _ in range(STEPS):
shape.move(xincr, yincr)
win = GraphWin("MyWindow", WIDTH, HEIGHT)
win.setBackground("blue")
cir = Circle(POSITION, RADIUS)
cir.setFill("red")
cir.draw(win)
MouseTracker(win, cir)
You will need to close the window to break out of the tracking loop -- in a real program you'd handle this as part of the design (i.e. some user action causes a break in the while True: loop.)
How can I play with a turtle and how can I use a turtle?
I have trouble getting the thing to work as in the picture shown below (ignore the colors).
from turtle import *
from math import *
def formulaX(R, r, p, t):
x = (R-r)*cos(t) - (r + p)*cos((R-r)/r*t)
def formulaY(R, r, p, t):
y = (R-r)*sin(t) - (r + p)*sin((R-r)/r*t)
def t_iterating(R, r, p):
t = 2*pi
up()
goto(formulaX, formulaY)
down()
while (True):
t = t + 0.01
formulaX(R, r, p, t)
formulaY(R, r, p, t)
def main():
R = int(input("The radius of the fixed circle: "))
r = int(input("The radius of the moving circle: "))
p = int(input("The offset of the pen point, between <10 - 100>: "))
if p < 10 or p > 100:
input("Incorrect value for p!")
t_iterating(R, r, p)
input("Hit enter to close...")
main()'
I am trying to make that kind of shape. Here is the coding I have done so far.
Try changing your t_iterating function to this:
def t_iterating(R, r, p):
t = 2*pi # It seems odd to me to start from 2*pi rather than 0.
down()
while t < 20*pi: # This loops while t goes from 2*pi to 20*pi.
t = t+0.01
goto(formulaX(R, r, p, t), formulaY(R, r, p, t))
up()
No! You're missing the point of the turtle! You should try to do it all with relative movements of the turtle. Think about how you would draw the shape if you were the turtle, crawling on a large floor, dragging a paintbrush from your butt.
At each small fragment of time, the turtle will perform one small iteration of a differential equation which governs the whole behavior. It is not generally wise to precompute the x y coordinates and use the turtle's GOTO function.
The turtle itself should have only relative knowledge of its surroundings. It has a direction, and a position. And these two pieces of state are modified by turning and moving.
So, think about how you would draw the spiral. Particularly, think about drawing the very first circle. As the circle appears to close, something interesting happens: it misses. It misses by a tiny little amount, which turns out to be a fraction of a circle. It is this missing curvature that closes the large pattern of circles in a circle, as they add up to one complete turn.
When the whole figure is drawn, the turtle is back to its original position and orientation.
This is my code. The color may not be exact, but here it is:
from turtle import *
from random import randint
speed(10000)
for i in range(20):
col = randint(1, 5)
if col == 1:
pencolor("orange")
elif col == 2:
pencolor("blue")
elif col == 3:
pencolor("green")
elif col == 4:
pencolor("purple")
elif col == 5:
pencolor("dark blue")
circle(50)
left(20)
This is the output:
My code is here and the function was built for automatically choosing the random colour.
from turtle import Turtle, Screen
import random
timmy = Turtle()
screen = Screen()
screen.colormode(255)
timmy.shape("turtle")
timmy.speed("fastest")
angle = [0, 90, 180, 270]
def random_color():
red = random.randint(0, 255)
green = random.randint(0, 255)
blue = random.randint(0, 255)
colour = (red, green, blue)
return colour
def draw_circles(num_of_gap):
for _ in range(int(360 / num_of_gap)):
timmy.color(random_color())
timmy.circle(100)
timmy.right(num_of_gap)
draw_circles(20)
screen.exitonclick()
Spirograph using Python Turtle with random colours
Code:
import random
from turtle import Turtle, Screen
tim = Turtle()
tim.shape("classic")
def turtle_color():
R = random.random()
G = random.random()
B = random.random()
return tim.pencolor(R, G, B)
tim.speed("fastest")
for _ in range(72):
turtle_color()
tim.circle(100)
tim.left(5)
screen = Screen()
screen.exitonclick()
Output:
You basically get the turtle to loop through the 360 degrees and you can choose two pen colours.
from turtle import Turtle, Screen
tim = Turtle()
tim.shape("turtle")
tim.color("green")
### total degrees in circle = 360
### turn left must be a divisor of 360 (1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18, 20, 24, 30, 36, 40, 45, 60, 72, 90) NOTE: some divisors do not work as well
degrees = 360
turn_left = 12
total_circles = int(degrees / turn_left)
tim.pensize(3)
tim.speed(0)
def circle_colour1():
### choose your colour here:
tim.pencolor("pink")
tim.circle(-100)
tim.left(turn_left)
def circle_colour2():
### choose your colour here:
tim.pencolor("grey")
tim.circle(-100)
tim.left(turn_left)
for _ in range(0, int(total_circles / 2)):
circle_colour1()
circle_colour2()
screen = Screen()
screen.exitonclick()
Real basic (360°/10) is:
from turtle import Turtle as d
draw = d()
draw.speed(0)
draw.pensize(3)
for _ in range(0, 36):
draw.circle(-100)
draw.left(10)