Tkinter Rectangle connected to Keypress - python

The following program draws a rectangle in the center of the canvas. The rectangle is supposed to get wider when the right arrow key is pressed, and narrower when the left arrow key is pressed.
Here's the code:
from tkinter import *
root = Tk()
canvas = Canvas(root, width=400, height=300, bg="#000000")
canvas.pack()
x1 = 150
y1 = 100
x2 = 250
y2 = 200
class ResizeRect:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.rect = canvas.create_rectangle(0,0,1,1)
def draw(self):
canvas.delete(self.rect)
self.rect = canvas.create_rectangle(x1, y1, x2, y2,
outline="#00B000", width=2)
def narrower(self):
self.x1 = self.x1 + 5
self.x2 = self.x2 - 5
def wider(self):
self.x1 = self.x1 - 5
self.x2 = self.x2 + 5
r = ResizeRect(150, 100, 250, 200)
r.draw()
def left(event):
r.narrower()
r.draw()
def right(event):
r.wider()
r.draw()
canvas.bind_all('<KeyPress-Left>', left)
canvas.bind_all('<KeyPress-Right>', right)
My teacher told me that I need to add the 'self' keyword to the parameters in the draw function but I don't know what he means. (I can't ask him more because he's in a bad mood right now.). Any help is much appreciated.

Related

How should I fix code where the rectangle I created with tkinter doesn't move with the arrow keys?

I am trying to create a program that draws a rectangle in the center of the canvas. The rectangle is supposed to get wider when the right arrow key is pressed, and narrower when the left arrow key is pressed. I seem to have the rectangle, but there is no movement.
So far, the code I have is:
from tkinter import *
root = Tk()
canvas = Canvas(root, width=400, height=300, bg="#000000")
canvas.pack()
x1 = 150
y1 = 100
x2 = 250
y2 = 200
class ResizeRect:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
self.rect = canvas.create_rectangle(0,0,1,1)
def draw(self):
canvas.delete(self.rect)
self.rect = canvas.create_rectangle(x1, y1, x2, y2,
outline="#00B000", width=2)
def narrower(self):
self.x1 = self.x1 + 5
self.x2 = self.x2 - 5
def wider(self):
self.x1 = self.x1 - 5
self.x2 = self.x2 + 5
r = ResizeRect(150, 100, 250, 200)
r.draw()
def left():
r.narrower()
def right():
r.wider()
canvas.bind_all('<KeyPress-Left>', left)
canvas.bind_all('<KeyPress-Right>', right)
WHen I run this code, a rectangle appears but does not move when I press the arrow keys. How can I fix this without altering my original code too much?
You have two problems. First, left and right will automatically be passed an object representing the event. You need to make sure that these functions accept this parameter even if you don't use it.
def left(event):
r.narrower()
def right(event):
r.wider()
Second, simply setting the coordinate won't cause the rectangle to move. You must configure the rectangle with new coordinates using the coords method if you want the coordinates of the actual object to change.
def narrower(self):
self.x1 = self.x1 + 5
self.x2 = self.x2 - 5
canvas.coords(self.rect, self.x1, self.y1, self.x2, self.y2)
def wider(self):
self.x1 = self.x1 - 5
self.x2 = self.x2 + 5
canvas.coords(self.rect, self.x1, self.y1, self.x2, self.y2)

How to resize a tkinter overrideredirect window with a shape in it?

I have made a tkinter window which is round in shape.
I am trying to resize the window.
Everything works fine, but when I try to move it, it becomes square again.
I have added the code to draw the shape again, but still it becomes squared.
Here's the code:
from tkinter import Label, Tk, Canvas, BOTH, PhotoImage, Toplevel
from tkinter.constants import BOTTOM, E, NW, RAISED
import pyautogui as pg
root = Tk()
root.overrideredirect(1)
root.attributes("-transparentcolor", 'white')
root.attributes("-topmost", 1)
root.geometry("500x500")
# Creating a canvas for placing the squircle shape.
canvas = Canvas(root, height=500, width=500, highlightthickness=0, bg='white')
canvas.pack(fill=BOTH, expand=1)
def place_center(): # Placing the window in the center of the screen
global x, y
reso = pg.size()
rx = reso[0]
ry = reso[1]
x = int((rx/2) - (500/2))
y = int((ry/2) - (500/2))
root.geometry(f"500x500+{x}+{y}")
def move(event):
global rect
fx = root.winfo_pointerx() - 250
fy = root.winfo_pointery() - 10
root.geometry(f"500x500+{fx}+{fy}")
# if fx > 1 and fy > 1:
# canvas.delete(rect)
# rect = round_rectangle(0, 0, fx, fy, radius=50, fill="#1fa5fe")
def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs): # Creating a rounded rectangle
points = [x1+radius, y1,
x1+radius, y1,
x2-radius, y1,
x2-radius, y1,
x2, y1,
x2, y1+radius,
x2, y1+radius,
x2, y2-radius,
x2, y2-radius,
x2, y2,
x2-radius, y2,
x2-radius, y2,
x1+radius, y2,
x1+radius, y2,
x1, y2,
x1, y2-radius,
x1, y2-radius,
x1, y1+radius,
x1, y1+radius,
x1, y1]
return canvas.create_polygon(points, **kwargs, smooth=True)
def cl(event):
root.quit()
def resize(event):
def end(event):
global rect
root.bind("<B1-Motion>", move)
global rect
global x, y
root.unbind("<B1-Motion>")
x = root.winfo_pointerx() - root.winfo_rootx()
y = root.winfo_pointery() - root.winfo_rooty()
if x > 0:
fx = root.winfo_rootx()
fy = root.winfo_rooty() + y
ht = root.winfo_height() - y
if ht > 0:
root.geometry(f"{x}x{ht}+{fx}+{fy}")
canvas.delete(rect)
rect = round_rectangle(0, 0, x, ht, radius=50, fill="#1fa5fe")
root.bind("<ButtonRelease-1>", end)
place_center()
# Creating the squircle
rect = round_rectangle(0, 0, 500, 500, radius=50, fill="#1fa5fe")
root.bind("<B1-Motion>", move)
root.bind("<Button-3>", cl)
rx = root.winfo_rootx()
ry = root.winfo_rooty()
side = Label(canvas, text=' \n', background="blue")
side.place(x=500-10, y=500-10)
side.bind("<B1-Motion>", resize)
root.unbind("<B1-Motion>")
root.mainloop()
Here're some images.
Before resizing:
After resizing and moving:
If you need, I am using Windows 10.
PS: Sorry if the code isn't written in good manner! I am creating this as a sample, which I will apply in my other apps when done.
Thank you.
NVM, I solved the problem.
When I was moving the window, it was set to default geometry, which made it look round.
I have changed it and here's the updated code for the move() function:
def move(event):
global rect
fx = root.winfo_pointerx() - 250
fy = root.winfo_pointery() - 10
try:
root.geometry(f"{x}x{ht}+{fx}+{fy}")
except Exception:
root.geometry(f"500x500+{fx}+{fy}")
The final code (with a few changes):
from tkinter import Label, Tk, Canvas, BOTH, PhotoImage, Toplevel
from tkinter.constants import BOTTOM, E, NW, RAISED, TOP
root = Tk()
root.overrideredirect(1)
root.attributes("-transparentcolor", 'white')
root.attributes("-topmost", 1)
root.geometry("500x500")
# Creating a canvas for placing the squircle shape.
canvas = Canvas(root, height=500, width=500, highlightthickness=0, bg='white')
canvas.pack(fill=BOTH, expand=1)
def place_center(): # Placing the window in the center of the screen
global x, y
rx = root.winfo_screenwidth()
ry = root.winfo_screenheight()
x = int((rx/2) - (500/2))
y = int((ry/2) - (500/2))
root.geometry(f"500x500+{x}+{y}")
def move(event):
global rect
fx = root.winfo_pointerx() - 250
fy = root.winfo_pointery() - 10
try:
root.geometry(f"{x}x{ht}+{fx}+{fy}")
except Exception:
root.geometry(f"500x500+{fx}+{fy}")
# if fx > 1 and fy > 1:
# canvas.delete(rect)
# rect = round_rectangle(0, 0, fx, fy, radius=50, fill="#1fa5fe")
def round_rectangle(x1, y1, x2, y2, radius=25, **kwargs): # Creating a rounded rectangle
points = [x1+radius, y1,
x1+radius, y1,
x2-radius, y1,
x2-radius, y1,
x2, y1,
x2, y1+radius,
x2, y1+radius,
x2, y2-radius,
x2, y2-radius,
x2, y2,
x2-radius, y2,
x2-radius, y2,
x1+radius, y2,
x1+radius, y2,
x1, y2,
x1, y2-radius,
x1, y2-radius,
x1, y1+radius,
x1, y1+radius,
x1, y1]
return canvas.create_polygon(points, **kwargs, smooth=True)
def cl(event):
root.quit()
def resize(event):
global rect
global x, y, ht
x = root.winfo_pointerx() - root.winfo_rootx()
y = root.winfo_pointery() - root.winfo_rooty()
if x > 0:
fx = root.winfo_rootx()
fy = root.winfo_rooty() + y
ht = root.winfo_height() - y
if ht > 0:
root.geometry(f"{x}x{ht}+{fx}+{fy}")
canvas.delete(rect)
rect = round_rectangle(0, 0, x, ht, radius=50, fill="#1fa5fe")
place_center()
top = Canvas(canvas, height=50, bg="#1fa5fe", highlightthickness=0)
top.pack(side=TOP, pady=2)
# Creating the squircle
rect = round_rectangle(0, 0, 500, 500, radius=50, fill="#1fa5fe")
top.bind("<B1-Motion>", move)
root.bind("<Button-3>", cl)
rx = root.winfo_rootx()
ry = root.winfo_rooty()
side = Label(canvas, text=' \n', background="blue")
side.place(x=500-10, y=500-10)
side.bind("<B1-Motion>", resize)
root.unbind("<B1-Motion>")
root.mainloop()

Drawing lines between two object in tkinter UI

Thanks to lots of help in Drag and drop the object in Tkinter UI, I could manage to draw three square that are draggable.
Now I am trying to draw 3 lines between each squares and I cannot find way to enable it. What I tried is following :
from tkinter import *
window = Tk()
window.state('zoomed')
window.configure(bg = 'white')
def drag(event):
new_x = event.x_root - window.winfo_rootx()
new_y = event.y_root - window.winfo_rooty()
event.widget.place(x=new_x, y=new_y,anchor=CENTER)
card = Canvas(window, width=10, height=10, bg='red1')
card.place(x=300, y=600,anchor=CENTER)
card.bind("<B1-Motion>", drag)
another_card = Canvas(window, width=10, height=10, bg='red2')
another_card.place(x=600, y=600,anchor=CENTER)
another_card.bind("<B1-Motion>", drag)
third_card = Canvas(window, width=10, height=10, bg='red3')
third_card.place(x=600, y=600,anchor=CENTER)
third_card.bind("<B1-Motion>", drag)
def line(x1, y1, x2, y2):
print(x1, y1, x2, y2)
Canvas.create_line(x1, y1, x2, y2, fill="green")
coor_1 = canvas.coords(card)
coor_2 = canvas.coords(another_card)
line(coor_1[0],coor_1[1],coor_1[0],coor_2[1])
window.mainloop()
It didn't work and I don't think it will work since this code does not catch the change occurred by dragging object, But I cannot guess how to code since I do not understand how the event function works completely. How should I make a code for it ?
for the purpose of self-study:
import tkinter as tk
window = tk.Tk()
window.state('zoomed')
class DragAndDropArea(tk.Canvas):
def __init__(self,master, **kwargs):
tk.Canvas.__init__(self,master, **kwargs)
self.active = None
card_I = self.draw_card(300,600, 100,100, 'red1')
card_II = self.draw_card(600,600, 100,100, 'red2')
card_III = self.draw_card(400,400, 100,100, 'red3')
self.bind_tention(card_I,card_III)
self.bind_tention(card_I,card_II)
self.bind_tention(card_III,card_II)
self.bind('<ButtonPress-1>', self.get_item)
self.bind('<B1-Motion>',self.move_active)
self.bind('<ButtonRelease-1>', self.set_none)
def set_none(self,event):
self.active = None
def get_item(self,event):
try:
item = self.find_withtag('current')
self.active = item[0]
except IndexError:
print('no item was clicked')
def move_active(self,event):
if self.active != None:
coords = self.coords(self.active)
width = coords[2] - coords[0] #x2-x1
height= coords[1] - coords[3] #y1-y2
position = coords[0],coords[1]#x1,y1
x1 = event.x - width/2
y1 = event.y - height/2
x2 = event.x + width/2
y2 = event.y + height/2
self.coords(self.active, x1,y1, x2,y2)
try:
self.update_tention(self.active)
except IndexError:
print('no tentions found')
def update_tention(self, tag):
tentions = self.find_withtag(f'card {tag}')
for tention in tentions:
bounded_cards = self.gettags(tention)
card = bounded_cards[0].split()[-1]
card2= bounded_cards[1].split()[-1]
x1,y1 = self.get_mid_point(card)
x2,y2 = self.get_mid_point(card2)
self.coords(tention, x1,y1, x2,y2)
self.lower(tention)
def draw_card(self, x,y, width,height, color):
x1,y1 = x,y
x2,y2 = x+width,y+height
reference = self.create_rectangle(x1,y1,x2,y2,
fill = color)
return reference
def bind_tention(self, card, another_card):
x1,y1 = self.get_mid_point(card)
x2,y2 = self.get_mid_point(another_card)
tag_I = f'card {card}'
tag_II= f'card {another_card}'
reference = self.create_line(x1,y1,x2,y2, fill='green',
tags=(tag_I,tag_II))
self.lower(reference)
def get_mid_point(self, card):
coords = self.coords(card)
width = coords[2] - coords[0] #x2-x1
height= coords[1] - coords[3] #y1-y2
position = coords[0],coords[1]#x1,y1
mid_x = position[0] + width/2
mid_y = position[1] - height/2
return mid_x,mid_y
area = DragAndDropArea(window, bg='white')
area.pack(fill='both',expand=1)
window.mainloop()

python returns function name not value

so... i have been stumped on this problem for some time. After researching online i have not found an answer. when i call the function: spawnLoc.getX1 from player draw line, i print it to see what it is and it prints this: and i was expecting it to print the number 30. can anyone lend a hand
import simplegui
#charictor position decliration
x1 = 30
x2 = 30
y1 = 35
y2 = 35
class Room:
def __init__(self, roomName, sizeX=0, sizeY=0):
self.roomName = roomName
self.sizeX = sizeX
self.sizeY = sizeY
class player:
def __init__(self, name, spawnLoc):
self.name = name
self.spawnLoc = spawnLoc
def draw(canvas, spawnLoc):
print spawnLoc.getX1
print spawnLoc.getX2
print spawnLoc.getY1
print spawnLoc.getY2
canvas.draw_line((spawnLoc.getX1, spawnLoc.getX2), (spawnLoc.getY1, spawnLoc.getY2), 6, 'Red')
class spawn:
def __init__(self, spawnName, spawmX1,spawmX2, spawmY1, spawmY2):
self.name = spawnName
self.x1 = spawmX1
self.x2 = spawmX2
self.y1 = spawmY1
self.y2 = spawmY2
def getX1(self):
return x1
def getX2(self):
return x2
def getY1(self):
return self.y1
def getY2(self):
return self.y2
#def Collisions(rooms, selectedPlayer):
#for Room in rooms
#if room.x == player.x
rooms = [
Room('Ticket Hall', 300, 500)
]
#spawns = [spawn('first', x1, x2, y1, y2)]
spawn('first', x1, x2, y1, y2)
player1 = [ player('one', spawn)]
#player('one', spawns)
# Handler to draw on canvas
def draw(canvas):
#room draw
canvas.draw_line((0, 0), (0, 500), 6, 'Red')
canvas.draw_line((0, 0), (300, 0), 6, 'Red')
canvas.draw_line((300, 0), (300, 500), 6, 'Red')
canvas.draw_line((0, 500), (300, 500), 6, 'Red')
#player draw
#canvas.draw_line((x1, x2), (y1, y2), 5, 'Red')
player.draw(canvas, spawn)
# Create a frame and assign callbacks to event handlers
frame = simplegui.create_frame("Home", 600, 600)
#frame.add_button("Click me", click)
frame.set_draw_handler(draw)
# Start the frame animation
frame.start()
You are forgetting the parentheses.
spawnLoc.getX1
Is a function handle.
spawnLoc.getX1()
Calls the function.

Script not working (Tkinter and Functions)

So I'm creating a script to test tkinter, which is supposed to generate random rectangles on a canvas. Here is my script:
from Tkinter import *
import random
tk = Tk()
canvas = Canvas(tk, width=400, height=400)
canvas.pack()
Option = StringVar()
Option.set("None")
menu = OptionMenu(tk, Option,"None", "Colored Outlines", "Colored Fills")
menu.pack()
option = Option.get()
button = button = Button(tk, text="Generate", command="Generate")
button.pack()
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
def random_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
canvas.create_rectangle(x1, y1, x2, y2)
def random_outline_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
color = random.choice(colors)
canvas.create_rectangle(x1, y1, x2, y2, outline = color)
def random_color_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
color = random.choice(colors)
canvas.create_rectangle(x1, y1, x2, y2, fill = color)
def Generate():
global option
if option == "None":
for x in range(0,100):
random_rectangle(400, 400)
elif option == "Colored Outlines":
for x in range(0,100):
random_outline_rectangle(400,400)
elif option == "Colored Fills":
for x in range(0,1000):
random_color_rectangle(400,400)
tk.mainloop()
So my code works perfecty without the generate button (If I remove def Generate:()) but when I run it with that, and press the button, it does nothing. Without, you must set the option by changing the code at Option.set(). I do not understand why pressing the button does nothing, however, with the original code. Any help? And how can I fix this?
Ok, I found my solution. Rawing was right, but I also needed to move the button creation to after I defined all my functions. The updated code is as follows:
from Tkinter import *
import random
tk = Tk()
canvas = Canvas(tk, width=400, height=400)
canvas.pack()
Option = StringVar()
Option.set("None")
menu = OptionMenu(tk, Option,"None", "Colored Outlines", "Colored Fills")
menu.pack()
colors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']
def random_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
canvas.create_rectangle(x1, y1, x2, y2)
def random_outline_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
color = random.choice(colors)
canvas.create_rectangle(x1, y1, x2, y2, outline=color)
def random_color_rectangle(width, height):
x1 = random.randrange(width)
y1 = random.randrange(height)
x2 = x1 + random.randrange(width)
y2 = y1 + random.randrange(height)
color = random.choice(colors)
canvas.create_rectangle(x1, y1, x2, y2, fill=color, outline=color)
def Generate():
global option
canvas.delete("all")
if Option.get() == "None":
for x in range(0,100):
random_rectangle(400, 400)
elif Option.get() == "Colored Outlines":
for x in range(0,100):
random_outline_rectangle(400,400)
elif Option.get() == "Colored Fills":
for x in range(0,1000):
random_color_rectangle(400,400)
button = button = Button(tk, text="Generate", command=Generate)
button.pack()
tk.mainloop()

Categories