Embedding matplotlib figure in QtDesigner GUI - python

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)

Related

Adding a leaflet map to PyQt5

I am new to Qt and PyQt and I appreciate it if someone can help me with this. I am trying to add a leaflet map to a GUI that I am creating using Qt designer and Python. Since there is no such widget, I create a simple Widget in the Qt Designer and promote it to a "LeafWidget" as shown below:
I save this file as (user_interface.ui) and then in my index.py file (under the same folder), I use the class leafWidget (with the same name as what defined in the Qt Designer) to define this new widget. I'm not sure if it is right or not. My "index.py" file that I run is as below.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from pyqtlet import L, MapWidget
from PyQt5.uic import loadUiType
ui,_=loadUiType('user_interface.ui')
class LeafWidget (QWidget):
def __init__(self):
QWidget.__init__(self)
self.mapWidget = MapWidget()
self.map = L.map(self.mapWidget)
self.map.setView([39.764075, -86.159019], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.layout = QVBoxLayout(self)
self.layout.addWidget(self.mapWidget)
self.show()
class MainApp(QMainWindow, ui):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
self.mainWindow_tabWidget.setCurrentIndex(1)
def main():
app=QApplication(sys.argv)
window = MainApp()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
When I run it, it does open up the window, but the place for the map widget is empty as you see below. Am I defining this class correctly? In my MainApp class, do I need to add this new custom widget somehow? I don't get any specific errors in my debug console, So I'm not sure what I am missing.

navigation tool bar inside widget

I would like to put a navigation toolbar inside a widget I created with Qt designer.
I have a GUI, made in QT designer, that has 10 tabs. Each tab has a widget that I promoted to canvas.
I would like to put a navigation toolbar inside each widget.
Up to now I have tried this.
But this code simply adds a navigation toolbar at the top of the GUI
import woop
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
from PyQt4 import Qt, QtCore,QtGui
from matplotlib import cm
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import os
class woop(QtGui.QMainWindow, woop.Ui_MainWindow):
"""
Main control function for Woop GUI.
"""
# ----------------------------
def __init__(self, parent=None):
"""
Setup the GUI, and connect the buttons to functions.
"""
import os
super(woop, self).__init__(parent)
self.setupUi(self)
toolBar1 = NavigationToolbar(self.widget_LID1, self)
toolBar2 = NavigationToolbar(self.widget_LID2, self)
toolBar3 = NavigationToolbar(self.widget_LID3, self)
toolBar4 = NavigationToolbar(self.widget_LID4, self)
toolBar5 = NavigationToolbar(self.widget_LID5, self)
toolBar6 = NavigationToolbar(self.widget_LID6, self)
toolBar7 = NavigationToolbar(self.widget_LID7, self)
toolBar8 = NavigationToolbar(self.widget_LID8, self)
toolBarALL = NavigationToolbar(self.widget_LID_ALL, self)
toolBarMIR = NavigationToolbar(self.widget_MIR, self)
self.addToolBar(toolBar1)
self.addToolBar(toolBar2)
self.addToolBar(toolBar3)
self.addToolBar(toolBar4)
self.addToolBar(toolBar5)
self.addToolBar(toolBar6)
self.addToolBar(toolBar7)
self.addToolBar(toolBar8)
self.addToolBar(toolBarALL)
self.addToolBar(toolBarMIR)
self.widget_LID1.figure.clear()
self.widget_LID1.draw()
self.widget_LID2.figure.clear()
self.widget_LID2.draw()
self.widget_LID3.figure.clear()
self.widget_LID3.draw()
self.widget_LID4.figure.clear()
self.widget_LID4.draw()
self.widget_LID5.figure.clear()
self.widget_LID5.draw()
self.widget_LID6.figure.clear()
self.widget_LID6.draw()
self.widget_LID7.figure.clear()
self.widget_LID7.draw()
self.widget_LID8.figure.clear()
self.widget_LID8.draw()
self.widget_LID_ALL.figure.clear()
self.widget_LID_ALL.draw()
self.widget_LID_14.figure.clear()
self.widget_LID_14.draw()
self.widget_LID_58.figure.clear()
self.widget_LID_58.draw()
self.widget_MIR.figure.clear()
self.widget_MIR.draw()
def main():
"""
Main function
the only input to the GUI is the debug
by default is set to INFO
"""
import sys
app = QtGui.QApplication(sys.argv)
MainWindow = woop()
#MainWindow.show()
MainWindow.showMaximized()
app.exec_()
if __name__ == '__main__':
main()
Try to add each toolbar directly to the corresponding widget using a QVBoxLayout as described in How to embed matplotlib in pyqt - for Dummies.
Or, if you still prefer to use the toolbar area of the QMainWindow, you need to connect the currentChanged signal of the QTabWidget to a slot replacing the toolbar by the one corresponding to the widget of the selected widget.
I managed to solve in this way.
I created this function
def _initialize_widget(self,widget):
"""
function that:
- initialises every tab (widget)
- add layout
- add navigation toolbar and position it at the bottom of the tab
:param widget:
:return:
"""
widget.figure.clear()
widget.draw()
widget.setLayout(QtGui.QVBoxLayout())
widget.layout().setContentsMargins(0, 710, 50, -0)#(left, top, right, bottom)
widget.layout().setSpacing(0)
toolbar = NavigationToolbar(widget, self)
widget.layout().addWidget(toolbar)
widget.figure.clear()
widget.draw()
and then I initialize the widgets (canvas)
self._initialize_widget(self.widget_LID1)
In this way I add a navigation toolbar at the bottom of the canvas.

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