create a python dictionary after collecting data from Tkinter image - python

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'

Related

A _tkinter.TclError occured while trying to make a scrollable frame in python

So I have been trying to make a scrollable frame. I've had been searching and these 3 had the most impact:
https://stackoverflow.com/questions/16188420/tkinter-scrollbar-for-frame\
https://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-group-of-widgets-in-tkinter\
How could I get a Frame with a scrollbar in Tkinter?
And I have come up with the following code:
from tkinter import *
class Test_tk_stuff():
def __init__(self):
self.screen = Tk()
self.screen.geometry("500x500")
self.structure()
self.screen.mainloop()
def scroller(self, canvas):
canvas.configure(scrollregion = canvas.bbox("all"))
def frame_expander(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.frame, width = canvas_width)
def structure(self):
parent = Frame(self.screen)
parent.pack(expand = True, fill = BOTH)
self.canvas = Canvas(parent, bg = "green", highlightthickness = 0, relief = RAISED)
self.frame = Frame(self.canvas, bg = "blue")
myscrollbar = Scrollbar(parent, orient = "vertical", command = self.canvas.yview)
self.canvas.configure(yscrollcommand = myscrollbar.set)
myscrollbar.pack(side = RIGHT, fill = Y)
self.canvas.pack(side = LEFT, fill = BOTH, expand = True)
self.canvas.create_window((0, 0), window = self.frame)
# can't do: self.frame.pack(expand = True, fill = BOTH) because it will become unscrollable
# Event Bind
self.frame.bind("<Configure>", lambda event, canvas = self.canvas: self.scroller(self.canvas))
self.canvas.bind("<Configure>", self.frame_expander)
# initialize number of minimum columns
for num_columns in range(3):
self.frame.columnconfigure(num_columns, weight = 1)
a = "Button Text!"
# fill it to - 1.) test scrollbar 2.) actually using the frame inside
for place2 in range(10):
Button(self.frame, text = a, bg = "black", fg = "white").grid(row = place2, column = 1, sticky = "NSEW", ipady = 15)
if __name__ == "__main__":
Test_tk_stuff()
But somehow when I run it, it shows a _tkinter.TclError. I tried searching what that is and how to fix it, but, as you can see, I wasn't able to fix it.
Is there something wrong with my implementation?
Thanks in advance.
canvas.itemconfig() is used on canvas item returned by canvas.create_xxxxx() functions, not on tkinter widget (self.frame).
Save the canvas item ID for the self.frame and use it in canvas.itemconfig():
def frame_expander(self, event):
canvas_width = event.width
self.canvas.itemconfig(self.frame_item, width=canvas_width)
def structure(self):
...
self.frame_item = self.canvas.create_window((0, 0), window = self.frame)
...
use self.frame.config(width=canvas_width) instead of canvas.itemconfig()

Python canvas move items with mouse tkinter

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)

AttributeError: '_tkinter.tkapp' object has no attribute 'mousePressEvent'

I'm having trouble with two tkinter classes that I want to interact. I think it comes from my lack of understanding of the Tk.Toplevel. Instead of creating an entirely new full-screen window for screen-grabbing, my code gives an error:
AttributeError: '_tkinter.tkapp' object has no attribute 'mousePressEvent'
Can someone help me understand the hierarchy of parent-child relationships in my code? I'm not asking anyone to rewrite my code, I just want to understand what I already have, so I can figure out what's wrong. I haven't found many examples in SO of multiple classes that interact with multiple windows - so it's hard to grasp the inter-workings of inheritance.
This is the structure I think I have:
root
object: AquireImage
method: show_dialogs()
object: ScreenSnip
method: get_snip()
method: mousePressEvent()
method: mouseMoveEvent()
method: mouseReleaseEvent()
Is this accurate?
CODE
import tkinter as tk
from PIL import ImageGrab, ImageTk, Image
import cv2
import numpy as np
from tkinter import filedialog
class ScreenSnip(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
def get_snip(self):
self.configure(cursor='cross')
self.attributes('-fullscreen', True)
self.attributes('-alpha', 0.4)
self.canvas = tk.Canvas(self, bg='dark gray')
self.canvas.pack(fill='both', expand=True)
self.begin_x = 0
self.begin_y = 0
self.end_x = 0
self.end_y = 0
self.click_drag = False
self.canvas.create_rectangle(0, 0, 0, 0, outline='#0052d6', width=2, fill='white', tags='snip_rect')
self.canvas.bind('<ButtonPress-1>', self.mousePressEvent)
self.canvas.bind('<B1-Motion>', self.mouseMoveEvent)
self.canvas.bind('<ButtonRelease-1>', self.mouseReleaseEvent)
print('Capture the screen...')
def mousePressEvent(self, event):
self.begin_x = event.x
self.begin_y = event.y
print(self.begin_x,self.begin_y)
def mouseMoveEvent(self, event):
self.click_drag = True
self.end_x = event.x
self.cur_y = event.y
width = self.end_x - self.begin_x
height = abs(width * 2/3)
if self.cur_y < self.begin_y:
height *= -1
self.end_y = self.begin_y + height
self.canvas.coords('snip_rect', self.begin_x, self.begin_y, self.end_x, self.end_y)
def mouseReleaseEvent(self, event):
self.destroy()
self.master.update_idletasks()
self.master.after(100) # give time for screen to be refreshed so as not to see the blue box on the screenshot
if not self.click_drag: # if the user just clicks, instead of clicking and dragging
self.begin_x -= 300
self.begin_y += 200
self.end_x = self.begin_x + 600
self.end_y = self.begin_y - 400
x1 = min(self.begin_x, self.end_x)
y1 = min(self.begin_y, self.end_y)
x2 = max(self.begin_x, self.end_x)
y2 = max(self.begin_y, self.end_y)
img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
self.img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)
cv2.imshow('Captured Image', self.img)
cv2.waitKey(0)
font1 = ("arial", 18, "bold")
class AcquireImage:
def __init__(self, master):
self.master = master
self.fontA = ("arial", 20, "bold")
self.frame = tk.Frame(master, bg="#1B2631")
self.frame.pack(fill="both", expand=True)
self.button1 = tk.Button(self.frame, text="Select Image File", padx=10, pady=10, bg="#d9a193",
font = self.fontA, command =lambda: self.show_dialogs(1))
self.button1.grid(row=0, column=0, sticky="nsew")#, padx=10, pady=10)
self.button2 = tk.Button(self.frame, text="Get Screen Snip", padx=10, pady=10, bg="#d9a193",
font = self.fontA, command=lambda: self.show_dialogs(2))
self.button2.grid(row=0, column=1, sticky="nsew")#, padx=10, pady=10)
self.image_selected = None
def show_dialogs(self, method):
if method == 1:
ret = filedialog.askopenfilename() #filedialog.askopenfilename(initialdir='/home/user/images/')
if ret:
self.image_selected = ImageTk.PhotoImage(file = ret)
self.master.destroy()
elif method == 2:
newWin = ScreenSnip.get_snip(self.master)
ret = newWin.img
if ret:
self.image_selected = ImageTk.PhotoImage(file = ret)
def main():
root = tk.Tk()
AcquireImage(root)
root.mainloop()
if __name__ == '__main__':
main()
This answer is derived from #jasonharper's comment:
Instances of ScreenSnip would indeed have a mousePressEvent
attribute. But you never create any such instance; instead, you
attempt to call get_snip() on the class itself, which ends up
providing a completely inappropriate value for its self parameter.
The mistake I made was here:
newWin = ScreenSnip.get_snip(self.master)
I tried to create a new object newWin without putting the parent in the correct place, and while calling the method get_snip at the same time. This is what solved the issue:
newWin = ScreenSnip(self.master)
newWin.get_snip()
First create the object ScreenSnip from the class, with the parent
then call the method `get_snip'

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 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