Python canvas move items with mouse tkinter - python

I wrote a simple python code which creates canvas and items in canvas can be moved freely using mouse only. Also, previous shapes created in canvas can be moved. However, I have some problems here:
When moving items like 1 rect. 1 circle, somehow they overlap or one of them completely unseen in canvas.
My delete button works but I can't do anything after using delete button. System gives error. (I think I couldn't make object_id value 0 because after reset, I add rect. and object_id doesn't start from 0, and interestingly it has no coord?? )
When I try to drag shapes in canvas, mouse automatically picks top left corner of the shape, how can I change it? (Maybe moving shapes from the point that I try to click with mouse)
My code:
from tkinter import *
from tkinter import messagebox
def click(event):
my_label.config(text="Coordinates: x: "+ str(event.x) +" y: " +str(event.y))
if object_id is not None:
for i in range(object_id):
coord = my_canvas.coords(i+1) # +1 added because for loop starts from 0
print(coord)
print(object_id)
if ((event.x<=coord[2]+10) and (event.x>=coord[0]-10) and (event.y<=coord[3]+10) and
(event.y>=coord[1])-10):
print(coord)
width = coord[2] - coord[0]
height = coord[3] - coord[1]
my_canvas.coords(i+1, event.x, event.y, event.x+width, event.y+height)
else:
pass
else:
pass
def label_mod(event): #While not pressing button, show coords!
my_label.config(text="Coordinates: x: "+ str(event.x) +" y: " +str(event.y))
def delete():
global object_id #Delete butonunda sistem fail oluyor
msg = messagebox.askyesnocancel('Info', 'Delete my_canvas ?')
if msg == True:
my_canvas.delete(ALL)
print(object_id)
object_id=0
print(object_id)
def create_rectangle():
global object_id
object_id=my_canvas.create_rectangle(10, 10, 70, 70, fill='white', outline='blue', width=3)
def create_line():
global object_id
object_id=my_canvas.create_line(200, 200, 100, 100, fill='red', width=5)
def create_circle():
global object_id
object_id=my_canvas.create_oval(10, 10, 70, 70, fill='orange', outline='blue')
# Main Codes
object_id = 0
root = Tk()
root.title('Moving objects')
root.resizable(width=False, height=False)
root.geometry('1200x600+100+50')
root.configure(bg='light green')
my_label=Label(root, text="")
my_label.pack()
my_canvas = Canvas(root, bg='white', height=500, width=500)
my_canvas.pack(side=RIGHT)
my_canvas.bind("<B1-Motion>", click)
my_canvas.bind("<Motion>", label_mod)
btn_line = Button(root, text='Line', width=30, command=create_line)
btn_line.pack()
btn_rectangle = Button(root, text='Rectangle', width=30, command=create_rectangle)
btn_rectangle.pack()
btn_circle = Button(root, text='Circle', width=30, command=create_circle)
btn_circle.pack()
btn_delete = Button(root, text='Delete', width=30, command=delete)
btn_delete.pack()
root.mainloop()

You don't need object_id at all. You can use my_canvas.find_overlapping() to select the canvas item you want, and use my_canvas.move() to move the selected item:
def on_click(event):
selected = my_canvas.find_overlapping(event.x-10, event.y-10, event.x+10, event.y+10)
if selected:
my_canvas.selected = selected[-1] # select the top-most item
my_canvas.startxy = (event.x, event.y)
print(my_canvas.selected, my_canvas.startxy)
else:
my_canvas.selected = None
def on_drag(event):
if my_canvas.selected:
# calculate distance moved from last position
dx, dy = event.x-my_canvas.startxy[0], event.y-my_canvas.startxy[1]
# move the selected item
my_canvas.move(my_canvas.selected, dx, dy)
# update last position
my_canvas.startxy = (event.x, event.y)
def delete():
msg = messagebox.askyesnocancel('Info', 'Delete my_canvas ?')
if msg == True:
my_canvas.delete(ALL)
def create_rectangle():
my_canvas.create_rectangle(10, 10, 70, 70, fill='white', outline='blue', width=3)
def create_line():
my_canvas.create_line(200, 200, 100, 100, fill='red', width=5)
def create_circle():
my_canvas.create_oval(10, 10, 70, 70, fill='orange', outline='blue')
...
my_canvas.bind("<Button-1>", on_click)
my_canvas.bind("<B1-Motion>", on_drag)

Related

How to animate a "movie style" credit reel with tkinter and canvas?

I am trying to create a "movie" style credit reel using tkinter's canvas. I'd like to iterate through a list of names and have them scroll across the window. While I've successfully gotten a single name to scroll, I'm struggling to iterate over several names.
I appreciate your help and apologies for any gross oversights on my part.
from tkinter import *
import time
window = Tk()
window.geometry("1920x1080")
window.title("Window 1")
canvas1 = Canvas(window, width=1920, height=1080, bg="green", bd=0, highlightthickness=0, relief='ridge')
canvas1.pack()
class CreditList:
def __init__(self, text):
self.text = text
self.location = 1080
def credit_roll_function(self):
text = canvas1.create_text(960,self.location, text=self.text, anchor=S, fill="black",
font="Time 40")
while True:
canvas1.move(text, 0, -3)
window.update()
time.sleep(.03)
credit_list = ["First Name", "Second Name", "Third Name"]
for credit in credit_list:
item = CreditList(credit)
item.credit_roll_function()
window.mainloop()
Here's a simple way to do it that uses the universal widget after() method instead of calling time.sleep() which interferes with tkinter's mainloop(). Each time the class' roll_credits() method is called, it moves the text upward a little and schedules another call to itself if the text isn't at the top of the window yet.
import tkinter as tk
from tkinter.constants import *
class CreditList:
def __init__(self, lines):
self.location = HEIGHT
self.text = canvas.create_text(0, 0, text='\n'.join(lines), justify=CENTER,
anchor=NW, fill='black', font='Time 40')
xl, yt, xr, yb = canvas.bbox(self.text)
txtwidth = xr - xl
xpos = (WIDTH-txtwidth) // 2 # To center text horizontally.
canvas.move(self.text, xpos, self.location)
def roll_credits(self):
xl, yt, xr, yb = canvas.bbox(self.text)
if yb <= 0: # Completely off top of screen?
canvas.pack_forget()
tk.Button(text='Done', font=('Courier New', 20), relief=GROOVE, bg='orange',
command=window.quit).place(x=WIDTH/2, y=HEIGHT/2)
return # Stop.
canvas.move(self.text, 0, -3)
window.after(DELAY, self.roll_credits) # Keep going.
DELAY = 25 # Millisecs.
window = tk.Tk()
window.attributes('-fullscreen', True)
window.update_idletasks()
WIDTH, HEIGHT = window.winfo_width(), window.winfo_height() # Get screen size.
window.geometry(f'{WIDTH}x{HEIGHT}+0+0')
window.title('Window 1')
canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT, bg='green', bd=0,
highlightthickness=0, relief='ridge')
canvas.pack()
credits = 'First Name', 'Second Name', 'Third Name'
cl = CreditList(credits)
window.after(DELAY, cl.roll_credits) # Start rolling credits "loop".
window.mainloop()
If you give all of your text items the same tag, you can move them all at the same time. Another solution is to create a single text item that is created by joining all of your strings with a newline.
For example, the following code illustrates how to create all of the items with a given tag, then moves them all one pixel at a time every 33 ms until they no longer are visible.
import tkinter as tk
class CreditList(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
def roll_credits(self, credit_list):
x = self.winfo_width() // 2
y = self.winfo_height()
temp = self.create_text(0,0,text="Hello")
bbox = self.bbox(temp)
self.delete(temp)
lineheight = bbox[3]-bbox[1]
linespacing = 4
for credit in credit_list:
self.create_text(x, y, text=credit, anchor="n", tags=("credit",))
y += lineheight + linespacing
self._animate()
def _animate(self):
self.move("credit", 0, -1)
x0, y0, x1, y1 = self.bbox("credit")
if y1 > 0:
self.after(33, self._animate)
root = tk.Tk()
credits = CreditList(root)
credits.pack(side="top", fill="both", expand=True)
credit_list = 'First Name', 'Second Name', 'Third Name'
root.after(1000, credits.roll_credits, credit_list)
root.mainloop()

Python RawTurtle not following goto() command

I am trying to create a program where you click on a Tkinter canvas and a RawTurtle moves to the mouse, but my code is not working. The canvas has a Button-1 event binded to it to tell the program the coordinates of the mouse.
But, when you click on the canvas, instead of the turtle going to the mouse, it kind of mirrors what you would expect it to do (moves away from the mosue as if another mosue is being mirrored). Both the event and the turtle position coordinates are the same when they are printed out.
Code:
import turtle
from tkinter import *
def move(event):
global t
x = event.x
y = event.y
t.setpos(x,y)
print(t.pos())
print(event)
def penState(event):
global penDown,t
if penDown == True:
t.penup()
penDown = False
else:
t.pendown()
penDown = True
def changeWidth(w):
t.pensize(w)
def changeColour(e=None):
global colourBox
t.color(colourBox.get())
colourBox.configure(fg=colourBox.get())
def doCircle():
global checkFillIsTrue,circleSizeBox
if checkFillIsTrue.get() == 1:
begin_fill()
t.circle(int(circleSizeBox.get()))
end_fill()
else:
circle(int(circleSizeBox.get()))
window = Tk('Paint')
window.title('onionPaint')
root = Frame(window)
root.pack(side=LEFT)
cv = Canvas(window,width=500,height=500)
t = turtle.RawTurtle(cv)
t.resizemode('user')
cv.bind('<Button-1>',move)
cv.bind('<Button-2>',penState)
cv.pack(side=RIGHT)
checkFillIsTrue=IntVar()
penDown = True
#Pen width box
sizeLabel = Label(root, text="Pen Width")
sizeLabel.grid()
sizeScale = Scale( root, variable = \
'var',orient=HORIZONTAL,command=changeWidth )
sizeScale.grid()
#Colour box
colourLabel = Label(root, text="Color(HEX or name):")
colourLabel.grid()
colourFrame = Frame(root)
colourFrame.grid()
colourBox = Entry(colourFrame, bd=1)
colourBox.pack(side=LEFT)
colourSubmit = Button(colourFrame,text="OK",command=changeColour)
colourSubmit.pack(side=RIGHT)
#Fill
fillLabel = Label(root,text='Fill')
fillLabel.grid()
fillFrame = Frame(root)
fillFrame.grid()
beginFill = Button(fillFrame,text='Begin Fill',command=t.begin_fill)
endFill = Button(fillFrame,text='End Fill',command=t.end_fill)
beginFill.pack(side=LEFT)
endFill.pack(side=RIGHT)
#Mmore shapes
Label(root,text='Shapes').grid()
#Circle form
Label(root,text='Circle',font=('Heveltica',8)).grid()
circleSize = Frame(root)
circleSize.grid()
circleSizeBox = Entry(circleSize,bd=1)
circleSizeBox.insert(0,'Radius')
circleSizeBox.pack(side=LEFT)
fillCheck =
Checkbutton(circleSize,text='Fill',variable=checkFillIsTrue).pack(side=LEFT)
circleSizeSubmit =
Button(circleSize,text='Draw',command=doCircle).pack(side=RIGHT)
#Text form
Label(root,text='Text',font=('Heveltica',8)).grid()
textFrame = Frame(root)
textFrame.grid()
window.mainloop()
Any help with this problem would be greatly appreciated.
Your code is a disaster. You should specifically read up on the 'global' keyword in Python and when it must be used. And Python code style in general. I believe the key fix to your program is to introduce a TurtleScreen overlay on the canvas and then switch to turtle event handing rather than tkinter event handing.
I've reworked your code below, fixing as many problems as I could:
import turtle
from tkinter import *
FONT = ('Helvetica', 8)
def move(x, y):
terrapin.setpos(x, y)
print(terrapin.pos())
def penState(x, y):
global penDown
if penDown:
terrapin.penup()
penDown = False
else:
terrapin.pendown()
penDown = True
def changeWidth(w):
terrapin.pensize(w)
def changeColour():
color = colourBox.get()
terrapin.color(color)
colourBox.configure(fg=color)
def doCircle():
radius = int(circleSizeBox.get())
if checkFillIsTrue.get():
terrapin.begin_fill()
terrapin.circle(radius)
terrapin.end_fill()
else:
terrapin.circle(radius)
window = Tk('Paint')
window.title('onionPaint')
root = Frame(window)
root.pack(side=LEFT)
canvas = Canvas(window, width=500, height=500)
screen = turtle.TurtleScreen(canvas)
terrapin = turtle.RawTurtle(screen)
screen.onclick(move, btn=1)
screen.onclick(penState, btn=2)
canvas.pack(side=RIGHT)
checkFillIsTrue = BooleanVar()
penDown = True
# Pen width box
sizeLabel = Label(root, text="Pen Width")
sizeLabel.grid()
sizeScale = Scale(root, variable='var', orient=HORIZONTAL, command=changeWidth)
sizeScale.grid()
# Colour box
colourLabel = Label(root, text="Color(HEX or name):")
colourLabel.grid()
colourFrame = Frame(root)
colourFrame.grid()
colourBox = Entry(colourFrame, bd=1)
colourBox.pack(side=LEFT)
colourSubmit = Button(colourFrame, text="OK", command=changeColour)
colourSubmit.pack(side=RIGHT)
# Fill
fillLabel = Label(root, text='Fill')
fillLabel.grid()
fillFrame = Frame(root)
fillFrame.grid()
beginFill = Button(fillFrame, text='Begin Fill', command=terrapin.begin_fill)
endFill = Button(fillFrame, text='End Fill', command=terrapin.end_fill)
beginFill.pack(side=LEFT)
endFill.pack(side=RIGHT)
# More shapes
Label(root, text='Shapes').grid()
# Circle form
Label(root, text='Circle', font=FONT).grid()
circleSize = Frame(root)
circleSize.grid()
circleSizeBox = Entry(circleSize, bd=1)
circleSizeBox.insert(0, 'Radius')
circleSizeBox.pack(side=LEFT)
fillCheck = Checkbutton(circleSize, text='Fill', variable=checkFillIsTrue).pack(side=LEFT)
circleSizeSubmit = Button(circleSize, text='Draw', command=doCircle).pack(side=RIGHT)
# Text form
Label(root, text='Text', font=FONT).grid()
textFrame = Frame(root)
textFrame.grid()
window.mainloop()

create a python dictionary after collecting data from Tkinter image

i am trying to create a list of dictionaries, based on the information I take out from the image(coordinates and image type) by selecting it with a rectangle. On button release, i want to append the dictionary extracted to an empty list. The code works fine for the first dictionary, but when i select the second triangle the dictionary i obtain overrides the first one.
Could you please come up with a solution so that in the end i get the list of dictionaries like this:
[{'bottom_right_coords': [447, 349], 'type': 'middle name', 'top_left_coords': [290, 311]}, {'bottom_right_coords': [447, 349], 'type': 'first name', 'top_left_coords': [290, 311]}, etc etc etc. ]
import Tkinter as tk
from Tkinter import *
import PIL
from PIL import Image, ImageTk
import pygame
import Pmw
from collections import OrderedDict
pygame.init()
global d, D, dict_list
global DEFAULTVALUE_OPTION
global options
DEFAULTVALUE_OPTION = "Select an option"
options = ['address',
'name',
'occupation']
d = {}
dict_list = [None] * 2000
list = range(2000)
D = OrderedDict.fromkeys(list)
class ExampleApp(tk.Tk):
def __init__(self, parent = None):
tk.Tk.__init__(self, parent)
self.x = self.y = 0
self.canvas = tk.Canvas(self, width=700, height=700, cursor="cross", relief=SUNKEN)
self.canvas.bind("<ButtonPress-1>", self.on_button_press)
self.canvas.bind("<B1-Motion>", self.on_move_press)
self.canvas.bind("<ButtonRelease-1>", self.on_button_release)
self.rect = None
self.start_x = None
self.start_y = None
self._draw_image()
def _draw_image(self):
self.sbarV = Scrollbar(self, orient=VERTICAL)
self.sbarH = Scrollbar(self, orient=HORIZONTAL)
self.sbarV.config(command=self.canvas.yview)
self.sbarH.config(command=self.canvas.xview)
self.canvas.config(yscrollcommand=self.sbarV.set)
self.canvas.config(xscrollcommand=self.sbarH.set)
self.sbarV.pack(side=RIGHT, fill=Y)
self.sbarH.pack(side=BOTTOM, fill=X)
self.canvas.pack(side="top", fill="both", expand=True)
self.im = Image.open("/home/madalina/madalina/image/page-001.jpg")
width, height = self.im.size
self.canvas.config(scrollregion=(0, 0, width, height))
self.tk_im = ImageTk.PhotoImage(self.im)
self.canvas.create_image(0,0,anchor="nw",image=self.tk_im)
def on_button_press(self, event):
# save mouse drag start position
self.start_x = event.x
self.start_y = event.y
d["top_left_coords"] = [self.start_x, self.start_y]
# create rectangle if not yet exist
#if not self.rect:
self.rect = self.canvas.create_rectangle(self.x, self.y, 100, 100, width = 2, outline = "gold")
def on_move_press(self, event):
curX, curY = (event.x, event.y)
# expand rectangle as you drag the mouse
self.canvas.coords(self.rect, self.start_x, self.start_y, curX, curY)
def on_button_release(self, event):
top = Tk()
Label(top, text="New Option").grid(row=0)
Label(top, text = "Coordinates").grid(row=1)
E1 = Entry(top, bd = 5)
E2 = Entry(top, bd = 5)
# e1 = E1.get()
# e2 = E2.get()
E1.grid(row=0, column=1)
E2.grid(row=1, column=1)
def addOption():
d["type"] = E1.get()
print d
def createDict():
dict_list.append(d)
print "lista de dictionare este " + str(dict_list)
B1 = Button(top, text = "ADD", command = addOption)
B1.grid(row=3, column=0, sticky=W, pady=4)
B1 = Button(top, text = "Create Dict", command = createDict)
B1.grid(row=3, column=1, sticky=W, pady=4)
d["bottom_right_coords"] = [event.x, event.y]
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()
Finding your Problem
Let's look at this function here
def on_button_press(self, event):
# save mouse drag start position
self.start_x = event.x
self.start_y = event.y
d["top_left_coords"] = [self.start_x, self.start_y]
I like those first two lines. So I suspect that last one is the culpret.
d["top_left_coords"] = [self.start_x, self.start_y]
Not Your Problem But Worth Mentioning
My first impression was: "d is undeclared, so this will crash". Not so, since you've declared it as a global variable. So my first recomendation is: make sure you definately need a global variable there. As this 2000+ voted answer says
I imagine the reason for it is that, since global variables are so dangerous, Python wants to make sure that you really know that's what you're playing with by explicitly requiring the global keyword.
Here's somewhere to start if you want to remove globals.
If you decide global variable is the way to go, please please name it something more helpful than d
But Onto Your problem
Make d a list of dictionaries and append to it. And by the way, tuples might make more sense than lists for coordinates, since they're immutable
d = []
...
d.append({self.start_x,self.start_y})
...
#retrieving last click
print d[-1]
# adding other things to the 'last click dictionary'
d[-1]['type'] = 'foobar'

How do you create a grid of buttons in a frame using .pack() or .grid()?

from tkinter import *
from random import *
class Game:
def __init__(self):
self.root = Tk()
self.frame1 = Frame(self.root, width = 1055, height = 30)
self.frame1.pack()
self.frame_lvl = Frame(self.root, width = 1055, height = 1055)
self.frame_lvl.pack()
for frame_lvl in range(0,31):
self.frame_lvl = Frame(self.root)
self.frame_lvl.pack(side = BOTTOM)
for i in range(0,31):
for j in range(0,31):
button = Button(self.i, width = 30, height = 30, padx = 2, pady = 2)
button.pack(side = LEFT)
self.root.mainloop()
app = Game()
So I try to create a new frame level so the buttons won't keep printing on the same line but I'm not sure if the frame level will be saved as self.0, self.1, self.2, etc...
When I tried making the frame a grid and adjusting the width, height, rowspan, and column span, I got the error ("cannot use geometry manager grid inside . which already has slaves managed by pack") The error comes from these lines:
self.frame2 = Frame(width = 1055, height = 1055)
self.frame2.grid(columnspan = 30, rowspan = 30)
Any suggestions.
Note that there is only one self.frame_lvl so each time you assign it a new value you lose the existing value. In the end it will only contain the last value assigned to it. Also
for i in range(31):
for j in range(31):
creates 31*31 buttons(which is over 900 buttons). While you are learning, stick with all grid() or all pack() in a program until you learn how to mix the two. To create 3 rows of 5 buttons using grid()
from tkinter import *
from functools import partial
class Game:
def __init__(self):
self.root = Tk()
self.frame1 = Frame(self.root, width = 900, height = 30)
self.frame1.grid()
self.label=Label(self.frame1, text="")
self.label.grid(row=0, column=0, columnspan=5)
## save each button id in a list
self.list_of_button_ids=[]
for ctr in range(15):
## use partial to pass the button number to the
## function each time the button is pressed
button = Button(self.frame1, text=str(ctr), width = 20,
height = 20, padx = 2, pady = 2,
command=partial(self.button_callback, ctr))
this_row, this_col=divmod(ctr, 5)
## the label is in the top row so add one to each row
button.grid(row=this_row+1, column=this_col)
## add button's id to list
self.list_of_button_ids.append(button)
self.root.mainloop()
def button_callback(self, button_num):
""" display button number pressed in the label
"""
self.label.config(text="Button number %s and it's id=%s"
% (button_num, self.list_of_button_ids[button_num]))
app = Game()

create an image in the canvas using a button event tkinter

I need to add an event to my buttons in a Tkinter gui that will create an image in the canvas. How do I do it?
Thanks All
It works but I need to get a dynamic number images of the same image file, but everytime I create a new image the old image gets garbage collected. I cant think of what to do. I want to have multiple instances of the same image on my canvas. Please, would be glad for some suggestions
my code
from Tkinter import *
import tkMessageBox
def callback():
if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
class AppUI(Frame):
def __init__(self, master):
Frame.__init__(self, master, background="white", relief=FLAT, bd=2, height=768, width=1024)
self.menubar = Menu(self)
menu = Menu(self.menubar)
self.menubar.add_cascade(label="File", menu=menu)
menu.add_command(label="New")
menu.add_command(label="Open")
menu.add_command(label="Save As")
menu.add_command(label="Save Copy of")
menu.add_separator()
menu.add_command(label="exit")
menu = Menu(self.menubar)
self.menubar.add_cascade(label="Edit", menu=menu)
menu.add_command(label="Cut")
menu.add_command(label="Copy")
menu.add_command(label="Paste")
menu = Menu(self.menubar)
self.menubar.add_cascade(label="Debug", menu=menu)
menu.add_command(label="Open Debugger")
menu.add_command(label="Auto-debug")
menu.add_command(label="Revert")
menu = Menu(self.menubar)
self.menubar.add_cascade(label="Run", menu=menu)
menu.add_command(label="Stimulation")
menu.add_command(label="Check")
menu.add_command(label="Scan Module")
menu = Menu(self.menubar)
self.menubar.add_cascade(label="Help", menu=menu)
menu.add_command(label="Help files")
menu.add_command(label="FTA site")
menu.add_separator()
menu.add_command(label="Credits")
self.master.config(menu=self.menubar)
f0 = Frame(root, background="white")
b1 = Button(f0, bd=2, padx=15, pady=15, justify = LEFT)
photo1 = PhotoImage(file="Images/p1.gif")
b1.config(image = photo1,width="50",height="50", command=self.create_image1)
b1.image = photo1
b1.pack(side=LEFT)
b2 = Button(f0, bd=2, padx=15, pady=15, justify = LEFT)
photo2 = PhotoImage(file="Images/p4.gif")
b2.config(image=photo2, width="50",height="50", command = self.create_image2)
b2.image = photo2
b2.pack(side=LEFT)
b3 = Button(f0,padx=15, bd=2, pady=15, justify = LEFT)
photo3 = PhotoImage(file="Images/p8.gif")
b3.config(image=photo3, width="50",height="50", command = self.create_image3)
b3.image = photo3
b3.pack(side=LEFT)
b4 = Button(f0,padx=15, bd=2, pady=15, justify = LEFT)
photo4 = PhotoImage(file="Images/p7.gif")
b4.config(image=photo4, width="50",height="50", command = self.create_image4)
b4.image = photo4
b4.pack(side=LEFT)
b5 = Button(f0,padx=15, bd=2, pady=15, justify = LEFT)
photo5 = PhotoImage(file="Images/p5.gif")
b5.config(image=photo5, width="50",height="50", command=self.create_image5)
b5.image = photo5
b5.pack(side=LEFT)
f0.pack(anchor=NW, side=TOP)
self.canvas = Canvas(self, height=750, width=1500, bg="white")
self.canvas.pack(side=LEFT)
def create_image1(self):
photos1 = PhotoImage(file="Images/p1.gif")
self.photos1=photos1
self.img1=self.canvas.create_image(60, 60, image=photos1)
self.canvas.bind("<B1-Motion>", self.move_image1)
def create_image2(self):
photos2 = PhotoImage(file="Images/p4.gif")
self.photos2=photos2
self.img2=self.canvas.create_image(60, 60, image=photos2)
self.canvas.bind("<B1-Motion>", self.move_image2)
def create_image3(self):
photos3 = PhotoImage(file="Images/p8.gif")
self.photos3=photos3
self.img3=self.canvas.create_image(60, 60, image=photos3)
self.canvas.bind("<B1-Motion>", self.move_image3)
def create_image4(self):
photos4 = PhotoImage(file="Images/p7.gif")
self.photos4=photos4
self.img4=self.canvas.create_image(60, 60, image=photos4)
self.canvas.bind("<B1-Motion>", self.move_image4)
def create_image5(self):
photos5 = PhotoImage(file="Images/p5.gif")
self.photos5=photos5
self.img5=self.canvas.create_image(60, 60, image=photos5)
self.canvas.bind("<B1-Motion>", self.move_image5)
def move_image1(self, event):
self.canvas.delete(self.img1)
x = event.x
y = event.y
self.img1 = self.canvas.create_image(x, y, image=self.photos1, anchor='nw')
self.canvas.update()
def move_image2(self, event):
self.canvas.delete(self.img2)
x = event.x
y = event.y
self.img2 = self.canvas.create_image(x, y, image=self.photos2, anchor='nw')
self.canvas.update()
def move_image3(self, event):
self.canvas.delete(self.img3)
x = event.x
y = event.y
self.img3 = self.canvas.create_image(x, y, image=self.photos3, anchor='nw')
self.canvas.update()
def move_image4(self, event):
self.canvas.delete(self.img4)
x = event.x
y = event.y
self.img4 = self.canvas.create_image(x, y, image=self.photos4, anchor='nw')
self.canvas.update()
def move_image5(self, event):
self.canvas.delete(self.img5)
x = event.x
y = event.y
self.img5 = self.canvas.create_image(x, y, image=self.photos5, anchor='nw')
self.canvas.update()
root = Tk()
root.protocol("WM_DELETE_WINDOW", callback)
app = AppUI(root)
app.pack()
root.mainloop()
Also, only the last drawn image can be dragged and dropped All previous images cannot be interacted with. And, as I said, I cant seem to create multiple clone images. I know that I should anchor all the images so that they don't get garbage collected but I cant seem to find out a way to do that. I tried return the image instance of the function create_image to an array. but it didn't work. Thanks in advance
What problem are you specifically having? There's nothing tricky about it:
def __init__(...):
...
self.canvas = tk.Canvas(...)
b = tk.Button(..., command=self.create_image)
...
def create_image(self):
self.canvas.create_image(...)
The important thing to remember is that the images will get garbage-collected unless you keep a reference to them. Since you are creating multiple images, an easy way to do this is to create a list to which you can append references to the images:
def __init__(...):
...
self.images = []
...
photo1 = PhotoImage(file="Images/p1.gif")
self.images.append(photo1)
...
There are some other problems with your code. For one, you don't need to delete and then recreate the images when they are moved. The canvas has a move method specifically for moving existing items. Also, the calls to self.canvas.update() are completely useless.
Finally, your code will be considerably easier to maintain if you only have a single "move" method, rather than one for each image. That's off topic for this specific question, but you might want to search for lambda and functools.partial as solutions for being able to pass arguments via bindings.

Categories