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

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)

Related

Why is the turtle offset?

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)

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.

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.

Pygame : Two layered scrolling background, can you help me?

I would like create a two layered scrolling background but everytime I run the programm below it make everything wrong, and I don't know why :/
Can you help me ?
I've try the code below :
'bg scrolling try'
import pygame as pg
from pygame.locals import *
pg.init()
screen = pg.display.set_mode((1920, 1080), pg.NOFRAME)
a = True
land_0 = pg.image.load('sprites/land_0.png').convert_alpha()
land_1 = pg.image.load('sprites/land_1.png').convert_alpha()
bg = pg.image.load('sprites/bg.png').convert_alpha()
pos_l0_0 = 2
pos_l0_1 = 1920
pos_l1_0 = -1000
pos_l1_1 = 920
hight_land = 500
hight_land_1 = 400
s_speed = 2
while a:
screen.blit(bg,(0, 0))
pos_l1_0 = pos_l1_0 - s_speed
pos_l1_1 = pos_l1_1 - s_speed
if pos_l1_0 == - 1920:
pos_l1_0 = 1920
elif pos_l1_1 == - 1920:
pos_l1_0 = 1920
screen.blit(land_1,(pos_l1_0, hight_land_1))
screen.blit(land_1,(pos_l1_1, hight_land_1))
# 2nd
pos_l0_0 = pos_l0_0 - s_speed/2
pos_l0_1 = pos_l0_1 - s_speed/2
if pos_l0_0 == - 1920:
pos_l0_0 = 1920
elif pos_l0_1 == - 1920:
pos_l0_0 = 1920
screen.blit(land_0,(pos_l0_0, hight_land))
screen.blit(land_0,(pos_l0_1, hight_land))
pg.display.update()
I would like the first layer scroll fast and the second ( in background scroll slow ), I working prettry during the first 20 seconds but after this it random : one layer diseappear or one is blitting, so strange...
The scrolling land can be imagined as a endless row of tiles. If you want to draw the background at a certain position, then you have to calculate the position of the tile relative to the screen by the modulo (%) operator. The position of the 2nd part tile is shifted by the width of the land surface (e.g. land_0.get_width()).
This means it is sufficient to define 1 position (offset) for each background land, because the 2nd position can be calculated by the width of the land.
Write a function which shifts the land by a certain offset (speed), draws the land surface and returns the new position:
def drawLand(pos, height, speed, land):
width = land.get_width()
pos = (pos + speed) % width
screen.blit(land, (pos, height))
pos_2 = pos + width if pos < 0 else pos - width
screen.blit(land, (pos_2, height))
return pos
Call the function for each land in the main loop of the application:
pos_l0, hight_land = 2, 500
pos_l1, hight_land_1 = -1000, 400
s_speed = 2
while a:
# [...]
screen.blit(bg,(0, 0))
pos_l1 = drawLand(pos_l1, hight_land_1, -s_speed, land_1)
pos_l0 = drawLand(pos_l0, hight_land, -s_speed // 2, land_0)
pg.display.update()

Categories

Resources