I need to make a fill function on Canvas Tkinter - python

I can't get the x or y of where the user has drawn already.
I'd like to be able to press a fill button then press in the area, and if a circle has been drawn for example, it would fill with a certain colour.
def fill(event):
global colour
for x in range(x1,x2):
x = canvas.create_oval(x1,y1,x2,y2,fill=colour)

To fill an existing element in the canvas there is .itemconfig:
def fill(canvas, element_id, event):
canvas.itemconfig(element_id, fill=colour)
To bind it to an event there is .tag_bind, to which we can pass the unique id of our circle and the above function with the corresponding arguments:
canvas.tag_bind(circle, "<Button-1>", lambda e: fill(canvas, circle, e))
Where "<Button-1>" is the event of a left-click
We can then make it a class to make it cleaner:
class FillableOval:
def __init__(self, canvas, x0, y0, x1, y1, **kwargs):
self.canvas = canvas
self.id = self.canvas.create_oval(x0, y0, x1, y1, **kwargs)
self.canvas.tag_bind(self.id, "<Button-1>", self.fill)
def fill(self, event):
self.canvas.itemconfig(self.id, fill=colour)

I am not good at English.
so
I'll show you a simple example code I made.
import tkinter as tk
def draw_start(event):
line_points.extend((event.x, event.y))
draw_obj = draw_variety.get()
if draw_obj == 'circle':
global obj_id
obj_id = canvas.create_oval(line_points[0], line_points[1], line_points[0],
line_points[1], width=1, outline='black')
if draw_obj == 'fill':
fill(event)
def state_draw_op(event):
draw_obj=draw_variety.get()
if draw_obj == 'circle':
global obj_id
canvas.coords(obj_id, line_points[0], line_points[1], event.x, event.y)
def end_draw(event=None):
draw_obj = draw_variety.get()
if draw_obj == 'circle':
global obj_id
canvas.coords(obj_id, line_points[0], line_points[1], event.x, event.y)
line_points.clear()
def fill(event):
color = color_variety.get()
item = canvas.find_closest(event.x, event.y)
canvas.itemconfigure(item, fill=color)
line_points=[]
root=tk.Tk()
canvas=tk.Canvas(root, relief="solid", bd=2)
canvas.pack()
draw_variety=tk.StringVar()
color_variety=tk.StringVar()
radio_red = tk.Radiobutton(root, value='red', indicatoron=False, relief='flat', bg='red',
height=2, width=5,
overrelief='flat', activebackground='red', highlightcolor='red',
highlightbackground='red', selectcolor='red',
variable=color_variety)
radio_blue = tk.Radiobutton(root, value='blue', indicatoron=False, relief='flat', bg='blue',
height=2, width=5,
overrelief='flat', activebackground='blue', highlightcolor='blue',
highlightbackground='blue', selectcolor='blue',
variable=color_variety)
radio_cir = tk.Radiobutton(root, text='ㅇ', value='circle', indicatoron=False, relief='flat',
font=('Consolas', 20,),
overrelief='flat', command=None,
variable=draw_variety,
selectcolor='red')
radio_fill = tk.Radiobutton(root, text='fill', value='fill', indicatoron=False, relief='flat',
font=('Consolas', 20,),
overrelief='flat', command=None,
variable=draw_variety,
selectcolor='red')
radio_red.pack()
radio_blue.pack()
radio_cir.pack()
radio_fill.pack()
canvas.bind('<Button-1>', draw_start)
canvas.bind('<B1-Motion>', state_draw_op)
canvas.bind('<ButtonRelease-1>', end_draw)
root.mainloop()
In this case, it would be better to use a class, but for the sake of simplicity I used a global variable.

I copied your code and modified it into my code workaround.
No need to do global and add a constant thickness.
Do not use the keyword fill. It is valid for Python.
Snippet:
_COLOR = "black"
T = 2
def paint(event):
x1, y1 = (event.x - T), (event.y - T)
x2, y2 = (event.x + T), (event.y + T)
x = canvas.create_oval(x1,y1,x2,y2,fill=_COLOR)
Screenshot:

Related

How to change size of tkinter canvas text item while zooming in or out? [duplicate]

I was trying to understand how the scaling of canvas works.
Take for example, the following code. Why is that canvas.scale("all", ...), which is bind to mouse wheel, is scaling all the rectangles and not the text as well.
How can I achieve scaling of text along with the rectangles?
import Tkinter as tk
import random
pressed = False
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(self, width=400, height=400, background="bisque")
self.xsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview)
self.ysb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set)
self.canvas.configure(scrollregion=(0,0,1000,1000))
self.xsb.grid(row=1, column=0, sticky="ew")
self.ysb.grid(row=0, column=1, sticky="ns")
self.canvas.grid(row=0, column=0, sticky="nsew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
#Plot some rectangles
for n in range(50):
x0 = random.randint(0, 900)
y0 = random.randint(50, 900)
x1 = x0 + random.randint(50, 100)
y1 = y0 + random.randint(50,100)
color = ("red", "orange", "yellow", "green", "blue")[random.randint(0,4)]
self.canvas.create_rectangle(x0,y0,x1,y1, outline="black", fill=color, activefill="black", tags=n)
self.canvas.create_text(50,10, anchor="nw", text="Click and drag to move the canvas\nScroll to zoom.")
# This is what enables using the mouse:
self.canvas.bind("<ButtonPress-1>", self.move_start)
self.canvas.bind("<B1-Motion>", self.move_move)
self.canvas.bind("<ButtonPress-2>", self.pressed2)
self.canvas.bind("<Motion>", self.move_move2)
#linux scroll
self.canvas.bind("<Button-4>", self.zoomerP)
self.canvas.bind("<Button-5>", self.zoomerM)
#windows scroll
self.canvas.bind("<MouseWheel>",self.zoomer)
# Hack to make zoom work on Windows
root.bind_all("<MouseWheel>",self.zoomer)
#move
def move_start(self, event):
self.canvas.scan_mark(event.x, event.y)
def move_move(self, event):
self.canvas.scan_dragto(event.x, event.y, gain=1)
#move
def pressed2(self, event):
global pressed
pressed = not pressed
self.canvas.scan_mark(event.x, event.y)
def move_move2(self, event):
if pressed:
self.canvas.scan_dragto(event.x, event.y, gain=1)
#windows zoom
def zoomer(self,event):
if (event.delta > 0):
self.canvas.scale("all", event.x, event.y, 1.1, 1.1)
elif (event.delta < 0):
self.canvas.scale("all", event.x, event.y, 0.9, 0.9)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
#linux zoom
def zoomerP(self,event):
self.canvas.scale("all", event.x, event.y, 1.1, 1.1)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
def zoomerM(self,event):
self.canvas.scale("all", event.x, event.y, 0.9, 0.9)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
The Scaling of text is not "really" a scaling of text. It is (at least with the Canvas-widget) a change of font.
From the documentation :
This method will not change the size of a text item, but may move it.
Edit:
This behaviour also applies to images (created with Tkinter.Canvas.create_image(...)) and windows (created with Tkinter.Canvas.create_window(...)). As the documentation defines scaling as
.scale(tagOrId, xOffset, yOffset, xScale, yScale)
Scale all objects according to their distance from a point P=(xOffset, yOffset). The scale factors xScale and yScale are based on a value of 1.0, which means no scaling. Every point in the objects selected by tagOrId is moved so that its x distance from P is multiplied by xScale and its y distance is multiplied by yScale.
Please note the italic part for how scaling works.
End Edit
If you want to perform an increase of font-size you need to configure all the text elements by yourself using the itemconfigure method.
To Implement it in a very functional way you could use tags to identify all text elements. Increasing can happen not only absolute but also relatively if you first get the fontsize (itemcget) and then increase it depending on scale factor.
I know this post is old but I solved a similar question so I thought I would post a solution for the problem as well.
The idea is to add a tag to the text item and then reconfigure it each time the zoomer is called. Note, this solution does not change the linux zoom (zoomerP)
import tkinter as tk
import random
pressed = False
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.canvas = tk.Canvas(self, width=400, height=400, background="bisque")
self.xsb = tk.Scrollbar(self, orient="horizontal", command=self.canvas.xview)
self.ysb = tk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.ysb.set, xscrollcommand=self.xsb.set)
self.canvas.configure(scrollregion=(0,0,1000,1000))
self.xsb.grid(row=1, column=0, sticky="ew")
self.ysb.grid(row=0, column=1, sticky="ns")
self.canvas.grid(row=0, column=0, sticky="nsew")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.fontSize = 10
#Plot some rectangles
for n in range(50):
x0 = random.randint(0, 900)
y0 = random.randint(50, 900)
x1 = x0 + random.randint(50, 100)
y1 = y0 + random.randint(50,100)
color = ("red", "orange", "yellow", "green", "blue")[random.randint(0,4)]
self.canvas.create_rectangle(x0,y0,x1,y1, outline="black", fill=color, activefill="black", tags=n)
self.canvas.create_text(50,10, anchor="nw", text="Click and drag to move the canvas\nScroll to zoom.", font = ("Helvetica", int(self.fontSize)), tags="text")
# This is what enables using the mouse:
self.canvas.bind("<ButtonPress-1>", self.move_start)
self.canvas.bind("<B1-Motion>", self.move_move)
self.canvas.bind("<ButtonPress-2>", self.pressed2)
self.canvas.bind("<Motion>", self.move_move2)
#linux scroll
self.canvas.bind("<Button-4>", self.zoomerP)
self.canvas.bind("<Button-5>", self.zoomerM)
#windows scroll
self.canvas.bind("<MouseWheel>",self.zoomer)
# Hack to make zoom work on Windows
root.bind_all("<MouseWheel>",self.zoomer)
#move
def move_start(self, event):
self.canvas.scan_mark(event.x, event.y)
def move_move(self, event):
self.canvas.scan_dragto(event.x, event.y, gain=1)
#move
def pressed2(self, event):
global pressed
pressed = not pressed
self.canvas.scan_mark(event.x, event.y)
def move_move2(self, event):
if pressed:
self.canvas.scan_dragto(event.x, event.y, gain=1)
#windows zoom
def zoomer(self,event):
if (event.delta > 0):
self.canvas.scale("all", event.x, event.y, 1.1, 1.1)
self.fontSize = self.fontSize * 1.1
elif (event.delta < 0):
self.canvas.scale("all", event.x, event.y, 0.9, 0.9)
self.fontSize = self.fontSize * 0.9
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
for child_widget in self.canvas.find_withtag("text"):
self.canvas.itemconfigure(child_widget, font=("Helvetica", int(self.fontSize)))
print(self.fontSize)
#linux zoom
def zoomerP(self,event):
self.canvas.scale("all", event.x, event.y, 1.1, 1.1)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
def zoomerM(self,event):
self.canvas.scale("all", event.x, event.y, 0.9, 0.9)
self.canvas.configure(scrollregion = self.canvas.bbox("all"))
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()

How to make a custom scrollbar in tkinter for frames

I have been using the tkinter scrollbar for my project. But, when I use the properties troughcolor, the color of the scrollbar doesn't change. So, I want to make a custom scrollbar for tkinter and python that can be used to scroll through a frame. I then will add color to this custom scrollbar. Is there any way to do so? Here is my code:
root=Tk()
container = ttk.Frame(root)
canvas = Canvas(container, highlightbackground="black", highlightthickness=1, bg="black", width=400, height=600)
scrollbar = Scrollbar(container, orient="vertical", command=canvas.yview, troughcolor="red")
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(
scrollregion=canvas.bbox("all")
)
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
container.pack()
canvas.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
root.mainloop()
Based on Bryan Oakley's comment and a variety of his posts on SO, and several days of work on my part, here is a pretty complete answer to the question. This scrollbar is configurable and can be optionally hidden when not needed. I am a beginner and this can be improved, but it seems to work OK. I left my comments in, as well as Bryan Oakley's from an original post where he showed part of this code. This was not easy for me to do and the comments might help someone understand it better. I wrote this code a few weeks ago and it's working fine so far.
import tkinter as tk
'''
Much of this code was elucidated by Bryan Oakley on StackOverflow.com.
Without his explanations and examples, I would not have figured out how to
create a configurable Tkinter scrollbar. Any mistakes in this code are mine
of course.
I didn't add the little arrows at the ends of the trough.
'''
class Scrollbar(tk.Canvas):
'''
A scrollbar is gridded as a sibling of what it's scrolling.
'''
def __init__(self, parent, orient='vertical', hideable=False, **kwargs):
print('kwargs is', kwargs)
'''
kwargs is {
'width': 17,
'command': <bound method YView.yview of
<widgets.Text object .!canvas.!frame.!frame.!text>>}
https://stackoverflow.com/questions/15411107
You can use dict.pop:... delete an item in a dictionary only if the given key exists... not certain if key exists in the dictionary...
mydict.pop("key", None)
...if the second argument, None is not given, KeyError is raised if the key is not in the dictionary. Providing the second argument prevents the conditional exception... the second argument to .pop() is what it returns if the key is not found.
'''
self.command = kwargs.pop('command', None)
print('self.command is', self.command)
tk.Canvas.__init__(self, parent, **kwargs)
self.orient = orient
self.hideable = hideable
self.new_start_y = 0
self.new_start_x = 0
self.first_y = 0
self.first_x = 0
self.slidercolor = 'steelblue'
self.troughcolor = 'lightgray'
self.config(bg=self.troughcolor, bd=0, highlightthickness=0)
# coordinates are irrelevant; they will be recomputed
# in the 'set' method
self.create_rectangle(
0, 0, 1, 1,
fill=self.slidercolor,
width=2, # this is border width
outline='teal',
tags=('slider',))
self.bind('<ButtonPress-1>', self.move_on_click)
self.bind('<ButtonPress-1>', self.start_scroll, add='+')
self.bind('<B1-Motion>', self.move_on_scroll)
self.bind('<ButtonRelease-1>', self.end_scroll)
def set(self, lo, hi):
'''
For resizing & repositioning the slider. The hideable
scrollbar portion is by Fredrik Lundh, one of Tkinter's authors.
'''
lo = float(lo)
hi = float(hi)
if self.hideable is True:
if lo <= 0.0 and hi >= 1.0:
self.grid_remove()
return
else:
self.grid()
height = self.winfo_height()
width = self.winfo_width()
if self.orient == 'vertical':
x0 = 2
y0 = max(int(height * lo), 0)
x1 = width - 2
y1 = min(int(height * hi), height)
# This was the tricky part of making a horizontal scrollbar
# when I already knew how to make a vertical one.
# You can't just change all the "height" to "width"
# and "y" to "x". You also have to reverse what x0 etc
# are equal to, comparing code in if and elif. Till that was
# done, everything worked but the horizontal scrollbar's
# slider moved up & down.
elif self.orient == 'horizontal':
x0 = max(int(width * lo), 0)
y0 = 2
x1 = min(int(width * hi), width)
y1 = height
self.coords('slider', x0, y0, x1, y1)
self.x0 = x0
self.y0 = y0
self.x1 = x1
self.y1 = y1
def move_on_click(self, event):
if self.orient == 'vertical':
# don't scroll on click if mouse pointer is w/in slider
y = event.y / self.winfo_height()
if event.y < self.y0 or event.y > self.y1:
self.command('moveto', y)
# get starting position of a scrolling event
else:
self.first_y = event.y
elif self.orient == 'horizontal':
# do nothing if mouse pointer is w/in slider
x = event.x / self.winfo_width()
if event.x < self.x0 or event.x > self.x1:
self.command('moveto', x)
# get starting position of a scrolling event
else:
self.first_x = event.x
def start_scroll(self, event):
if self.orient == 'vertical':
self.last_y = event.y
self.y_move_on_click = int(event.y - self.coords('slider')[1])
elif self.orient == 'horizontal':
self.last_x = event.x
self.x_move_on_click = int(event.x - self.coords('slider')[0])
def end_scroll(self, event):
if self.orient == 'vertical':
self.new_start_y = event.y
elif self.orient == 'horizontal':
self.new_start_x = event.x
def move_on_scroll(self, event):
# Only scroll if the mouse moves a few pixels. This makes
# the click-in-trough work right even if the click smears
# a little. Otherwise, a perfectly motionless mouse click
# is the only way to get the trough click to work right.
# Setting jerkiness to 5 or more makes very sloppy trough
# clicking work, but then scrolling is not smooth. 3 is OK.
jerkiness = 3
if self.orient == 'vertical':
if abs(event.y - self.last_y) < jerkiness:
return
# scroll the scrolled widget in proportion to mouse motion
# compute whether scrolling up or down
delta = 1 if event.y > self.last_y else -1
# remember this location for the next time this is called
self.last_y = event.y
# do the scroll
self.command('scroll', delta, 'units')
# afix slider to mouse pointer
mouse_pos = event.y - self.first_y
if self.new_start_y != 0:
mouse_pos = event.y - self.y_move_on_click
self.command('moveto', mouse_pos/self.winfo_height())
elif self.orient == 'horizontal':
if abs(event.x - self.last_x) < jerkiness:
return
# scroll the scrolled widget in proportion to mouse motion
# compute whether scrolling left or right
delta = 1 if event.x > self.last_x else -1
# remember this location for the next time this is called
self.last_x = event.x
# do the scroll
self.command('scroll', delta, 'units')
# afix slider to mouse pointer
mouse_pos = event.x - self.first_x
if self.new_start_x != 0:
mouse_pos = event.x - self.x_move_on_click
self.command('moveto', mouse_pos/self.winfo_width())
def colorize(self):
print('colorize')
self.slidercolor = 'blue'
self.troughcolor = 'bisque'
self.config(bg=self.troughcolor)
if __name__ == '__main__':
def resize_scrollbar():
root.update_idletasks()
canvas.config(scrollregion=canvas.bbox('all'))
def resize_window():
root.update_idletasks()
page_x = content.winfo_reqwidth()
page_y = content.winfo_reqheight()
root.geometry('{}x{}'.format(page_x, page_y))
root = tk.Tk()
root.config(bg='yellow')
root.grid_columnconfigure(0, weight=1)
root.grid_rowconfigure(0, weight=1)
root.grid_rowconfigure(1, weight=0)
canvas = tk.Canvas(root, bg='tan')
canvas.grid(column=0, row=0, sticky='news')
content = tk.Frame(canvas)
content.grid_columnconfigure(0, weight=1)
content.grid_rowconfigure(0, weight=1)
ysb_canv = Scrollbar(root, width=24, hideable=True, command=canvas.yview)
xsb_canv = Scrollbar(root, height=24, hideable=True, command=canvas.xview, orient='horizontal')
canvas.config(yscrollcommand=ysb_canv.set, xscrollcommand=xsb_canv.set)
frame = tk.Frame(content)
frame.grid_columnconfigure(0, weight=0)
frame.grid_rowconfigure(0, weight=1)
text = tk.Text(frame, bd=0)
ysb_txt = Scrollbar(frame, width=17, command=text.yview)
text.config(yscrollcommand=ysb_txt.set)
space = tk.Frame(content, width=1200, height=500)
ysb_canv.grid(column=1, row=0, sticky='ns')
xsb_canv.grid(column=0, row=1, sticky='ew')
frame.grid(column=0, row=0, sticky='news')
text.grid(column=0, row=0)
ysb_txt.grid(column=1, row=0, sticky='ns')
space.grid(column=0, row=1)
with open(__file__, 'r') as f:
text.insert('end', f.read())
canvas.create_window(0, 0, anchor='nw', window=content)
resize_scrollbar()
resize_window()
root.mainloop()

tkinter canvas won't update object's color after click on the object

I am working on a project which kind of basic paint in python and I'm stuck on this flood fill part, it won't update the color of the object and there's no error so I can't figure out which part is wrong.
from tkinter import *
from tkinter.colorchooser import askcolor
coords = {"x1":0,"y1":0,"x2":0,"y2":0}
lines = []
class Filling():
def __init__(self, main):
self.main = main
self.main.title('Filling')
self.main.geometry("800x620")
btncir=Button(main, text="CIRCLE", fg='black', width=8, command=self.circle)
btncir.place(x=10, y=200)
btnfill=Button(main, text="FILL", fg='black', width=8, command=self.clickfillrec)
btnfill.place(x=10, y=250)
btnclear=Button(main, text="COLOR", fg='black', width=8, command=self.color_choice)
btnclear.place(x=10, y=300)
self.canvas = Canvas(self.main, bg='white', bd=5, relief=RIDGE, height=600, width=700)
self.canvas.place(x=80, y=0)
def circle(self):
self.canvas.bind("<ButtonPress-1>", self.circle_click)
self.canvas.bind("<B1-Motion>", self.drag)
def circle_click(self, e):
coords["x1"] = e.x
coords["y1"] = e.y
lines.append(self.canvas.create_oval(coords["x1"],coords["y1"],coords["x1"],coords["y1"]))
def drag(self, e):
coords["x2"] = e.x
coords["y2"] = e.y
self.canvas.coords(lines[-1], coords["x1"],coords["y1"],coords["x2"],coords["y2"])
def color_choice(self):
self.DEFAULT_COLOR = self.color
self.color = askcolor()
def clickfillrec(self):
self.canvas.bind("<Button-1>", self.ffillrec)
self.canvas.bind("<B1-Motion>", self.nothing)
def nothing(self, event):
pass
def ffillrec(self, event):
item = self.canvas.find_closest(event.x, event.y)
x = event.x
y = event.y
current_color = self.canvas.itemcget(item, 'fill')
if (x > 0) and (self.canvas.itemcget((event.x-1, event.y), 'fill')) == current_color:
self.canvas.itemconfig((event.x-1, event.y), fill = self.color)
if (y > 0) and (self.canvas.itemcget((event.x, event.y-1), 'fill')) == current_color :
self.canvas.itemconfig((event.x, event.y-1), fill = self.color)
if (x < self.canvas.winfo_screenwidth()) and (self.canvas.itemcget((event.x+1, event.y), 'fill')) == current_color :
self.canvas.itemconfig((event.x+1, event.y), fill = self.color)
if (y < self.canvas.winfo_screenheight()) and (self.canvas.itemcget((event.x, event.y+1), 'fill')) == current_color :
self.canvas.itemconfig((event.x, event.y+1), fill = self.color)
main = Tk()
p = Filling(main)
main.mainloop()
I tried main.update() and self.canvas.update_idletasks() but still doesn't work

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'

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