I'm trying to display some Plot.ly or Plot.ly Dash plots ( I haven't settled on using one or the other, so I'm experimenting with both right now) in a PyQt5 GUI using QWebEngineView. This doesn't work for any plots larger than 2MB due to some Chromium-level hardcoded restriction.
I found one similar question that is pretty much identical in terms of our needs. It looks like the OP actually found an answer, but unfortunately for me, they didn't post an example of working code or explain what they did to make it work. I do not understand enough of the underlying theory to piece together an answer with the resources linked in this other question, and my Stack reputation isn't high enough to comment and ask the OP what exactly worked.
Here is a minimum reproducible example that displays a plot embedded in the GUI. It's a modification of an answer to a question about embedding Plotly plots in PyQt5 GUIs here:
import numpy as np
import plotly.offline as po
import plotly.graph_objs as go
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
def show_qt(fig):
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += po.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
fig_view = QWebEngineView()
# setHtml has a 2MB size limit, need to switch to setUrl on tmp file
# for large figures.
fig_view.setHtml(raw_html)
# fig_view.setUrl(QUrl('temp-plot.html'))
fig_view.show()
fig_view.raise_()
return fig_view
if __name__ == '__main__':
app = QApplication(sys.argv)
# Working small plot:
fig = go.Figure(data=[{'type': 'scattergl', 'y': [2, 1, 3, 1]}])
# Not working large plot:
# t = np.arange(0, 200000, 1)
# y = np.sin(t/20000)
fig = go.Figure(data=[{'type': 'scattergl', 'y': y}])
# po.plot(fig)
fig_view = show_qt(fig)
sys.exit(app.exec_())
Here is a modified version that demonstrates how a large data set cannot be displayed the same way:
import numpy as np
import plotly.offline as po
import plotly.graph_objs as go
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
def show_qt(fig):
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += po.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
fig_view = QWebEngineView()
# setHtml has a 2MB size limit, need to switch to setUrl on tmp file
# for large figures.
fig_view.setHtml(raw_html)
# fig_view.setUrl(QUrl('temp-plot.html'))
fig_view.show()
fig_view.raise_()
return fig_view
if __name__ == '__main__':
app = QApplication(sys.argv)
# Working small plot:
# fig = go.Figure(data=[{'type': 'scattergl', 'y': [2, 1, 3, 1]}])
# Not working large plot:
t = np.arange(0, 200000, 1)
y = np.sin(t/20000)
fig = go.Figure(data=[{'type': 'scattergl', 'y': y}])
# po.plot(fig)
fig_view = show_qt(fig)
sys.exit(app.exec_())
Lastly, here is something I tried to get the large plot to display with QUrl pointing to a local html plot on the disk:
import numpy as np
import plotly.offline as po
import plotly.graph_objs as go
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
def show_qt(fig):
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += '<body>'
raw_html += po.plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
fig_view = QWebEngineView()
# setHtml has a 2MB size limit, need to switch to setUrl on tmp file
# for large figures.
# fig_view.setHtml(raw_html)
fig_view.setUrl(QUrl('temp-plot.html'))
fig_view.show()
fig_view.raise_()
return fig_view
if __name__ == '__main__':
app = QApplication(sys.argv)
# Working small plot:
# fig = go.Figure(data=[{'type': 'scattergl', 'y': [2, 1, 3, 1]}])
# Not working large plot:
t = np.arange(0, 200000, 1)
y = np.sin(t/20000)
fig = go.Figure(data=[{'type': 'scattergl', 'y': y}])
# po.plot(fig)
fig_view = show_qt(fig)
sys.exit(app.exec_())
The plot was generated with:
import numpy as np
import plotly.offline as po
import plotly.graph_objs as go
t = np.arange(0, 200000, 1)
y = np.sin(t/20000)
fig = go.Figure(data=[{'type': 'scattergl', 'y': y}])
po.plot(fig)
As this answer indicates a possible solution is to use a QWebEngineUrlSchemeHandler, in the next section I have created a class that allows you to register functions that are invoked through custom urls:
qtplotly.py
from PyQt5 import QtCore, QtWebEngineCore, QtWebEngineWidgets
import plotly.offline as po
import plotly.graph_objs as go
class PlotlySchemeHandler(QtWebEngineCore.QWebEngineUrlSchemeHandler):
def __init__(self, app):
super().__init__(app)
self.m_app = app
def requestStarted(self, request):
url = request.requestUrl()
name = url.host()
if self.m_app.verify_name(name):
fig = self.m_app.fig_by_name(name)
if isinstance(fig, go.Figure):
raw_html = '<html><head><meta charset="utf-8" />'
raw_html += '<script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>'
raw_html += "<body>"
raw_html += po.plot(fig, include_plotlyjs=False, output_type="div")
raw_html += "</body></html>"
buf = QtCore.QBuffer(parent=self)
request.destroyed.connect(buf.deleteLater)
buf.open(QtCore.QIODevice.WriteOnly)
buf.write(raw_html.encode())
buf.seek(0)
buf.close()
request.reply(b"text/html", buf)
return
request.fail(QtWebEngineCore.QWebEngineUrlRequestJob.UrlNotFound)
class PlotlyApplication(QtCore.QObject):
scheme = b"plotly"
def __init__(self, parent=None):
super().__init__(parent)
scheme = QtWebEngineCore.QWebEngineUrlScheme(PlotlyApplication.scheme)
QtWebEngineCore.QWebEngineUrlScheme.registerScheme(scheme)
self.m_functions = dict()
def init_handler(self, profile=None):
if profile is None:
profile = QtWebEngineWidgets.QWebEngineProfile.defaultProfile()
handler = profile.urlSchemeHandler(PlotlyApplication.scheme)
if handler is not None:
profile.removeUrlSchemeHandler(handler)
self.m_handler = PlotlySchemeHandler(self)
profile.installUrlSchemeHandler(PlotlyApplication.scheme, self.m_handler)
def verify_name(self, name):
return name in self.m_functions
def fig_by_name(self, name):
return self.m_functions.get(name, lambda: None)()
def register(self, name):
def decorator(f):
self.m_functions[name] = f
return f
return decorator
def create_url(self, name):
url = QtCore.QUrl()
url.setScheme(PlotlyApplication.scheme.decode())
url.setHost(name)
return url
main.py
import numpy as np
import plotly.graph_objs as go
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from qtplotly import PlotlyApplication
# PlotlyApplication must be created before the creation
# of QGuiApplication or QApplication
plotly_app = PlotlyApplication()
#plotly_app.register("scatter")
def scatter():
t = np.arange(0, 200000, 1)
y = np.sin(t / 20000)
fig = go.Figure(data=[{"type": "scattergl", "y": y}])
return fig
#plotly_app.register("scatter2")
def scatter2():
N = 100000
r = np.random.uniform(0, 1, N)
theta = np.random.uniform(0, 2 * np.pi, N)
fig = go.Figure(
data=[
{
"type": "scattergl",
"x": r * np.cos(theta),
"y": r * np.sin(theta),
"marker": dict(color=np.random.randn(N), colorscale="Viridis"),
}
]
)
return fig
#plotly_app.register("scatter3")
def scatter3():
x0 = np.random.normal(2, 0.45, 30000)
y0 = np.random.normal(2, 0.45, 30000)
x1 = np.random.normal(6, 0.4, 20000)
y1 = np.random.normal(6, 0.4, 20000)
x2 = np.random.normal(4, 0.3, 20000)
y2 = np.random.normal(4, 0.3, 20000)
traces = []
for x, y in ((x0, y0), (x1, y1), (x2, y2)):
trace = go.Scatter(x=x, y=y, mode="markers")
traces.append(trace)
fig = go.Figure(data=traces)
return fig
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.m_view = QtWebEngineWidgets.QWebEngineView()
combobox = QtWidgets.QComboBox()
combobox.currentIndexChanged[str].connect(self.onCurrentIndexChanged)
combobox.addItems(["scatter", "scatter2", "scatter3"])
vlay = QtWidgets.QVBoxLayout(self)
hlay = QtWidgets.QHBoxLayout()
hlay.addWidget(QtWidgets.QLabel("Select:"))
hlay.addWidget(combobox)
vlay.addLayout(hlay)
vlay.addWidget(self.m_view)
self.resize(640, 480)
#QtCore.pyqtSlot(str)
def onCurrentIndexChanged(self, name):
self.m_view.load(plotly_app.create_url(name))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
# Init_handler must be invoked before after the creation
# of QGuiApplication or QApplication
plotly_app.init_handler()
w = Widget()
w.show()
sys.exit(app.exec_())
Structure:
├── main.py
└── qtplotly.py
Output:
Related
currently, I am using plotly offline like this:
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import *
import plotly.graph_objects as go
import plotly
import numpy as np
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
grid_layout = QGridLayout()
self.pb = QPushButton('plot')
# some example data
x = np.arange(0, 2*np.pi, 0.001)
y = np.sin(x)
# create the plotly figure
fig = go.Figure(go.Scatter(x=x, y=y))
# we create html code of the figure
html = "".join(['<html><body>',
plotly.offline.plot(fig, output_type='div', include_plotlyjs='cdn'),
'</body></html>']
)
# we create an instance of QWebEngineView and set the html code
self.plot_widget = QWebEngineView()
self.plot_widget.setHtml(html)
grid_layout.addWidget(self.plot_widget, 0, 0)
grid_layout.addWidget(self.pb, 1, 0)
self.setLayout(grid_layout)
self.pb.clicked.connect(self.newplot)
def newplot(self):
# some example data
x = np.arange(0, 2 * np.pi, 0.001)
y = np.sin(x+np.random.uniform(low=0, high=2*np.pi))
# create the plotly figure
fig = go.Figure(go.Scatter(x=x, y=y))
# we create html code of the figure
html = "".join(['<html><body>',
plotly.offline.plot(fig, output_type='div', include_plotlyjs='cdn'),
'</body></html>']
)
self.plot_widget.setHtml(html)
if __name__ == '__main__':
app = QApplication([])
window = Window()
window.show()
app.exec_()
My problem is, this way when I draw a new plot, the whole thing regenerates. Is there a way to do it more smoothly and only update the existing figure? Keep the axes, and just replace the old line with a new one in the plot.
I have been using Plotly for Python for more than a year and I have never heard of the possibility that you described.
I am working on GUI where I have a system with graphs.
I want to use the spanselector in the graph i do visualize.
I have searched and i can't understand how to use the span selector while calling the matplotlib widget.
This is an example i'm following to plot. it has 3 parts(main,mplwidget,ui file)
the main code file
# ------------------------------------------------------
# ---------------------- main.py -----------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from PyQt5.uic import loadUi
from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar)
import numpy as np
import random
#from matplotlib.widgets import SpanSelector
class MatplotlibWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
loadUi("qt_designer.ui",self)
self.setWindowTitle("PyQt5 & Matplotlib Example GUI")
self.pushButton_generate_random_signal.clicked.connect(self.update_graph)
self.addToolBar(NavigationToolbar(self.MplWidget.canvas, self))
def update_graph(self):
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(t, cosinus_signal)
self.MplWidget.canvas.axes.plot(t, sinus_signal)
self.MplWidget.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.MplWidget.canvas.axes.set_title('Cosinus - Sinus Signal')
self.MplWidget.canvas.draw()
#span = self.MplWidget.canvas.axes.SpanSelector(ax1, onselect, 'horizontal', useblit=True,rectprops=dict(alpha=0.5, facecolor='red'))
def onselect(min_value, max_value):
print(min_value, max_value)
return min_value, max_value
app = QApplication([])
window = MatplotlibWidget()
window.show()
app.exec_()
the mplwidget file
# ------------------------------------------------------
# -------------------- mplwidget.py --------------------
# ------------------------------------------------------
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
the ui file is attached to this link with all the codes:
here
in the other way this is the example code to use the span selector:
import matplotlib.pyplot as plt
import matplotlib.widgets as mwidgets
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [10, 50, 100])
def onselect(vmin, vmax):
print(vmin, vmax)
rectprops = dict(facecolor='blue', alpha=0.5)
span = mwidgets.SpanSelector(ax, onselect, 'horizontal',span_stays=True,button=1,,rectprops=rectprops)
fig.show()
//////////////////////////////////////////////////////////////////////////////
i tried a lot of ways to assess the span selector but im a little bit confused in the way it works and how i should connect the the structure of code?
if i run whithin The comented line:
span = self.MplWidget.canvas.axes.SpanSelector(ax1, onselect, 'horizontal', useblit=True,rectprops=dict(alpha=0.5, facecolor='red'))
its shown the following error:
AttributeError:'AxesSubplot' object has no attribute 'SpanSelector'
finally, this is the desire result
You have to pass the self.MplWidget.canvas.axes as ax:
# ...
self.MplWidget.canvas.draw()
self.span = SpanSelector(
self.MplWidget.canvas.axes,
self.onselect,
"horizontal",
useblit=True,
rectprops=dict(alpha=0.5, facecolor="red"),
)
def onselect(self, min_value, max_value):
print(min_value, max_value)
Note: since select is a method of the class, it must have self as the first parameter, and it must be invoked with self.select.
I'm trying to plot a numpy array using QScatterSeries, however only the axis are updated and the points are not displayed. I'm not sure why it is not working.
projectionwindow.py
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QWidget, QHBoxLayout
from PySide2.QtGui import QColor, QPen
from PySide2.QtCharts import QtCharts
class ProjectionWindow(QWidget):
"""
TODO
"""
def __init__(self, parent=None) -> 'None':
super().__init__()
self.setWindowTitle('Projection')
self.resize(800, 800)
self.chart = QtCharts.QChart()
self.chart_view = QtCharts.QChartView(self.chart)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.chart_view)
self.setLayout(self.layout)
self.show()
def loadCharts(self, data: 'ndarray') -> 'None':
points = QtCharts.QScatterSeries()
points.setMarkerSize(2.0)
for i in range(data.shape[0]):
points.append(data[i, 0], data[i, 1])
self.chart.addSeries(points)
self.chart.createDefaultAxes()
self.chart.show()
This is my current result when calling
main.py
import sys
import numpy as np
from PySide2.QtWidgets import QApplication
from ui.projectionwindow import ProjectionWindow
if __name__ == "__main__":
app = QApplication(sys.argv)
data = np.array([[1,2],
[3,4]])
window = ProjectionWindow(app)
window.loadCharts(data)
sys.exit(app.exec_())
Resulted obtained:
You have 2 errors:
The markerSize is very small that makes it indistinguishable to the eye.
When establishing a series for the first time, the QChart takes the minimum rectangle so that in your case it is in the corners, so the solution is to change the minimum and maximum value of the axes considering an adequate margin.
def loadCharts(self, data: "ndarray") -> "None":
points = QtCharts.QScatterSeries()
points.setMarkerSize(20)
for i in range(data.shape[0]):
points.append(data[i, 0], data[i, 1])
self.chart.addSeries(points)
self.chart.createDefaultAxes()
m_x, M_x = min(data[:, 0]), max(data[:, 0])
m_y, M_y = min(data[:, 1]), max(data[:, 1])
ax = self.chart.axes(Qt.Horizontal, points)[0]
ax.setMin(m_x - 1)
ax.setMax(M_x + 1)
ay = self.chart.axes(Qt.Vertical, points)[0]
ay.setMin(m_y - 1)
ay.setMax(M_y + 1)
Output:
I'm using matplotlib with pyqt5 to draw data into 3 axes, and than user can make selection in one plot that will be shown in other two plots too. Since I'm working with big data (up to 10 millions of points), drawing selection could be slow, especially when I need to draw to scatterplot.
I am trying to use matplotlib blit function, but have some issues with result. Here is minimum simple example.
import matplotlib
matplotlib.use('Qt5Agg')
import numpy as np
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(self.static_canvas)
layout.addWidget(NavigationToolbar(self.static_canvas, self))
axes = self.static_canvas.figure.subplots(2, 1)
self.ax1 = axes[0]
self.ax2 = axes[1]
self.ax1.cla()
self.ax2.cla()
button = QtWidgets.QPushButton('Click me!')
button.clicked.connect(self.update_canvas_blit)
layout.addWidget(button)
# Fixing random state for reproducibility
np.random.seed(19680801)
# Create random data
N = 50000
x = np.random.rand(N)
y = np.random.rand(N)
self.ax1.scatter(x, y)
self.points = self.ax1.scatter([],[], s=5, color='red')
x = np.linspace(0, 1000, 100000)
self.ax2.plot(x, np.sin(x))
self.lines, = self.ax2.plot([],[], color='red')
self.static_canvas.draw()
self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)
def update_canvas_blit(self):
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
self.static_canvas.restore_region(self.background1)
self.points.set_offsets(np.c_[x,y])
self.ax1.draw_artist(self.points)
self.ax1.figure.canvas.blit(self.ax1.bbox)
self.static_canvas.restore_region(self.background2)
x = np.linspace(0, np.random.randint(500,1000), 1000)
self.lines.set_data(x, np.sin(x))
self.ax2.draw_artist(self.lines)
self.ax2.figure.canvas.blit(self.ax2.bbox)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
When clicking button, expected output should be still same background with random points/lines redrawing. In a way it is happening but there are some strange artifacts that looks like somehow axes are drawn to each other. But when I try to save it to .png, it will restore to good state.
The problem is that the snapshot of the background is taken at a moment in time where the figure has not yet been shown on screen. At that point the figure is 10 by 10 inches large. Later, it is shown inside the QMainWindow and resized to fit into the widget.
Only once that has happened, it makes sense to take the background snapshot.
One option is to use a timer of 1 second and only then copy the background. This would look as follows.
import numpy as np
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(self.static_canvas)
layout.addWidget(NavigationToolbar(self.static_canvas, self))
axes = self.static_canvas.figure.subplots(2, 1)
self.ax1 = axes[0]
self.ax2 = axes[1]
self.ax1.cla()
self.ax2.cla()
button = QtWidgets.QPushButton('Click me!')
button.clicked.connect(self.update_canvas_blit)
layout.addWidget(button)
# Fixing random state for reproducibility
np.random.seed(19680801)
# Create random data
N = 50000
x = np.random.rand(N)
y = np.random.rand(N)
self.ax1.scatter(x, y)
self.points = self.ax1.scatter([],[], s=5, color='red')
x = np.linspace(0, 1000, 100000)
self.ax2.plot(x, np.sin(x))
self.lines, = self.ax2.plot([],[], color='red')
self.static_canvas.draw()
self._later()
def _later(self, evt=None):
self.timer = self.static_canvas.new_timer(interval=1000)
self.timer.single_shot = True
self.timer.add_callback(self.update_background)
self.timer.start()
def update_background(self, evt=None):
self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)
def update_canvas_blit(self):
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
self.static_canvas.restore_region(self.background1)
self.points.set_offsets(np.c_[x,y])
self.ax1.draw_artist(self.points)
self.ax1.figure.canvas.blit(self.ax1.bbox)
self.static_canvas.restore_region(self.background2)
x = np.linspace(0, np.random.randint(500,1000), 1000)
self.lines.set_data(x, np.sin(x))
self.ax2.draw_artist(self.lines)
self.ax2.figure.canvas.blit(self.ax2.bbox)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
I'm trying to plot data of diferentes sensors en realtime,so I decided to plot the data using PyQtGraph in PyQt, in order to make it work with several sensor's data from differnte sources.
Searching example on the internet, i found one and i tried to adapt it ,
Because of QtGui.QApplication.instance().exec_(), which carries the inconvenient side effect of blocking the execution of the rest of the code after it. I tried to manage to used threads using Multiproccessing. I could make the rest of the code works, but how coud I update the plot using external data (Plo2D.update2),I tried to used multiprocessing.Queue , but I didn't work, instead appears massage of the window must be closed.
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
from numpy import arange
import pyqtgraph as pg
import sys
import multiprocessing
class Plot2D():
def __init__(self,):
self.traces = dict()
self.app = QtGui.QApplication([])
self.win = pg.GraphicsWindow(title="Dibujar")
self.win.resize(1000, 600)
self.win.setWindowTitle('Ejemplo')
pg.setConfigOptions(antialias=True)
#self.canvas = self.win.addPlot(title="Pytelemetry")
self.waveform1 = self.win.addPlot(title='WAVEFORM1', row=1, col=1)
self.waveform2 = self.win.addPlot(title='WAVEFORM2', row=2, col=1)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def set_plotdata(self, name, datax, datay):
if name in self.traces:
self.traces[name].setData(datax, datay)
else:
if name == '910D':
self.traces[name] = self.waveform1.plot(pen='c', width=3)
if name == 'MPU':
self.traces[name] = self.waveform2.plot(pen='c', width=3)
def update2(self):
# Trying to get external data
ptm1 = globals()['DatExt1']
ptm2 = globals()['DatExt2']
while ptm1.empty() is False:
self.data1 = ptm1.get()
self.set_plotdata('MPU', self.data1[0], self.data1[1])
# csvWriterG910D.writerows(Informa)
# file1.flush()
while ptm2.empty() is False:
self.data2 = ptm2.get()
self.set_plotdata('910D', self.data1[0], self.data1[1])
def animation(self):
timer = QtCore.QTimer()
timer.timeout.connect(self.update2)
timer.start(60)
self.start()
# It is thread started from main.py
def ShowData(Data1, Data2): # Data1,Data2 : multiprocessing.Queue
DatExt1 = Data1
DatExt2 = Data2
p = Plot2D()
p.animation()
the main.py:
if __name__ == '__main__':
Data1 = multiprocessing.Queue()
Data2 = multiprocessing.Queue()
Plottingdata = Process(target=PlotData.ShowData, args=(Data1, Data2, ))
Plottingdata.start()
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.sin(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t + i)
time.sleep(1)
Data1.put([t, s])
i = i + 0.1
thanks ind advanced for help
Instead of using MultiProcessing you should use MultiThreading, that is, create threads that are responsible for collecting data (in your example emulate data) and then send the data to the GUI by signals.
PlotData.py
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
import sys
class Plot2D(pg.GraphicsWindow):
def __init__(self):
pg.GraphicsWindow.__init__(self, title="Dibujar")
self.traces = dict()
self.resize(1000, 600)
pg.setConfigOptions(antialias=True)
#self.canvas = self.win.addPlot(title="Pytelemetry")
self.waveform1 = self.addPlot(title='WAVEFORM1', row=1, col=1)
self.waveform2 = self.addPlot(title='WAVEFORM2', row=2, col=1)
def set_plotdata(self, name, x, y):
if name in self.traces:
self.traces[name].setData(x, y)
else:
if name == "910D":
self.traces[name] = self.waveform1.plot(x, y, pen='y', width=3)
elif name == "MPU":
self.traces[name] = self.waveform2.plot(x, y, pen='y', width=3)
#QtCore.pyqtSlot(str, tuple)
def updateData(self, name, ptm):
x, y = ptm
self.set_plotdata(name, x, y)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
plot = Plot2D()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
main.py
import sys
from pyqtgraph.Qt import QtCore, QtGui
import threading
import numpy as np
import time
from PlotData import Plot2D
class Helper(QtCore.QObject):
changedSignal = QtCore.pyqtSignal(str, tuple)
def create_data1(helper, name):
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.sin(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t + i)
time.sleep(.1)
helper.changedSignal.emit(name, (t, s))
i = i + 0.1
def create_data2(helper, name):
t = np.arange(-3.0, 2.0, 0.01)
i = 0.0
while True:
s = np.cos(2 * 2 * 3.1416 * t) / (2 * 3.1416 * t - i)
time.sleep(.1)
helper.changedSignal.emit(name, (t, s))
i = i + 0.1
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
helper = Helper()
plot = Plot2D()
helper.changedSignal.connect(plot.updateData, QtCore.Qt.QueuedConnection)
threading.Thread(target=create_data1, args=(helper, "910D"), daemon=True).start()
threading.Thread(target=create_data2, args=(helper, "MPU"), daemon=True).start()
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()