Embed Matplotlib in PyQt with multiple plot - python

everyone! I want to embed my data into Gui. Here I created 2 Plot button so that I showed my data one by one.Plot1 contained 2 subplot, Plot2 contained 1 plot.
But when I clicked Plot1 and then clicked Plot2, I can't see my data in Plot2, It looks like coordinate doesn't change. How should I fix this?
import matplotlib.pyplot as plt
import numpy as np
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class PrettyWidget(QtGui.QWidget):
def __init__(self):
super(PrettyWidget, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(100,100,800,600)
self.center()
self.setWindowTitle('S Plot')
grid = QtGui.QGridLayout()
self.setLayout(grid)
btn1 = QtGui.QPushButton('Plot 1 ',self)
btn1.resize(btn1.sizeHint())
btn1.clicked.connect(self.plot1)
grid.addWidget(btn1,5,0)
btn2 = QtGui.QPushButton('Plot 2 ',self)
btn2.resize(btn2.sizeHint())
btn2.clicked.connect(self.plot2)
grid.addWidget(btn2,5,1)
self.figure = plt.figure(figsize = (15,5))
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
grid.addWidget(self.canvas, 3,0,1,2)
grid.addWidget(self.canvas, 3,0,1,2)
self.show()
def plot1(self):
plt.cla()
ax1 = self.figure.add_subplot(211)
x1 = [i for i in range(100)]
y1 = [i**0.5 for i in x1]
ax1.plot(x1,y1,'b.-')
ax2 = self.figure.add_subplot(212)
x2 = [i for i in range(100)]
y2 = [i for i in x2]
ax2.plot(x2,y2,'b.-')
self.canvas.draw()
def plot2(self):
plt.cla()
ax3 = self.figure.add_subplot(111)
x = [i for i in range(100)]
y = [i**0.5 for i in x]
ax3.plot(x,y,'r.-')
ax3.set_title('Square Root Plot')
self.canvas.draw()
def center(self):
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
app = QtGui.QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
GUI = PrettyWidget()
sys.exit(app.exec_())

I strongly advise against using pyplot whe doing embedding, the global state management and the FigureManager classes will get in your way.
import sys
from PyQt4 import QtGui
import matplotlib
import matplotlib.figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class PrettyWidget(QtGui.QWidget):
def __init__(self):
super(PrettyWidget, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(100, 100, 800, 600)
self.center()
self.setWindowTitle('S Plot')
grid = QtGui.QGridLayout()
self.setLayout(grid)
btn1 = QtGui.QPushButton('Plot 1 ', self)
btn1.resize(btn1.sizeHint())
btn1.clicked.connect(self.plot1)
grid.addWidget(btn1, 5, 0)
btn2 = QtGui.QPushButton('Plot 2 ', self)
btn2.resize(btn2.sizeHint())
btn2.clicked.connect(self.plot2)
grid.addWidget(btn2, 5, 1)
self.figure = matplotlib.figure.Figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
grid.addWidget(self.canvas, 3, 0, 1, 2)
# grid.addWidget(self.toolbar, ??)
self.show()
def plot1(self):
self.figure.clf()
ax1 = self.figure.add_subplot(211)
x1 = [i for i in range(100)]
y1 = [i**0.5 for i in x1]
ax1.plot(x1, y1, 'b.-')
ax2 = self.figure.add_subplot(212)
x2 = [i for i in range(100)]
y2 = [i for i in x2]
ax2.plot(x2, y2, 'b.-')
self.canvas.draw_idle()
def plot2(self):
self.figure.clf()
ax3 = self.figure.add_subplot(111)
x = [i for i in range(100)]
y = [i**0.5 for i in x]
ax3.plot(x, y, 'r.-')
ax3.set_title('Square Root Plot')
self.canvas.draw_idle()
def center(self):
qr = self.frameGeometry()
cp = QtGui.QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
app = QtGui.QApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
GUI = PrettyWidget()
sys.exit(app.exec_())

Related

Why there are two duplicate axes labels when I embed Matplotlib figure inside a PyQt5 window?

I am trying to embed a Matplotlib Plot in a PyQt5 Window. I am using the following code:
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton
import sys
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
class Window(QMainWindow):
def __init__(self):
super().__init__()
title ='Matplotlib Embedding In PyQt5'
top= 400
left = top
width = 900
height = 500
self.setWindowTitle(title)
self.setGeometry(left, top, width, height)
self.ui()
def ui(self):
canvas1 = Canvas(self, width=4, height=4)
button = QPushButton('Click me', self)
button.move(250, 450)
self.plot(canvas1)
def plot(self, canvas):
x= np.linspace(0, 1, 200)
y = np.sinc(x)
ax = canvas.figure.add_subplot(111)
ax.plot(x,y)
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi = 100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
app = QtWidgets.QApplication(sys.argv)
main_window = Window()
main_window.show()
sys.exit(app.exec())
However, when I run that (using Python 3.8.10), I get:
As you can see, there is something wrong with the axes labels.
How can I fix that?
You are creating 2 axes:
self.axes = fig.add_subplot(111)
ax = canvas.figure.add_subplot(111)
The solution is to reuse the existing axes:
def plot(self, canvas):
x = np.linspace(0, 1, 200)
y = np.sinc(x)
canvas.axes.plot(x, y)
Or clean the figure before:
def plot(self, canvas):
x = np.linspace(0, 1, 200)
y = np.sinc(x)
canvas.figure.clear()
ax = canvas.figure.add_subplot(111)
ax.plot(x, y)

How to make matplotlib widget in PyQt5 clickable?

I am working on GUI where I have tab system with graphs. I want that if a user clicks (or puts cursor) at any point in the graph, it shows the exact x and y values in that point like that:
I know that in usual matplotlib it is easy to implement; however I do not know how to do that in PyQt5.
My tabs system and canvas look like that:
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QApplication, QWidget,QVBoxLayout,QTabWidget
import sys
import matplotlib.pyplot as plt
def mouse_move(event):
x, y = event.xdata, event.ydata
print(x, y)
plt.connect('motion_notify_event', mouse_move)
class Canvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=5, dpi=80):
fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = fig.add_subplot(111)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self.plot()
def plot(self):
x = ['22-02 11:16:15', '22-02 15:31:54', '22-02 15:32:30',
'22-02 15:32:45', '22-02 15:33:57', '22-02 15:34:13',
'22-02 15:34:46']
y = [1, 4, 3, 4, 8, 9, 2]
self.figure.tight_layout()
ax = self.figure.add_subplot(111)
ax.plot(x, y)
class MainWindow(QDialog):
def __init__(self):
super().__init__()
self.top = 255
self.left = 150
self.setGeometry(self.left, self.top, 900, 900)
self.Mainlayout = QVBoxLayout(self)
self.tabs = QTabWidget()
self.graphUP = QWidget()
self.graphUP.layout = QVBoxLayout( self.graphUP)
self.graphUP.layout.addWidget(Canvas())
self.tabs.setFixedHeight(800)
self.tabs.setFixedWidth(800)
self.tabs.addTab(self.graphUP, "Graph1")
self.Mainlayout.addWidget(self.tabs)
self.show()
if __name__ == '__main__':
App = QApplication(sys.argv)
window = MainWindow()
sys.exit(App.exec())
import this module:
import mplcursors as mpl
and add : mpl.cursor(hover=True)
in your def plot() function.

importing matplotlib draggable lines module to PyQt5

i am trying to apply draggable line class to pyqt5 file with multiple subplots figure, it is showing the requried lines but not responding to events from draggable line class,
Drag file
import matplotlib.pyplot as plt
import matplotlib.lines as lines
from matplotlib.figure import Figure
class draggable_lines:
def __init__(self, ax, kind, X1,X2,Y1, Y2):
self.ax = ax
self.canvas = ax.get_figure().canvas
self.o = kind
self.X1 = X1
self.X2 = X2
self.Y1 = Y1
self.Y2 = Y2
if kind == "h":
x = [X1, X2]
y = [Y1,Y2]
elif kind == "v":
x = [X1, X2]
y = [Y1,Y1]
self.line = lines.Line2D(x, y, picker=5)
self.ax.add_line(self.line)
self.canvas.draw_idle()
self.sid = self.canvas.mpl_connect('pick_event', self.clickonline)
def clickonline(self, event):
if event.artist == self.line:
self.follower = self.canvas.mpl_connect("motion_notify_event", self.followmouse)
self.releaser = self.canvas.mpl_connect("button_press_event", self.releaseonclick)
def followmouse(self, event):
if self.o == "h":
self.line.set_ydata([event.ydata, event.ydata])
else:
self.line.set_xdata([event.xdata, event.xdata])
self.canvas.draw_idle()
def releaseonclick(self, event):
if self.o == "h":
self.Y = self.line.get_ydata()[0]
else:
self.X = self.line.get_xdata()[0]
self.canvas.mpl_disconnect(self.releaser)
self.canvas.mpl_disconnect(self.follower)
#
#fig = plt.figure()
#ax = fig.add_subplot(111)
#m = [2,5,7]
#n = [1,3,6]
#lineaa = []
#ax.set_xlim(0,10)
#for i,j in zip(n,m):
# line = draggable_lines(ax, "h", i, j, 0.5,0.5)
# lineaa.append(line)
#plt.show()
and this a sample code for PyQt mpl figure
import sys
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5 import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import matplotlib.lines as lines
from matplotlib.figure import Figure
from Drag import draggable_lines
class Window(QtWidgets.QDialog):
def __init__(self,):
super().__init__()
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self)
self.button1 = QtWidgets.QPushButton('Plot')
self.button1.clicked.connect(self.plot)
self.layout = QVBoxLayout()
self.layout.addWidget(self.toolbar)
self.layout.addWidget(self.canvas)
btnlayout = QHBoxLayout()
btnlayout.addWidget(self.button1)
qw = QWidget(self)
qw.setLayout(btnlayout)
self.layout.addWidget(qw)
self.setLayout(self.layout)
def plot(self):
ax = self.figure.add_subplot(121)
ax1 = self.figure.add_subplot(122)
m = [2, 5, 7]
n = [1, 3, 6]
lineaa = []
ax.set_xlim(0, 10)
for i, j in zip(n, m):
line = draggable_lines(ax, "h", i, j, 0.5, 0.5)
lineaa.append(line)
self.canvas.draw_idle()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
#main.setWindowTitle('Simple ')
main.show()
sys.exit(app.exec_())
i am not sure it is the different between canvas connecting to event in drag class file and the other in the main file
any advice will be appreciated

How to persist changes made by the NavigationToolbar on a periodically updated graph

I have a PyQt5 GUI application. This application shows a graph (with my default xlim and ylim) that gets updated every second, a real-time graph basically. This functionality I have, but I want to add a NavigationToolbar so one can zoom in/out the graph.
I added the toolbar to my layout and it gets displayed. So far so good. Now I zoom in, the graph gets zoomed in, but once the graph gets periodically updated the xlim and ylim are defaulted again and the zoom is gone. What properties I need to call from the toolbar so I can save them and pass them to my _update_canvas function? I looked at https://matplotlib.org/3.1.0/api/axes_api.html, and noticed the function get_ylim. So i tried as followed:
self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim()) and self._dynamic_ax.set_ylim(self._dynamic_ax2.get_ylim())
As well as:
self._dynamic_ax.set_navigate(True)
However these didn't work. How can I persist the settings set by NavigationToolbar? Not only the zoom but also the pan.
A minimal runnable code sample:
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
buffer_size = 120
t = [t for t in range(buffer_size)]
bitthrough = [t for t in range(buffer_size)]
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
self._dynamic_ax.clear()
self._dynamic_ax.plot(t, bitthrough, color="black")
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120) #self._dynamic_ax.get_ylim())
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
self._dynamic_ax2.clear()
self._dynamic_ax2.plot(t, errors, color="blue")
self._dynamic_ax2.set_ylabel("Errors", color="blue")
self._dynamic_ax2.set_ylim(-4, 100) #self._dynamic_ax2.get_ylim())
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
My real _update_canvas function:
def _update_canvas(self):
wh_green = [a <= b for a, b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c
for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
# self._dynamic_ax.clear()
# self._dynamic_ax2.clear()
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_red, color="red", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_orange, color="orange", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_green, color="green", interpolate=True
)
# self._dynamic_ax.plot(t, bitthrough, color="black")
# self._dynamic_ax.set_xlabel("time (s)")
# self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
# #self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim())
# self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax2.plot(t, errors, color="blue")
# self._dynamic_ax2.set_ylabel("Errors", color="blue")
# #self._dynamic_ax2.set_ylim(self._dynamic_ax2.get_ylim())
# self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
# logging.debug("redrawing graph!!")
self._dynamic_ax.figure.canvas.draw_idle()
The solution of #DizietAsahi doesn't work while using fill_between. The area gets overwritten and not cleared. So they are displayed on top of eachother.
My advice would be to not clear the figure at each update. Instead, store a reference to the Line2D artists created by plot() and update the {x|y}data (using set_data() or set_ydata()) in your update function.
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
buffer_size = 120
t = np.linspace(0, 100, buffer_size)
bitthrough = 120*np.random.random(size=(buffer_size,))
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
##
## Create plots here (initially empty)
##
self._plot1, = self._dynamic_ax.plot(t, np.empty(shape=(buffer_size,)), color="black")
self._plot2, = self._dynamic_ax2.plot(t, np.empty(shape=(buffer_size,)), color="blue")
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
bitthrough = 120*np.random.random(size=(buffer_size, ))
errors = bitthrough[::-1]
##
## update the content of the plots here, without clearing the figure
##
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
self._fill1.remove()
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
EDIT
I've added some code for handling fill_between().
fill_between() returns a PolyCollection which is a pain to update, so the best option there is to remove the PolyCollection and re-create it at each update (but not clear the whole figure).

matplotlib onPick Event registering twice

As part of a larger project I am updating a graph on matplotlib embedded in Pyqt5. I have an onpick event that adds and removes annotation to a scatter point. After I updated my plot in a fashion similar to below the onPick feature registers twice. There is something underlying that is not getting removed correctly. I was wondering what I might clear, besides the figure, that would correct my issue.
Simple Example Highlighting the issue:
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
class plot(QWidget):
def __init__(self):
super().__init__()
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.Layout = QVBoxLayout()
self.xarray = [1,2,3,4,5,6]
self.yarray = [6,7,5,4,2,1]
update_btn = QPushButton("Update Plot", self)
self.Layout.addWidget(update_btn, 1)
update_btn.clicked.connect(self.updateplot)
self.createplot()
self.setLayout(self.Layout)
def updateplot(self):
self.xarray = [6,7,5,3,2,1]
self.figure.clear()
self.createplot()
def createplot(self):
ax = self.figure.add_subplot(1,1,1)
ax.grid()
val = self.displayval(ax)
self.plot = ax.plot(self.xarray, self.yarray,'o', marker = 'o', c= 'b', picker = 5)[0]
self.Layout.addWidget(self.canvas, 2)
self.canvas.draw()
def displayval(self, ax):
def onPick(event):
print("connecting")
plot = event.artist
xval = plot.get_xdata()
yval = plot.get_ydata()
ind = event.ind
if xval[ind].size > 1 or yval[ind].size > 1: return
xy = (xval[ind][0], yval[ind][0])
ann = ax.annotate('(%f , %f)' % xy, xy= xy)
self.figure.canvas.draw()
self.figure.canvas.mpl_connect('pick_event', onPick)
return onPick
if __name__ == '__main__':
appl = QApplication(sys.argv)
main = plot()
main.show()
sys.exit(appl.exec_())
Note: I have tried updating the axis values with something like
ax.set_xaxis()
However this does not render the data correctly in my program, therefore an alternative answer would be preferred.
Here is a Solution to the issue. Thanks to #ImportanceOfBeingErnest for explaining why the issue was occurring. From there I was able to fix the problem. Once updated the on click annotation only occurs once. The solution is only to call displayval once and change ax to 'self.ax' and update the object variable.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
class plot(QWidget):
def __init__(self):
super().__init__()
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.Layout = QVBoxLayout()
self.xarray = [1,2,3,4,5,6]
self.yarray = [6,7,5,4,2,1]
self.init_trigger = True
update_btn = QPushButton("Update Plot", self)
self.Layout.addWidget(update_btn, 1)
update_btn.clicked.connect(self.updateplot)
self.createplot()
self.setLayout(self.Layout)
def updateplot(self):
self.xarray = [6,7,5,3,2,1]
self.figure.clear()
self.createplot()
def createplot(self):
self.ax = self.figure.add_subplot(1,1,1)
self.ax.grid()
if self.init_trigger: val = self.displayval()
self.plot = self.ax.plot(self.xarray, self.yarray,'o', marker = 'o', c= 'b', picker = 5)[0]
self.Layout.addWidget(self.canvas, 2)
self.canvas.draw()
self.init_trigger = False
def displayval(self):
def onPick(event):
print("connecting")
plot = event.artist
xval = plot.get_xdata()
yval = plot.get_ydata()
ind = event.ind
if xval[ind].size > 1 or yval[ind].size > 1: return
xy = (xval[ind][0], yval[ind][0])
ann = self.ax.annotate('(%f , %f)' % xy, xy= xy)
self.figure.canvas.draw()
self.figure.canvas.mpl_connect('pick_event', onPick)
return onPick
if __name__ == '__main__':
appl = QApplication(sys.argv)
main = plot()
main.show()
sys.exit(appl.exec_())

Categories

Resources