0

I'm trying to modify an example script that was posted here:

board-drawing code to move an oval

The original script allowed you to click and drag objects around the canvas. I changed it so that a button would move the first object, but I get a button error: TypeError: button_move() takes exactly 2 arguments (1 given) I don't understand what the second argument should be.

Here is the modified script:

import Tkinter as tk

class Example(tk.Frame):
    '''Illustrate how to drag items on a Tkinter canvas'''

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        # create a canvas
        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

        # this data is used to keep track of an 
        # item being dragged
        self._drag_data = {"x": 0, "y": 0, "item": None}

        # create a couple of movable objects
        self._create_token((100, 100), "white")
        self._create_token((200, 100), "black")

        # add bindings for clicking, dragging and releasing over
        # any object with the "token" tag
        self.canvas.tag_bind("token", "<ButtonPress-1>", self.on_token_press)
        self.canvas.tag_bind("token", "<ButtonRelease-1>", self.on_token_release)
        self.canvas.tag_bind("token", "<B1-Motion>", self.on_token_motion)
#-----------------------------------------------------------------------------
        self.canvas.button2 = tk.Button(self.canvas, text="Button Test",
                                 command=self.button_move)
        self.canvas.button2.config(bg="cyan",fg="black")
        self.canvas.button2.pack(side='top')

        self.canvas.tag_bind("button2", "<ButtonPress-1>", self.button_move)
#-----------------------------------------------------------------------------
    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-25, y-25, x+25, y+25, 
                                outline=color, fill=color, tags="token")

    def on_token_press(self, event):
        '''Begining drag of an object'''
        # record the item and its location
        self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def on_token_release(self, event):
        '''End drag of an object'''
        # reset the drag information
        self._drag_data["item"] = None
        self._drag_data["x"] = 0
        self._drag_data["y"] = 0

    def on_token_motion(self, event):
        '''Handle dragging of an object'''
        # compute how much the mouse has moved
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        # move the object the appropriate amount
        self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y
#-----------------------------------------------------------------------------
    def button_move(self, event):
        '''Handle dragging of an object'''
        # set movement amount
        delta_x = 15
        delta_y = 15
        # move the object the appropriate amount
        #self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        self.canvas.move(self._drag_data[1], delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y
#-----------------------------------------------------------------------------
if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Community
  • 1
  • 1
R. Wayne
  • 417
  • 1
  • 9
  • 17

3 Answers3

1

command= executes function without event but you assign function which expects event - command=self.button_move -> def button_move(self, event)

But bind executes the same function with event so you need this function with event.

Solution: use default value for event, ie None

def button_move(self, event=None):

But you can't use event.x, event.y in this function if you what to execute with command=

furas
  • 134,197
  • 12
  • 106
  • 148
  • Adding "event=None" is the key. I also commented out "self._drag_data["x"] = event.x" and "self._drag_data["y"] = event.y" in the "button_move" function. – R. Wayne Feb 01 '17 at 00:28
0

Here is the updated code using furas's advice:

import Tkinter as tk

class Example(tk.Frame):
    '''Illustrate how to drag items on a Tkinter canvas'''

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        # create a canvas
        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

        # this data is used to keep track of an 
        # item being dragged
        self._drag_data = {"x": 0, "y": 0, "item": None}

        # create a couple of movable objects
        self._create_token((100, 100), "white")
        self._create_token((200, 100), "black")

        # add bindings for clicking, dragging and releasing over
        # any object with the "token" tag
        self.canvas.tag_bind("token", "<ButtonPress-1>", self.on_token_press)
        self.canvas.tag_bind("token", "<ButtonRelease-1>", self.on_token_release)
        self.canvas.tag_bind("token", "<B1-Motion>", self.on_token_motion)
#-----------------------------------------------------------------------------
        self.canvas.button2 = tk.Button(self.canvas, text="Button Test",
                                 command=self.button_move)
        self.canvas.button2.config(bg="cyan",fg="black")
        self.canvas.button2.pack(side='top')

        self.canvas.tag_bind("button2", "<ButtonPress-1>", self.button_move)
#-----------------------------------------------------------------------------
    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-25, y-25, x+25, y+25, 
                                outline=color, fill=color, tags="token")

    def on_token_press(self, event):
        '''Begining drag of an object'''
        # record the item and its location
        self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def on_token_release(self, event):
        '''End drag of an object'''
        # reset the drag information
        self._drag_data["item"] = None
        self._drag_data["x"] = 0
        self._drag_data["y"] = 0

    def on_token_motion(self, event):
        '''Handle dragging of an object'''
        # compute how much the mouse has moved
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        # move the object the appropriate amount
        self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y
#-----------------------------------------------------------------------------
    def button_move(self, event=None):
        '''Handle dragging of an object'''
        # set movement amount
        delta_x = 15
        delta_y = 15
        # move the object the appropriate amount
        #self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        #self.canvas.move(self._drag_data[1], delta_x, delta_y)
        self.canvas.move(1, delta_x, delta_y) # moves item 1, when cursor is over token
        # record the new position
        #self._drag_data["x"] = event.x
        #self._drag_data["y"] = event.y
#-----------------------------------------------------------------------------
if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
R. Wayne
  • 417
  • 1
  • 9
  • 17
0

When you configure a callback such as button_move() as the button's command, the only argument given to it when it's called is self, just as if you called it like this:

self.button_move()

But because button_move() also expects an event argument, you get the following error:

TypeError: button_move() takes exactly 2 arguments (1 given).

So removing the event parameter from button_move() definition would be the first step.

Also, since event wouldn't be in button_move() scope, you should remove these two lines:

self._drag_data["x"] = event.x
self._drag_data["y"] = event.y

Finally, you need the id from the canvas object you want to move in order for the self.canvas.move() line to work. I suggest you remove the line

self._drag_data["item"] = None

from on_token_release(), so that the id from the canvas object that was last clicked is saved in self._drag_data["item"]. Then all you need to do is check if self._drag_data["item"] is not None before trying to call canvas.move().

Applying all these changes yields the following code:

import Tkinter as tk

class Example(tk.Frame):
    '''Illustrate how to drag items on a Tkinter canvas'''

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        # create a canvas
        self.canvas = tk.Canvas(width=400, height=400)
        self.canvas.pack(fill="both", expand=True)

        # this data is used to keep track of an
        # item being dragged
        self._drag_data = {"x": 0, "y": 0, "item": None}

        # create a couple of movable objects
        self._create_token((100, 100), "white")
        self._create_token((200, 100), "black")

        # add bindings for clicking, dragging and releasing over
        # any object with the "token" tag
        self.canvas.tag_bind("token", "<ButtonPress-1>", self.on_token_press)
        self.canvas.tag_bind("token", "<ButtonRelease-1>", self.on_token_release)
        self.canvas.tag_bind("token", "<B1-Motion>", self.on_token_motion)

        self.canvas.button2 = tk.Button(self.canvas, text="Button Test",
                                 command=self.button_move)
        self.canvas.button2.config(bg="cyan",fg="black")
        self.canvas.button2.pack(side='top')

        self.canvas.tag_bind("button2", "<ButtonPress-1>", self.button_move)

    def _create_token(self, coord, color):
        '''Create a token at the given coordinate in the given color'''
        (x,y) = coord
        self.canvas.create_oval(x-25, y-25, x+25, y+25,
                                outline=color, fill=color, tags="token")

    def on_token_press(self, event):
        '''Begining drag of an object'''
        # record the item and its location
        self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def on_token_release(self, event):
        '''End drag of an object'''
        # reset the drag information
        self._drag_data["x"] = 0
        self._drag_data["y"] = 0

    def on_token_motion(self, event):
        '''Handle dragging of an object'''
        # compute how much the mouse has moved
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        # move the object the appropriate amount
        self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def button_move(self):
        if not self._drag_data["item"]:
            return

        '''Handle dragging of an object'''
        # set movement amount
        delta_x = 15
        delta_y = 15
        # move the object the appropriate amount
        #self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        self.canvas.move(self._drag_data["item"], delta_x, delta_y)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
bzrr
  • 1,490
  • 3
  • 20
  • 39