On Sun, Nov 16, 2008 at 9:47 PM, <[EMAIL PROTECTED]> wrote: > Yes, this works great, thanks very much. I don't want to just copy your code > though, I'm trying to understand what I'm doing wrong, so I tried modifying > my code to more or less mirror your technique. I am now getting an unknown > error message that I can't figure out, and I suspect that I am "thinking > about things" incorrectly. I've stripped things down, even deleting the > onDrag method for now, and still can't identify what I'm doing wrong. Here's > the stripped down code; now I'm only trying to draw the box and not worrying > about anything else for now: > > from Tkinter import * > > WINDOWWIDTH=500 > WINDOWHEIGHT=500 > > > class App: def __init__ (self,master): > self.x=10 > self.y=10 > self.display = Frame(master, width=WINDOWWIDTH, > height=WINDOWHEIGHT) > self.canvas=Canvas(self.display, bg='blue') > #Draw boxes, set up screen layout > > self.box1=self.canvas.drawBox() > self.display.pack() > self.canvas.pack() > def drawBox(self): > newbox=self.create_rectangle(self.x, self.y, self.x+70, self.y+70, > width=5, fill='red') > self.tag_bind(newbox, '<B1-Motion>', self.onDrag) > return newbox > root= Tk() > app=App(root) > root.mainloop() > > My error is "AttributeError: Canvas instance has no attribute 'drawBox'".
drawBox is a method of App, not Canvas, so you have to call it as self.drawBox() > I've tried deleting the return statement and just calling > "self.canvas.drawBox with the same result. If I deleted the drawBox function > and just put the statements in "init", it works correctly. I thought that by > calling "self.canvas.drawBox()", I pass "self.canvas" as self, so that in > effect I am calling "newbox=self.canvas.create_rectangle(self.x, self.y, > self.x+70, self.y+70, width=5, fill='red')". Clearly this is inaccurate > because if I write "newbox=self.create_rectangle(self.x, self.y, self.x+70, > self.y+70, width=5, fill='red')" in the init, it works correctly. Where am I > thinking about this incorrectly? > You are never passing self.canvas as self, you may think you are but are not. The easiest form of playing with self as a Tkinter.Canvas is subclassing this class App from Tkinter.Canvas (notice that is what I did in the previous email). It is hard to follow the indentation there too, so if possible put it in a pastebin next time. There are several things wrong, so you might consider reading the python tutorial. > Thanks again, and sorry for all the questions - I am obviously new to this > and it has been driving me crazy. > > > > Quoting Guilherme Polo <[EMAIL PROTECTED]>: > >> On Sun, Nov 16, 2008 at 4:24 PM, <[EMAIL PROTECTED]> wrote: >>> >>> Quoting Guilherme Polo <[EMAIL PROTECTED]>: >>> >>>> On Sun, Nov 16, 2008 at 3:47 PM, <[EMAIL PROTECTED]> wrote: >>>>> >>>>> Hello everyone, >>>>> >>>>> I am trying to create bindable shapes with Tkinter that are dragged >>>>> when >>>>> the >>>>> user drags with the mouse. Here is an example (some code edited out for >>>>> simplicity). This is all within an "App" class: >>>>> >>>>> self.canvas=Canvas(self.display, bg='blue') >>>>> self.canvas.bind('<B1-Motion>', self.onDrag) >>>>> self.x=10 >>>>> self.y=10 >>>>> #Draw ball, set up screen layout >>>>> self.box1=self.drawBox(self.canvas) >>>>> self.canvas.pack() >>>>> def drawBox(self,master): >>>>> newbox=master.create_rectangle(self.x, self.y, self.x+70, >>>>> self.y+70, >>>>> width=5, fill='red') >>>>> return newbox >>>>> def onDrag(self,event): >>>>> >>>>> self.canvas.move(self.box1,event.x-self.x,event.y-self.y) >>>>> self.x=event.x >>>>> self.y=event.y >>>>> >>>>> >>>>> >>>>> >>>>> This works as written, but it is not good form because it can only be >>>>> used >>>>> with box1. <B1-Motion> is bound to the canvas rather that the box, and >>>>> "self.box1" is explicitly stated in the onDrag method. Therefore, if I >>>>> wanted to create 10 boxes (or even 2) I would have to write a new >>>>> onDrag >>>>> method for each one. Also, if I want the box to only drag when the user >>>>> clicks INSIDE the box, I would have to create a new "dimensions" list >>>>> for >>>>> each new box, and then use an if statement to see if the mouse is >>>>> inside >>>>> the >>>>> box, etc. I actually tried doing this and it makes the program run >>>>> extremely >>>>> slow with only one box, so I don't think this is a good solution. It >>>>> seems >>>>> logical to bind <B1-Motion> to box1 rather than the canvas, and that >>>>> way >>>>> the >>>>> box will react only if the mouse is inside the box, and I could create >>>>> multiple boxes, each with its own binding to <B1-Motion>. When I try to >>>>> do >>>>> this, though, using something like this: >>>>> >>>>> newbox=master.create_rectangle(self.x, self.y, self.x+70, self.y+70, >>>>> width=5, fill='red') >>>>> newbox.bind('<B1-Motion>', self.onDrag) >>>>> >>>>> it gives me an error saying that I can't bind this method to this >>>>> instance. >>>> >>>> Didn't you mean an AttributeError ? The id returned my >>>> create_rectangle surely doesn't have a bind method. >>>> >>>>> How can I make this work? >>>> >>>> That is why Canvas has a method called tag_bind, to bind an item in the >>>> canvas: >>>> >>>> master.tag_bind(newbox, '<B1-Motion>', self.on_drag) >>>> >>>> >>>>> >>>>> Thanks! >>>>> >>>> >>>> -- >>>> -- Guilherme H. Polo Goncalves >>>> >>> >>> >>> Thanks. Yes, you're right, the message is "AttributeError: App instance >>> has >>> no attribute 'on_drag'" However, when I make this change I get no >>> response >>> at all when the program is run. >> >> I suspect there are other things wrong in your app then. >> >>> My drawBox function now looks like this: >>> >>> def drawBox(self,master): >>> newbox=master.create_rectangle(self.x, self.y, self.x+70, self.y+70, >>> width=5, fill='red') >>> master.tag_bind(newbox, '<B1-Motion>', self.onDrag) >>> >> >> Don't you want a ButtonPress-1 there too ? >> >>> >>> >>> and my onDrag function looks like this: >>> >>> def onDrag(self,event): >>> >>> self.canvas.move(self,event.x-self.x,event.y-self.y) >>> self.x=event.x >>> self.y=event.y >>> This looks like it should work: it passed newbox as self and the >>> coordinates >>> as event to the onDrag function, but I get no response at all. >>> >> >> Uhm, no, self is the class instance so it is surely not the newbox item. >> >> Well, I've done a sample here based on what you are after, check if >> this works for you: >> >> import Tkinter >> >> class Boxes(Tkinter.Canvas): >> def __init__(self, master=None, **kw): >> Tkinter.Canvas.__init__(self, master, **kw) >> >> def create_box(self, x, y, size, **kw): >> box = self.create_rectangle(x, y, x + size, y + size, **kw) >> self.tag_bind(box, '<ButtonPress-1>', self.on_click) >> self.tag_bind(box, '<B1-Motion>', self.on_drag) >> >> def on_click(self, event): >> self.curr_x = event.x >> self.curr_y = event.y >> >> def on_drag(self, event): >> x, y, _, _ = self.coords('current') >> self.move('current', (event.x - self.curr_x), (event.y - >> self.curr_y)) >> self.curr_x = event.x >> self.curr_y = event.y >> >> >> app = Boxes(bg='blue') >> app.create_box(10, 10, 70, width=5, fill='red') >> app.create_box(20, 20, 70, width=5, fill='yellow') >> app.pack() >> app.mainloop() >> >> >> -- >> -- Guilherme H. Polo Goncalves >> > > > -- -- Guilherme H. Polo Goncalves _______________________________________________ Tkinter-discuss mailing list Tkinter-discuss@python.org http://mail.python.org/mailman/listinfo/tkinter-discuss