Reputation: 1065
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
Upvotes: 1
Views: 1228
Reputation: 244122
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())
Upvotes: 2