error in adding matplotlib widget into pyqt4 - python

I was trying to add a custom widget into qtdesginer using following code
from PyQt4 import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import PySide
from matplotlib.figure import 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, QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
class MplWidget(QtGui.QWidget):
def __init__(self, parent = None):
QtGui.QWidget.__init__(self, parent)
self.canvas = MplCanvas()
self.vbl = QtGui.QVBoxLayout()
self.vbl.addWidget(self.canvas)
self.setLayout(self.vbl)
But i just give me an error of
TypeError: 'PySide.QtGui.QWidget.setSizePolicy' called with wrong argument types:
PySide.QtGui.QWidget.setSizePolicy(Policy, Policy)
Supported signatures:
PySide.QtGui.QWidget.setSizePolicy(PySide.QtGui.QSizePolicy)
PySide.QtGui.QWidget.setSizePolicy(PySide.QtGui.QSizePolicy.Policy, PySide.QtGui.QSizePolicy.Policy)
I am not exactly sure what caused the error, since i bascially followed this part http://packtlib.packtpub.com/library/9781847197900/ch06lvl1sec04
Any suggestions would be good,since i am new to this qt designer.

I've had good luck inheriting FigureCanvas with super(...).__init__() in my custom matplotlib Widgets rather than the BaseClass.__init__(self) method. Your widget worked for me with a few minor changes:
class MplCanvas(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
super(MplCanvas, self).__init__(self.fig)
self.setSizePolicy(QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
self.updateGeometry()
Also... I agree with #tcaswell that you should probably pick either PyQt4 or PySide and avoid importing both :)

Related

How to detect pan and zoom action in matplotlib navibar?

I am using the matplotlib canvas and navbar by creating a custom MplWidget in python, as follows:
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
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)
self.canvas.toolbar = NavigationToolbar(self.canvas, self)
self.layout().addWidget(self.canvas.toolbar)
self.layout().addWidget(self.canvas)
self.canvas.axes.grid(b=True, which='both', axis='both')
self.canvas.figure.set_tight_layout(True)
I want to detect when the pan or the zoom tool is toggled. I found this: in matplotlib how do I catch that event "zoom tool" has been selected?
Following the solution there, I tried
self.canvas.toolbar.get_state()['_current_action']
or simply just self.canvas.toolbar.get_state(), but I get the error:
AttributeError: 'NavigationToolbar2QT' object has no attribute 'get_state'
It seems like a very basic function to see which action is in use currently, so I am sure there is a simple solution, but I can't seem to find it.
zoom toggled
I've found two solutions, but I don't know if they work with all kinds of backends (not an expert in those).
The first is axes-dependant:
ax.get_navigate_mode()
it returns 'PAN', 'ZOOM', or None.
(Link to documentation)
The second is figure-dependant:
fig.canvas.toolbar.mode
it returns "pan/zoom", "zoom rect" or "".
(Link to source code of the NavigationToolbar2 object)

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)

Multiple embedded matplotlib canvases in pyqt change size on mouse over

I'm trying to embed multiple matplotlib plots in a multi-column layout in a PyQt GUI. At first sight I succeed in setting up the layout as wanted but when moving the mouse over any of the canvasses they change size and 'flicker'. When pressing the zoom button on the toolbar this becomes more pronounced.
For each matplotlib canvas I have connected a toolbar. If I do not connect the toolbars the problem does not appear. I have tried arranging the toolbars and canvases several ways - with a QGridLayout or nested QVBoxLayouts and QHBoxLayouts. Either way the problem appears if there are plots along side each other. If I put all plots in a single column it does not.
I have tried this in Python 3.6 in Windows (Anaconda 5.0.1) with PyQt4 and Python 3.5.2 in Linux (KDE Neon 64 bit) and with both PyQt4 and PyQt5 (v. 5.7.1), matplotlib 1.5.1 but with the same result. I have also tried using add_axes instead of add_subplot. Can someone help me understand what is causing this or find some kind of workaround? I can not use matplotlib subplots.
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
figure1 = Figure()
figure2 = Figure()
figure3 = Figure()
figure4 = Figure()
canvas1 = FigureCanvas(figure1)
canvas2 = FigureCanvas(figure2)
canvas3 = FigureCanvas(figure3)
canvas4 = FigureCanvas(figure4)
ax1 = figure1.add_subplot(111)
ax2 = figure2.add_subplot(111)
ax3 = figure3.add_subplot(111)
ax4 = figure4.add_subplot(111)
toolbar1 = NavigationToolbar(canvas1, self)
toolbar2 = NavigationToolbar(canvas2, self)
toolbar3 = NavigationToolbar(canvas3, self)
toolbar4 = NavigationToolbar(canvas4, self)
mainLayout = QtWidgets.QGridLayout()
mainLayout.addWidget(toolbar1,0,0)
mainLayout.addWidget(toolbar2,0,1)
mainLayout.addWidget(toolbar3,2,0)
mainLayout.addWidget(toolbar4,2,1)
mainLayout.addWidget(canvas1,1,0)
mainLayout.addWidget(canvas2,1,1)
mainLayout.addWidget(canvas3,3,0)
mainLayout.addWidget(canvas4,3,1)
self.setLayout(mainLayout)
self.setWindowTitle("Flow Layout")
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWin = Window()
mainWin.show()
sys.exit(app.exec_())
I had a similar issue and I solved it by:
toolbar1.setMinimumWidth(canvas1.width())
toolbar2.setMinimumWidth(canvas2.width())
toolbar3.setMinimumWidth(canvas3.width())
toolbar4.setMinimumWidth(canvas4.width())
The problem is when the toolbar becomes wider than 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))
...

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