Python Forum

Full Version: Update Point Coordinates from Mouse Event in a Plot
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hi everyone !

This is my first object-oriented project, thus some parts could be wrote in a better way (advices would be appreciate.. Big Grin )

I manage do build a code that allows points to be drag verticaly with mouse event.

https://ibb.co/CVvH0ww
https://ibb.co/P1MCJCp

I am able to retrieve the new coordinates, but only within the DraggablePoint Class.

Now I want the new coordinates from the drag point to be update in a list, in real time.
I mean, whenever the plot change with a mouse event, the list value should be update.

The main issue is that I don't know how to access and retrieve coordinates values outside of the event class

I tried to store the new coordinates in an attributes of the instance DraggablePoint, but didn't manage to get what I want..

Once again, I am quite new in this object-oriented world
Could someone give me advices ?

Thanks a lot !

The DraggablePoint class is the Following :

# drag.py
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.lines import Line2D
import PySide2.QtCore as QtCore


class DraggablePoint:

    lock = None #  only one can be animated at a time

    def __init__(self, parent, x=0.1, y=0.1, size=0.1):

        global dragx, dragy

        self.parent = parent
        self.point = patches.Ellipse((x, y), size, size , fc='r', alpha=0.5, edgecolor='r')
        self.x = x
        self.y = y
        parent.fig.axes[0].add_patch(self.point)
        self.press = None
        self.background = None
        self.connect()

        # if another point already exist we draw a line
        if self.parent.list_points:
            line_x = [self.parent.list_points[-1].x, self.x]
            line_y = [self.parent.list_points[-1].y, self.y]

            self.line = Line2D(line_x, line_y, color='r', alpha=0.5)
            parent.fig.axes[0].add_line(self.line)

        self.coordinates = []


    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 rectangle and store the pixel buffer
        canvas = self.point.figure.canvas
        axes = self.point.axes
        self.point.set_animated(True)

        # TODO also the line of some other points needs to be released
        point_number =  self.parent.list_points.index(self)

        if self == self.parent.list_points[0]:
            self.parent.list_points[1].line.set_animated(True)
        elif self == self.parent.list_points[-1]:
            self.line.set_animated(True)
        else:
            self.line.set_animated(True)
            self.parent.list_points[point_number+1].line.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

        dy = event.ydata - ypress
        self.point.center = (self.point.center[0], 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)

        point_number =  self.parent.list_points.index(self)
        self.x = self.point.center[0]
        self.y = self.point.center[1]

        # We check if the point is A or B
        if self == self.parent.list_points[0]:
            # or we draw the other line of the point
            self.parent.list_points[1].line.set_animated(True)
            axes.draw_artist(self.parent.list_points[1].line)

        elif self == self.parent.list_points[-1]:
            # we draw the line of the point
            axes.draw_artist(self.line)

        else:
            # we draw the line of the point
            axes.draw_artist(self.line)
            #self.parent.list_points[point_number+1].line.set_animated(True)
            axes.draw_artist(self.parent.list_points[point_number+1].line)




        if self == self.parent.list_points[0]:
            # The first point is especial because it has no line
            line_x = [self.x, self.parent.list_points[1].x]
            line_y = [self.y, self.parent.list_points[1].y]
            # this is were the line is updated
            self.parent.list_points[1].line.set_data(line_x, line_y)

        elif self == self.parent.list_points[-1]:
            line_x = [self.parent.list_points[-2].x, self.x]
            line_y = [self.parent.list_points[-2].y, self.y]
            self.line.set_data(line_x, line_y)
        else:
            # The first point is especial because it has no line
            line_x = [self.x, self.parent.list_points[point_number+1].x]
            line_y = [self.y, self.parent.list_points[point_number+1].y]
            # this is were the line is updated
            self.parent.list_points[point_number+1].line.set_data(line_x, line_y)

            line_x = [self.parent.list_points[point_number-1].x, self.x]
            line_y = [self.parent.list_points[point_number-1].y, self.y]
            self.line.set_data(line_x, line_y)

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

        point_number =  self.parent.list_points.index(self)

        if self == self.parent.list_points[0]:
            self.parent.list_points[1].line.set_animated(False)
        elif self == self.parent.list_points[-1]:
            self.line.set_animated(False)
        else:
            self.line.set_animated(False)
            self.parent.list_points[point_number+1].line.set_animated(False)


        self.background = None

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


        self.x = self.point.center[0]
        self.y = self.point.center[1]

        self.coordinates.append([self.x, self.y])


        #print("Access from Class", self.x)
        #print("Access from Class", self.y)
        print("Access from Class", self.coordinates)




    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)
The main piece of code :

#-------------------------------------------------------------------------------
# Name:        module5
# Purpose:
#
# Author:      jguillerm
#
# Created:     05/01/2023
# Copyright:   (c) jguillerm 2023
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import sys
import matplotlib
matplotlib.use("Qt5Agg")
from PyQt5 import QtWidgets, QtGui

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

import PySide2.QtCore as QtCore

# Personnal modules
from drag import DraggablePoint


class MyGraph(FigureCanvas):

    """A canvas that updates itself every second with a new plot."""

    def __init__(self, parent=None, width=5, height=4, dpi=100):

        PK = [0, 10, 20, 30, 40, 50]
        FE = [251, 252, 248, 250, 251, 253]


        self.fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.fig.add_subplot()

        self.axes.grid(True)

        self.axes.set_xlim([0,50])
        self.axes.set_ylim([240,260])

        FigureCanvas.__init__(self, self.fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

        # To store the 2 draggable points
        self.list_points = []


        self.show()
        self.plotDraggablePoints(PK, FE)


    def plotDraggablePoints(self, listX, listY, size=1):

        """Plot and define the 2 draggable points of the baseline"""

        n = len(listX)

        for i in range(0,n):
            DragPoint = DraggablePoint(self, listX[i], listY[i], size)
            self.list_points.append(DragPoint)

            #print(" Access from script ", DragPoint.x)
            #print(" Access from script ", DragPoint.y)

        self.updateFigure()

        print(listY)

    def clearFigure(self):

        """Clear the graph"""

        self.axes.clear()
        self.axes.grid(True)
        del(self.list_points[:])
        self.updateFigure()


    def updateFigure(self):

        """Update the graph. Necessary, to call after each plot"""

        self.draw()



if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    ex = MyGraph()
    sys.exit(app.exec_())
    print(PK)
Your image links are broken, and you posted the main program twice when the problem is probably in DraggablePoint.py.
(Jan-09-2023, 02:51 PM)deanhystad Wrote: [ -> ]Your image links are broken, and you posted the main program twice when the problem is probably in DraggablePoint.py.

Sorry about that, i edited my post. Thanks in advance.