2

I have a class with some mouse events I made :

class graphic_object(object):

   def mouse_click(self,event):
       #do something

   def mouse_move(self,event):
       #do something

   def mouse_unpressed(self,event):
       #do something

Instances of this class aren't literally graphic objects on the screen, but they have their graphic representation, which is circle-shaped, and as I said, they listen to the mouse events. Both, graphic representation and event handling are managed by tkinter.Canvas object, which is their visual container.
When I make one istance of this class:

graphic1 = graphic_object(a,b,c,d)   # init method takes coordinates of the circle as arguments; a,b,c,d - numbers

Everything works as it should, object responds on the mouse events in desired way. But when I make two instances:

graphic1 = graphic_object(a,b,c,d)
graphic2 = graphic_object(e,f,g,h)

only the last created object responds on the mouse events.

This is the condition where I check if the mouse is over the circle:

if d < self.radius:

where d is distance between mouse position, and the center of the circle, and radius is radius of the circle. In the debugger I see that self.center is always the center of the last created object, so condition is always on the second circle. So, how can I make that both objects respond to the mouse events?

Events handling:

C = Canvas()
C.bind("<Button-1>" ,self.mouse_click)
C.bind("<B1-Motion>",self.mouse_move)
C.bind("<ButtonRelease-1>",self.mouse_unpressed)
roberto
  • 679
  • 1
  • 6
  • 11
  • possible duplicate of [Python; class instances](http://stackoverflow.com/questions/22511090/python-class-instances) – jonrsharpe Mar 20 '14 at 11:10
  • What GUI tool kit are you using? How do you handle mouse events? What are `mouse_click` etc. on your class? Please show more code. – Sven Marnach Mar 20 '14 at 11:12
  • Can we please have a look at your `__init__` method for `class1` – sshashank124 Mar 20 '14 at 11:19
  • You need to provide us with your init method for the object as well :) – Alex Koukoulas Mar 20 '14 at 11:20
  • It's too huge; Basically it contains instance variables(like mentioned `circle, radius`), and it creates the circles; also bindings are within the __init__ methiod. – roberto Mar 20 '14 at 11:23
  • @roberto, Well in that case, can you tell us whether all the variables that you define in the `__init__` method are like this: `self.variable_name = some_value` and not just `variable_name = some_value` – sshashank124 Mar 20 '14 at 11:25
  • can you show us the actual code in `mouse_click`? It sounds like you're simply failing to find the current object. Since you're setting the binding on the canvas rather than the individual objects, you must be doing something to determine which item was clicked on. Whatever you're doing is probably the cause of your problem. – Bryan Oakley Mar 21 '14 at 02:34
  • Yes, I figured out, that the main problem is binding. And how to bind those events to my objects; they don't have their own `bind` method? Since they are on the same canvas, and events are bound on canvas, canvas simply takes only the last object. If they were on two separate canvases, this would probably work. – roberto Mar 21 '14 at 08:20

1 Answers1

3

It appears that in your mouse binding you are relying on a pre-computed global variable (d). This is not how you should implement such bindings. The first thing you should do in the binding is get the current mouse coordinates, and then calculate d.

Your other choice is to put the binding on each canvas object using the tag_bind method of the canvas. See this question for an example: How do I attach event bindings to items on a canvas using Tkinter?

You wrote in a comment to this answer that you are only sometimes getting mouse clicks. There is not enough detail in your code to know what you're doing, but I can assure you that the canvas doesn't normally fail in such a manner.

I can't debug your code since you are only showing bits and pieces, but here's a working example that tries to illustrate the use of tag_bind. I took some liberties with your code. For example, I added a name parameter so I can print out which circle you clicked on. When I test this, every click seems to register on the proper circle.

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.canvas = tk.Canvas(self, width=400,  height=400, 
                                background="bisque")
        self.canvas.pack(fill="both", expand=True)

        graphic1 = GraphicObject(10,10,100,100, name="graphic1")
        graphic2 = GraphicObject(110,110,200,200, name="graphic2")

        graphic1.draw(self.canvas)
        graphic2.draw(self.canvas)

class GraphicObject(object):
    def __init__(self, x0,y0,x1,y1, name=None):
        self.coords = (x0,y0,x1,y1)
        self.name = name

    def draw(self, canvas, outline="black", fill="white"):
        item = canvas.create_oval(self.coords, outline=outline, fill=fill)
        canvas.tag_bind(item, "<1>", self.mouse_click)

    def mouse_click(self, event):
        print "I got a mouse click (%s)" % self.name

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Nope, that's not global variable, it's local variable of event function. I've posted the computation now (see `mouse_click`), and I don't think that the computation is causing problems. – roberto Mar 21 '14 at 13:03
  • This `tag_bind` is only half-responsive, but both objects respond. By half-responsive I mean that only half or less mouse-clicks are registered. – roberto Mar 21 '14 at 14:36
  • @roberto: without seeing your code I can't explain why, but I've never witnessed this with the canvas. Binding on an object is 100% reliable in my experience. – Bryan Oakley Mar 21 '14 at 14:53
  • @roberto: I fail to see how an IDE would affect your python code. I've updated my answer to show some code that tries to mimic what you've described. Perhaps you can learn from it how it works. – Bryan Oakley Mar 21 '14 at 15:13
  • Solved it :-) My code draws another lines inside the oval, so the lines have been covering my objects partially. I used to think of them like they are part of my object:/ Just fixed that, and it works completely. Thank you for this `tag_bind` suggestion – roberto Mar 22 '14 at 10:35