How to delete a polygon in tkinter? - python

I'm trying to make a basic game in Tkinter that involves pressing a start button and making a shape appear which is working, then when you click on the shape it gets deleted and moved to a different random location.
I am getting NameError: name 'square' is not defined when I try to run it.
root=Tk()
frame=Frame(root)
can = Canvas(root, width=400, height=400)
can.pack(side=TOP)
def makeShape():
xpos = random.randint(1, 400)
ypos = random.randint(1, 400)
square=can.create_polygon(xpos, ypos, xpos + 40, ypos, xpos + 40, ypos + 40,
xpos, ypos + 40, fill="blue")
can.tag_bind(square,"<Button-1>",deleteShape)
def deleteShape(event):
can.delete(square)
but1 = Button(frame, text="Start", command=makeShape)
but1.grid(row=1, column=2)
frame.pack(side=BOTTOM)
root.mainloop()

It is because square is a local variable inside makeShape(), so it cannot be accessed outside the function.
You can use tags option in create_polygon() instead. If you want to move the square when it is clicked, deleteShape() is not necessary at all. Just using makeShape() is enough:
from tkinter import *
import random
root=Tk()
frame=Frame(root)
can = Canvas(root, width=400, height=400)
can.pack(side=TOP)
def makeShape():
# delete existing square
can.delete("square")
# create square at random position
xpos = random.randint(1, 360)
ypos = random.randint(1, 360)
can.create_polygon(xpos, ypos, xpos+40, ypos, xpos+40, ypos+40, xpos, ypos+40,
fill="blue", tags="square")
# call makeShape() when the square is clicked
can.tag_bind("square", "<Button-1>", lambda e: makeShape())
but1 = Button(frame, text="Start", command=makeShape)
but1.grid(row=1, column=2)
frame.pack(side=BOTTOM)
root.mainloop()

While it's not good practice, if you add line global square to makeShape() it will run as expected.
That's because if the name is assigned first time inside a block, it won't be visible to parent or sibling blocks.
There are alternatives, considered better for readability and more practical, but my suggestion is the quickest fix to your problem.

Related

I want my object to continue moving but it won't work

So I want it to keep moving with the assigned speed, but it just moves once and it stops.
I thought the after function would help but it doesnt..
Also I want to know what the number does inside the after brackets
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=1000, height=1000, background='black')
canvas.pack()
circle = canvas.create_oval(0, 0, 10, 10, fill='white')
def move():
canvas.move(circle, 100, 100)
move()
canvas.after(40, move())
root.mainloop()
You have to pass only function name in after() as below...
canvas.after(40, move)
Instead of...
canvas.after(40, move())
Mean after 40 milliseconds after() function will be call and then your object will be move.
You need to call after(...) inside move():
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=1000, height=1000, background='black')
canvas.pack()
circle = canvas.create_oval(0, 0, 10, 10, fill='white')
def move():
canvas.move(circle, 10, 10)
canvas.after(50, move)
move()
root.mainloop()
Refer here on how to use after() function.

Canvas.Tag_bind not working with OOP | Python 3 [duplicate]

In my simple code, a red ball is falling down in a straight line (that's working). When I push the right arrow key, I want the ball to also move in right direction. This is not working, however. What am I doing wrong?
from tkinter import *
root = Tk()
canvas = Canvas(root, height=400, width=500, background='black')
canvas.pack()
class Bird:
def __init__(self, canvas, coords):
self.canvas = canvas
self.coords = coords
self.bird = canvas.create_rectangle(coords, fill='red')
def gravity(self):
self.canvas.move(self.bird, 0, 10)
self.canvas.after(200, self.gravity)
def moveRight(self, event):
self.canvas.move(self.bird, 10, 0)
self.canvas.after(200, self.moveRight)
bird = Bird(canvas, (100, 100, 110, 110))
bird.gravity()
canvas.bind('<Right>', bird.moveRight)
root.mainloop()
I have another additional question:
Is it possible to call this "after"-function or a similar function for the whole canvas instead of the two methods separately?
If you see any other flaws with my code plz let me know!
Thanks!
You must bind the right key to the canvas inside the class, and set the focus on the canvas:
from tkinter import *
root = Tk()
canvas = Canvas(root, height=400, width=500, background='black')
canvas.pack()
class Bird:
def __init__(self, canvas, coords):
self.canvas = canvas
self.coords = coords
self.bird = canvas.create_rectangle(coords, fill='red')
self.canvas.bind('<Right>', self.moveRight)
self.canvas.focus_set()
def gravity(self):
self.canvas.move(self.bird, 0, 10)
self.canvas.after(200, self.gravity)
def moveRight(self, event=None):
self.canvas.move(self.bird, 10, 0)
self.canvas.after(200, self.moveRight)
bird = Bird(canvas, (100, 100, 110, 110))
bird.gravity()
root.mainloop()
The problem you are facing is that you are binding keyboard events, but the events can only work if the widget with the bindings has the keyboard focus. You can give the canvas the keyboard focus with focus_set():
canvas = Canvas(root, height=400, width=500, background='black')
canvas.focus_set()
Is it possible to call this "after"-function or a similar function for the whole canvas instead of the two methods separately?
Yes. Your binding can call any function you want. If you expect to have more than one object and you want them all to move at the same time, you can move them all from a function.
First, remove the call to after from moveRight. Next, define a global function that calls moveRight for every object. For example:
def move_them_all():
bird1.moveRight()
bird2.moveRight()
something_else.moveRight()
self.canvas.after(1000, move_them_all)
...
canvas = Canvas(root, height=400, width=500, background='black')
...
canvas.bind('<right>', move_them_all)

How can I set the size of a button in pixels - python [duplicate]

This question already has answers here:
How do I resize buttons in pixels? (Tkinter)
(5 answers)
Closed 4 years ago.
I'm using Python 3, and I want to set the size of a button in pixels.
I wanted to make it width = 100 pixels, height = 30 pixels, but it didn't work.
It was much bigger than I expected.
Here's My code:
from tkinter import *
def background():
root = Tk()
root.geometry('1160x640')
btn_easy = Button(root, text = 'Easy', width = 100, height = 50)
btn_easy.place(x = 100, y = 450)
root.mainloop()
background()
How can I make it?
http://effbot.org/tkinterbook/button.htm
You can also use the height and width options to explicitly set the
size. If you display text in the button, these options define the size
of the button in text units. If you display bitmaps or images instead,
they define the size in pixels (or other screen units). You can
specify the size in pixels even for text buttons, but that requires
some magic. Here’s one way to do it (there are others):
f = Frame(master, height=32, width=32)
f.pack_propagate(0) # don't shrink
f.pack()
b = Button(f, text="Sure!")
b.pack(fill=BOTH, expand=1)
from tkinter import *
def background():
root = Tk()
root.geometry('1160x640')
f = Frame(root, height=50, width=50)
f.pack_propagate(0) # don't shrink
f.place(x = 100, y = 450)
btn_easy = Button(f, text = 'Easy')
btn_easy.pack(fill=BOTH, expand=1)
root.mainloop()
background()
Bonus: many buttons (just to get the idea)
from tkinter import *
def sizedButton(root, x,y):
f = Frame(root, height=50, width=50)
f.pack_propagate(0) # don't shrink
f.place(x = x, y = y)
btn_easy = Button(f, text = 'Easy')
btn_easy.pack(fill=BOTH, expand=1)
def background():
root = Tk()
root.geometry('1160x640')
for x in range(50,350,100):
for y in range(50,350,100):
sizedButton(root, x,y)
root.mainloop()
background()

How to move a rectangle to the mouse position with Tkinter/Python?

I'm using Tkinter/Python's Canva class' coord() method to move a rectangle. What should I pass as paramters in order to make it work?
from tkinter import *
root = Tk()
def key(event):
print ("pressed", repr(event.char))
def callback(event):
position = (event.x,event.y)
event.widget.coords(item, position)
canvas= Canvas(root, width=100, height=100)
canvas.bind("<Key>", key)
canvas.bind("<Button-1>", callback)
item = canvas.create_rectangle(10,10,5,5)
canvas.pack()
move widget using mouse
from tkinter import *
import pyautogui
def on_move(event):
component=event.widget
locx, locy = component.winfo_x(), component.winfo_y()
w , h =master.winfo_width(),master.winfo_height()
mx ,my =component.winfo_width(),component.winfo_height()
xpos=(locx+event.x)-(15)
ypos=(locy+event.y)-int(my/2)
if xpos>=0 and ypos>=0 and w-abs(xpos)>=0 and h-abs(ypos)>=0 and xpos<=w-5 and ypos<=h-5:
component.place(x=xpos,y=ypos)
master = Tk()
master.geometry("%dx%d+0+0" % (500,500))
msg = Label(master, text = "Click & Move")
msg.config(bg='lightgreen', font=('times', 24, 'italic'))
msg.bind('<B1-Motion>',on_move)
msg.place(x=10,y=20)
mainloop()
This seems your first post. Welcome to SO :D
Updated answer: After some research and testing, it seems that you just need to pass the coordenates without the tuple. Storing x and y in a tuple is a problem, but also not providing the values for x2 and y2.
def callback(event):
event.widget.coords(item, event.x + 5, event.y + 5, event.x, event.y)
You can learn more here
Original wrong answer:
You can't move items on tk. Maybe try to clean the canvas and create the item at the new coordinates.
canvas.delete("all")
canvas.create_rectangle(event.x + 5,event.y + 5, position)

Tkinter GUI canvas

I am working on a Lego Mindstorms project where we create a GUI that can be used to control the robot. The thing I need to do is to create something that shows the robots position after every move. I am using a canvas where I draw a rectangle and then a dot that shows the current position of the robot. I have a whole bunch of code but I am just showing you a small piece of it relevant to my problem.
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="Move", command=lambda: do_move())
self.button.pack(side=TOP)
self.canvas = Canvas(master, width=300, height=450)
self.canvas.place(x=250, y=550)
self.canvas.create_rectangle(0, 0, 300, 450, fill="white")
self.canvas.create_oval(150, 300, 160, 310, fill="blue", tags="Position")
x, y = self.canvas.coords("Position")
x = int(x)
y = int(y)
x2 = self.canvas.canvasx(self.x)
y2 = self.canvas.canvasy(self.y)
x2 = int(x2)
y2 = int(y2)
def move_forward():
self.canvas.move(Position, x2, y2)
def move_backwards():
self.canvas.move(Position, , )
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
For the move function that I have a button for, I choose a value and that value will move the robot forward/backward. When the robot has moved, I want to also move my blue circle on my canvas. X and Y are the coordinates for the circles current position, and the rest about X2 and Y2 are taken from another site. I am not really sure why you have to write x=int(x) and I dont really understand the parts for X2 and Y2. Any explanations and suggestions about how I can write the rest of my code?
The first new function that I define at the end will be used with my move button so that I have two commands for the button. When I click the button, the Position-circle will also be moved to the new coordinates. I will also need to write somewhere that a specific value of the unit I use for my move function equals for example a move of 5 coordinates in my canvas. Any tips on how to do that?
I hope you understand the task and my formulations. Any help is appreciated!
You have a couple issues with your sample code, hopefully this minimal example will help you get on track:
from Tkinter import *
import random
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.move_button = Button(self, text="Move", command=self.do_move)
self.move_button.pack()
self.random_button = Button(
self, text="Random!",
command=self.random_move)
self.random_button.pack()
self.canvas = Canvas(self, width=300, height=450)
self.canvas.config(
highlightbackground="grey",
borderwidth=2,
relief="flat")
self.canvas.pack()
self.canvas.create_oval(
150, 300, 160, 310, fill="blue", tag="Oval")
self.pack()
def do_move(self):
self.canvas.move("Oval", 10, 10)
def random_move(self):
x = random.randint(1, 290)
y = random.randint(1, 440)
self.canvas.coords("Oval", x, y, x+10, y+10)
root = Tk()
root.title("Mindstorms GUI")
root.geometry("400x600")
app = App(root)
root.mainloop()
Notice that the Canvas move method takes an offset. Alternatively, you could use the coords method with coordinates as arguments if you know the new location of the oval, however note that the coordinates should be a list of coordinate pairs. I've added a random button to show how to use coords.
Sounds like a cool project, have fun!

Categories