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)
8
  • possible duplicate of Python; class instances Commented Mar 20, 2014 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. Commented Mar 20, 2014 at 11:12
  • Can we please have a look at your __init__ method for class1 Commented Mar 20, 2014 at 11:19
  • You need to provide us with your init method for the object as well :) Commented Mar 20, 2014 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. Commented Mar 20, 2014 at 11:23

1 Answer 1

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()
Sign up to request clarification or add additional context in comments.

5 Comments

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.
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: 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.
@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.
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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.