0

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()

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
nirmal6353
  • 17
  • 7
  • when you create rectangele the tkinter gives you object ID - `object_id = create_rectangle(...)` which you can keep in variable or list and use to move rectangle when you move other object. – furas Mar 08 '20 at 08:35
  • Does this answer your question? [board-drawing code to move an oval](https://stackoverflow.com/questions/6740855/board-drawing-code-to-move-an-oval) – stovfl Mar 08 '20 at 10:52
  • How can I use this in an simple 2D image to move the object surrounded by rectangle? @furas – nirmal6353 Mar 08 '20 at 12:43
  • I think you could rather use [find_enclosed(x1, y1, x2, y2)](http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.find_enclosed-method) to find elements which are fully inside rectangle and eventually [find_overlapping(x1, y1, x2, y2)](http://effbot.org/tkinterbook/canvas.htm#Tkinter.Canvas.find_overlapping-method) to find elements which are partially inside rectangle and you can use `canvas.move([list_of_objects], ...)` to move all elements at once. I think I made example for something like this to some old question. – furas Mar 08 '20 at 12:57
  • correctio: it can't be list in `canvas.move([list_of_objects], ...)` but you can move both elements if you use `tag` - in your code `canvas.move("token", ...)` so you can use `find_` to get all elements and assign tag ie. "drag"` and use `canvas.move("drag", ...)` – furas Mar 08 '20 at 13:06
  • Iam able to move the objects inside the rectangle created by me. But , How to move the part of the image which is surrounded by the created rectangle . I could not add any tags to the part of the image since it is not created. @furas – nirmal6353 Mar 08 '20 at 13:23
  • in answer I use `self.canvas.addtag_enclosed("drag", *rect)` to add tag to all items which are inside rectangle - and then I can move them. it doesn't matter if it was created with `create_rectange`, `create_oval`, `create_image` or even `create_window` – furas Mar 08 '20 at 13:31
  • @furas please see my answer to this question. It explains what I'm actually trying to ask you. My rectangle is inside the image and it is surrounding an object in the image which I want to move. Does it related to the backend ? I 'm confused. – nirmal6353 Mar 08 '20 at 19:04
  • you can't move part of image. You need module like `pillow` to duplicate image, cut it off to size of selected rectangle and then you can move this new image which will have olny object (and background) from original image. – furas Mar 09 '20 at 02:23

2 Answers2

1

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()
furas
  • 134,197
  • 12
  • 106
  • 148
0

These minor changes to the previous example prevent irrevocably dragging objects off of the canvas (mostly see the drag function). And thanks for the previous example.

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(self,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 - 20,
            y - 10,
            x + 20,
            y + 5,
            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"]
        
        w=self.winfo_width()
        h=self.winfo_height()
        rect = self.canvas.bbox(self._drag_data["item"])
        if 0:
            ##don't allow any part of items to move off the canvas 
            if rect[3]+delta_y > h: delta_y=0 #stop down
            if rect[1]+delta_y < 0: delta_y=0 #stop up
            if rect[2]+delta_x > w: delta_x=0 #stop right
            if rect[0]+delta_x < 0: delta_x=0 #stop down
        else:
            ##don't allow the last 10 pixels to move off the canvas 
            pixels=10
            if rect[1]+delta_y+pixels > h: delta_y=0 #stop down
            if rect[3]+delta_y-pixels < 0: delta_y=0 #stop up
            if rect[0]+delta_x+pixels > w: delta_x=0 #stop right
            if rect[2]+delta_x-pixels < 0: delta_x=0 #stop down

        # 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()
    root.geometry("800x500")
    #Example(root).pack(fill="both", expand=True)
    Example(root).place(relx=0.1,rely=0.1,relwidth=0.8,relheight=0.8)
    root.mainloop()
Elden Crom
  • 21
  • 3
  • For something like this, it would be better to edit the other answer or add a suggestion to it as a comment, since this is less of a unique answer and more of an add-on to another answer. It is understandable having limited options due do reputation level, but in cases like this it is better just to let be. If the OP wanted to keep the rectangles in the canvas, and didn't know how, they would add a comment or post another question about it. – Sylvester Kruin Sep 25 '21 at 21:33