python canvas.find_overlapping appears to have inverted y-axis - python

I am trying to find the color of the canvas under a Python turtle. I use canvas.find_overlapping but it is only successful when I negate the ycor, implying that the y-axis is inverted in the canvas object, compared to what is shown. Is there a problem with my code or is the y-axis inverted?
import turtle
wn = turtle.Screen()
maze_drawer = turtle.Turtle()
maze_drawer.color("purple")
maze_drawer.speed("fastest")
path_width = 15
def get_pixel_color(x, y):
c = turtle.Screen().getcanvas()
# -y should not work??
items = c.find_overlapping(x, -y, x, -y)
if len(items) > 0:
return c.itemcget(items[0], "fill") # get 0 object (canvas)
# draw simplified maze
wall_len = 0
for i in range(10):
maze_drawer.left(90)
wall_len += path_width
maze_drawer.forward(wall_len)
# navigate maze from center
maze_runner = turtle.Turtle()
maze_runner.color("green")
maze_runner.penup()
maze_runner.goto(-path_width, -path_width)
# test in y dir: maze_runner.setheading(90)
clear = True
while(clear):
maze_runner.forward(1)
color_at_turtle = get_pixel_color(maze_runner.xcor(), maze_runner.ycor())
if (color_at_turtle == "purple"):
clear = False
wn.exitonclick()

Neat use of tkinter pixel detection within turtle! If the inverted Y coordinate is bothersome, you can flip it from turtle's perspective:
from turtle import Screen, Turtle
screen = Screen()
width, height = screen.window_width() / 2, screen.window_height() / 2
screen.setworldcoordinates(-width, height, width, -height) # flip Y coordinate
Then your code doesn't have to think about negating Y as long as you know you're drawing upside down:
from turtle import Screen, Turtle
PATH_WIDTH = 15
def get_pixel_color(x, y):
canvas = screen.getcanvas()
items = canvas.find_overlapping(x, y, x, y)
if items:
return canvas.itemcget(items[0], "fill") # get 0 object (canvas)
return None
screen = Screen()
width, height = screen.window_width() / 2, screen.window_height() / 2
screen.setworldcoordinates(-width, height, width, -height)
maze_drawer = Turtle(visible=False)
maze_drawer.color("purple")
maze_drawer.speed("fastest")
# draw simplified maze
wall_len = 0
for _ in range(20):
maze_drawer.left(90)
wall_len += PATH_WIDTH
maze_drawer.forward(wall_len)
# navigate maze from center
maze_runner = Turtle()
maze_runner.color("dark green", "green")
maze_runner.penup()
maze_runner.goto(-PATH_WIDTH, -PATH_WIDTH)
def run_maze():
maze_runner.forward(1)
x, y = maze_runner.position()
color_at_turtle = get_pixel_color(x, y)
if color_at_turtle == "purple":
maze_runner.backward(PATH_WIDTH - 1)
maze_runner.left(90)
x, y = maze_runner.position()
if -width < x < width and -height < y < height:
screen.ontimer(run_maze, 10)
run_maze()
screen.exitonclick()

Related

How can i print random color circles in turtle?

the below code is one part of my code. I would like to print random colored circles. But it does not work. Can someone please help me to correct this code? circles shouldnt be overalapped!!
ERROR is bad color sequence: (164, 13, 120)
from random import randint
from svg_turtle import SvgTurtle
import turtle
from turtle import Turtle
import json
def fiber_circle(fiber, width, height):
fiber_r=25
fiber_num = 100
cursor_size = 20
fiber.hideturtle()
fiber.screen.bgcolor("white")
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
fiber.color(r,g,b)
fiber.screen.colormode(255)
fiber.shape("circle")
fiber.shapesize(fiber_r / cursor_size)
fiber.speed("fastest")
fiber.penup()
fibers = []
for _ in range(fiber_num):
fiberr = fiber.clone()
fiberr.setposition(
randint(-width / 2, width / 2),
randint(-height / 2, height / 2),
)
Your program, if it ran, would simply change the turtle cursor into different colors, but in one location. If you want to draw randomly colored circles all over your window, you can try something like the following which uses dot() to draw them:
from turtle import Screen, Turtle
from random import randint
FIBER_RADIUS = 25
FIBER_NUMBER = 100
CURSOR_SIZE = 20
def fiber_circle(fiber):
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
x = randint(FIBER_RADIUS - width//2, width//2 - FIBER_RADIUS)
y = randint(FIBER_RADIUS - height//2, height//2 - FIBER_RADIUS)
fiber.color(r, g, b)
fiber.goto(x, y)
fiber.dot(FIBER_RADIUS * 2) # dot() takes a diameter
screen = Screen()
screen.colormode(255)
width, height = screen.window_width(), screen.window_height()
fiber = Turtle()
fiber.hideturtle()
fiber.speed("fastest")
fiber.penup()
for _ in range(FIBER_NUMBER):
fiber_circle(fiber)
screen.exitonclick()
Alternatively, you can use your original approach of shaping, sizing and coloring the turtle itself, but then use stamp() to leave behind a circle while moving the turtle randomly around the screen:
...
def fiber_circle(fiber):
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
x = randint(FIBER_RADIUS - width//2, width//2 - FIBER_RADIUS)
y = randint(FIBER_RADIUS - height//2, height//2 - FIBER_RADIUS)
fiber.color(r, g, b)
fiber.goto(x, y)
fiber.stamp()
screen = Screen()
screen.colormode(255)
width, height = screen.window_width(), screen.window_height()
fiber = Turtle()
fiber.hideturtle()
fiber.shape('circle')
fiber.shapesize(FIBER_RADIUS * 2 / CURSOR_SIZE) # CURSOR_SIZE is effectively a diameter
fiber.speed('fastest')
fiber.penup()
for _ in range(FIBER_NUMBER):
fiber_circle(fiber)
...

Tkinter Collision Detection

I'm trying to code collision detection between objects in tkinter for my coursework (unfortunately we cannot use pygame). I have a red square and a black square and I move the red square, and I want the black square to delete when the red square overalaps with it. However this doesn't happen as when I overlap the red square into the black square, the latter doesn't delete. How do I fix this?
import tkinter as tk
from tkinter import *
window = Tk()
window.geometry("1366x768")
canvas = tk.Canvas(window, width = 1300, height = 700)
rec1 = canvas.create_rectangle(100, 100, 150, 150, fill = "black") #black rectangle
mover = canvas.create_rectangle(400, 400, 450, 450, fill = "red", outline = "red") #red moving rectangle
canvas.pack()
def left(event):
x = -10
y = 0
canvas.move(mover, x, y)
def right(event):
x = 10
y = 0
canvas.move(mover, x, y)
def up (event):
x = 0
y = -10
canvas.move(mover, x, y)
def down(event):
x = 0
y = 10
canvas.move(mover, x, y)
window.bind("<Left>", left)
window.bind("<Right>", right)
window.bind("<Up>", up )
window.bind("<Down>", down)
a = canvas.bbox(1) # coordinates of the black rectangle
b = canvas.bbox(2) # coordinates of the red, moving rectangle
if b[0] in range(a[0],a[2]) or b[2] in range(a[0],a[2]) and b[1] in range(a[1],a[3]) or b[3] in range(a[1],a[3]):
canvas.delete(rec1) #if they overlap, then delete the static black rectangle
window.mainloop()

In the turtle module, how to find the maximum values of coordinates?

import turtle as t
from random import randint, random
def draw_star(points, size, col, x, y):
t.penup()
t.goto(x, y)
t.pendown()
angle = 180 - (180 / points)
t.color(col)
t.begin_fill()
for i in range(points):
t.forward(size)
t.right(angle)
t.end_fill()
# Main code
while True:
ranPts = randint(2, 5) * 2 + 1
ranSize = randint(10, 50)
ranCol = (random(), random(), random())
ranX = randint(-350, 300)
ranY = randint(-250, 250)
draw_star(ranPts, ranSize, ranCol, ranX, ranY)
Question:
How could I know the maximum values of coordinates of my screen? So I can have a better idea on how to set the values of ranX and ranY?
Thanks.
You could use t.setworldcoordinates(llx, lly, urx, ury)
The parameters:
llx = x of lower left corner
lly = y of lower left corner
urx= x of upper right corner
ury = y of upper right corner
You can create a function and find the values of coordinates yourself by clicking on the screen like this:
# turtle library
import turtle
#This to make turtle object
tess=turtle.Turtle()
# self defined function to print coordinate
def buttonclick(x,y):
print("You clicked at this coordinate({0},{1})".format(x,y))
#onscreen function to send coordinate
turtle.onscreenclick(buttonclick,1)
turtle.listen() # listen to incoming connections
turtle.speed(10) # set the speed
turtle.done() # hold the screen
This will print everytime you click on the screen and print the coordinates out.
The screensize() function returns the canvas width and the canvas height as a tuple.
You can use this to find the max coordinates of the canvas.
screenSize = t.screensize() #returns (width, height)
# Main code
while True:
ranPts = randint(2, 5) * 2 + 1
ranSize = randint(10, 50)
ranCol = (random(), random(), random())
ranX = randint(50-screenSize[0], screenSize[0] - 100)
ranY = randint(50-screenSize[1], screenSize[1] - 100)
draw_star(ranPts, ranSize, ranCol, ranX, ranY)
I found out this is what I need: t.window_width() and t.window_height().

Tic Tac Toe Game using Turtle

This is for an extra credit assignment in Python. I've finished most until the last part where I have to determine the area of the tictactoe box chosen.
I can only detect diagonal boxes, used from combining both code reply's below.
I can detect those 3 boxes but the rest still show as none and most logic is used with loop so I can understand where I am going wrong.
import turtle
from time import sleep
import sys
CURSOR_SIZE = 20
SQUARE_SIZE = 99
FONT_SIZE = 40
FONT = ('Arial', FONT_SIZE, 'bold')
BOXES = {}
# TRACK BOX
pen = turtle.Turtle()
pen.penup()
def mouse(x, y):
print('|--------------X={0} Y={1}--------------|'.format(x, y))
for key in BOXES:
minx, miny, maxx, maxy = BOXES[key]
print(key, BOXES[key])
if (minx <= x <= maxx) and (miny <= y <= maxy):
print("Found", key)
return key
print('None')
return None # Not found.
class TicTacToe:
global BOXES
def __init__(self):
# CREATES 2D LIST FOR INTERROGATION
self.board = [['?'] * 3 for i in range(3)]
def minmax(self, points):
""" Find extreme x and y values in a list of 2-D coordinates. """
minx, miny, maxx, maxy = points[0][0], points[0][1], points[0][0], points[0][1]
for x, y in points[1:]:
if x < minx:
minx = x
if y < minx:
miny = y
if x > maxx:
maxx = x
if y > maxy:
maxy = y
return minx, miny, maxx, maxy
def drawBoard(self):
##############################################
turtle.shape('square')
turtle.shapesize(SQUARE_SIZE * 3 / CURSOR_SIZE)
turtle.color('black')
turtle.stamp()
turtle.hideturtle()
##############################################
for j in range(3):
for i in range(3):
# CREATES SHAPE AND STORES IN PLACEHOLDER
turtle.shape('square')
box = turtle.shape('square')
# CREATES SHAPE SIZE AND STORES IN PLACEHOLDER
turtle.shapesize(SQUARE_SIZE / CURSOR_SIZE)
boxsize = turtle.shapesize()
# CREATES SHAPE COLOR
turtle.color('white')
turtle.penup()
# CREATES SHAPE POS AND STORES IN PLACEHOLDER
turtle.goto(i * (SQUARE_SIZE + 2) - (SQUARE_SIZE + 2), j * (SQUARE_SIZE + 2) - (SQUARE_SIZE + 2))
boxpos = turtle.pos()
mypos = []
pen.goto(boxpos[0]-50,boxpos[1]+50)
##############################################
for line in range(0, 4):
pen.forward(SQUARE_SIZE)
pen.right(90)
mypos.append(pen.pos())
turtle.showturtle()
turtle.stamp()
##############################################
a = mypos[0]
b = mypos[1]
c = mypos[2]
d = mypos[3]
self.board[j][i] = [a, b, c, d]
##############################################
BOXES['BOX01'] = self.minmax(self.board[0][0])
BOXES['BOX02'] = self.minmax(self.board[0][1])
BOXES['BOX03'] = self.minmax(self.board[0][2])
##############################################
BOXES['BOX11'] = self.minmax(self.board[1][0])
BOXES['BOX12'] = self.minmax(self.board[1][1])
BOXES['BOX13'] = self.minmax(self.board[1][2])
##############################################
BOXES['BOX21'] = self.minmax(self.board[2][0])
BOXES['BOX22'] = self.minmax(self.board[2][1])
BOXES['BOX23'] = self.minmax(self.board[2][2])
##############################################
turtle.onscreenclick(mouse)
turtle.setup(800, 600)
wn = turtle.Screen()
z = TicTacToe()
z.drawBoard()
turtle.mainloop()
I believe you're making the problem harder than necessary by not taking full advantage of Python turtle. Instead of trying to find a square within the board when clicking on the screen, make the squares of the board themselves turtles that respond to mouse clicks. Then there's nothing to figure out, position-wise.
Here's a reimplementation that draws a board, allows you to click on it, alternately sets the clicked sections to 'X' or 'O':
from turtle import Turtle, Screen
CURSOR_SIZE = 20
SQUARE_SIZE = 50
FONT_SIZE = 40
FONT = ('Arial', FONT_SIZE, 'bold')
class TicTacToe:
def __init__(self):
self.board = [['?'] * 3 for i in range(3)] # so you can interrogate squares later
self.turn = 'X'
def drawBoard(self):
background = Turtle('square')
background.shapesize(SQUARE_SIZE * 3 / CURSOR_SIZE)
background.color('black')
background.stamp()
background.hideturtle()
for j in range(3):
for i in range(3):
box = Turtle('square', visible=False)
box.shapesize(SQUARE_SIZE / CURSOR_SIZE)
box.color('white')
box.penup()
box.goto(i * (SQUARE_SIZE + 2) - (SQUARE_SIZE + 2), j * (SQUARE_SIZE + 2) - (SQUARE_SIZE + 2))
box.showturtle()
box.stamp() # blank out background behind turtle (for later)
self.board[j][i] = box
box.onclick(lambda x, y, box=box, i=i, j=j: self.mouse(box, i, j))
def mouse(self, box, i, j):
box.onclick(None) # disable further moves on this square
# replace square/turtle with (written) X or O
box.hideturtle()
box.color('black')
box.sety(box.ycor() - FONT_SIZE / 2)
box.write(self.turn, align='center', font=FONT)
self.board[j][i] = self.turn # record move
self.turn = ['X', 'O'][self.turn == 'X'] # switch turns
screen = Screen()
game = TicTacToe()
game.drawBoard()
screen.mainloop()
You can use board to do scoring, or implement a smart computer player, or whatever you desire.
This should give you the basic idea, which is compute the minimum and maximum x and y values of each box and store them in BOXES. This makes it very easy to determine if the given x and y coordinates passed to the mouse() callback function are within any of them.
With the multiple boxes in your real code, make sure to apply the new minmax() function to the corners of each one of them.
import turtle
""" This will be based off 1 box instead of all 9"""
pen = turtle.Turtle()
corners = []
BOXES = {}
for line in range(0, 4):
pen.forward(50)
pen.left(90)
corners.append(pen.pos())
def minmax(points):
""" Find extreme x and y values in a list of 2-D coordinates. """
minx, miny, maxx, maxy = points[0][0], points[0][1], points[0][0], points[0][1]
for x, y in points[1:]:
if x < minx:
minx = x
if y < minx:
miny = y
if x > maxx:
maxx = x
if y > maxy:
maxy = y
return minx, miny, maxx, maxy
BOXES['MIDDLEBOX'] = minmax(corners)
for i in BOXES:
print(i, BOXES[i])
"""
POINTS FROM LEFT TO RIGHT
(1) - TOP LEFT CORNER
(2) - BOTTOM LEFT CORNER
(3) - BOTTOM RIGHT CORNER
(4) - TOP RIGHT CORNER
"""
def mouse(x, y):
""" Return key of box if x, y are within global BOXES
or None if it's not.
"""
for key in BOXES:
minx, miny, maxx, maxy = BOXES[key]
if (minx <= x <= maxx) and (miny <= y <= maxy):
print(key)
return key
print('None')
return None # Not found.
turtle.onscreenclick(mouse)
turtle.mainloop()

turtle - How to get mouse cursor position in window?

How would I go about finding the current position of the mouse pointer in the turtle screen? I want it so I have the mouse position before I click and while im moving the cursor. I have searched google and this website can't find anything besides how to get the position after a click.
turtle.getcanvas() returns a Tkinter Canvas.
Like with a Tkinter window, you can get the current mouse pointer coordinates by winfo_pointerx and .winfo_pointery on it:
canvas = turtle.getcanvas()
x, y = canvas.winfo_pointerx(), canvas.winfo_pointery()
# or
# x, y = canvas.winfo_pointerxy()
If you want to only react to movement instead of e.g. polling mouse pointer positions in a loop, register an event:
def motion(event):
x, y = event.x, event.y
print('{}, {}'.format(x, y))
canvas = turtle.getcanvas()
canvas.bind('<Motion>', motion)
Note that events only fire while the mouse pointer hovers over the turtle canvas.
All these coordinates will be window coordinates (with the origin (0, 0) at the top left of the turtle window), not turtle coordinates (with the origin (0, 0) at the canvas center), so if you want to use them for turtle positioning or orientation, you'll have to do some transformation.
Following up on this answer, here's an example of one way to normalize the Tkinter coordinates for turtle using the canvas.bind('<Motion>', motion) approach (canvas.winfo_pointerxy() didn't provide values that made sense to me, although I didn't look into it much):
import turtle
def on_motion(event):
x = event.x - turtle.window_width() / 2
y = -event.y + turtle.window_height() / 2
turtle.goto(x, y)
turtle.stamp()
print(x, y)
turtle.tracer(0)
turtle.penup()
turtle.getcanvas().bind("<Motion>", on_motion)
turtle.exitonclick()
Using this in a real-time app rather than only on motion:
import turtle
def on_motion(event):
global mouse_x, mouse_y
mouse_x = event.x - turtle.window_width() / 2
mouse_y = -event.y + turtle.window_height() / 2
def tick():
print(mouse_x, mouse_y)
turtle.goto(mouse_x, mouse_y)
turtle.stamp()
win.ontimer(tick, frame_delay_ms)
turtle.tracer(0)
mouse_x, mouse_y = 0, 0
turtle.getcanvas().bind("<Motion>", on_motion)
turtle.shape("circle")
turtle.penup()
frame_delay_ms = 1000 // 30
win = turtle.Screen()
tick()
turtle.exitonclick()
Putting it into a more full-featured app without global:
import turtle
from math import sin
def add_mouse_listener():
def on_motion(event):
nonlocal x, y
x = event.x - turtle.window_width() / 2
y = -event.y + turtle.window_height() / 2
turtle.getcanvas().bind("<Motion>", on_motion)
x, y = 0, 0
return lambda: (x, y)
def color(c, a):
return sin(c + a) / 2 + 0.5
def colors(r, ra, g, ga, b, ba):
return color(r, ra), color(g, ga), color(b, ba)
def main():
ra = 0
ba = 0
ga = 0
r = 0.5
b = 0
g = 1
frame_delay_ms = 1000 // 30
turtle.tracer(0)
turtle.pensize(40)
mouse_pos = add_mouse_listener()
win = turtle.Screen()
def tick():
nonlocal ra, ba, ga
turtle.color(colors(r, ra, g, ga, b, ba))
ra += 0.03
ba += 0.0311
ga += 0.032
x, y = mouse_pos()
turtle.setheading(turtle.towards(x, y))
turtle.forward(10)
turtle.update()
win.ontimer(tick, frame_delay_ms)
tick()
turtle.exitonclick()
if __name__ == "__main__":
main()

Categories