How to properly interact with turtle canvas/screen sizing? - python

I am attempting to draw some images using Python Turtle and the screen sizing/canvas sizing is eluding me.
Here is what I have to create the current image:
import turtle
import math
from PIL import Image
import os
t = turtle.Turtle()
Image.MAX_IMAGE_PIXELS = None
screen = turtle.Screen()
fileName = "test"
seedString = fileName
turtle.setup(int(15000),int(15000),int(7500),int(7500))
turtle.screensize(int(15000), int(15000))
turtle.hideturtle()
screen.tracer(False)
t.pen(pensize=2, pencolor='black')
print(screen.screensize())
print(t.pos())
t.color('black')
t.begin_fill()
t.goto(7500,0)
t.right(90)
t.forward(7500)
t.right(90)
t.forward(15000)
t.right(90)
t.forward(15000)
t.right(90)
t.forward(15000)
t.right(90)
t.forward(7500)
t.end_fill()
print(t.pos())
t.goto(0,0)
t.pen(pencolor='white', pensize=5)
t.circle(4000, 360)
print(t.pos())
print(screen.screensize())
canvas = screen.getcanvas()
canvas.postscript(file=fileName, height=15000, width=15000)
img = Image.open(fileName)
img.save(seedString+'.jpg')
img.close()
os.system('move \"'+fileName+'\" somefilelocation')
os.system('move \"'+seedString+'.jpg\" somefilelocation')
Based on my understanding this script should set the Screen size to 15,000 by 15,000 pixels and move the origin to the center at (7,500,7,500) using turtle.setup(). I'm not sure if this is the canvas or just the window that we see the turtle move around in. But I also included the turtle.screensize() function to set it to 15000 x 15000. Perhaps this is redundant as it sizes the same thing, but the documentation is sort of vague regarding it.
At the end I save a PS file with the same size of 15000 x 15000 pixels but this image is what is created:
The border was added by me to make the actual size of the image more viewable.
What I'm struggling to understand is a few things. One if the setup() and screensize() function is actually just changing the window size itself then how do I change the size of the actual canvas the drawing is created on. Second I start by manually drawing a rectangle that is filled black by the goto() and right() combination of functions above. But the image itself (the one in this post is scaled for the sake of size) is saved as 11251 x 11251. This does not only make no sense to me as I am setting the saved PS file to 15000 x 15000 but even if the original image was 11251 x 11251 after saving then why is my rectangle, which should be 15000 x 15000 based on how I scripted it, much much smaller than that. Also the origin does not appear anywhere near the center of either the black rectangle or the overall canvas/image that is saved.
Lastly I cannot understand why the circle() function is only producing a half circle despite me explicitly setting the extent argument to 360. I left it at 0 which is defaulted to a full circle and it produced the same semi circle.
I've tried only including setup or screensize, using absolute values like 1.0 vs pixel values and nothing seems to get me what I want. Which is a 15000 x 15000 pixel image with a circle drawn from the origin, being the center of the canvas at (7500,7500). I know that wouldn't be the center of the circle itself as it starts on an edge and creates a circle from that point to a point above or below it (radius,0) away. But that's not how my script is currently working.
It is clear that there is a fundamental understanding that I am not grasping. Any assistance would be much appreciated as I'd love to learn more about this library and better understand the canvas/coordinate system to more accurately create images.
Thank you!

You've a couple of flaws in your logic. First, you call tracer(False) but never call screen.update() when you finish drawing, which is why your circle is incomplete.
Second, although turtle crawls atop tkinter, it uses a different default coordinate system than Canvas. So we need to adjust for that in our Canvas.postscript() method invocation:
from turtle import Turtle, Screen
WIDTH, HEIGHT = 15_000, 15_000
fileName = "test"
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
turtle.pensize(5)
turtle.penup()
turtle.goto(-WIDTH/2, -HEIGHT/2)
turtle.begin_fill()
for _ in range(2):
turtle.forward(WIDTH)
turtle.left(90)
turtle.forward(HEIGHT)
turtle.left(90)
turtle.end_fill()
turtle.goto(0, -HEIGHT/4)
turtle.pencolor('white')
turtle.pendown()
turtle.circle(HEIGHT/4)
screen.update()
canvas = screen.getcanvas()
canvas.postscript(file=fileName + ".eps", width=WIDTH, height=HEIGHT, x=-WIDTH/2, y=-HEIGHT/2)
You'll note that the image width/height (not fully shown above) is 200 inches, which is the inch equivalent of 15_000 points (using 75 points per inch instead of 72.)
I leave the final step of conversion to JPG to you.

Related

Can you make a Python Turtle detect if it's touching a specific colour?

Is it possible for a turtle to detect whether it is touching a specific colour, or not, without using a grid system (i.e. allocating each cell a colour.) I am trying to create a pixelated world that a creature will navigate, and interact with, depending on what sort of tile it is touching, which will be determined based on the tile's colour.
The best you could try to replicate this Scratch project in Python would be a grid based system using the python framework, pygame.
This would mean you need to code the background, user, interface, commands, collisions. A bigger feat to do all by your own hands.
My files indicate that this would be a good video series to get started:
Setup: https://youtu.be/VO8rTszcW4s
Creating The Game: https://youtu.be/3UxnelT9aCo
I hope your endeavor is fruitful!
We can force turtle to do this by dropping down to the tkinter level. Although we think of things that the turtle draws as dead ink (as opposed to shaped turtles or stamps), they are in fact live ink from tkinter's point of view -- which is why we can clear an individual turtle's drawings and call undo(). Here's a fragile example that does this:
from turtle import Screen, Turtle
from random import random
WIDTH, HEIGHT = 800, 800
DIAMETER = 200
def chameleon(x, y):
turtle.ondrag(None)
overlapping = canvas.find_overlapping(x, -y, x, -y) # adjust to tkinter coordinates
if overlapping:
color = canvas.itemcget(overlapping[0], "fill")
if color:
turtle.fillcolor(color)
turtle.goto(x, y)
turtle.ondrag(chameleon)
screen = Screen()
screen.setup(WIDTH, HEIGHT)
canvas = screen.getcanvas()
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest')
turtle.penup()
for x in range(-WIDTH//2, WIDTH//2, DIAMETER):
for y in range(-HEIGHT//2, HEIGHT//2, DIAMETER):
turtle.goto(x + DIAMETER/2, y + DIAMETER/2)
color = random(), random(), random()
turtle.dot(DIAMETER - 1, color)
turtle.home()
turtle.shape('turtle')
turtle.shapesize(2)
turtle.showturtle()
turtle.ondrag(chameleon)
screen.mainloop()
As you drag the turtle around the screen, it will pick up color from things drawn on the screen. This isn't a clear turtle that you're seeing through, it's reading the ink, which you can confirm for yourself as you move over the background. This code may be turtle implementation specific.
I'm not sure how well this will scale up (or more likely scale down towards pixel size objects) but should give you an idea what's possible if you're willing to embrace turtle's tkinter underpinnings, or simply use tkinter itself.

Circle shape in turtle with smaller radius

This is my first question on StackOverflow, so bear with me. I am trying to have my turtle's shape be a small circle. However, the default circle is too big for my project. Is there a way to make it smaller? My company won't let me load a gif file to be the shape of a turtle.
What I've tried so far is with a gif file where I do:
import turtle
screen = turtle.Screen()
screen.register_shape('circle1', 'circle.gif')
t = turtle.Turtle()
t.shape('circle1')
t.forward(10)
This works but uses a gif file, which my company doesn't allow. Is there a way I can do this without a gif?
Working with the set of cursors that Python provides, including 'circle', you can adjust the size of the cursor using the shapesize() method (aka turtlesize):
from turtle import Screen, Turtle
screen = Screen()
turtle = Turtle()
turtle.shape('circle')
turtle.shapesize(0.5) # make the cursor half the default size
turtle.forward(100)
screen.exitonclick()
Above, we resized it relative to it's default size. If we want to size it to a specific pixel size, we start with the knowledge that the provided cursors are based on a 20px by 20px square, and adjust accordingly:
from turtle import Screen, Turtle
CURSOR_SIZE = 20
screen = Screen()
turtle = Turtle()
turtle.shape('circle')
turtle.shapesize(15 / CURSOR_SIZE) # make the cursor 15 pixels diameter
turtle.color('black', 'white') # make it an open circle this time
turtle.forward(100)
screen.exitonclick()
What you can do is input the polynomial corresponding to the size of the circle you want.
register_shape takes in more than just a file name, it can also take in coordinates.
i.e. screen.register_shape(name, coordinates) is another way of using that function.
If you want to make the circle smaller, draw the circle with the corresponding size and append it to a list. Then convert that list into a tuple and that can be your new shape.
In general you can draw any shape, append the coordinates and use that.
Here is an example for a circle:
Specific Example
import turtle
def drawCircle(radius,iterations, t): # returns tuples
coords = []
for i in range(iterations):
t.forward(radius)
t.right(360.0/iterations)
coords.append((t.xcor(), t.ycor()))
return tuple(coords)
t = turtle.Turtle()
screen = turtle.Screen()
screen.register_shape('myCircle', drawCircle(1, 72, t))
t.shape('myCircle')
General Example
import turtle
def drawShape(t):
coords = []
while # turtle move
coords.append((t.xcor(), t.ycor()))
return tuple(coords)
t = turtle.Turtle()
screen = turtle.Screen()
screen.register_shape('myShape', drawShape(t))
t.shape('myShape')
Note that when you do have a custom shape, then turtle will automatically fill it in for you. So this won't be a circle, but a filled-in circle instead.

How to draw a single point using Turtle Graphic

Is there any way to draw a single point using Turtle library instead of drawing a square? I would love to use less memory and be able to draw it faster. At the moment I am using this:
def draw_p(alex, d):
# trick to save local state PUSH
cwd = alex.position()
alex.pendown()
alex.forward(d)
alex.left(90)
alex.forward(d)
alex.left(90)
alex.forward(d)
alex.left(90)
alex.forward(d)
alex.penup()
alex.goto(cwd) # POP retrieve back to enter state
You could use the method penup() instead of pendown() if you don't want to draw while the cursor is moving, then you'll draw only points.
Besides, you could change the arrow-style shape for a circle-style shape.
I've written a simple code to create a spiral made up of several points.
Code (Updated)
import turtle
turtle.setup(800, 600)
wn = turtle.Screen()
spiral = turtle.Turtle()
spiral.color("blue")
spiral.penup()
size = 10
for i in range(35):
spiral.dot()
size = size + 2
spiral.forward(size)
spiral.right(24)
Plot

Make Python turtle window size same as canvas size

I'm drawing the Syrian flag with Python turtle. I left out the two green stars for now, as they require an external dependency (oh wow, how's that for an accidental political pun? lol). Here's the code:
from turtle import *
# Set up the drawing environment
length = 600
height = 400
setup(length, height)
setworldcoordinates(0,0,length,height)
goto(0,0)
colours = ["red","black","white","Darkgreen"]
bgcolor(colours[0])
speed('fastest')
## Black Stripe ##
pencolor(colours[1])
pendown()
fillcolor(colours[1])
begin_fill()
goto(length,0)
goto(length,height/3)
goto(0,height/3)
goto(0,0)
end_fill()
penup()
# White Stripe
goto(0,height/3)
pencolor(colours[2])
pendown()
fillcolor(colours[2])
begin_fill()
goto(length,height/3)
goto(length,(height/3)*2)
goto(0,(height/3)*2)
goto(0,height/3)
end_fill()
penup()
ht()
done()
Why is there a red border around the image? I have set the canvas size and the coordinate system to equal each other so that 1 pixel equals 1 pixel. The black box, for example, should start at the bottom-left edge of the canvas as I have told it to, but in it's stead lies a red border. If you resize the window manually after running the code, no matter how small you make it in an attempt to overlap the red border, there will always be a red border. I haven't even written a border into the code.
I think it might have something to do with the innards of Python turtle, that it just allows a small border in order to see what lies on the coordinates (0,0), but I can't find any documentation that actually shows how turtle works, they all just say things like "setup(x,y) = set canvas size" or bgcolor("enter colour here") and don't explain any of the definitions that make up the functions.
Does anyone know how to either:
Remove the border?
Make window size equal to canvas size, hence covering the red border?
Cheers!
(edit: By the way, screensize(length,height) gives me red borders and scrollbars.)
Not sure if i can alter it unless i make a tk window from scratch
which defeats the purpose of using turtle in the first place.
You can create your window structure with tkinter and then embed a turtle canvas within it. Below is an example of your program done this way, in which you can switch between turtle's ScrolledCanvas and tkiner's plain `Canvas' to see the difference:
import tkinter as tk
from turtle import TurtleScreen, RawTurtle, ScrolledCanvas
WIDTH, HEIGHT = 640, 480
root = tk.Tk()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT)
# canvas = ScrolledCanvas(root, width=WIDTH, height=HEIGHT)
canvas.pack()
screen = TurtleScreen(canvas)
screen.setworldcoordinates(0, 0, WIDTH, HEIGHT)
screen.bgcolor('red')
turtle = RawTurtle(screen, visible=False)
turtle.speed('fastest')
## Black Stripe ##
turtle.color('black')
turtle.begin_fill()
turtle.setx(WIDTH)
turtle.sety(HEIGHT / 3)
turtle.setx(0)
turtle.sety(0)
turtle.end_fill()
## White Stripe ##
turtle.penup()
turtle.sety(HEIGHT / 3)
turtle.pendown()
turtle.color('white')
turtle.begin_fill()
turtle.setx(WIDTH)
turtle.sety(2 * HEIGHT / 3)
turtle.setx(0)
turtle.sety(HEIGHT / 3)
turtle.end_fill()
screen.mainloop()
The ScrolledCanvas introduces more chrome (borders and other overhead) which shifts things around, sometimes in undesireable ways. Here's a vertical slice showing the difference between plain Canvas on the left and ScrolledCanvas on the right:
as more chrome is added to the window, the drawing area moves slightly. Though the large amount of offset you're seeing in your output that partially disappears when you move the window certainly seems a bug.

Creating multiple colored turtles in one window in editor on Python 3.4

So far I have this and it makes two circles but one is off-screen. I want to center it and have them separate from each other. Right now it does two loops but I want it to do one small circle then go on to make a larger one around the first in the middle of the screen. Both need to be diff. colors.
def sun_and_earth():
import turtle #allows me to use the turtles library
turtle.Turtle()
turtle.Screen() #creates turtle screen
turtle.window_height()
turtle.window_width()
turtle.bgcolor('grey') #makes background color
turtle.color("red", "green")
turtle.circle(2, 360) #draws a (size, radius) circle
turtle.circle(218, 360)
turtle.exitonclick() #exits out of turtle window on click of window
I think you may have some misunderstanding with regard to some of the functions in the turtle library. Firstly, turtle.window_height() and turtle.window_width() return the height and width of the window, so (as these values are not being assigned) those two lines do nothing. Similarly, turtle.Screen() returns an object, so again that line does nothing.
In order to centre your circles, you need to change where the turtle starts by using the turtle.setpos() function. This will change the x and y coordinates of where your turtle is. If you start the turtle one radius down, this will effectively centre the circle at (0, 0), because the center of the circle is (from the documentation) one radius to the left.
Remember to take your pen off the page when you are moving so that you don't draw lines between the two points by accident, and to put the pen back down again when you want to draw again.
Try this code:
import turtle
turtle.Turtle()
turtle.bgcolor('grey')
# decide what your small circle will look like
smallColour = "red"
smallRadius = 5
# draw the small circle
turtle.color(smallColour)
turtle.penup()
turtle.setpos(0, -smallRadius)
turtle.pendown()
turtle.circle(smallRadius)
# decide what your large circle will look like
largeColour = "white"
largeRadius = 100
# draw the large circle
turtle.color(largeColour)
turtle.penup()
print(turtle.pos())
turtle.setpos(0, -largeRadius)
print(turtle.pos())
turtle.pendown()
turtle.circle(largeRadius)
turtle.penup()
turtle.setpos(0, 0)
I hope this helps, but I think that you have a few misunderstandings about the use of the turtle, it might be a good idea to look at a tutorial or maybe take a look at the documentation
Best of luck

Categories

Resources