Why is the turtle offset? - python

I am trying to create a program to move the turtle to where the mouse is.
Right now I am doing:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
width = canvas.winfo_width()
height = canvas.winfo_height()
midpointX = width / 2
midpointY = height / 2
t.speed(0)
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
turtleX = mouseX - midpointX
turtleY = (mouseY - midpointY) * -1
t.goto(turtleX, turtleY)
The turtle is offset when I run it in PyCharm or through the command line, but not when I run it on replit.
I am using Windows 11 if that helps.
This is the extent to which the turtle goes if my mouse is on the edge of the screen:

This happens for 2 reasons:
canvas.winfo_pointerxy() gives you the mouse coordinates relative to your screen, not the canvas
When you resize the window, width and height don't change because they're just stored values from the beginning
You can fix this by moving midpointX and midpointY inside the loop and using canvas.winfo_rootx(), which gives you the position on the screen:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
t.speed(0)
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
midpointX = canvas.winfo_width() / 2
midpointY = canvas.winfo_height() / 2
turtleX = mouseX - midpointX - canvas.winfo_rootx()
turtleY = -mouseY + midpointY + canvas.winfo_rooty()
t.goto(turtleX, turtleY)

Related

How to convert window coords into turtle coords (Python Turtle)

I am trying to create a program to move the turtle to where the mouse is.
I am doing:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
t.goto(mouseX, mouseY)
but the turtle keeps moving off the screen.
I read from this question that canvas.winfo_pointerxy() returns 'window coordinates' (0, 0 at the top left of the window) and that I need to convert them to 'turtle coordinates' (0, 0 at the center of the window) but I don't know how to do that.
First, you need to find the size of the canvas. for this example, I used a set width and height, but for your purposes, you may find it easier to find the size instead of entering it.
width = 500
height = 300
t = turtle.Turtle()
canvas = turtle.getcanvas()
turtle.screensize(canvwidth=width, canvheight=height)
you can use this code to find the width and height
width = canvas.winfo_width()
height = canvas.winfo_height()
When you measure the mouse's position, however, you'll need to do this calculation to get the right value.
mouseX = canvas.winfo_pointerx() - width/2
mouseY = (canvas.winfo_pointery()*-1) + height/2
t.goto(mouseX, mouseY)
You have to use the dimensions of the window to calculate the center of the window. You just need to subtract the midpoint from the mouse coordinates (since the 0,0 for that is the top-left of the screen) to get it.
You need to add the following:
width = canvas.winfo_width()
height = canvas.winfo_height()
midpoint_x = width / 2
midpoint_y = height / 2
turtleX = mouseX - midpoint_x
turtleY = mouseY - midpoint_y
You end up with something like this:
import turtle
t = turtle.Turtle()
canvas = turtle.getcanvas()
width = canvas.winfo_width()
height = canvas.winfo_height()
midpoint_x = width / 2
midpoint_y = height / 2
while True:
mouseX, mouseY = canvas.winfo_pointerxy()
turtleX = mouseX - midpoint_x
turtleY = mouseY - midpoint_y
t.goto(turtleX, turtleY)

Can I set an exact location of an object using tkinter?

Using Python3.7 I have created code that will move a ball from the top left corner to the bottom right corner. I am using coords to position the ball and move for the motion of the ball. However, I want the ball to start in a certin place. How can I set the position of the ball?
I have tried using place function and I get the error: 'int' object has no attribute 'place'
I tried using coords and I get the error: IndexError: list index out of range
I have tried changing my create_oval code. It works for the size of the ball but not where it starts from.
The code here works with no errors. How and where should I have a line for the exact coordinates of where the ball will start.
import tkinter as tkr
import time
tk = tkr.Tk()
canvas = tkr.Canvas(tk, width=480, height=480)
canvas.grid()
ball = canvas.create_oval(10,10,20,20,fill="blue")
x = 1
y = 1
while True:
canvas.move(ball,x,y)
pos = canvas.coords(ball)
if pos [3] >= 480 or pos[1] <=0:
y = -y
if pos[2] >= 480 or pos[0] <= 0:
x = -x
tk.update()
time.sleep(0.0099)
pass
tk.mainloop()
Also if I can get rid of the deprecation warning, that would be great as well.
Here's how you do a loop like this within the confines of an event-driven UI framework. Each callback does one little bit of work, then goes back to the loop to wait for future events.
import tkinter as tk
import time
win = tk.Tk()
canvas = tk.Canvas(win, width=480, height=480)
canvas.grid()
x = 10
y = 10
dx = 1
dy = 1
def moveball():
global x, dx
global y, dy
x += dx
y += dy
canvas.move(ball,dx,dy)
if y >= 480 or y <=0:
dy = -dy
if x >= 480 or x <= 0:
dx = -dx
win.after( 10, moveball )
ball = canvas.create_oval(x,y,x+10,y+10,fill="blue")
win.after( 100, moveball )
win.mainloop()
You'll note that the ball doesn't change directions until after it's all the way off the edge of the screen. That's because we're tracking the upper left corner of the ball and not taking the size into account. That's an easy thing to fix.
Used variables with the create_oval.
import tkinter as tkr
import time
tk = tkr.Tk()
canvas = tkr.Canvas(tk, width=480, height=480)
canvas.grid()
x = 47
y = 185
ball = canvas.create_oval(x,y,x+10,y+10,fill="blue")
dx = 1
dy = 1
while True:
canvas.move(ball,dx,dy)
pos = canvas.coords(ball)
if pos [3] >= 480 or pos[1] <=0:
dy = -dy
if pos[2] >= 480 or pos[0] <= 0:
dx = -dx
tk.update()
time.sleep(0.0099)
pass
tk.mainloop()
Big thanks to Tim Roberts. I end up taking his coding advice and edit mine original code.

How to make the object bounce back with turtle

I want to make the yellow block jump on the line when there are other shapes. Also, how could I make the screen to close when the yellow block touches the other blocks.
I have though about using the time module and put time.sleep for 1 seconds after blockself.goto(0,30) then make the block go down to (0,0). However, when I did that the whole screen frozed for 1 second. This means that the time.sleep was not aimed for the blockself(yellow block) it self. Is there any way to fix this.
from turtle import Screen, Turtle
import random
WIDTH, HEIGHT = 800, 400
CURSOR_SIZE = 20
BASELINE = -CURSOR_SIZE/2
def move_block(block):
block.x -= CURSOR_SIZE/2
block.setx(block.x)
if block.x <= CURSOR_SIZE/2 - WIDTH/2:
block.x += WIDTH + CURSOR_SIZE
block.setx(block.x)
screen.update()
screen.ontimer(lambda: move_block(block), 40) # delay in milliseconds
screen = Screen()
screen.title("Jump over!")
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.goto(WIDTH/2, BASELINE)
marker.pendown()
marker.goto(-WIDTH/2, BASELINE)
#three blocks
shape1=["square","triangle","circle"]
block_1 = Turtle(shape=(random.choice(shape1)))
block_1.up()
block_1.color('black')
block_1.x = WIDTH/2 + CURSOR_SIZE # user defined property
block_1.setx(block_1.x)
shape2=["square","triangle","circle"]
block_2 = Turtle(shape=(random.choice(shape2)))
block_2.up()
block_2.color('black')
block_2.x = block_1.x + 300
block_2.setx(block_2.x)
shape3=["square","triangle","circle"]
block_3 = Turtle(shape=(random.choice(shape3)))
block_3.up()
block_3.color('black')
block_3.x = block_2.x + 300
block_3.setx(block_3.x)
move_block(block_1)
move_block(block_2)
move_block(block_3)
# self
blockself= Turtle(shape="square")
blockself.color('yellow')
blockself.setx(0)
blockself.direction="Stop"
def goup():
blockself.up()
blockself.goto(0,30)
screen.listen ()
screen.onkey ( goup , "w" )
screen.mainloop()
We can make the yellow block vertical jump independent of the other motion using the same mechanism I gave you for the horizontal block motion, ontimer(). The jump() method below is invoked by the keyboard event, moving the yellow block up and down as a series of timer events:
from turtle import Screen, Turtle
from random import choice
SHAPES = ['square', 'triangle', 'circle']
WIDTH, HEIGHT = 800, 400
CURSOR_SIZE = 20
BASELINE = -CURSOR_SIZE/2
def move_block(block):
block.x -= CURSOR_SIZE/2
block.setx(block.x)
if block.x <= CURSOR_SIZE/2 - WIDTH/2:
block.x += WIDTH + CURSOR_SIZE
block.setx(block.x)
screen.update()
if block.distance(block_self) < CURSOR_SIZE:
screen.bye()
else:
screen.ontimer(lambda: move_block(block), 65) # delay in milliseconds
def jump(direction):
screen.onkey(None, 'w') # disable jumping while jumping
y = block_self.ycor()
if direction > 0 and y >= 40:
direction = -1
elif direction < 0 and y <= 0:
block_self.sety(0)
direction = 0
if direction:
block_self.sety(y + direction)
screen.ontimer(lambda: jump(direction), 25)
else:
screen.onkey(lambda: jump(1), 'w') # jump over, reenable jumping
screen.update()
screen = Screen()
screen.title("Jump over!")
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
marker = Turtle()
marker.hideturtle()
marker.penup()
marker.goto(WIDTH/2, BASELINE)
marker.pendown()
marker.goto(-WIDTH/2, BASELINE)
block_1 = Turtle(shape=choice(SHAPES))
block_1.penup()
block_1.color('black')
block_1.x = WIDTH/2 + CURSOR_SIZE # user defined property
block_1.setx(block_1.x)
block_2 = Turtle(shape=choice(SHAPES))
block_2.penup()
block_2.color('black')
block_2.x = block_1.x + 266
block_2.setx(block_2.x)
block_3 = Turtle(shape=choice(SHAPES))
block_3.penup()
block_3.color('black')
block_3.x = block_2.x + 266
block_3.setx(block_3.x)
block_self = Turtle(shape='square')
block_self.penup()
block_self.color('yellow')
move_block(block_1)
move_block(block_2)
move_block(block_3)
screen.onkey(lambda: jump(1), 'w')
screen.listen()
screen.mainloop()
Also, how could I make the screen to close when the yellow block
touches the other blocks.
It seems a severe response to user miscalculation but I put it in the code above by adding the following conditional to the move_block() function:
if block.distance(block_self) < CURSOR_SIZE:
screen.bye()
Finally, as you're discovering yourself, time.sleep() has no business being used in an event-driven world like turtle.

Randomly choose positions of turtle circles without overlap

If I used randint() to choose the positions of turtle circles, how do I make sure they don't overlap. Here is my code for one circle. The screen has to be 500 x 500 pixels and the circles have to be 35 pixels:
asteroid.pencolor('grey')
asteroid.fillcolor('grey')
asteroid.begin_fill()
asteroid.hideturtle()
asteroid.penup()
asteroid.speed(15)
asteroid.setposition(randint(-400,400), randint(-400,400))
asteroid.pendown()
asteroid.circle(35)
asteroid.end_fill()
At first glance, your claim that "The screen has to be 500,500" and your use of the value -400/400 in the following:
setposition(randint(-400,400), randint(-400,400))
seem to be in conflict as a 500 x 500 screen implies randint(-250, 250)
Moving on, you can achieve your goal by testing each new asteroid against the locations of the others using something like any and turtle.distance():
from turtle import Screen, Turtle
from random import randint
WIDTH, HEIGHT = 500, 500
ASTEROID_RADIUS = 35
NUMBER_ASTEROIDS = 50
CURSOR_SIZE = 20
screen = Screen()
screen.setup(WIDTH, HEIGHT)
asteroid_prototype = Turtle()
asteroid_prototype.hideturtle()
asteroid_prototype.color('grey')
asteroid_prototype.shape('circle')
asteroid_prototype.shapesize(ASTEROID_RADIUS / CURSOR_SIZE)
asteroid_prototype.speed('fastest') # because 15 isn't a valid argument
asteroid_prototype.penup()
asteroids = []
for _ in range(NUMBER_ASTEROIDS):
asteroid = asteroid_prototype.clone()
asteroid.setposition( \
randint(ASTEROID_RADIUS - WIDTH/2, WIDTH/2 - ASTEROID_RADIUS), \
randint(ASTEROID_RADIUS - HEIGHT/2, HEIGHT/2 - ASTEROID_RADIUS) \
)
while any(map((lambda a: lambda b: a.distance(b) < ASTEROID_RADIUS)(asteroid), asteroids)):
asteroid.setposition( \
randint(ASTEROID_RADIUS - WIDTH/2, WIDTH/2 - ASTEROID_RADIUS), \
randint(ASTEROID_RADIUS - HEIGHT/2, HEIGHT/2 - ASTEROID_RADIUS) \
)
asteroid.showturtle()
asteroids.append(asteroid)
screen.exitonclick()

Python Turtle, change visible part

On the canvas,I draw two dots,right one is at (100,0),left one is at (-1000,0).After initializing the program,the orginal screen location(visible part) is near the right dot,just like pic1 show
pic 1:[1]: https://i.stack.imgur.com/KtPRN.png
And now I wanna move the the screen(visible part) to the left dot using coordinate so that i can see it(pic2).What should I do?
pic 2:https://i.stack.imgur.com/Rtfrv.png
def drawDot(x):
penup()
goto(x, 0)
pendown()
dot('pink')
write(x)
b = -1000 #left dot(-1000,0)
a = 100 #right dot(100,0)
speed(0)
delay(0)
tracer(0, 0)
hideturtle()
screensize(500,500)
color('red')
bgcolor('black')
drawDot(a)
drawDot(b)
done()
I believe the following does what you describe. When the window opens, it's centered on (0, 0) and point a is visible off to the right and point b isn't visible at all. When you click on the window, it scrolls so that the window is centered on point b:
from turtle import Screen, Turtle
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 500
CANVAS_WIDTH, CANVAS_HEIGHT = 3000, 1000
def drawDot(x):
turtle.penup()
turtle.setx(x)
turtle.dot('pink')
turtle.write(x)
def scrollToDot(x, y): # unused arguments
canvas = screen.getcanvas()
# tkinter has a different coordinate system
# we have to describe left edge of scrolled
# window as percentage in its coordinates:
screen_center = CANVAS_WIDTH / 2
dot_center = screen_center + b
left_edge = dot_center - screen.window_width() / 2
canvas.xview_moveto(left_edge / CANVAS_WIDTH) # percentage
a = 100 # right dot(100, 0)
b = -1000 # left dot(-1000, 0)
screen = Screen()
screen.setup(WINDOW_WIDTH, WINDOW_HEIGHT) # What we see
screen.screensize(CANVAS_WIDTH, CANVAS_HEIGHT) # What there is
screen.bgcolor('black')
turtle = Turtle()
turtle.hideturtle()
turtle.speed('fastest')
turtle.color('red')
drawDot(a)
drawDot(b)
screen.onclick(scrollToDot)
screen.mainloop()
To do this, we have to access the tkinter Canvas underpinnings of turtle. However, the Canvas coordinate system is different than turtle's, so we have to make an adjustment as noted in the code comments.

Categories

Resources