3

I am trying to create a program that changes the object colour on click from white to black or from white to black depending of the previous colour. I would want the program change the colour only if the object is a rectangle. How can I make the this happen?

Here is my code:

import tkinter as tk

root = tk.Tk()

cv = tk.Canvas(root, height=800, width=800)
cv.pack()

def onclick(event):
    item = cv.find_closest(event.x, event.y)

    current_color = cv.itemcget(item, 'fill')

    if current_color == 'black':
        cv.itemconfig(item, fill='white')

    else:
        cv.itemconfig(item, fill='black')


cv.bind('<Button-1>', onclick)

cv.create_line(50, 50, 60, 60, width=2)

cv. create_rectangle(80, 80, 100, 100)

root.mainloop()

In this code the program changes the fill colour for any object. I would want it to change it only for rectangles.

Thanks for the help.

Leero11
  • 365
  • 2
  • 4
  • 17

4 Answers4

9

Here are three common solutions to this problem:

Using the item type

You can ask the canvas for the type of the object:

item_type = cv.type(item)
if item_type == "rectangle":
    # this item is a rectangle
else:
    # this item is NOT a rectangle

Using tags

Another solution is to give each item one or more tags. You can then query the tags for the current item.

First, include one or more tags on the items you want to be clickable:

cv. create_rectangle(80, 80, 100, 100, tags=("clickable",))

Next, check for the tags on the item you're curious about, and check to see if your tag is in the set of tags for that item:

tags = cv.itemcget(item, "tags")
if "clickable" in tags:
    # this item has the "clickable" tag
else:
    # this item does NOT have the "clickable" tag

Create bindings on tags

A third option is to attach the bindings to the tags rather than the canvas as a whole. When you do this, your function will only be called when you click on item with the given tag, eliminating the need to do any sort of runtime check:

cv.tag_bind("clickable", "<1>", onclick)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
4

The solution of BPL is fine. Here is another solution that doesn't require any global variable. There's a system of tags in Tkinter which could be compared with "class" from HTML/CSS. It enables us to associate a list of tags to a graphic object. See item specifiers.

Here is how it would be:

import tkinter as tk


def onclick(event):
    global rectangles
    item = cv.find_closest(event.x, event.y)
    if 'rect' in cv.gettags(item):
        current_color = cv.itemcget(item, 'fill')

        if current_color == 'black':
            cv.itemconfig(item, fill='white')
        else:
            cv.itemconfig(item, fill='black')

rectangles = []

root = tk.Tk()
cv = tk.Canvas(root, height=800, width=800)
cv.pack()
cv.bind('<Button-1>', onclick)

id_a = cv.create_line(50, 50, 60, 60, width=2)
id_b = cv.create_rectangle(80, 80, 100, 100, tags=('rect'))

root.mainloop()

I think it's a bit more object oriented in some way. ;)

Louis Durand
  • 187
  • 9
2

What about this solution:

import tkinter as tk


def onclick(event):
    global rectangles
    item = cv.find_closest(event.x, event.y)
    if item[0] in rectangles:
        current_color = cv.itemcget(item, 'fill')

        if current_color == 'black':
            cv.itemconfig(item, fill='white')
        else:
            cv.itemconfig(item, fill='black')

rectangles = []

root = tk.Tk()
cv = tk.Canvas(root, height=800, width=800)
cv.pack()
cv.bind('<Button-1>', onclick)

id_a = cv.create_line(50, 50, 60, 60, width=2)
id_b = cv.create_rectangle(80, 80, 100, 100)
rectangles.append(id_b)

root.mainloop()

The main idea of this is you add the items id you want to change color to a global array (global variables are discouraged though). Then when you click with the mouse, the itemcget function will give you the id from the closest item, so you need to check whether this id is inside of the array of your rectangle items.

BPL
  • 9,632
  • 9
  • 59
  • 117
  • Your answer would be better if you described what you did differently. Otherwise the reader is forced to compare your code line-by-line and maybe character-by-character to see what changes you made. – Bryan Oakley Aug 16 '16 at 19:16
  • @BryanOakley I thought showing the code would be enough but you're right, i've made few annotations to make it clearer – BPL Aug 16 '16 at 19:40
0

Based on this accepted answer you need to tag_bind your rectangles. Also, you should be assigning IDs to each object you create. If you need to do this repeatedly, you can wrap the process in a loop or functions, but the base theory won't change. GL with tkinter!

Community
  • 1
  • 1
dblclik
  • 406
  • 2
  • 8