Source code for continuousflex.viewers.nma_gui.matplotlib_point_path

# **************************************************************************
# *
# * Authors:     J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************

from math import sqrt
from continuousflex.viewers.nma_plotter import plotArray2D_xy

STATE_NO_POINTS = 0  # no points have been selected, double-click will add first one
STATE_DRAW_POINTS = 1  # still adding points, double-click will set the last one
STATE_ADJUST_POINTS = 2  # no more points will be added, just adjust the current ones


[docs]class PointPath(): """ Graphical manager based on Matplotlib to handle mouse events to create a path of points. It also allow to modify the point positions on the path. """ def __init__(self, ax, data, pathData, callback=None, tolerance=3, maxPoints=10, LimitL = None, LimitH = None): self.ax = ax self.data = data # plotArray2D(ax, self.data) if(LimitL): plotArray2D_xy(ax, self.data, vvmin=LimitL, vvmax=LimitH) else: plotArray2D_xy(ax, self.data) self.callback = callback self.dragIndex = None self.tolerance = tolerance self.maxPoints = maxPoints self.cidpress = ax.figure.canvas.mpl_connect('button_press_event', self.onClick) self.cidrelease = ax.figure.canvas.mpl_connect('button_release_event', self.onRelease) self.cidmotion = ax.figure.canvas.mpl_connect('motion_notify_event', self.onMotion) self.cidkey = ax.figure.canvas.mpl_connect('key_press_event', self.onKeyPress) self.pathData = pathData if pathData.getSize(): # this means there is a path self.setState(STATE_ADJUST_POINTS) self.plotPath() else: self.setState(STATE_NO_POINTS) self.path_line = None self.path_points = None
[docs] def setState(self, state, notify=False): self.drawing = state if state == STATE_NO_POINTS: self.ax.set_title('Double click to select first point of trajectory') elif state == STATE_DRAW_POINTS: self.ax.set_title('Click to add points and double click to end trajectory.') elif state == STATE_ADJUST_POINTS: self.ax.set_title('Click and drag points to adjust trajectory.') else: raise Exception("Invalid PointPath state: %d" % state) if notify and self.callback: self.callback()
[docs] def onClick(self, event): if event.inaxes != self.ax: return # ignore click event if toolbar is active if self.ax.get_navigate_mode(): return doubleClick = event.dblclick ex = event.xdata ey = event.ydata if self.drawing == STATE_NO_POINTS: if doubleClick: self.setState(STATE_DRAW_POINTS) doubleClick = False if self.drawing == STATE_DRAW_POINTS: if doubleClick: self.setState(STATE_ADJUST_POINTS, notify=True) else: point = self.pathData.createEmptyPoint() point.setX(ex) point.setY(ey) self.pathData.addPoint(point) if self.pathData.getSize() == 1: # first point is added self.plotPath() else: xs, ys = self.getXYData() self.path_line.set_data(xs, ys) self.path_points.set_data(xs, ys) self.ax.figure.canvas.draw() if self.pathData.getSize() == self.maxPoints: self.setState(STATE_ADJUST_POINTS, notify=True) if self.drawing == STATE_ADJUST_POINTS and not doubleClick: # Points moving state self.dragIndex = None for i, point in enumerate(self.pathData): x = point.getX() y = point.getY() if sqrt((ex - x) ** 2 + (ey - y) ** 2) < self.tolerance: self.dragIndex = i break
[docs] def getXYData(self): xs = self.pathData.getXData() ys = self.pathData.getYData() return xs, ys
[docs] def plotPath(self): xs, ys = self.getXYData() self.path_line, = self.ax.plot(xs, ys, alpha=0.75, color='green') self.path_points, = self.ax.plot(xs, ys, 'o', color='red') # 5 points tolerance, mark line points
[docs] def onMotion(self, event): if self.dragIndex is None or self.drawing < 2: return # ignore click event if toolbar is active if self.ax.get_navigate_mode(): return ex, ey = event.xdata, event.ydata point = self.pathData.getPoint(self.dragIndex) point.setX(ex) point.setY(ey) self.update()
[docs] def onRelease(self, event): self.dragIndex = None
[docs] def onKeyPress(self, event): n = self.pathData.getSize() if event.key == 'ctrl+z' and n > 1: self.pathData.removeLastPoint() if n == 1: # Removed last point, change state appropriately self.setState(STATE_NO_POINTS) self.update()
[docs] def update(self): xs, ys = self.getXYData() self.path_line.set_data(xs, ys) self.path_points.set_data(xs, ys) self.ax.figure.canvas.draw()