Embedd Matplotlib chart in a PyQt GUI - python

I have written the following script which is creating an empty GUI with a button that calls a Matplotlib chart:
import sys
import os
from PyQt4 import QtGui
from PyQt4 import *
import matplotlib.pyplot as plt
class SmallGUI(QtGui.QMainWindow):
def __init__(self):
super(SmallGUI,self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(1010,800,1010,800)
self.setWindowTitle('Sample')
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.Background,QtCore.Qt.white)
self.setPalette(palette)
#Chart button
self.MyButton = QtGui.QPushButton(self)
self.MyButton.setGeometry(QtCore.QRect(88,65,110,20))
self.MyButton.setText('Create chart')
###############
QtCore.QObject.connect(self.MyButton,QtCore.SIGNAL("clicked(bool)"),self.makeChart)
self.show()
def makeChart(self):
plt.plot([1,2,3,4])
plt.ylabel('some numbers')
plt.show()
def main():
app = QtGui.QApplication(sys.argv)
sampleForm = SmallGUI()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I am wondering if there is any way to embedd the chart into the GUI. In other words, is it possible to make the chart showing attached to the user interface rather than popping up a new TK window?

Related

How do I make the matplotlib toolbar able to dock to the side in a pyqt5 window?

I have embedded a matplotlib figure in a pyqt5 window along with the toolbar, this is my code:
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from PyQt5 import QtWidgets
class PlotWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(PlotWindow, self).__init__(parent=parent)
self.figure = Figure(tight_layout=True)
self.canvas = FigureCanvas(self.figure)
self.setCentralWidget(self.canvas)
self.toolbar = NavigationToolbar(self.canvas, self)
self.addToolBar(self.toolbar)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mw = PlotWindow()
ax = mw.figure.add_subplot()
mw.show()
exit(app.exec_())
And this is what it looks like:
I can drag the toolbar to the bottom and dock it there:
But I can't dock it to the side (left or right).
This is something you can do with any other standard pyqt5 toolbar, why does it not work with the matplotlib toolbar? and how do I make it work?
I can do it in code by changing this line:
# self.addToolBar(self.toolbar)
self.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar)
The result is:
Which is what I'm after, but if I then undock the toolbar from the left side I can no longer dock it there again and can only dock it in top and at the bottom.
The QToolBar provided by matplotlib is configured to only allow dragging on the top and information as seen in the source code:
self.setAllowedAreas(QtCore.Qt.ToolBarArea(
_to_int(_enum("QtCore.Qt.ToolBarArea").TopToolBarArea) |
_to_int(_enum("QtCore.Qt.ToolBarArea").BottomToolBarArea)))
The solution is to establish all areas:
self.toolbar.setAllowedAreas(QtCore.Qt.ToolBarArea.AllToolBarAreas)

Embedding matplotlib figure in QtDesigner GUI

I am trying to muddle my way through embedding a matplotlib figure inside of a Qt GUI created using Qt Designer. I am already able to create the figure I want just through Python. I've created a basic GUI with widgets to allow me to select/load input files, and plot the data in those files in a matplotlib figure that is embedded in the GUI. I accomplish this by adding a blank widget to the GUI called plotwidget, and then calling the GraphInit class with this widget as input.
The problem I am currently facing is that while my plot shows up and updates as desired inside the GUI, it doesn't seem to pay attention to the size policy defined for it in either my Python code (where the FigureCanvas is created) or for the plotWidget widget in Qt Designer. I have been using this demo, among others, as a starting point for this project. However, all of these examples have generated the GUI entirely from within Python. I suspect I am doing something wrong in assigning the matplotlib figure to the widget, but I haven't been able to figure out what.
Code (import & plotting code removed, but the core where figure is added is there):
import sys
import os
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QMessageBox, QFileDialog, QPushButton, QLabel, QRadioButton, QDoubleSpinBox, QSpinBox, QWidget, QSizePolicy, QMainWindow
import PyQt5.uic
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import json
from datetime import datetime
import numpy as np
class LogViewer(QApplication):
def __init__(self):
QApplication.__init__(self, sys.argv)
self.ui = UI()
#staticmethod
def main():
vwr = LogViewer()
vwr.run()
def run(self):
self.ui.win.show() # Show the UI
self.ui.run() # Execute the UI run script
self.exec_()
class Graph_init(FigureCanvas):
def __init__(self, parent=None):
fig = Figure()
self.axes = fig.add_subplot(111)
self.compute_initial_figure()
self.axes.grid()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self, QSizePolicy.Expanding, QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class UI(object):
def __init__(self):
ui_path = os.path.dirname(os.path.realpath(__file__))
self.win = PyQt5.uic.loadUi(ui_path + '\\logview.ui')
self.filename = ui_path + '\\test.txt'
self.plotWin = Graph_init(self.win.plotWidget)
def run(self):
self.init_ui() # get initial values from the controllers
self.attach_ui_connections()
def init_ui(self):
w = self.win
w.txtLogFilename.setText(self.filename.split('/')[-1]) # just the file (no path)
def attach_ui_connections(self):
w = self.win
if __name__ == '__main__':
LogViewer.main()
GUI is available here. Any suggestions appreciated!
I checked your ui file and plotWidget has no layout at all. Try to give it one, I suggest a grid layout.
Then, after parenting the canvas
self.setParent(parent)
try adding it to the parent layout:
parent.layout().addWidget(self)

How to use matplotlib with PyQt4

I want to plot a figure with embedded matplotlib in PyQt.
I am using Qt Designer for the main window, and writing python code for the signal and slots connexion part.
So my code looks like this :
import sys
from PyQt4 import QtCore, QtGui, uic
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
qtCreatorFile = "main.ui" # my Qt Designer file
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.figure = plt.figure()
self.canvas = FigureCanvas(self.figure)
self.csvbutton.clicked.connect(self.plot)
def plot(self):
filePath="/path to csv file here"
df= pd.read_csv(str(filePath),index_col='date')
df.index = pd.to_datetime(df.index, unit='s')
ax = self.figure.add_subplot(111)
ax.hold(False)
ax.plot(df, '*-')
self.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
My main problem is the connexion between the Qt Designer file and the python code, I couldn't set a canvas widget directly in Qt Designer and I'm still struggling to find where the error lays in my code.
Your help is very appreciated, thank you.
In order to use matplotlib in Qt Designer can not be done directly, for this we must promote a QWidget to use FigureCanvas or better a class that inherits from it as I show below, first we create a class called Canvas in file called canvas.py:
canvas.py
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
class Canvas(FigureCanvas):
def __init__(self, parent=None):
self.figure = plt.figure()
FigureCanvas.__init__(self, self.figure)
self.setParent(parent)
After creating the design through Qt Designer, we have all the elements we want, but where we want to place the argument we use the Widget element that is in Containers, and we name it canvas:
Then we promote it by right click and choose the option promoted to ...:
Obtaining what is shown in the following image, in Promoted Class Name we place Canvas as the name of the class, and in Header File we place canvas.h (in Header File the file.py file is placed, for example package.subpackage.file.h), then press Add and after Promote:
At the end we get a file structure similar to the following:
.
├── canvas.py
└── main.ui
Then we create the file main.py where we place your code with small variations:
main.py
import matplotlib
matplotlib.use('Qt4Agg')
import sys
from PyQt4 import QtCore, QtGui, uic
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
qtCreatorFile = "main.ui" # my Qt Designer file
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.csvbutton.clicked.connect(self.plot)
def plot(self):
filePath="data.csv"
df= pd.read_csv(str(filePath),index_col='date')
ax = self.canvas.figure.add_subplot(111)
ax.hold(False)
ax.plot(df, '*-')
self.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
In the end we get the following:
You can find the complete project here
If you want to add the NavigationToolbar you can use the following code:
...
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
...
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.addToolBar(NavigationToolbar(self.canvas, self))
...

Updating Graph generated using Matplotlib by button click

I am working on a GUI application, which generates graphs using Matplotlib package, for gui design i am using PyQt5.
In this application users loads the data from a line and then on pressing the generate button, a processed graph is generated, now the problem is that, on closing the graph, when user loads the new data, and press the generate button, graph is not displayed again.
Code
import sys
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
plt.subplots_adjust(hspace=0)
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 100, 100)
self.setWindowTitle('Generate Graph')
self.home()
def home(self):
btn = QPushButton('Generate', self)
btn.clicked.connect(self.generate_graph)
#btn.resize(100, 100)
#btn.move(100, 100)
self.show()
def generate_graph(self):
# In real application these points gets updated
x = [0,1,2,3,4,5,6,7,8,9]
y1 = [0,1,2,3,4,5,6,7,8,9]
y2 = [0,1,2,3,4,5,6,7,8,9]
ax1.plot(x,y1)
ax2.plot(x,y2)
plt.show()
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
So i am posting the sample program which can show my problem, in this i created a button and generated two plots.
(Note: these are two subplots, i created two subplots because, i need to write ylabel on the adjacent axis, so it is a requirement i can't change and it must be like this)
I pressed the generate button, graph gets generated.
I closed the graph, and again pressed the generate button but its not re-generated.
Please suggest me what i can add to make this happen.
Is it possible to generate new graph every-time user presses the generate button, i think this will also solve the problem.
Please suggest and thanks in advance.
I had searched with this topic on this forum, and tried various thing like clearing the axis etc etc, but i think i am doing something wrong as i am new to all this.
You're mixing matplotlib.pyplot's show GUI with another PyQt GUI. The problem is that the figure to show in the matplotlib GUI is created only once. As soon as it's closed, it's lost.
The simple solution is to create it within the generate_graph function. Thereby a new figure is created and shown every time the button is pressed.
import sys
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
import matplotlib.pyplot as plt
class window(QMainWindow):
def __init__(self):
super(window, self).__init__()
self.setGeometry(50, 50, 100, 100)
self.setWindowTitle('Generate Graph')
self.home()
def home(self):
btn = QPushButton('Generate', self)
btn.clicked.connect(self.generate_graph)
self.show()
def generate_graph(self):
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
plt.subplots_adjust(hspace=0)
x = [0,1,2,3,4,5,6,7,8,9]
y1 = [0,1,2,3,4,5,6,7,8,9]
y2 = [0,1,2,3,4,5,6,7,8,9]
ax1.plot(x,y1)
ax2.plot(x,y2)
plt.show()
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()

Python script crashes when closing after running matplotlib with qt4agg because of import

I have written a script using matplotlib, which runs just fine with the standard matplotlib. The script is written with the plot as a class, and calling Plot() is enough to get it running.
Now I want to add some buttons to the toolbar, and to do this I am using qt4agg because I have installed matplotlib via Anaconda. When writing the code for the main window I used this example, and it runs just fine. In order to use the plot script I have already written I want to pass the figure created in the QT-script to the Plot()-class.
This solution works just fine, until I try to close the window. The window closes, and python crashes. It crashes even though I do not call the Plot()-class, and the only way to get it to not crash is to remove the line importing the file. Is there something special I need to think about when importing a script into a window?
from __future__ import print_function
import sys
from matplotlib.figure import Figure
from matplotlib.backend_bases import key_press_handler
### LINE CAUSING TROUBLE
from plotting import Plot
###
from test import *
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backends import qt_compat
use_pyside = qt_compat.QT_API == qt_compat.QT_API_PYSIDE
if use_pyside:
print ("USING PYSIDE")
from PySide.QtCore import *
from PySide.QtGui import *
else:
print("NOT USING PYSIDE")
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class AppForm(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.create_main_frame()
self.on_draw()
def create_main_frame(self):
self.main_frame = QWidget()
self.fig = Figure()
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self.main_frame)
self.canvas.setFocusPolicy(Qt.StrongFocus)
self.canvas.setFocus()
self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame)
self.canvas.mpl_connect('key_press_event', self.on_key_press)
vbox = QVBoxLayout()
vbox.addWidget(self.canvas)
vbox.addWidget(self.mpl_toolbar)
self.main_frame.setLayout(vbox)
self.setCentralWidget(self.main_frame)
def on_draw(self):
self.fig.clear()
#Plot(self.fig)
self.canvas.draw()
def on_key_press(self, event):
key_press_handler(event, self.canvas, self.mpl_toolbar)
def main():
app = QApplication(sys.argv)
form = AppForm()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Here is a very compressed version of the other file that still causes the error.
import matplotlib.pyplot as pplt
class Plot():
def __init__(self, figure=pplt.figure()):
self.figure = figure
i had the same problems with Anaconda and Python(x,y). then i tried to install Python from scratch :
python-2.7.10.msi
PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe
VCForPython27.msi
pip install matplotlib
it doesn't solve all crashes. you could also try this :
self.setAttribute(Qt.WA_DeleteOnClose)
for example :
class AppForm(QMainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.setAttribute(Qt.WA_DeleteOnClose) # <---
self.create_main_frame()
self.on_draw()
...

Categories