Hello!
So I got this button Im clicking to plot a figure then I want that figure to update with every mouse buttton push to draw out a cross hair.
But I can't get it to update the figure after the pressing the button but it works great if I just plot outside on the class.
import cross_hair
import numpy as np
import matplotlib.pyplot as plt
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
import sys
class PushButton(QWidget):
def __init__(self):
super(PushButton,self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("PushButton")
self.setGeometry(400,400,300,260)
self.closeButton = QPushButton(self)
self.closeButton.setText("Press") #text
self.closeButton.clicked.connect(self.button_pressed)
def button_pressed(self):
#Fuction variables
self.x= np.arange(0, 1, 0.01)
self.y = np.sin(2 * 2 * np.pi * self.x)
#Figure
self.fig, self.ax = plt.subplots()
#title
self.ax.set_title('Snapping cursor')
#Plotting
self.line, = self.ax.plot(self.x, self.y, 'o')
#
snap_cursor = cross_hair.SnappingCursor(self.ax, self.line)
self.fig.canvas.mpl_connect('button_press_event', snap_cursor.on_mouse_click)
plt.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = PushButton()
ex.show()
sys.exit(app.exec_())
cross hair code looks like this (cross_hair.py)
import numpy as np
class SnappingCursor:
def __init__(self, ax, line):
self.ax = ax
self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--')
self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--')
self.x, self.y = line.get_data()
self._last_index = None
# text location in axes coords
self.text = ax.text(0.72, 0.9, '', transform=ax.transAxes)
def set_cross_hair_visible(self, visible):
need_redraw = self.horizontal_line.get_visible() != visible
self.horizontal_line.set_visible(visible)
self.vertical_line.set_visible(visible)
self.text.set_visible(visible)
return need_redraw
def on_mouse_click(self, event):
if not event.inaxes:
self._last_index = None
need_redraw = self.set_cross_hair_visible(False)
print('you no pressed' )#, event.button, event.xdata, event.ydata)
if need_redraw:
self.ax.figure.canvas.draw()
else:
self.set_cross_hair_visible(True)
x, y = event.xdata, event.ydata
print('you pressed') # , event.button, event.xdata, event.ydata)
index = min(np.searchsorted(self.x, x), len(self.x) - 1)
if index == self._last_index:
return # still on the same data point. Nothing to do.
self._last_index = index
x = self.x[index]
y = self.y[index]
# update the line positions
self.horizontal_line.set_ydata(y)
self.vertical_line.set_xdata(x)
self.text.set_text('x=%1.2f, y=%1.2f' % (x, y))
self.ax.figure.canvas.draw()
Solved
So I tried to solve this for some time and now did it with the easiest solution... I think?
added a function update_plot() in the class PushButton with the following
def update_plot(self):
print('Testing')
self.fig.canvas.mpl_connect('button_press_event', self.snap_cursor.on_mouse_click)
And in def button_pressed added
self.update_plot()
plt.show()
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.update_plot)
self.timer.start(100)
In def button_pressed
and ofc also used
from PyQt5 import QtCore
Related
This is my first object-oriented project, thus some parts could be wrote in a better way (advices would be appreciate.. )
I manage do build a code that allows points to be drag verticaly with a mouse event.
Initial values
After dragging points
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
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 :
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_())
I tried to store the new coordinates in an attributes of the instance DraggablePoint, but didn't manage to get what I want..
is there any way to relate a click on the Canvas but outside of a plot axes to the closest axes of the click? I have a canvas with x number of subplots and I'm trying to find out the closest subplot near the mouse click. Ultimately, this would help me create a zoom-in-rectangle that allows the user to zoom in the area of selected subplots (the navigation toolbar only zoom in one subplot).
#import os
#os.environ['QT_API'] = 'pyside'
from PyQt4 import QtGui, QtCore
import sys
import matplotlib
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
class Canvas(FigureCanvasQTAgg):
def __init__(self, parent=None):
self.figure = Figure()
super(Canvas, self).__init__(self.figure)
self.ax1 = self.figure.add_subplot(1,1,1)
self.figure.subplots_adjust(left = 0.05, bottom = 0.02, right = 0.98, top = 0.99)
self.setMinimumWidth(1000)
self.ax1.plot([1,2,3])
self.draw()
def add_subplot(self, data=[]):
rows = len(self.figure.axes) + 1
for index, axes in enumerate(self.figure.axes, start=1):
axes.change_geometry(rows, 1, index)
ax = self.figure.add_subplot(rows, 1, index+1)
x = np.arange(1000,1)
y = np.arange(1000,1)
ax.step(x,y)
ax.patch.set_facecolor('None')
self.figure.set_figheight(self.figure.get_figheight()*self.figScalingfactor)
def figScaling(self, numSubplot):
self.figScalingfactor = round(1.1729*pow(numSubplot, -0.028),3)
class Window(QtGui.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.showMaximized()
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(5)
self.canvas = Canvas(self)
self.scroll = QtGui.QScrollArea(self.widget)
self.scroll.setWidget(self.canvas)
self.scroll.setWidgetResizable(False)
self.nav = NavigationToolbar(self.canvas, self.widget)
self.numSubplots = 30
self.canvas.figScaling(self.numSubplots)
for x in range(self.numSubplots):
self.canvas.add_subplot()
self.canvas.adjustSize()
self.canvas.draw_idle()
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.scroll)
self.showVline = False
self.hoveringLine = None
self.canvas.mpl_connect("scroll_event", self.scrolling)
self.canvas.mpl_connect("button_press_event", self.onClick)
self.canvas.mpl_connect("motion_notify_event", self.onMove)
def scrolling(self, event):
val = self.scroll.verticalScrollBar().value()
if event.button =="down":
self.scroll.verticalScrollBar().setValue(val+100)
else:
self.scroll.verticalScrollBar().setValue(val-100)
def onClick(self, event):
if event.dblclick and self.showVline == False:
self.background = self.canvas.copy_from_bbox(self.canvas.figure.bbox)
self.showVline = True
self.hoveringLine = self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
lw=2, zorder=0, clip_on=False)
elif event.dblclick and self.showVline:
self.showVline = False
self.hoveringLine = None
self.canvas.ax1.axvline(x=event.xdata, ymin=-1.2*self.numSubplots, ymax=1.2,
lw=2, zorder=0, clip_on=False)
self.canvas.draw()
else:
print(event.xdata)
print(event.ydata)
def onMove(self, event):
if (self.showVline):
self.canvas.restore_region(self.background)
self.hoveringLine.set_xdata(event.xdata)
self.canvas.ax1.draw_artist(self.hoveringLine)
self.canvas.blit(self.canvas.figure.bbox)
def main():
app = QtGui.QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
GUI = Window()
GUI.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I'm trying to make a program in which I have a main window and a second window. The second window should be opened by checking a Check-Box in the main window and closed by unchecking it.
The following minimal example works already fine (thanks to ImportanceOfBeingErnest !), but I want to spin the arrow (the one, which is already bent when you run the example) by changing the SpinBox in the main window.
Solution: See 5th comment in the first answer!
import sys
from PyQt4 import QtGui, QtCore
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation
import numpy as np
class Newsphere(QtGui.QMainWindow):
def __init__(self):
super(Newsphere, self).__init__()
self.mainbox = QtGui.QWidget()
self.mainbox.setLayout(QtGui.QHBoxLayout())
self.setCentralWidget(self.mainbox)
self.spin = QtGui.QSpinBox()
self.spin.setValue(20)
self.spin.setMaximum(100)
self.spin.setMinimum(-100)
self.checkPlot = QtGui.QCheckBox("Check")
self.mainbox.layout().addWidget(self.spin)
self.mainbox.layout().addWidget(self.checkPlot)
self.Plot = None
self.checkPlot.clicked.connect(self.showPlot)
def showPlot(self):
if self.Plot == None:
self.Plot = Plot(self.kinematic())
self.Plot.show()
# register signal for closure
self.Plot.signalClose.connect(self.uncheck)
# register signal for spin value changed
self.spin.valueChanged.connect(self.kinematic)
else:
self.Plot.close()
self.Plot = None
def kinematic(self):
x = self.spin.value() / 100
v = np.matrix([[1.,x,0.],[0.,1.,0.],[0.,0.,1.]])
zero = np.matrix([[0.,0.,0.],[0.,0.,0.],[0.,0.,0.]])
pos = np.hstack([v, zero])
return pos
def uncheck(self):
self.checkPlot.setChecked(False)
self.Plot = None
class Plot(QtGui.QWidget):
signalClose = QtCore.pyqtSignal()
def __init__(self, pos=None):
super(Plot, self).__init__()
self.setLayout(QtGui.QHBoxLayout())
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.fig.tight_layout()
self.ax.view_init(40, 225)
''' dashed coordinate system '''
self.ax.plot([0,1], [0,0], [0,0], label='$X_0$', linestyle="dashed", color="red")
self.ax.plot([0,0], [0,-10], [0,0], label='$Y_0$', linestyle="dashed", color="green")
self.ax.plot([0,0], [0,0], [0,1], label='$Z_0$', linestyle="dashed", color="blue")
self.ax.set_xlim3d(-3,3)
self.ax.set_ylim3d(-3,3)
self.ax.set_zlim3d(-3,3)
self.canvas = FigureCanvas(self.fig)
self.layout().addWidget(self.canvas)
self.pos = pos
self.setup_plot()
self.ani = animation.FuncAnimation(self.fig, self.update_plot, init_func=self.setup_plot, blit=True)
def setup_plot(self):
self.ax.legend(loc='best')
self.position = self.ax.quiver(0, 0, 0, 0, 0, 0, pivot="tail", color="black")
return self.position,
def update_plot(self, i):
x_zero = self.pos[:,3]
y_zero = self.pos[:,4]
z_zero = self.pos[:,5]
v_x = self.pos[0,0:3]
v_y = self.pos[1,0:3]
v_z = self.pos[2,0:3]
self.position = self.ax.quiver(-x_zero, -y_zero, z_zero, -v_x[0,:], v_y[0,:], v_z[0,:], pivot="tail", color="black")
self.canvas.draw()
return self.position,
# We need to make sure the animation stops, when the window is closed
def closeEvent(self, event):
self.signalClose.emit()
self.close()
super(Plot, self).closeEvent(event)
def close(self):
self.ani.event_source.stop()
super(Plot, self).close()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Newsphere()
main.show()
sys.exit(app.exec_())
Here is an working example of what I think you are trying to achieve.
The Main Window has a spin box and a check box. Once the checkbox is clicked, a new window with a plot will show up and an animation will start. The current value and some array will be given to the plot window. If you change the spin box value while the animation is running, it will be updated. When the plot window is closed or when the checkbox is unchecked, the animation will stop (and be deleted).
import sys
from PyQt4 import QtGui, QtCore
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation
import numpy as np
class Newsphere(QtGui.QMainWindow):
def __init__(self):
super(Newsphere, self).__init__()
self.mainbox = QtGui.QWidget()
self.mainbox.setLayout(QtGui.QHBoxLayout())
self.setCentralWidget(self.mainbox)
self.spin = QtGui.QSpinBox()
self.spin.setValue(5)
self.spin.setMaximum(10)
self.spin.setMinimum(1)
self.checkPlot = QtGui.QCheckBox("Check")
self.mainbox.layout().addWidget(self.spin)
self.mainbox.layout().addWidget(self.checkPlot)
self.Plot = None
self.checkPlot.clicked.connect(self.showPlot)
def showPlot(self):
if self.Plot == None:
self.Plot = Plot(self.kinematic(), self.spin.value())
self.Plot.show()
# register signal for closure
self.Plot.signalClose.connect(self.uncheck)
# register signal for spin value changed
self.spin.valueChanged.connect(self.Plot.update_factor)
else:
self.Plot.close()
self.Plot = None
def kinematic(self):
v = np.array([[1.,2.,3.],[2.,1.,3.],[3.,2.,1.]])
return v
def uncheck(self):
self.checkPlot.setChecked(False)
self.Plot = None
class Plot(QtGui.QWidget):
signalClose = QtCore.pyqtSignal()
def __init__(self, v=None, factor=1):
super(Plot, self).__init__()
self.setLayout(QtGui.QHBoxLayout())
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.ax.set_aspect('equal')
self.fig.tight_layout()
self.ax.view_init(40, 225)
self.ax.set_xlim3d(0,3)
self.ax.set_ylim3d(0,3)
self.ax.set_zlim3d(0,4)
self.canvas = FigureCanvas(self.fig)
self.layout().addWidget(self.canvas)
self.pos = v
self.setup_plot()
self.update_factor(factor)
self.ani = animation.FuncAnimation(self.fig, self.update_plot, blit=False)
def setup_plot(self):
xpos, ypos = np.meshgrid(np.arange(self.pos.shape[0]),np.arange(self.pos.shape[1]) )
self.xpos = xpos.flatten('F')
self.ypos = ypos.flatten('F')
self.zpos = np.zeros_like(self.xpos)
self.bar = None
def update_factor(self, factor):
self.factor = factor
self.dx = np.ones_like(self.xpos)*np.min(np.abs(self.factor/10.), 0.1)
self.dy = self.dx.copy()
def update_plot(self, i):
if self.bar != None:
self.bar.remove()
del self.bar
pos = self.pos+np.sin(i/8.)
dz = pos.flatten()
self.bar = self.ax.bar3d(self.xpos, self.ypos, self.zpos, self.dx, self.dy, dz,
color=(1.-self.factor/10.,0,self.factor/10.), zsort='average', linewidth=0)
self.canvas.draw()
# We need to make sure the animation stops, when the window is closed
def closeEvent(self, event):
self.signalClose.emit()
self.close()
super(Plot, self).closeEvent(event)
def close(self):
self.ani.event_source.stop()
super(Plot, self).close()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Newsphere()
main.show()
sys.exit(app.exec_())
Since I wasn't sure about what you want to animate, I changed the plot to a barplot, but you can change it back to whatever you need. Hope that helps.
I'm attempting to expand on a draggable plot tutorial by creating a subplot that can be dragged (the matplotlib curve, not the whole window). I feel like I'm close but just missing a critical detail.
Most of the code is just creating cookie cutter subplots, figure 3 is the only one where I'm trying to drag the plot data.
Any help would be appreciated!
import wxversion
wxversion.ensureMinimal('2.8')
import numpy as np
import matplotlib
matplotlib.interactive(True)
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure
import wx
class DraggableCurve:
def __init__(self,curve):
self.curve = curve[0]
self.press = None
def connect(self):
'connect to all the events we need'
self.cidpress = self.curve.figure.canvas.mpl_connect(
'button_press_event', self.on_press)
self.cidrelease = self.curve.figure.canvas.mpl_connect(
'button_release_event', self.on_release)
self.cidmotion = self.curve.figure.canvas.mpl_connect(
'motion_notify_event', self.on_motion)
def on_press(self, event):
print "on_press"
'on button press we will see if the mouse is over us and store some data'
if event.inaxes != self.curve.axes: return
contains, attrd = self.curve.contains(event)
if not contains: return
print 'event contains', self.curve.xy
x0, y0 = self.curve.xy
# print x0,y0
self.press = x0, y0, event.xdata, event.ydata
def on_motion(self, event):
print "on_motion"
'on motion we will move the curve if the mouse is over us'
if self.press is None: return
if event.inaxes != self.curve.axes: return
x0, y0, xpress, ypress = self.press
print xpress, ypress
dx = event.xdata - xpress
dy = event.ydata - ypress
#print 'x0=%f, xpress=%f, event.xdata=%f, dx=%f, x0+dx=%f'%(x0, xpress, event.xdata, dx, x0+dx)
self.curve.set_x(x0+dx)
self.curve.set_y(y0+dy)
# print x0+dx, y0+dy
#self.curve.figure.canvas.draw()
self.curve.figure.canvas.draw_idle()
def on_release(self, event):
print "on_release"
'on release we reset the press data'
self.press = None
#self.curve.figure.canvas.draw()
self.curve.figure.canvas.draw_idle()
def disconnect(self):
'disconnect all the stored connection ids'
self.curve.figure.canvas.mpl_disconnect(self.cidpress)
self.curve.figure.canvas.mpl_disconnect(self.cidrelease)
self.curve.figure.canvas.mpl_disconnect(self.cidmotion)
class CanvasFrame(wx.Frame):
def __init__(self):
#create frame
frame = wx.Frame.__init__(self,None,-1,
'Test',size=(550,350))
#set background
self.SetBackgroundColour(wx.NamedColour("WHITE"))
#initialize figures
self.figure1 = Figure()
self.figure2 = Figure()
self.figure3 = Figure()
self.figure4 = Figure()
#initialize figure1
self.axes1 = self.figure1.add_subplot(111)
self.axes1.text(0.5,0.5, 'Test 1', horizontalalignment='center', fontsize=15)
self.axes1.get_xaxis().set_visible(False)
self.axes1.get_yaxis().set_visible(False)
self.canvas1 = FigureCanvas(self, -1, self.figure1)
#initialize figure2
self.axes2 = self.figure2.add_subplot(111)
self.axes2.text(0.5,0.5, 'Test 2', horizontalalignment='center', fontsize=15)
self.axes2.get_xaxis().set_visible(False)
self.axes2.get_yaxis().set_visible(False)
self.canvas2 = FigureCanvas(self, -1, self.figure2)
#initialize figure3
self.axes3 = self.figure3.add_subplot(111)
curve = self.axes3.plot(np.arange(1,11),10*np.random.rand(10),color='r',marker='o')
self.canvas3 = FigureCanvas(self, -1, self.figure3)
# self.axes3.get_xaxis().set_visible(True)
# self.axes3.get_yaxis().set_visible(True)
# self.canvas3.draw()
# self.canvas3.draw_idle()
dc = DraggableCurve(curve)
dc.connect()
#initialize figure4
self.axes4 = self.figure4.add_subplot(111)
self.axes4.text(0.5,0.5, 'Test4', horizontalalignment='center', fontsize=15)
self.axes4.get_xaxis().set_visible(False)
self.axes4.get_yaxis().set_visible(False)
self.canvas4 = FigureCanvas(self, -1, self.figure4)
#create figures into the 2x2 grid
self.sizer = wx.GridSizer(rows=2, cols=2, hgap=5, vgap=5)
self.sizer.Add(self.canvas1, 1, wx.EXPAND)
self.sizer.Add(self.canvas2, 1, wx.EXPAND)
self.sizer.Add(self.canvas3, 1, wx.EXPAND)
self.sizer.Add(self.canvas4, 1, wx.EXPAND)
self.SetSizer(self.sizer)
self.Fit()
return
class App(wx.App):
def OnInit(self):
'Create the main window and insert the custom frame'
frame = CanvasFrame()
frame.Show(True)
return True
app = App(0)
app.MainLoop()
Check this example:
# -*- coding: utf-8 -*-
import wxversion
wxversion.ensureMinimal('2.8')
import wx
import numpy as np
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
from matplotlib.figure import Figure
class FigureCanvas(FigureCanvasWxAgg):
def __init__(self,parent,id,figure,**kwargs):
FigureCanvasWxAgg.__init__(self,parent=parent, id=id, figure=figure,**kwargs)
self.figure = figure
self.axes = self.figure.get_axes()[0] # Get axes
self.connect() # Connect event
def connect(self):
"""Connect pick event"""
self.MOVE_LINE_EVT = self.mpl_connect("pick_event", self.on_pick)
def on_pick(self,event):
self._selected_line = event.artist # Get selected line
# Get initial x,y data
self._p0 = (event.mouseevent.xdata, event.mouseevent.ydata)
self._xdata0 = self._selected_line.get_xdata()
self._ydata0 = self._selected_line.get_ydata()
# Connect events for motion and release.
self._on_motion = self.mpl_connect("motion_notify_event", self.on_motion)
self._on_release = self.mpl_connect("button_release_event", self.on_release)
def on_motion(self,event):
cx = event.xdata # Current xdata
cy = event.ydata # Current ydata
deltax = cx - self._p0[0]
deltay = cy - self._p0[1]
self._selected_line.set_xdata(self._xdata0 + deltax)
self._selected_line.set_ydata(self._ydata0 + deltay)
self.draw()
def on_release(self,event):
"""On release, disconnect motion and release"""
self.mpl_disconnect(self._on_motion)
self.mpl_disconnect(self._on_release)
self.axes.relim()
self.axes.autoscale_view(True,True,True)
self.draw()
class Frame(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title,size=(800,600))
self.initCtrls()
self.plotting()
self.Centre(True)
self.Show()
def initCtrls(self):
self.mainsizer = wx.GridSizer(rows=2, cols=2, hgap=2, vgap=2)
# 1
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, wx.ID_ANY, self.figure)
# 2
self.figure2 = Figure()
self.axes2 = self.figure2.add_subplot(111)
self.canvas2 = FigureCanvas(self, wx.ID_ANY, self.figure2)
self.figure3 = Figure()
self.axes3 = self.figure3.add_subplot(111)
self.canvas3 = FigureCanvas(self, wx.ID_ANY, self.figure3)
self.figure4 = Figure()
self.axes4 = self.figure4.add_subplot(111)
self.canvas4 = FigureCanvas(self, wx.ID_ANY, self.figure4)
self.mainsizer.Add(self.canvas, 1, wx.EXPAND)
self.mainsizer.Add(self.canvas2, 1, wx.EXPAND)
self.mainsizer.Add(self.canvas3, 1, wx.EXPAND)
self.mainsizer.Add(self.canvas4, 1, wx.EXPAND)
self.SetSizer(self.mainsizer)
def plotting(self):
# Set picker property -> true
self.axes2.plot(np.arange(1,11),10*np.random.rand(10),color='b',
marker='o', picker=True)
self.axes3.plot(np.arange(1,11),10*np.random.rand(10),color='r',
marker='o', picker=True)
self.canvas.draw()
if __name__=='__main__':
app = wx.App()
frame = Frame(None, "Matplotlib Demo")
frame.Show()
app.MainLoop()
Basically the idea is to define a custom FigureCanvas, which supports the selection and movement of the lines using the pick event.
Obviously this code still needs a lot of review, to work properly.
I want to plot xcoord vs. ycoord on a canvas. What I get right now is only an empty plot window (1 high x 1 wide) .
How can I make the datapoints appear in the plot window?
I have checked the xcoord and ycoord arrays. They exist and they are correct.
class SurveyRoute(SurveyPlotWidget):
"""docstring for SurveyRoute"""
def __init__(self, survey_name, parent=None, model=None):
self.survey_name = survey_name
SurveyPlotWidget.__init__(self, parent, model)
def read_coordinate_file(self, survey_name):
self.coords = station_coordinates.get_coordinates_all(survey_name)
df = pd.DataFrame(self.coords,index=['UTM X','UTM Y','depth'])
df = DataFrame.transpose(df)
self.xcoord = df['UTM X'].values.tolist()
self.ycoord = df['UTM Y'].values.tolist()
print self.xcoord
def on_hover(self, event):
self.fig_text.set_text('')
contains, attrd = self.points.contains(event)
if contains == True:
ind = attrd['ind'][0]
self.fig_text.set_text('bm {}'.format(self.model.benchmarks[ind]))
self.canvas.draw()
def plot_coords(self):
fig = plt.figure()
plt.plot(self.xcoord, self.ycoord, marker='o', ms=10, linestyle='', alpha=1.0, color='r', picker = True)[0]
plt.xlabel('UTM x-coordinate')
plt.ylabel('UTM y-coordinate')
fig.canvas.mpl_connect('pick_event', self.on_hover)
fig.canvas.draw()
running the class
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
obj = SurveyRoute('mikkel')
obj.read_coordinate_file('mikkel')
obj.plot_coords()
obj.show()
app.exec_()
fig = plt.figure()
should be removed and
fig.canvas.mpl_connect('pick_event', self.on_hover)
fig.canvas.draw()
should be substituted by
self.canvas.mpl_connect('pick_event', self.on_hover)
self.canvas.draw()