2

Currently I have a

canvas.tag_bind(tag, "<ButtonRelease-1>", func=move_point)

statement for moving my points, which deletes old ones and creates new ones where the user released button-1. This works, but it feels janky and button-1 is used up and can't be used for making connections between points for example. I have tried using b1-motion but it would just move the center to where I first move the mouse.

My question is how can I make a clean animation (like one on windows file drag-and-drop)?

Below is a picture of a graph where i moved a couple of points around.

Graph

loyk
  • 47
  • 5
  • You could add a bind that detects when the button is clicked, and then, while it's not been released, update the x,y coords of the element clicked until the button release event is triggered. – Peter May 16 '20 at 11:10
  • Relevant [how-can-i-create-a-drag-and-drop-interface](https://stackoverflow.com/questions/44887576) – stovfl May 16 '20 at 16:56

1 Answers1

5

The following example will get you started:

  • shift click on the canvas to create a circle.
  • click on a circle to select a circle - it will follow the mouse movement until you release the button.
  • releasing the mouse button will drop the circle at the location of the mouse.
  • This last action will reset the state as it was at the beginning.

You will need a bit more machinery to create links between circles, and move them around - maybe even a state machine to handle the transitions - but this is a start.

enter image description here

import tkinter as tk


class App(tk.Tk):
    radius = 20

    def __init__(self):
        super().__init__()
        self.canvas = tk.Canvas(self, width=500, height=500, bg='beige')
        self.canvas.pack()

        self.canvas.bind('<1>', self.select_circle)
        self.canvas.bind('<Shift-1>', self.make_circle)

        self.selected = None

    def make_circle(self, event):
        x, y, r = event.x, event.y, self.radius
        self.canvas.create_oval(x-r, y-r, x+r, y+r, outline='black', fill='white')

    def select_circle(self, event):
        self.canvas.bind('<Motion>', self.move_circle)
        self.canvas.bind('<ButtonRelease-1>', self.deselect)

        self.canvas.addtag_withtag('selected', tk.CURRENT)

    def move_circle(self, event):
        x, y, r = event.x, event.y, self.radius
        self.canvas.coords('selected', x-r, y-r, x+r, y+r)

    def deselect(self, event):
        self.canvas.dtag('selected')    # removes the 'selected' tag
        self.canvas.unbind('<Motion>')
        self.canvas.bind('<Shift-1>', self.make_circle)


if __name__ == '__main__':

    App().mainloop()
Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80
  • 1
    How does it know what circle it selected? With the tk.CURRENT? – loyk May 16 '20 at 16:52
  • Also I didn't realise that you could do just , they don't mention it on effbot.org, Thank you so much, tkinter and events are so cool, but kinda hard to visualize when coding. – loyk May 16 '20 at 17:10
  • 1
    Yes, `tk.CURRENT` is a tag that tkinter assigns to the object currently directly under the mouse cursor. – Reblochon Masque May 17 '20 at 00:07