Python drawing images and dynamic shape size - python

I have a couple of questions. Firstly, I am wondering how to I get shape sizes for shapes I call to be dynamic, and adjust based on my movement of the window that they are in. Is there a simple command for this? Secondly, I am wondering if instead of using something like Turtle to draw images, how do I get an image to just appear once I run drawing code, as opposed to watching it be drawn?
from turtle import *
import math
radius = 100
t = turtle.Turtle()
radius = 100
colormode(255)
t.speed(1)
t.color(0,255,0)
fillcolor(200, 125, 200)
t.begin_fill()
t.circle(radius)
t.end_fill()
exitonclick()

One way you can go about this is by designing a turtle cursor and stamping it. Cursors are drawn all at once and have more graphics operations at their disposal, like resizing, shear, etc. Here's a simple example using turtle's built in circle shape but you can just as easily design your own and register it as a cursor:
RADIUS = 100
CURSOR_SIZE = 20
screen = Screen()
screen.colormode(255)
turtle = Turtle("circle", visible=False)
turtle.speed('fastest')
turtle.penup()
turtle.pencolor(0, 255, 0)
turtle.fillcolor(200, 125, 200)
turtle.shapesize(RADIUS / CURSOR_SIZE, outline=5)
turtle.stamp()
turtle.goto(250, 250)
turtle.shapesize(2 * RADIUS / CURSOR_SIZE, outline=10)
turtle.stamp()
screen.exitonclick()

Related

How to find out when the window is resized in python turtle?

I have a function that draws a circle based on the screensize of the window. Currently, if I make the window smaller, the entire circle isn't visible. Is there any way to find out if the window has been resized so I can redraw the circle to fit in the window?
Here's my code to draw the circle:
def draw_circle():
# Get circle radius
screensize = s.screensize()
if screensize[0] > screensize[1]:
smaller = 1
else:
smaller = 0
radius = screensize[smaller]
# Draw circle
t.pensize(15)
t.pu()
t.goto(0, -radius)
t.pd()
t.circle(radius)
t.pu()
I have tried to add an if statement that checks if the screensize changes in my main loop, but that didn't work either.
P.S. I tried searching Google and StackOverflow and found no answers.
There's no need to poll in a loop. Once you get the underlying Tkinter canvas with turtle.getcanvas(), you can look up the non-turtle Tkinter approach and use that. For example, adapting Tkinter track window resize specifically? to turtle gives the following minimal example:
import turtle
def resize(event):
print(event.width, event.height)
turtle.getcanvas().bind("<Configure>", resize)
turtle.exitonclick()
Note that this doesn't change the screen size, so you can draw your circle relative to the window instead:
import turtle
def draw():
r = min(turtle.window_width(), turtle.window_height()) / 2 - 30
turtle.clear()
turtle.penup()
turtle.begin_fill()
turtle.goto(0, -r)
turtle.pendown()
turtle.circle(r)
turtle.end_fill()
turtle.update()
def resize(event):
draw()
turtle.tracer(0)
turtle.hideturtle()
turtle.getcanvas().bind("<Configure>", resize)
draw()
turtle.exitonclick()
Drawing relative sizes for everything tends to be pretty tedious, so I recommend avoiding this if at all possible for your app.

randomly positioned circle clicking game

So I'm still very new to python and trying to learn through making small projects.
The game I'm making is meant to test your mouse accuracy by creating a bunch of random circles which the player is meant to click in a given amount of time. At the end of the game, it should tell the player their score, and how many misclicks they had.
I've been using turtle to try and do this, but I'm stuck:
import turtle
import random
t = turtle.Pen()
win = turtle.Screen()
win.bgcolor("lightgreen")
win.title("clicky")
def mycircle(red, green, blue):
t.color(red, green, blue)
t.begin_fill()
x = random.randint(10,50)
t.circle(x)
t.end_fill()
t.up()
y = random.randint(0,360)
t.seth(y)
if t.xcor() < -300 or t.xcor() > 300:
t.goto(0, 0)
elif t.ycor() < -300 or t.ycor() > 300:
t.goto(0, 0)
z = random.randint(0,100)
t.forward(z)
t.down()
for i in range(0, 20):
a = random.randint(0,100)/100.0
b = random.randint(0,100)/100.0
c = random.randint(0,100)/100.0
mycircle(a, b, c)
The main issues I've been trying to figure out are:
How can I make the circles spawn further from each other? They overlap
quite often and I want that to be avoided.
How can I make the circles spawn instantly rather than having to be
drawn?
How can I make the circles spawn further from each other?
We can keep track of circles already created and make sure their centers are at least a diameter away from each other. Your current circle placement logic is too complicated along with being faulty. Let's try to simplify it and make sure circles are drawn completely within the window.
How can I make the circles spawn instantly rather than having to be
drawn?
We could stamp them rather than draw them. However, since you are drawing so few circles, we can make every circle a turtle. This makes determining if you clicked on a circle, and removing that circle, simpler. I've added code, for you to expand on, that removes any circle that you click on:
from turtle import Turtle, Screen
from random import random, randint
CURSOR_SIZE = 20
def my_circle(color):
radius = randint(10, 50)
circle = Turtle('circle', visible=False)
circle.shapesize(radius / CURSOR_SIZE)
circle.color(color)
circle.penup()
while True:
nx = randint(2 * radius - width // 2, width // 2 - radius * 2)
ny = randint(2 * radius - height // 2, height // 2 - radius * 2)
circle.goto(nx, ny)
for other_radius, other_circle in circles:
if circle.distance(other_circle) < 2 * max(radius, other_radius):
break # too close, try again
else: # no break
break
circle.showturtle()
circle.onclick(lambda x, y, t=circle: t.hideturtle()) # expand this into a complete function
return radius, circle
screen = Screen()
screen.bgcolor("lightgreen")
screen.title("clicky")
width, height = screen.window_width(), screen.window_height()
circles = []
for _ in range(0, 20):
rgb = (random(), random(), random())
circles.append(my_circle(rgb))
screen.mainloop()
One issue you need to work out is making sure your circle color isn't too similar to (or the same as) your background color, otherwise you'll be hunting an invisible circle. Also, we might be able to speed up the circle drawing process even more, if needed.

Turtle Graphics "if touching colour"

I am trying to make a program in Turtle that draws a Christmas Tree and then some baubles, which I want to be placed randomly on the tree. However because a Christmas Tree is an irregular shape I am not able to place the baubles by randomly choosing x and y co-ordinates. Is there a way to randomly place the baubles on the tree?
I was considering an "turtle.pendown()" and then "if turtle.pen touching "green"" but I am not sure how to code this.
Any help would be greatly appreciated.
One simple, graphic, approach is to:
Find a Python module that has a routine for performing the "point
in polygon"
inclusion
test
Use turtle's begin_poly(), end_poly(), and get_poly() to capture the
vertices that your code generates when drawing the tree
Randomly generate ornaments within the bounding box of the tree but
also apply the crossing number test to see if their centers are on
the tree
Here's an example implementation using an (exceptionally) abstract tree and ornaments:
from turtle import Turtle, Screen
from random import randrange, choice
from point_in_polygon import cn_PnPoly
screen = Screen()
WINDOW_WIDTH, WINDOW_HEIGHT = screen.window_width(), screen.window_height()
COLORS = ["red", "yellow", "gold", "blue", "white", "pink"]
def draw_abstract_tree(turtle):
width = WINDOW_WIDTH//4
turtle.penup()
turtle.goto(0, -WINDOW_HEIGHT//4)
turtle.pendown()
for _ in range(8):
turtle.forward(width)
turtle.left(150)
turtle.forward(1.156 * width)
turtle.right(150)
width *= 0.9
turtle.left(210)
for _ in range(8):
turtle.forward(1.156 * width)
turtle.left(150)
turtle.forward(width)
turtle.right(150)
width /= 0.9
turtle.goto(0, -WINDOW_HEIGHT//4)
turtle.setheading(0)
def decorate_tree(turtle, polygon):
turtle.penup()
for _ in range(1000):
x = randrange(-WINDOW_WIDTH/4, WINDOW_WIDTH/4)
y = randrange(-WINDOW_HEIGHT/4, WINDOW_HEIGHT)
diameter = randrange(1, 12)
if cn_PnPoly((x, y), polygon):
turtle.goto(x, y)
turtle.color(choice(COLORS))
turtle.dot(diameter)
yertle = Turtle(visible=False)
yertle.speed("fastest")
yertle.color("darkgreen")
yertle.begin_poly()
draw_abstract_tree(yertle)
yertle.end_poly()
polygon = yertle.get_poly()
yertle.begin_fill()
draw_abstract_tree(yertle)
yertle.end_fill()
decorate_tree(yertle, polygon)
screen.exitonclick()
OUTPUT
I think turtle doesn't have method to check color.
But turtle uses Canvas from tkinter which have function find_overlaping(rectangle) to check if some objects overlaps this rectangle. Maybe it could works. Maybe you could check if there is tree in some small rectange in random place.
turtle.getcanvas()
tkinter: Canvas.find_overlapping()

Is it possible to change turtle's pen stroke?

I need to draw a bar graph using Python's turtle graphics and I figured it would be easier to simply make the pen a thick square so I could draw the bars like that and not have to worry about making dozens of rectangles and filling them in.
When I set the turtle shape using turtle.shape('square') though, it only changes the appearance of the pen but has no effect on the actual drawing:
Is there a way to make turtle actually draw a rectangular stroke, whether that be through built-in methods or through modifying the turtle file?
I DON'T want rounded edges, like this:
To answer the question asked in the title: No, it is not possible to change the pen stroke directly (see cdlane's answer for a possible way to do it by modifying the hardcoded values from tkinter).
I did find a workaround for the use case presented in the question body, however.
A custom pen shape (in this case, representing the exact shape and size of the bar) can be registered like this:
screen.register_shape("bar", ((width / 2, 0), (-width / 2, 0), (-width / 2, height), (width / 2, height)))`
We can then simply loop through each bar, update the pen shape with the new values, and use turtle.stamp to stamp the completed bars onto the graph, no drawing required.
It looks like changing the shape of the pen stroke itself isn't possible. turtle.shape('square') only changes the shape of the turtle, not the pen stroke. I suggest lowering the pen size, and creating a function to draw a rectangle. You could use this do draw the bars.
I've two solutions to this problem that I've used in various programs.
The first is a variation on your stamp solution. Rather than use screen.register_shape() to register a custom polygon for each line, use a square turtle and for each line turtle.turtlesize() it into the rectangle you want to stamp:
from turtle import Turtle, Screen
STAMP_SIZE = 20 # size of the square turtle shape
WIDTH, LENGTH = 25, 125
yertle = Turtle(shape="square")
yertle.penup()
yertle.turtlesize(WIDTH / STAMP_SIZE, LENGTH / STAMP_SIZE)
yertle.goto(100 + LENGTH//2, 100) # stamps are centered, so adjust X
yertle.stamp()
screen = Screen()
screen.exitonclick()
My other solution, when I need to draw instead of stamp, is to reach into turtle's tkinter underpinning and modify turtle's hardcoded line end shape itself:
from turtle import Turtle, Screen
import tkinter as _
_.ROUND = _.BUTT
WIDTH, LENGTH = 25, 125
yertle = Turtle()
yertle.width(WIDTH)
yertle.penup()
yertle.goto(100, 100)
yertle.pendown()
yertle.forward(LENGTH)
screen = Screen()
screen.exitonclick()
Use multiple stamps like so:
import turtle
turtle.shape("square")
for count in range(x):
turtle.stamp()
turtle.forward(1)

How to create a color wheel using turtle graphics?

I am trying to create somewhat of a color wheel using the turtle module in Python. Let's say I have a list of colors:
colors = ["#880000","#884400","#888800","#008800","#008888","#000088",
"#440088","#880088"]
I am aiming to go around a circle with a radius of 250px plotting in the colors:
def drawColors():
for color in colors:
turtle.color(dark)
for i in range(len(colors)):
turtle.begin_fill
turtle.circle(150)
turtle.end_fill()
turtle.done()
You can do it by dividing the circle up into multiple circular sectors (aka pie slices) and drawing each one in a different color. The tricky part doing it with turtle graphics is setting the initial position and heading (or direction) of the turtle to be at the start of the arc of each one. Also, unlike with the case with a full circle, you need to manually close the figure before filling it by drawing the final line segment from the end of the arc back to the center of the circle.
While this could be calculated mathematically, doing that is avoided in the following code by remembering, for all but the first sector, where the previous one left off and using that as the starting position and heading for the next. Fortunately for the initial one, these values are relatively simple to compute: the position is set to the (circle_center x value + radius, circle_center y value) with a due North heading of 90°.
import turtle
colors = ['#880000','#884400','#888800','#008800',
'#008888','#000088','#440088','#880088']
def draw_color_wheel(colors, radius, center=(0, 0)):
slice_angle = 360 / len(colors)
heading, position = 90, (center[0] + radius, center[1])
for color in colors:
turtle.color(color, color)
turtle.penup()
turtle.goto(position)
turtle.setheading(heading)
turtle.pendown()
turtle.begin_fill()
turtle.circle(radius, extent=slice_angle)
heading, position = turtle.heading(), turtle.position()
turtle.penup()
turtle.goto(center)
turtle.end_fill()
draw_color_wheel(colors, 150, center=(25, 50))
turtle.hideturtle()
print('done - press any key to exit')
turtle.onkeypress(exit)
turtle.listen()
turtle.done()
Result
Since this question has become active again, let's solve it using stamping rather than drawing:
from turtle import Turtle, Screen
colors = ['#880000', '#884400', '#888800', '#008800',
'#008888', '#000088', '#440088', '#880088']
def draw_color_wheel(colors, radius, center=(0, 0)):
slice_angle = 360 / len(colors)
yertle = Turtle(visible=False)
yertle.penup()
yertle.begin_poly()
yertle.sety(radius)
yertle.circle(-radius, extent=slice_angle)
yertle.home()
yertle.end_poly()
screen.register_shape('slice', yertle.get_poly())
yertle.shape('slice')
yertle.setposition(center)
for color in colors:
yertle.color(color)
yertle.stamp()
yertle.left(slice_angle)
screen = Screen()
draw_color_wheel(colors, 250, center=(25, 50))
screen.exitonclick()
OUTPUT
This approach takes slightly less code and produces noticeably faster output.

Categories