How to create a simple button in turtle, python, where if you click it, you can define it to print messages, or do other, more complex things.
You can embed turtle in tkinter, as #JoshuaNixon suggests in his comment, using tkinter buttons to control your turtle canvas. If you want to work within standalone turtle, I recommend using a turtle as a button as they can be coerced into any shape and/or color and have individual onclick event handlers so you don't have to figure out where the user clicked on the screen:
from turtle import Screen, Turtle
CURSOR_SIZE = 20
FONT_SIZE = 12
FONT = ('Arial', FONT_SIZE, 'bold')
def draw_onclick(x, y):
turtle.dot(100, 'cyan')
button = Turtle()
button.hideturtle()
button.shape('circle')
button.fillcolor('red')
button.penup()
button.goto(150, 150)
button.write("Click me!", align='center', font=FONT)
button.sety(150 + CURSOR_SIZE + FONT_SIZE)
button.onclick(draw_onclick)
button.showturtle()
turtle = Turtle()
turtle.hideturtle()
screen = Screen()
screen.mainloop()
Note that Turtle.onclick() is different than Screen().onclick -- one only happens when clicking on a specific turtle instance whereas the other happens when clicking anywhere on the screen.
Since the Python Turtle Graphics Module is built on top of Tkinter, a Tkinter button should work on a turtle screen
from turtle import Screen
from tkinter import *
screen = Screen()
screen.setup(width=600, height=400)
def do_something():
print("Good bye")
canvas = screen.getcanvas()
button = Button(canvas.master, text="Exit", command=do_something)
button.pack()
button.place(x=300, y=100) # place the button anywhere on the screen
screen.exitonclick()
I haven't tried this out but this might work:
root = turtle.Screen()._root
btn = Button(root, text="This button exists in turtle")
btn.pack()
That should be it!
Note: Since turtle is based on tkinter the turtle.Screen() contains the tk() root
We are able to access that root and create a tkinter button and add it to it.
Edit: If you add a command parameter in pack you can make the button execute a function
To create a simple button, there might be other ways, but this is how I do it.
import turtle
def button(x,y):
if x < 50 and x > -50 and y < 50 and y > -50:
print(f"Your coordinates are: ({x}, {y}).")
turtle.onscreenclick(button, 1, add=False)
turtle.done()
To explain this, button is just a function, it has nothing to do with an actual button yet. The if statement in there basically takes the x,y variables that are its parameters, and checks whether they are between two numbers, in this case, coordinates.
The onscreenclick function takes three parameters. The first is a function with two parameters. Wherever you click on the turtle pop-up, it will take the x,y coordinates of where you clicked and inserts it into the function. The second is a number. This number refers to how you are going to click it (Ex. right-click, left-click, etc.) In most cases, it is 1 since 1 is left-click. Finally, the third parameter is necessary when you have multiple buttons. If you are creating a second, third, etc. button, and you want to create the new button without overwriting the previous button, you write add=True. If you want to make it so all previous buttons are canceled, you write True. So, finally, the code above would print the coordinates of where you clicked if they were both between -50 and 50.
You can do a lot of useful things with this function. You can create it as a temporary button to help you while writing with turtle, where the "whole screen" is a large button where it prints the x,y coordinates of where you clicked. This can be useful in getting the approximate coordinates of where you want your turtle to go next.
Or you could use it your actual code, to get information from the user or as part of a game.
All in all, this is a simple way to create a button just using turtle and no other modules and has great flexibility.
If there are any other ways, using or not using turtle, complex or simple, please post it below as an answer.
NOTE: You wouldn't be able to "see" the button by default. If you wanted, though, you could make a turtle draw the outline of the button or something.
Related
I have a 2d array of tkinter buttons.
It looks like this:
I want to be able to click on a button, hold my mouse down, and every button that is hovered over while my mouse is pressed down changes colors. So far I have it to the point where if you hover over any square regardless if a mouse button is pressed it changes colors.
The code looks like this so far:
def draw(self, i, j):
button = self.buttons[i][j]
button.bind('<Enter>', lambda event: self.on_enter(event, button))
def on_enter(self, e, button):
button['background'] = 'green'
To be clear, I want to be able to change a button's color when left-click is held down and a button is hovered over at the same time.
Thanks for helping me.
EDIT: removed picture of code and provided something that can be copy and pasted.
2ND EDIT: the code is about 100 lines, but the gist is there's a 2d array of tkinter buttons, and the code I provided shows the 2 functions responsible for changing the color of the buttons. If more code is needed, I'll put it in.
You can bind <B1-Motion> on root window to a callback. Then inside the callback, use .winfo_pointerxy() to get the mouse position and .winfo_containing() to find which button is under the mouse pointer and change its background color:
Example:
def on_drag(event):
x, y = root.winfo_pointerxy()
btn = root.winfo_containing(x, y)
if btn:
btn.config(bg="green")
# root is the root window
root.bind('<B1-Motion>', on_drag)
How do I get Python Turtle to return the coordinates of a mouse click so that it can be used in a different function? I have this code:
import turtle as t
def main():
while True:
t.onscreenclick(t.goto)
x=t.xcor()
y=t.ycor()
print(str(x) + ', ' + str(y))
t.mainloop()
main()
I'm making a game where I want you to be able to click on the screen where you want to go, so for that, I was hoping to be able to use t.onscreenclick() to give me the coordinates of where the mouse was clicked, and be able to send those coordinates to another function, but this code only returns (0.0, 0.0) and no other coordinates no matter how many times I click.
Any help would be appreciated, this is my first attempted at making any sort of game using python, thanks.
You've completely misunderstood turtle event handling. I suggest you (re)read the documentation before you proceed. Although the introduction to turtle uses t.onscreenclick(t.goto) as an example, it's a very noisy handler so you're better off writing your own that disables the handler first, does the goto() and reenables the handler. Below is what I believe you're trying to implement:
from turtle import Screen, Turtle
def my_handler(x, y):
screen.onscreenclick(None) # disable handler inside hander
turtle.goto(x, y)
print(turtle.position())
screen.onscreenclick(my_handler)
turtle = Turtle()
screen = Screen()
screen.onscreenclick(my_handler)
screen.mainloop()
I am a newbie using python 3.2.3
When I ran the module in the python IDLE, the turtle drew a square without any update on the screen, so the window appeared blank, and after I input any key, the turtle and the square appeared due to the call of turtle.update().
However, when I double-clicked the .py file storing the below code in my document and executed it directly, the square always showed up before I input any key.
The interesting part is that the turtle was not shown but only the square was shown.
It seems that there was a turtle update only for the square after drawing even if I had already set turtle tracer to (0,0).
Is this considered a bug and how can I solve it? Thanks for the help.
import turtle
def drawSquare():
turtle.down();
turtle.begin_fill();
turtle.goto(10, 0);
turtle.goto(10, 10);
turtle.goto(0, 10);
turtle.goto(0, 0);
turtle.end_fill();
turtle.up();
def tUpdate():
turtle.update();
turtle.tracer(0,0);
drawSquare();
input("Not updated. Press any key.");
tUpdate();
print("Updated");
turtle.mainloop();
You've got a couple of things working against you: the poor documenation provided for tracer() and the fact that end_fill() and up() cause updates to occur. tracer() is not really meant to hide things from the user until you're ready to display them -- it's a speed optimization so that the user doesn't have to see every drawing step in a complicated image. You don't have full control over when updates will occur.
Here's a rework of your example that displays the behaviour you want at the cost of it no longer being a filled square. I've swapped your input() trigger for a mouse click on the window instead but your approach will work just as well here too:
from turtle import Turtle, Screen
def drawSquare(turtle):
turtle.goto(100, 0)
turtle.goto(100, 100)
turtle.goto(0, 100)
turtle.goto(0, 0)
screen = Screen()
screen.tracer(0, 0)
screen.onclick(lambda x, y: screen.update())
turtle = Turtle()
drawSquare(turtle)
screen.mainloop()
I also made which are turtle methods, and which are screen methods, more explicit. The beginning programmer friendly design of the turtle library tends to blur these in the interest of ease of use.
I can make a basic python application like so:
from tkinter import *
block = None
def moveUp(event):
field.move(block,0,-50)
root = Tk()
field = Canvas(root, width = 300, height = 300, bg = 'light blue')
field.pack()
block = field.create_rectangle(100,100,110,110)
field.bind('<Button-1>',moveUp)
mainloop()
and it will behave just like you would expect. It creates a square on a Canvas and moves that square up 50 pixels every time you click in the Canvas.
However, When I replace
field.bind('<Button-1>',moveUp)
to, for example,
field.bind('<Return>',moveUp)
the square does not move, no matter how many times I press the Enter key. This problem persists for any kind of keyboard input (e.g <space>, etc), but any input involving the mouse is fine.
Any input at all is appreciated. Thanks!
The field does not have focus, and therefore does not capture the keypress. One option is simply to make the binding more general:
field.bind('<Return>',moveUp)
to
root.bind('<Return>',moveUp)
Another option is to set the focus to the field:
field.bind('<Return>',moveUp)
field.focus_set()
Not entirely sure what's the reason, but it seems to work if you use bind_all instead of bind.
field.bind_all('<Return>',moveUp)
My guess is that using the keyboard, the canvas does not have focus and so does no register the event. Using bind_all, the event is registered when any widget of the application has focus.
See here for some information on levels of binding.
I am working on a project in python, and I made a method to draw a specific thing in tkinter. I want it so that whenever I press the spacebar, the image will redraw itself (run the method again because I coded the method so that it could redraw over itself). How exactly would I bind the spacebar to the method so that the program would run, draw, and re-draw if I pressed the spacebar?
for example, i want it so that whenever I press space, the program draws in a random location on the canvas:
from Tkinter import *
from random import *
root=Tk()
canvas=Canvas(root,width=400,height=300,bg='white')
def draw():
canvas.delete(ALL)# clear canvas first
canvas.create_oval(randint(0,399),randint(0,299),15,15,fill='red')
draw()
canvas.pack()
root.mainloop()
how would i bind the spacebar to the method?
from Tkinter import *
from random import *
root=Tk()
canvas=Canvas(root,width=400,height=300,bg='white')
def draw(event=None):
canvas.delete(ALL)# clear canvas first
canvas.create_oval(randint(0,399),randint(0,299),15,15,fill='red')
draw()
canvas.pack()
root.bind("<space>", draw)
root.mainloop()
You could do something like this:
from Tkinter import *
from random import *
root=Tk()
canvas=Canvas(root,width=400,height=300,bg='white')
def draw(event):
if event.char == ' ':
canvas.delete(ALL)# clear canvas first
canvas.create_oval(randint(0,399),randint(0,299),15,15,fill='red')
root.bind('<Key>', draw)
canvas.pack()
root.mainloop()
Basically, you bind your drawing function to some top-level element to the <Key> binding which is triggered whenever a key on the keyboard is pressed. Then, the event object that's passed in has a char member which contains a string representing the key that was pressed on the keyboard.
The event will only be triggered when the object it's bound to has focus, which is why I'm binding the draw method to the root object, since that'll always be in focus.
You can aslo use canvas.bind_all("<space>", yourFunction)
That will listen for events in the whole application and not only on widget.