Home > Back-end >  Annotate draggable Circle
Annotate draggable Circle

Time:08-16

I am looking to annotate a draggable circle with a number. The draggeable point is created from this class:

class DraggablePoint:
    lock = None #only one can be animated at a time
    def __init__(self, point):
        self.point = point
        self.press = None
        self.background = None

    def connect(self):
        'connect to all the events we need'
        self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)

    def on_press(self, event):
        if event.inaxes != self.point.axes: return
        if DraggablePoint.lock is not None: return
        contains, attrd = self.point.contains(event)
        if not contains: return
        self.press = (self.point.center), event.xdata, event.ydata
        DraggablePoint.lock = self

        # draw everything but the selected point and store the pixel buffer
        canvas = self.point.figure.canvas
        axes = self.point.axes
        self.point.set_animated(True)
        canvas.draw()
        self.background = canvas.copy_from_bbox(self.point.axes.bbox)

        # now redraw just the rectangle
        axes.draw_artist(self.point)

        # and blit just the redrawn area
        canvas.blit(axes.bbox)

    def on_motion(self, event):
        if DraggablePoint.lock is not self:
            return
        if event.inaxes != self.point.axes: return
        self.point.center, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress
        self.point.center = (self.point.center[0] dx, self.point.center[1] dy)

        canvas = self.point.figure.canvas
        axes = self.point.axes
        # restore the background region
        canvas.restore_region(self.background)

        # redraw just the current rectangle
        axes.draw_artist(self.point)

        # blit just the redrawn area
        canvas.blit(axes.bbox)

    def on_release(self, event):
        'on release we reset the press data'
        if DraggablePoint.lock is not self:
            return

        self.press = None
        DraggablePoint.lock = None

        # turn off the rect animation property and reset the background
        self.point.set_animated(False)
        self.background = None

        # redraw the full figure
        self.point.figure.canvas.draw()

    def disconnect(self):
        'disconnect all the stored connection ids'
        self.point.figure.canvas.mpl_disconnect(self.cidpress)
        self.point.figure.canvas.mpl_disconnect(self.cidrelease)
        self.point.figure.canvas.mpl_disconnect(self.cidmotion)

I am plotting with this (circles.append the last loop is the interesting code for this problem):

    drs = []
    circles = []
    for team, color, sec_color in zip([hometeam.loc[frame], awayteam.loc[frame]], team_colors, sec_colors):

        x_columns = [c for c in team.keys() if
                     c[-2:].lower() == '_x' and c != 'ball_x']  # column header for player x positions
        y_columns = [c for c in team.keys() if
                     c[-2:].lower() == '_y' and c != 'ball_y']  # column header for player y positions
        for x, y in zip(team[x_columns], team[y_columns]):
            #if ctr == 0:
            circles.append(patches.Circle((x, y), 1.4, fc=color, edgecolor=sec_color, linewidth=1.2, zorder=2))
    for circ in circles:
        ax.add_patch(circ)
        dr = DraggablePoint(circ)
        dr.connect()
        drs.append(dr)
    plt.show()

I can plot text at the circles center, however once I drag a circle, the text stays at the original position. Is there a way to add text to the circles, which would move alongside the circles when dragged?

CodePudding user response:

Sure. Add a label, record the relative position to the circle, and update its position on motion.

import matplotlib.pyplot as plt

class DraggablePoint:
    ...

class LabelledDraggablePoint(DraggablePoint):

    def __init__(self, point, label):
        super().__init__(point)
        self.label = label
        x1, y1 = self.label.get_position()
        x2, y2 = self.point.center
        self.label_offset = (x1 - x2, y1 - y2)

    def on_motion(self, event):
        super().on_motion(event)
        self.label.set_position(self.point.center   self.label_offset)


if __name__ == '__main__':

    fig, ax = plt.subplots()
    point = plt.Circle((0.5, 0.5), 0.1)
    ax.add_patch(point)
    ax.set_aspect('equal')

    label = ax.text(0.5, 0.5, 'Lorem ipsum', ha='center', va='center')

    instance = LabelledDraggablePoint(point, label)
    instance.connect()

    plt.show()

On a different note: the blitting is nausea inducing. You will get much smoother animations without it. I would cut all of that out.

  • Related