Display Matplotlib plots from Other File - python

I have a PyQt file that, on the click of a button, runs a function from another python file (let's call calc.py) that carries out a really advanced calculation and uses matplotlib to plot the result.
I would like to show this plot generated by calc.py in the PyQt window after the calculation is complete/the figure is generated. Although, I'm not sure the best way to carry this out given that the calculation and figure are made in another file.
pyqt.py
import PyQt5 . . .
from calc import do_calc
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.title_label = QLabel("Label Text", self) #random label in top of window
###code here to display matplotlib plot in my PyQt app####
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
calc.py
import matplotlib.pyplot as plt
def do_calc():
plt.figure()
for i in x:
... #really complex calculation here
plt.plot()
plt.draw()
I've been looking at other examples of how to display matplotlib plots in Qt but they usually orient around the calculation being done in the PyQt file or widget class which I can't really do in this instance. I can alter the calc.py file to return items or do anything else that might be helpful, but the calculations will likely need to stay in that file so it can be ran independently from the PyQt

The solution is a hack(may fail in the future) that assumes that the backend used by matplotlib in calc.py uses PyQt5, for this it is necessary to import PyQt5 first and then calc.py.
The logic is to make matplotlib not block the eventloop using plt.ion, and then search among the toplevels (windows) that have a FigureCanvas as their centralWidget.
calc.py
import matplotlib.pyplot as plt
import numpy as np
def do_calc():
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
fig, ax = plt.subplots()
ax.plot(t, s)
ax.set(
xlabel="time (s)",
ylabel="voltage (mV)",
title="About as simple as it gets, folks",
)
ax.grid()
plt.show()
main.py
import sys
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvas
from calc import do_calc
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.title_label = QLabel("Label Text")
central_widget = QWidget()
self.setCentralWidget(central_widget)
lay = QVBoxLayout(central_widget)
lay.addWidget(self.title_label)
plt.ion()
do_calc()
for tl in QApplication.topLevelWidgets():
if isinstance(tl, QMainWindow) and isinstance(
tl.centralWidget(), FigureCanvas
):
lay.addWidget(tl)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Another better option is to get all the Figures and then the canvas and finally the window of that canvas:
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.title_label = QLabel("Label Text")
central_widget = QWidget()
self.setCentralWidget(central_widget)
lay = QVBoxLayout(central_widget)
lay.addWidget(self.title_label)
plt.ion()
do_calc()
for i in plt.get_fignums():
canvas = plt.figure(i).canvas
if isinstance(canvas, QWidget):
lay.addWidget(canvas.window())

Related

Plotting a chart inside a PyQT GUI

I would like to plot a chart inside a pyQT GUI. I have already read a lot of tutorials, but those plot the chart in a separeted window. I need it to be ploted in the same window as the other buttons and fields.
In my main.py file, I import the Ui_MainWindow class generated with the QT designer. But I didn't figured out how to plot it inside the MainWindow
Python GUI
Matplotlib canvas class to create figure
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
FigureCanvas.setSizePolicy(self, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def atualizar(self):
global tempoInicio
tempoInicio = self.campoTempoInicio.dateTime()
print(tempoInicio.toPyDateTime())
x=range(0, 10)
y=range(0, 20, 2)
self.FigureCanvas.canvas.ax.plot(x, y)
self.FigureCanvas.canvas.draw()
I've tried as this, but it didn't worked as well (followed a tutorial)
Thank you in advance for reading this.
After looking into non-relatated tutorial, I've foud this one: https://www.pythonguis.com/tutorials/embed-pyqtgraph-custom-widgets-qt-app/
that teaches how to do what I need.
Don't now why it didn't appear before. But worked!
Here's a complete example that should help:
import sys
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
class PlotViewer(QtWidgets.QWidget):
doubleClickAction = QtCore.pyqtSignal(str)
def __init__(self, parent=None):
super(PlotViewer, self).__init__(parent)
self.figure = plt.figure(figsize=(5, 5))
self.figureCanvas = FigureCanvas(self.figure)
self.navigationToolbar = NavigationToolbar(self.figureCanvas, self)
# create main layout
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.navigationToolbar)
layout.addWidget(self.figureCanvas)
self.setLayout(layout)
# create an axis
x = range(0, 10)
y = range(0, 20, 2)
ax = self.figure.add_subplot(111)
ax.plot(x, y)
# show canvas
self.figureCanvas.show()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
widget = PlotViewer()
widget.show()
app.exec_()

Embeding matplotlib graph in pyqt5 widget [duplicate]

I am currently trying to embed a graph I want to plot in a pyqt4 user interface I designed. As I am almost completely new to programming - I do not get how people did the embedding in the examples I found - this one (at the bottom) and that one.
It would be awesome if anybody could post a step-by-step explanation or at least a very small, very simple code only creating e.g. a graph and a button in one pyqt4 GUI.
It is not that complicated actually. Relevant Qt widgets are in matplotlib.backends.backend_qt4agg. FigureCanvasQTAgg and NavigationToolbar2QT are usually what you need. These are regular Qt widgets. You treat them as any other widget. Below is a very simple example with a Figure, Navigation and a single button that draws some random data. I've added comments to explain things.
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import random
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = Figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
ax.clear()
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Edit:
Updated to reflect comments and API changes.
NavigationToolbar2QTAgg changed with NavigationToolbar2QT
Directly import Figure instead of pyplot
Replace deprecated ax.hold(False) with ax.clear()
Below is an adaptation of previous code for using under PyQt5 and Matplotlib 2.0.
There are a number of small changes: structure of PyQt submodules, other submodule from matplotlib, deprecated method has been replaced...
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
# ax.hold(False) # deprecated, see above
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
For those looking for a dynamic solution to embed Matplotlib in PyQt5 (even plot data using drag and drop). In PyQt5 you need to use super on the main window class to accept the drops. The dropevent function can be used to get the filename and rest is simple:
def dropEvent(self,e):
"""
This function will enable the drop file directly on to the
main window. The file location will be stored in the self.filename
"""
if e.mimeData().hasUrls:
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
for url in e.mimeData().urls():
if op_sys == 'Darwin':
fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path())
else:
fname = str(url.toLocalFile())
self.filename = fname
print("GOT ADDRESS:",self.filename)
self.readData()
else:
e.ignore() # just like above functions
For starters the reference complete code gives this output:

MatPlotlib inside PyQt5 QML

I have to use MatPlotlib in my project. However, I need to design QML Application in PyQt5 and as I understand matplotlib plots are Widgets. So, I need to use matplotlib plots inside QML.
My question is that Is there a way to display an interactive matplotlib plot in QML? (by interactive I mean not just a figure that has been saved as an image, ideally with the standard toolbar for zoom etc.)
In here, it asked before but it isn't answered. Can someone help me?
If you can live with widgets, it is not that much different from PyQT4. This answer has been greatly inspired by this thread.
The PyQT5-compatible code:
import sys, random
from PyQt5.QtWidgets import (
QMainWindow,
QApplication,
QWidget,
QVBoxLayout,
QPushButton,
QHBoxLayout,
)
import matplotlib
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class AppForm(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
# The main_frame keeps all widgets on itself
self.main_frame = QWidget()
# Create the interactive matplotlib figure
self.fig = Figure((10.0, 16.0), dpi=100)
# Create figure canvas that gets a reference to self.fig
# in its __init__
self.canvas = FigureCanvas(self.fig)
self.ax = self.fig.add_subplot(111)
# Create the matplotlib navigation toolbar
self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)
# VBox (V for vertical) where the canvas is placed above the toolbar
plotVbox = QVBoxLayout()
plotVbox.addWidget(self.canvas)
plotVbox.addWidget(self.mpl_toolbar)
# Another VBox with a button, could eventually hold more
# vertically ordered buttons
controls = QVBoxLayout()
self.button = QPushButton("Refresh")
self.button.clicked.connect(self.refresh)
controls.addWidget(self.button)
# HBox (h for horizontal) where the
# controls VBox with buttons is on the left
# and the VBox with the plot is on the riht
hbox = QHBoxLayout()
hbox.addLayout(controls)
hbox.addLayout(plotVbox)
self.main_frame.setLayout(hbox)
self.setCentralWidget(self.main_frame)
def refresh(self):
"""
Here, the functionality of the 'Refresh' button is implemented.
Note that we do not return anything, instead we modify the state
of the AppForm instance.
"""
self.ax.clear()
x = [random.random() for i in range(10)]
y = [random.random() for i in range(10)]
self.ax.plot(x, y, "o")
self.canvas.draw()
def main():
"""
Open the main window.
"""
app = QApplication(sys.argv)
form = AppForm()
form.show()
app.exec_()
if __name__ == "__main__":
main()

Pyplot crashes at interaction

I've got a pyplot which can be refreshed using a button. This works fine. But if I try to interact with the pyplot (move, resize window, zoom, etc.) it crashes:
Fatal Python error: PyEval_RestoreThread: NULL tstate
Is what eclipse says.
Below is a running minimal example to reproduce the fault. Since im a python novice my guess is that the problem is in my code.
Sys: Win7 64, Python 3.3.5, PyQt 5.2.1, Scipy-stack 14.4.1
# imports
import sys
from PyQt5.QtWidgets import (QApplication, QDialog, QCheckBox, QGridLayout, QPushButton)
import matplotlib.pyplot as plt
import numpy as np
class Dialog(QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent)
# elements
self.hold = QCheckBox("Hold")
self.hold.setCheckable(True)
self.refreshButton = QPushButton("Refresh")
self.refreshButton.clicked.connect(self.refresh)
# layout
layout = QGridLayout()
layout.setColumnStretch(1, 0)
layout.setColumnMinimumWidth(1, 10)
layout.addWidget(self.hold, 1, 0)
layout.addWidget(self.refreshButton, 1, 1)
self.setLayout(layout)
# open window
plt.figure()
plt.ion()
plt.show()
self.refresh()
def refresh(self):
# delete plot if hold is checked
if not self.hold.isChecked():
plt.clf()
# random plot
plt.plot(np.random.normal(10, .1, 100))
plt.grid(True)
plt.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Dialog()
dialog.show()
sys.exit(app.exec_())
Using plt.draw() instead of plt.show() makes no noticeable difference.

How to embed matplotlib in pyqt - for Dummies

I am currently trying to embed a graph I want to plot in a pyqt4 user interface I designed. As I am almost completely new to programming - I do not get how people did the embedding in the examples I found - this one (at the bottom) and that one.
It would be awesome if anybody could post a step-by-step explanation or at least a very small, very simple code only creating e.g. a graph and a button in one pyqt4 GUI.
It is not that complicated actually. Relevant Qt widgets are in matplotlib.backends.backend_qt4agg. FigureCanvasQTAgg and NavigationToolbar2QT are usually what you need. These are regular Qt widgets. You treat them as any other widget. Below is a very simple example with a Figure, Navigation and a single button that draws some random data. I've added comments to explain things.
import sys
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
import random
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = Figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QtGui.QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
ax.clear()
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Edit:
Updated to reflect comments and API changes.
NavigationToolbar2QTAgg changed with NavigationToolbar2QT
Directly import Figure instead of pyplot
Replace deprecated ax.hold(False) with ax.clear()
Below is an adaptation of previous code for using under PyQt5 and Matplotlib 2.0.
There are a number of small changes: structure of PyQt submodules, other submodule from matplotlib, deprecated method has been replaced...
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
''' plot some random stuff '''
# random data
data = [random.random() for i in range(10)]
# instead of ax.hold(False)
self.figure.clear()
# create an axis
ax = self.figure.add_subplot(111)
# discards the old graph
# ax.hold(False) # deprecated, see above
# plot data
ax.plot(data, '*-')
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
For those looking for a dynamic solution to embed Matplotlib in PyQt5 (even plot data using drag and drop). In PyQt5 you need to use super on the main window class to accept the drops. The dropevent function can be used to get the filename and rest is simple:
def dropEvent(self,e):
"""
This function will enable the drop file directly on to the
main window. The file location will be stored in the self.filename
"""
if e.mimeData().hasUrls:
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
for url in e.mimeData().urls():
if op_sys == 'Darwin':
fname = str(NSURL.URLWithString_(str(url.toString())).filePathURL().path())
else:
fname = str(url.toLocalFile())
self.filename = fname
print("GOT ADDRESS:",self.filename)
self.readData()
else:
e.ignore() # just like above functions
For starters the reference complete code gives this output:

Categories

Resources