In Python turtle, when drawing objects on the screen, if there was a way to have a circle's radius connected to the window width or height, so that it can be resized by altering the window size?
Yes, it is possible.
You will need to create an event that the turtle will listen to: In the following example, if you click on the turtle, a circle of half the width of the canvas will be drawn.
If you resize the canvas, and click on the turtle again, a new circle of half the
new width will be redrawn.
import turtle
def start(dummy_a, dummy_b):
t.reset()
y, x = screen.window_height(), screen.window_width()
t.home()
t.circle(x/4)
if __name__ == '__main__':
screen = turtle.Screen()
t = turtle.Turtle()
t.onclick(start, add=True)
screen.listen()
turtle.done()
Here's my alternative click to adjust drawing to resized window solution:
from turtle import Turtle, Screen
def onResize(x=0, y=0):
screen.onclick(None) # disable events inside event handler
screen.setworldcoordinates(-1, -1, 1, 1)
screen.onclick(onResize)
screen = Screen()
onResize() # establish initial coordinate system
turtle = Turtle(visible=False)
turtle.penup()
turtle.sety(-0.5)
turtle.pendown()
turtle.circle(0.5, steps=30)
screen.mainloop()
Note that we're not redrawing anything, we're just readjusting our virtual coordinates (unit square in this example) and letting turtle redraw things. If we're willing to peek under the hood, we can take this one step further:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen, ScrolledCanvas
class MyTurtleScreen(TurtleScreen):
def __init__(self, cv):
super().__init__(cv)
cv.bind('<Configure>', self.onResize)
def onResize(self, event=None):
self.setworldcoordinates(-1, -1, 1, 1)
root = tk.Tk()
canvas = ScrolledCanvas(root)
canvas.pack(fill=tk.BOTH, expand=tk.YES)
screen = MyTurtleScreen(canvas)
screen.onResize() # establish initial coordinate system
turtle = RawTurtle(screen, visible=False)
turtle.penup()
turtle.sety(-0.5)
turtle.pendown()
turtle.circle(0.5, steps=30)
screen.mainloop()
This is a generic embedding turtle in tkinter example except that I've customized TurtleScreen to accept a Configure event. Now, when you resize the window, the coordinate system trick from before kicks in automatically so you don't need to click on the window -- it just happens.
Related
I'm trying to draw on a small 200x200 screen using turtle, however the drawing doesn't pop up as full size, it opens a smaller window and I have to scroll up/down, left/right (just a bit) to see the whole drawing. I don't have this problem with larger windows. How do I prevent this?
import turtle
import random
height, width = 200, 200
screen = turtle.Screen()
screen.setup(width, height)
screen.setworldcoordinates(0, 0, width, height)
t = turtle.Turtle()
t.speed(1)
for _ in range(5):
t.penup()
t.goto(random.randint(20, width-20), random.randint(0, height-40))
t.pendown()
t.circle(20)
edit: screenshot, I want the actual size window instead of the scrolls
You could resize the window to 420×420.
If you don't want to resize your window, I suggest modifying the values for the keys "canvwidth" and "canvheight" keys in the turtle._CFG dictionary:
import turtle
import random
height, width = 200, 200
screen = turtle.Screen()
screen.setup(width, height)
screen.setworldcoordinates(0, 0, width, height)
turtle._CFG.update({"canvwidth": width-20, "canvheight": height-20}) # Removing the scroll bars
t = turtle.Turtle()
t.speed(1)
for _ in range(5):
t.penup()
t.goto(random.randint(20, width-20), random.randint(0, height-40))
t.pendown()
t.circle(20)
screen.exitonclick()
Using small windows in turtle is a can of worms. If #TheOneMusic's simple solution (+1) is good enough for your purposes, go for it! On my system, your setworldcoordinates() call gets rid of the scroll bars, so I don't even see the issue. So, another approximate solution might be to upgrade to current Python and tkinter.
However, neither is an exact solution. If we add code to draw a 200 x 200 box around our drawing area:
t.penup()
t.color('red')
t.goto(0, 0) # because of setworldcoordinates()
t.pendown()
for _ in range(4):
t.forward(200)
t.left(90)
We get the box skewed:
To solve this problem more precisely, involves uglier code:
from turtle import Screen, Turtle
from random import randint
TRUE_WIDTH, TRUE_HEIGHT = 200, 200
CURSOR_SIZE = 20 # for drawing frame around edge
RADIUS = 20
CHROME = 14 # magic number possibly derivable from tkinter
width, height = TRUE_WIDTH + CHROME, TRUE_HEIGHT + CHROME # needs to be slightly larger than 200 target
offset_x = CHROME / -2 + 2
offset_y = CHROME / 2 - 2
screen = Screen()
screen.setup(width, height)
screen.screensize(width/2, height/2) # backing store needs to be smaller than window
screen.setworldcoordinates(0, 0, TRUE_WIDTH, TRUE_HEIGHT)
# Draw red frame around edge to "prove" drawing area
frame = Turtle(shape='square', visible=False)
frame.shapesize(TRUE_HEIGHT / CURSOR_SIZE, TRUE_WIDTH / CURSOR_SIZE) # 200 x 200 frame
frame.color('red', 'white')
frame.penup()
frame.goto(TRUE_WIDTH/2 + offset_x, TRUE_HEIGHT/2 + offset_y)
frame.stamp()
turtle = Turtle()
turtle.speed('fastest') # because I have no patience
for _ in range(5):
turtle.penup()
turtle.goto(randint(RADIUS, TRUE_WIDTH - RADIUS) + offset_x, randint(0, TRUE_HEIGHT - RADIUS*2) + offset_y)
turtle.pendown()
turtle.circle(RADIUS)
screen.exitonclick()
But this sort of detail work could easily be undone by a future release of turtle and/or tkinter. If you can live with turtle's default window, life gets easier.
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.
How do you make turtle move without using turtle.goto(x,y) but turtle.speed(speed) and turtle.heading(angle)? I need this for a game I am making. Where the mouse is, I want to make it go in that direction. But when I change it, it goes to that place then to my mouse:
import turtle
screen = turtle.Screen()
screen.title("Test")
screen.bgcolor("white")
screen.setup(width=600, height=600)
ship = turtle.Turtle()
ship.speed(1)
ship.shape("triangle")
ship.penup()
ship.goto(0,0)
ship.direction = "stop"
ship.turtlesize(3)
turtle.hideturtle()
def onmove(self, fun, add=None):
if fun is None:
self.cv.unbind('<Motion>')
else:
def eventfun(event):
fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
self.cv.bind('<Motion>', eventfun, add)
def goto_handler(x, y):
onmove(turtle.Screen(), None)
ship.setheading(ship.towards(x, y)) #this is where you use the x,y cordinates and I have seat them to got to x,y and set heading
ship.goto(x,y)
onmove(turtle.Screen(), goto_handler)
onmove(screen, goto_handler)
If you only setheading and speed it just turns that way and does not move. If you try this code it works -- it is just that I use ship.goto(x, y) which makes it go to (x, y). But when you change your mouse when it is moving, it first goes to (x, y) then to your new mouse position. I pretty much just want it to just follow the mouse but I can not do that.
I believe the code below gives you the motion you desire. It only uses onmove() to stash the target's position and uses an ontimer() to aim and move the turtle. It also stops when the target has been enveloped:
from turtle import Screen, Turtle, Vec2D
def onmove(self, fun, add=None):
if fun is None:
self.cv.unbind('<Motion>')
else:
def eventfun(event):
fun(Vec2D(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale))
self.cv.bind('<Motion>', eventfun, add)
def goto_handler(position):
global target
onmove(screen, None)
target = position
onmove(screen, goto_handler)
def move():
if ship.distance(target) > 5:
ship.setheading(ship.towards(target))
ship.forward(5)
screen.ontimer(move, 50)
screen = Screen()
screen.title("Test")
screen.setup(width=600, height=600)
ship = Turtle("triangle")
ship.turtlesize(3)
ship.speed('fast')
ship.penup()
target = (0, 0)
onmove(screen, goto_handler)
move()
screen.mainloop()
In my code I've written a definition to change the background of the turtle after an action is completed. The definition to me looks ok and I don't see any issues with it but when the action is completed and the background image is suppose to change, the turtle window becomes unresponsive. Here is the definition that I'm talking about:
if default.distance(pickaxe) < 10:
screen.clearscreen()
wn.bgpic('TrumpTowersInside.gif')
And if the rest of the code is needed for whatever reason, here is the rest of the code for my turtle-based game:
from turtle import Screen, Turtle
def get_mouse_click_coor(x, y):
print(x, y)
def drag(x, y):
default.ondrag(None) # disable handler inside handler
default.goto(x, y)
if default.distance(scar) < 40:
default.shape('defaultscar.gif')
scar.hideturtle()
mini.hideturtle()
pickaxe.showturtle()
if default.distance(mini) < 40:
banshee.goto(-200,200)
banshee.showturtle()
banshee.speed(0)
for x in range(200):
banshee.forward(1)
banshee.right(90)
banshee.forward(1)
banshee.left(90)
banshee.shape('banshee.gif')
banshee.left(90)
banshee.forward(50)
scar.hideturtle()
mini.hideturtle()
banshee.shape('bansheescar.gif')
default.shape('defaultdead.gif')
if default.distance(pickaxe) < 10:
screen.clearscreen()
wn.bgpic('TrumpTowersInside.gif')
default.ondrag(drag)
wn = Screen()
wn.setup(500, 500)
wn.bgpic('TrumpTowers.gif')
wn.register_shape('default.gif')
wn.register_shape('scar.gif')
wn.register_shape('defaultscar.gif')
wn.register_shape('mini.gif')
wn.register_shape('defaultgliding.gif')
wn.register_shape('banshee.gif')
wn.register_shape('bansheescar.gif')
wn.register_shape('defaultdead.gif')
wn.register_shape('pickaxe.gif')
scar = Turtle('scar.gif', visible=False)
scar.speed(-1)
scar.color('pink')
scar.penup()
scar.left(90)
scar.forward(50)
scar.showturtle()
mini = Turtle('mini.gif', visible=False)
mini.speed(-1)
mini.color('pink')
mini.penup()
mini.forward(60)
mini.showturtle()
default = Turtle('default.gif', visible=False)
default.shapesize(2)
default.speed(1)
default.penup()
default.left(90)
default.backward(50)
default.showturtle()
default.ondrag(drag)
banshee = Turtle('defaultgliding.gif', visible=False)
banshee.shapesize(2)
banshee.speed(1)
banshee.penup()
# banshee.showturtle()
pickaxe = Turtle('pickaxe.gif', visible=False)
pickaxe.pu()
pickaxe.forward(10)
pickaxe.left(90)
pickaxe.forward(50)
wn.mainloop()
The documentation for clear() is clear on this:
Reset TurtleScreen to its initial state: white background,
no backgroundimage, no eventbindings and tracing on.
All of your event bindings (i.e. ondrag()) are undone by the clear() so you have redo them.
UPDATE
The screen's clear() method (aka clearscreen() fuction) is more severe than the documentation might lead one to believe. It seems to destroy all user created turtles and resets the default turtle to its initial state.
The screen's .reset() method isn't much better -- you get to keep your turtles but they lose all the attributes you set.
As an alternative to clearing or resetting the screen, I suggest you ask the turtles to clear() to clean up any drawing if the pen was down and then move them to new locations or home().
from turtle import Screen, Turtle
def drag(x, y):
default.ondrag(None) # disable handler inside handler
default.goto(x, y)
if default.distance(pickaxe) < 10:
wn.bgpic('TrumpTowersInside.gif')
pickaxe.hideturtle() # should move it elsewhere
default.ondrag(drag)
wn = Screen()
wn.setup(500, 500)
wn.bgpic('TrumpTowers.gif')
default = Turtle('turtle', visible=False)
default.color('red')
default.shapesize(2)
default.penup()
default.left(90)
default.backward(50)
default.showturtle()
default.ondrag(drag)
pickaxe = Turtle('turtle', visible=False)
pickaxe.color('green')
pickaxe.penup()
pickaxe.forward(10)
pickaxe.left(90)
pickaxe.forward(50)
pickaxe.showturtle()
wn.mainloop()
I have been typically setting the world coordinates to the bottom left hand corner when using turtle graphics.
import turtle
t=turtle.Pen()
turtle.setup(500,500)
turtle.setworldcoordinates(0, 0,500, 500)
The challenges is that when I insert a background image
turtle.bgpic("cat.gif")
It is also being moved the original origin (0,0) which is now in the bottom left hand corner of the screen. I need to move the center of the image to the center of my window. Is there to do this?
If you're willing to poke about a bit "beneath the shell", you can manipulate this at the tkinter level:
from turtle import Turtle, Screen
screen = Screen()
screen.setup(500, 500)
screen.setworldcoordinates(0, 0, 500, 500)
screen.bgpic("cat.gif")
canvas = screen.getcanvas()
canvas.itemconfig(screen._bgpic, anchor="sw") # pylint: disable=W0212
turtle = Turtle()
turtle.dot(100) # draw a large dot at (0, 0)
screen.mainloop()