Search code examples
pythontkinterdrag-and-droptkinter-canvas

how to drag entire contents in a rectangle using tkinter python?


On the output screen, there will be a rectangle enclosed in a rectangle if I drag the outer rectangle inside rectangle should also be dragged but only the outer one is getting dragged. How should the entire contents in the outer rectangle get dragged?. In any shape, if the shape is dragged contents in it also should be dragged but contents are not getting dragged only outer shape is getting dragged.

Here is my code.

import tkinter as tk     # python 3
# import Tkinter as tk   # python 2

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, background="bisque")
        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(50, 100, "white")
        self.create_token(200, 100, "black")
        self.create_token1(200,100,"white")
        # add bindings for clicking, dragging and releasing over
        # any object with the "token" tag
        self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
        self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
        self.canvas.tag_bind("token", "<B1-Motion>", self.drag)

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

    def create_token1(self,x,y,color):

        self.canvas.create_rectangle(
            x - 25,
            y - 10,
            x + 25,
            y + 10,
            outline=color,
            fill=color,
            tags=("token",),
        )

    def drag_start(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 drag_stop(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 drag(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

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()


Solution

  • In drag_start() I use outer rectangle to get its region and add tag "drag" to all elements which are fully inside this region

    rect = self.canvas.bbox(self._drag_data["item"])
    
    self.canvas.addtag_enclosed("drag", *rect)
    

    In dra() I move all elements with tag "drag"

    self.canvas.move("drag", delta_x, delta_y)
    

    In drag_stop() I remove tag "drag" from all elements which have tag "drag"

    self.canvas.dtag("drag", "drag")
    

    This way outer rectangle can move also inner rectangle. But if you move inner rectangle then inner rectangle doesn't move. If you want outer rectangle when you move innter rectangle then maybe you should use tag "token"

    self.canvas.move("token", delta_x, delta_y)
    

    import tkinter as tk     # python 3
    # import Tkinter as tk   # python 2
    
    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, background="bisque")
            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(50, 100, "white")
            self.create_token(200, 100, "black")
            self.create_token1(200,100,"white")
            # add bindings for clicking, dragging and releasing over
            # any object with the "token" tag
            self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
            self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
            self.canvas.tag_bind("token", "<B1-Motion>", self.drag)
    
        def create_token(self, x, y, color):
            """Create a token at the given coordinate in the given color"""
            self.canvas.create_rectangle(
                x - 25,
                y - 25,
                x + 25,
                y + 25,
                outline=color,
                fill=color,
                tags=("token",),
            )
    
        def create_token1(self,x,y,color):
    
            self.canvas.create_rectangle(
                x - 25,
                y - 10,
                x + 25,
                y + 10,
                outline=color,
                fill=color,
                tags=("token",),
            )
    
        def drag_start(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]
    
            rect = self.canvas.bbox(self._drag_data["item"])
            self.canvas.addtag_enclosed("drag", *rect)
            print(rect)
    
            self._drag_data["x"] = event.x
            self._drag_data["y"] = event.y
    
        def drag_stop(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
            self.canvas.dtag("drag", "drag")
    
        def drag(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)
            self.canvas.move("drag", 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()