1

I am still fairly new to tkinter and I am trying to create a network simulator. I have made a simpler version of my program without all of the extra network stuff. I have been stuck on a problem for quite a while now. I can't work out how to make it so when I try and move one of the circles, it moves the text with it. I had an idea to delete the text when I click down so I can then move the object and recreate the text when I drop the object but I am not sure how to do this. Any other ideas are certainly welcome.

This is my library:

from Tkinter import *
import time

#Window class for making different windows

class Window:

#Constructor
def __init__(self, window, colour="black", width=600, height=400):
    #Set variables
    self.width = width
    self.height = height
    self.colour = colour

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

    #Create canvas
    self.canvas = Canvas(window, bg=self.colour, height=self.height, width=self.width)
    self.canvas.pack()

    #Add bindings for clicking, dragging and releasing over any object with the "circledrag" tag
    self.canvas.tag_bind("circledrag", "<ButtonPress-1>", self.OnCircleButtonPress)
    self.canvas.tag_bind("circledrag", "<ButtonRelease-1>", self.OnCircleButtonRelease)
    self.canvas.tag_bind("circledrag", "<B1-Motion>", self.OnCircleMotion)

#This is used to draw particle objects on the canvas, notice the tag that has been added as an attribute
def _create_circle(self, xcoord, ycoord, color):
    self.canvas.create_oval(xcoord-25, ycoord-25, xcoord+25, ycoord+25, outline=color, fill=color, tags = "circledrag")

#This is used to draw text on top of the object on the canvas
def _create_text(self, xcoord, ycoord, text):
    self.canvas.create_text(xcoord, ycoord, text = text, tags = ("circledrag", "text"))

#This uses the find_closest method to get store the x and y positions of the nearest item into the dictionary
def OnCircleButtonPress(self, event):
    #print self.canvas.find_withtag("Current")
    self.canvas.delete("text")

    '''Begin 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

#This clears the dictionary once the mouse button has been released
def OnCircleButtonRelease(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

#This moves the item as it is being dragged around the screen
def OnCircleMotion(self, event):
    '''Handle dragging of an object'''
    # compute how much this object 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

class DragCircle:
#Constructor
def __init__(self, window, width=100, height=100, colour="red", text = "test"):
    self.window = window
    self.circle = self.window._create_circle(width, height, colour)
    self.circle_text = self.window._create_text(width, height, text)

and this is the main program:

from Tkinter import *
import my_module2

#Make a window from my own class
window = Tk()
window.title("Drag & Drop")

#Create an instance of the window class
main_window = my_module2.Window(window)

#Create a circle object from the DragCircle class
circle = my_module2.DragCircle(main_window)
circle2 = my_module2.DragCircle(main_window, 200, 200, "green", "hello")

#Start the animation loop
window.mainloop()

I would like to know how to move two objects at once.

AaronH
  • 35
  • 2
  • 5
  • Do you have a question? – jonrsharpe Mar 12 '15 at 16:30
  • I need to know how to move more than one object at once. I have a bit of an idea on how to do it like I said in the post but I don't know how to implement it. – AaronH Mar 12 '15 at 16:32
  • That's not really a question. Please see http://stackoverflow.com/help/how-to-ask. Show a minimal example of what you've tried, and be precise about the problem with it. – jonrsharpe Mar 12 '15 at 16:32
  • Alright... How do I move multiple objects at once on click? You can see what I have done in the code from the post. – AaronH Mar 12 '15 at 16:36
  • your indentation is incorrect in your code. – Bryan Oakley Mar 12 '15 at 17:11
  • there is a solution: https://stackoverflow.com/questions/54226164/how-do-i-move-multiple-objects-at-once-on-a-tkinter-canvas – 笑先生 Jan 20 '22 at 01:42

3 Answers3

2

Each time you create a circle/text pair, create a unique tag and associate the tag with both the circle and the text. You can then use this unique tag to move both objects at once.

For example, modify your DragCircle constructor to look like this:

class DragCircle:
    #Constructor
    def __init__(self, window, width=100, height=100, colour="red", text = "test"):
        self.window = window
        tag = "circle-%d" % id(self)
        self.circle = self.window._create_circle(width, height, colour, tag)
        self.circle_text = self.window._create_text(width, height, text, tag)

Next, modify the _create_circle and _create_text functions to accept the tag:

def _create_circle(self, xcoord, ycoord, color, tag):
    id_=self.canvas.create_oval(xcoord-25, ycoord-25, xcoord+25, ycoord+25, outline=color, fill=color, tags = ("circledrag", tag))

#This is used to draw text on top of the object on the canvas
def _create_text(self, xcoord, ycoord, text, tag):
    self.canvas.create_text(xcoord, ycoord, text = text, tags = ("circledrag", "text", tag))

Finally, modify your OnCircleButtonPress to get the tag and use it for moving the objects around:

def OnCircleButtonPress(self, event):
    ...
    item = self.canvas.find_closest(event.x, event.y)[0]
    tags = self.canvas.gettags(item)
    for tag in tags:
        if tag.startswith("circle-"):
            break
    self._drag_data["item"] = tag
    ...
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
1

My proposal which may not be the best

In the constructor you define an association table.

self.assoc = {}

add the method:

def _create_circlewt(self, xcoord, ycoord, color, text):
      tc = self.canvas.create_oval(xcoord-25, ycoord-25, xcoord+25, ycoord+25, outline=color, fill=color, tags = "circledrag")
      tx = self.canvas.create_text(xcoord, ycoord, text = text)
      self.assoc[tc] = tx

in the OnCircleMotion: add the line

self.canvas.move(self.assoc[self._drag_data["item"]], delta_x, delta_y)

just after the other move line

in DragCircle class constructor comment your definition and add the call to _create_circlewt

self.circle_w_text = self.window._create_circlewt(width, height, colour,text)

it works .. I let you enhance the canvas move to enable multiple objects association like

self.assoc[CircleIndex].append(otherobjectIndex)
Jerome Vacher
  • 314
  • 1
  • 7
0

There is a solution from How do I move multiple objects at once on a Tkinter canvas?

import tkinter as tk
import time

root = tk.Tk()

canvas = tk.Canvas(root)
canvas.pack()

canvas.create_oval(100, 105, 150, 150, tags="Bob", fill='light blue', outline='green')
canvas.create_oval(200, 205, 150, 150, tags="Bob", fill='light blue', outline='green')

for _ in range(50):
    canvas.move("Bob", 5, 0)
    canvas.update()
    time.sleep(0.05)

root.mainloop()
笑先生
  • 31
  • 5