Getting PySide to work with matplotlib - python

I have tried running the example code on the SciPy website, but I get this error:
Traceback (most recent call last):
File ".\matplotlibPySide.py", line 24, in <module>
win.setCentralWidget(canvas)
TypeError: 'PySide.QtGui.QMainWindow.setCentralWidget' called with wrong argument types:
PySide.QtGui.QMainWindow.setCentralWidget(FigureCanvasQTAgg)
Supported signatures:
PySide.QtGui.QMainWindow.setCentralWidget(PySide.QtGui.QWidget)
I am building a simple scientific data logger that will eventually be used in commercial applications, so I really need both the LGPL from PySide and plotting functionality. Does anyone have experience on how to get this to work or alternative plotting packages or ideas?
Thanks in advance.

The example that you mention:
http://www.scipy.org/Cookbook/Matplotlib/PySide
works, but you might need to suggest the use of PySide:
...
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
import pylab
...

I had similar goals (LGPL, potential commercial use) and here's how I ended up getting it to work.
Create a matplotlib widget (see here for a more detailed one for PyQt):
import matplotlib
matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4']='PySide'
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
class MatplotlibWidget(FigureCanvas):
def __init__(self, parent=None,xlabel='x',ylabel='y',title='Title'):
super(MatplotlibWidget, self).__init__(Figure())
self.setParent(parent)
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
self.axes = self.figure.add_subplot(111)
self.axes.set_xlabel(xlabel)
self.axes.set_ylabel(ylabel)
self.axes.set_title(title)
In Qt Designer I created a blank widget to hold my plot and then when I __init__ the main window I call setupPlot:
def setupPlot(self):
# create a matplotlib widget
self.DataPlot = MatplotlibWidget()
# create a layout inside the blank widget and add the matplotlib widget
layout = QtGui.QVBoxLayout(self.ui.widget_PlotArea)
layout.addWidget(self.DataPlot,1)
Then I call plotDataPoints as needed:
def plotDataPoints(self,x,y):
self.DataPlot.axes.clear()
self.DataPlot.axes.plot(x,y,'bo-')
self.DataPlot.draw()
Note: this clears and redraws the entire plot every time (since the shape of my data keeps changing) and so isn't fast.

I think you may have posted this on the matplotlib mailing list. But just in case someone else is looking for the answer. The best option is to use the master branch on Github, but if you can't or don't know how to work the Github version you can use the following code to render a plot in PySide.
import numpy as np
from matplotlib import use
use('AGG')
from matplotlib.transforms import Bbox
from matplotlib.path import Path
from matplotlib.patches import Rectangle
from matplotlib.pylab import *
from PySide import QtCore,QtGui
rect = Rectangle((-1, -1), 2, 2, facecolor="#aaaaaa")
gca().add_patch(rect)
bbox = Bbox.from_bounds(-1, -1, 2, 2)
for i in range(12):
vertices = (np.random.random((4, 2)) - 0.5) * 6.0
vertices = np.ma.masked_array(vertices, [[False, False], [True, True], [False, False], [False, False]])
path = Path(vertices)
if path.intersects_bbox(bbox):
color = 'r'
else:
color = 'b'
plot(vertices[:,0], vertices[:,1], color=color)
app = QtGui.QApplication(sys.argv)
gcf().canvas.draw()
stringBuffer = gcf().canvas.buffer_rgba(0,0)
l, b, w, h = gcf().bbox.bounds
qImage = QtGui.QImage(stringBuffer,
w,
h,
QtGui.QImage.Format_ARGB32)
scene = QtGui.QGraphicsScene()
view = QtGui.QGraphicsView(scene)
pixmap = QtGui.QPixmap.fromImage(qImage)
pixmapItem = QtGui.QGraphicsPixmapItem(pixmap)
scene.addItem(pixmapItem)
view.show()
app.exec_()

Related

Matplotlib FuncAnimation not working in windowed application using PyQT5

I have a windowed application that, when a button is pressed, is supposed to trigger an animation of a 3d scatter plot using matplotlib. Here's my code:
from __future__ import print_function
from PyQt5.QtWidgets import QApplication, QLabel, QVBoxLayout, QWidget, QListWidget, QPushButton, QMainWindow
from PyQt5.QtCore import QObject
from matplotlib.animation import FuncAnimation
from matplotlib import pyplot as plt
import platform
import sys
import six
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
i = 1
def main():
global device_app, device_listbox, device_win_conf_button, device_win
device_app = QApplication(sys.argv) # Initialize QApp
device_win = QWidget()
device_win_layout = QVBoxLayout() # Layout
device_win_conf_button = QPushButton("Proceed")
device_app.aboutToQuit.connect(quit)
device_win_conf_button.clicked.connect(plotAnimator)
device_win_layout.addWidget(device_win_conf_button)
device_win.setLayout(device_win_layout) # Assign layout to window and resize
device_win.resize(500, 500)
device_win.show()
device_app.exec_() # Enter loop
def plotAnimator():
global sc_plot, x, y, z
fig = plt.figure() # Create matplotlib fig
ax = fig.add_subplot(111, projection='3d') # Add plot
ax.set_title("Example")
ax.set_xlim3d(-10, 10) # Set bounds
ax.set_ylim3d(-10, 10)
ax.set_zlim3d(-10, 10)
x = [0]
y = [0]
z = [0]
sc_plot = ax.scatter(x,y,z) # Start at 0, 0, 0
print(2)
animation = FuncAnimation(fig, updatePlot, frames=2, interval=100)
plt.show()
def updatePlot(frame):
global i
x.append(i)
y.append(i)
z.append(i)
sc_plot._offsets3d = (x, y, z)
i += 1
main()
So when I run this and click the button, the scatter plot shows up with the single point I've initialized it with at (0, 0, 0). I can interact with the plot, rotating it around and zooming in and out, yet there's no animation. Further, the following is printed to the console when I click the "Proceed" button:
QCoreApplication::exec: The event loop is already running.
I've researched this error and from what I understand it comes from trying to execute a QApplication that's already being executed. I don't see that happening in my code. The only thing I can think of is that maybe matplotlib uses PyQT to create its windows? Even if that was the case, though, I'm having trouble thinking about how I would fix it.
Any help is greatly appreciated, thanks!

Embedding matplotlib into tkinter canvas opens two windows

The following code I am working on in not behaving the way I wish it to. I have embedded a matplotlib graph into a tkinter canvas. The program opens up two windows, one of which functions properly, and one of which is not necessary.I am not sure how to fix this. Here is the code, please ignore the unnecessary imports :)
import numpy as np
import sys
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib as mpl
from matplotlib import cm
from numpy.random import random
from matplotlib.widgets import Button
import matplotlib.colors
import tkinter as tk
import matplotlib.backends.tkagg as tkagg
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
DEBUG_MODE = False #Debug mode - True = ON
MATRIX_WIDTH = 50
MATRIX_HEIGHT = 50
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
LED_COUNT = MATRIX_WIDTH * MATRIX_HEIGHT
REFRESH_RATE = 30 #REFRESH_RATE used to control FuncAnimation interval
MATRIX = random((50,50)) #Fills MATRIX as not to be null for first print
plt.rcParams['toolbar'] = 'None' #Disables matplotlib toolbar
fig = plt.figure(figsize=(3,3)) #'figsize' measured in inches
im = plt.imshow(MATRIX, interpolation='nearest', cmap=cm.Spectral)
plt.axis('off') #Turns off x, y axis
def data_gen(): #Generates amd populates MATRIX with pattern data
while True:
MATRIX = random((MATRIX_WIDTH, MATRIX_HEIGHT))
yield MATRIX
if (DEBUG_MODE): print("MATRIX yeilded")
def update(data): #Updates/prints new MATRIX from data_gen()
im.set_array(data)
if (DEBUG_MODE): print("Updated data")
root = tk.Tk()
label = tk.Label(root,text="Matrix Program").grid(column=0, row=0)
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(column=0,row=1)
ani = animation.FuncAnimation(fig, update, data_gen, interval=REFRESH_RATE)
plt.show()
What needs to be done to this code so that it opens only one canvas from tkinter with the live matplotlib graph embedded?
How can I set the size of the canvas?
Do not call plt.show() if you want to show your figure inside a tk GUI. Best do not use pyplot at all when embedding.
On the other hand, you probably want to start the mainloop, tk.mainloop(), at some point.
Refer to the matplotlib example on how to embedd a matplotlib figure into tk.

How to save an array as an image in PyQt

I am writing an application in PyQt where I display some graphs with matplotlib. To do so, I use the following code :
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg
import matplotlib.figure as fig
self.IntFig = fig.Figure()
self.IntBeamCanvas = FigureCanvasQTAgg(self.IntFig)
self.AxesIntInit = self.IntFig.add_subplot(111)
self.AxesIntInit.hold(False)
self.AxesIntInit.imshow(self.Int,extent =[-xx/2+xx/N,xx/2,-xx/2+xx/N,xx/2])
self.IntBeamCanvas.draw()
Later in the code I manage to save the figure created with the following code :
fname = QtGui.QFileDialog.getSaveFileName(self,'Save Intensity','C:' )
self.IntFig.savefig(str(fname))
But this saves only the figures (with its axes I mean). What if I want to save only the data
self.Int
that is displayed? I know the the pyplot.imsave method but don't know how to use it here, since I do not use pyplot but figure.Figure.
Does anyone has an idea?
You can save the image by following method:
import numpy as np
import pylab as pl
y, x = np.ogrid[-1:1:50j, -1:1:100j]
z = np.sqrt(x*x + y*y)
im = pl.imshow(z)
img = im.make_image()
h, w, s = img.as_rgba_str()
a = np.fromstring(s, dtype=np.uint8).reshape(h, w, 4)
pl.imsave("tmp.png", a)
the saved image:

Python Matplotlib : Just the graph image

I cannot get pyplot to produce "cropped" images, that is, get rid of the grey left and right borders, as it is, it is not an accurate representation of the sound waveform : This sound file has no silence before and after.
My code :
import gtk
from matplotlib.figure import Figure
from numpy import arange, sin, pi
import scipy.io.wavfile as wavfile
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
win = gtk.Window()
win.connect("destroy", lambda x: gtk.main_quit())
win.set_default_size(400,300)
win.set_title("Cropping figure")
rate, data = wavfile.read(open('/home/px/gare_du_nord-catchlak.wav', 'r'))
f = Figure()
a = f.add_subplot(111, axisbg=(0.1843, 0.3098, 0.3098))
a.plot(range(len(data)),data, color="OrangeRed", linewidth=0.5, linestyle="-")
a.axis('off')
a.autoscale_view('tight')
canvas = FigureCanvas(f) # a gtk.DrawingArea
win.add(canvas)
win.show_all()
gtk.main()
OK, I got my answer :
f.subplots_adjust(0, 0, 1, 1)
EDIT:
This also works, in fact it works even better:
a.margins(0, 0)

matplotlib show() doesn't work twice

I have a strange problem, with matplotlib. If I run this program, I'm able to open and close several time the same figure.
import numpy
from pylab import figure, show
X = numpy.random.rand(100, 1000)
xs = numpy.mean(X, axis=1)
ys = numpy.std(X, axis=1)
fig = figure()
ax = fig.add_subplot(111)
ax.set_title('click on point to plot time series')
line, = ax.plot(xs, ys, 'o', picker=5) # 5 points tolerance
def onpick(event):
figi = figure()
ax = figi.add_subplot(111)
ax.plot([1,2,3,4])
figi.show()
fig.canvas.mpl_connect('pick_event', onpick)
show()
On the contrary, if I use the same code of onpick function into my custom widget it opens the figure only the first time, into the other events it enters into the functions but doesn't display the figure:
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
import time
STEP = 0.000152
class MplCanvas(FigureCanvas):
def __init__(self):
# initialization of the canvas
FigureCanvas.__init__(self, Figure())
self.queue = []
self.I_data = np.array([])
self.T_data = np.array([])
self.LvsT = self.figure.add_subplot(111)
self.LvsT.set_xlabel('Time, s')
self.LvsT.set_ylabel('PMT Voltage, V')
self.LvsT.set_title("Light vs Time")
self.LvsT.grid(True)
self.old_size = self.LvsT.bbox.width, self.LvsT.bbox.height
self.LvsT_background = self.copy_from_bbox(self.LvsT.bbox)
self.LvsT_plot, = self.LvsT.plot(self.T_data,self.I_data)
#self.LvsT_plot2, = self.LvsT.plot(self.T_data2,self.I_data2)
self.mpl_connect('axes_enter_event', self.enter_axes)
self.mpl_connect('button_press_event', self.onpick)
self.count = 0
self.draw()
def enter_axes(self,event):
print "dentro"
def onpick(self,event):
print "click"
print 'you pressed', event.canvas
a = np.arange(10)
print a
print self.count
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(a)
fig.show()
def Start_Plot(self,q,Vmin,Vmax,ScanRate,Cycles):
self.queue = q
self.LvsT.clear()
self.LvsT.set_xlim(0,abs(Vmin-Vmax)/ScanRate*Cycles)
self.LvsT.set_ylim(-3, 3)
self.LvsT.set_autoscale_on(False)
self.LvsT.clear()
self.draw()
self.T_data = np.array([])
self.I_data = np.array([])
# call the update method (to speed-up visualization)
self.timerEvent(None)
# start timer, trigger event every 1000 millisecs (=1sec)
self.timerLvsT = self.startTimer(3)
def timerEvent(self, evt):
current_size = self.LvsT.bbox.width, self.LvsT.bbox.height
if self.old_size != current_size:
self.old_size = current_size
self.LvsT.clear()
self.LvsT.grid()
self.draw()
self.LvsT_background = self.copy_from_bbox(self.LvsT.bbox)
self.restore_region(self.LvsT_background, bbox=self.LvsT.bbox)
result = self.queue.get()
if result == 'STOP':
self.LvsT.draw_artist(self.LvsT_plot)
self.killTimer(self.timerLvsT)
print "Plot finito LvsT"
else:
# append new data to the datasets
self.T_data = np.append(self.T_data,result[0:len(result)/2])
self.I_data = np.append(self.I_data,result[len(result)/2:len(result)])
self.LvsT_plot.set_data(self.T_data,self.I_data)#L_data
#self.LvsT_plot2.set_data(self.T_data2,self.I_data2)#L_data
self.LvsT.draw_artist(self.LvsT_plot)
self.blit(self.LvsT.bbox)
class LvsT_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)
This widget is needed for an animation plot and when the experiment is finished if I click on the plot it should appear a figure, that appears only the first time.
Do you have any clue?
Thank you very much.
At the start of your code, enable interactive mode via
plt.ion()
I have new information about this that a google search turned up
This is from the writer of matplotlib. This came from http://old.nabble.com/calling-show%28%29-twice-in-a-row-td24276907.html
Hi Ondrej,
I'm not sure where to find a good
explanation of that, but let me give
you some hints. It is intended to use
show only once per program. Namely
'show' should be the last line in your
script. If you want interactive
plotting you may consider interactive
mode (pyplot.ion-ioff) like in the
example below.
Furthermore for dynamic plotting all
animation demos might be useful.
Maybe you want to have also a look at
http://matplotlib.sourceforge.net/users/shell.html
.
best regards Matthias
So it seems it is an undocumented "feature" (bug?).
Edit: here is his code block:
from pylab import *
t = linspace(0.0, pi, 100)
x = cos(t)
y = sin(t)
ion() # turn on interactive mode
figure(0)
subplot(111, autoscale_on=False, xlim=(-1.2, 1.2), ylim=(-.2, 1.2))
point = plot([x[0]], [y[0]], marker='o', mfc='r', ms=3)
for j in arange(len(t)):
# reset x/y-data of point
setp(point[0], data=(x[j], y[j]))
draw() # redraw current figure
ioff() # turn off interactive mode
show()
So maybe by using draw() you can get what you want. I haven't tested this code, I'd like to know its behavior.
I had the same issue with show() only working the first time. Are you still on version 0.99.3 or thereabouts? I was able to resolve my problem recently, if you're still interested in changing the behaviour of show(), try this:
I noticed this paragraph titled multiple calls to show supported on the what's new part of the matplotlib download site.
A long standing request is to support multiple calls to show(). This has been difficult because it is hard to get consistent behavior across operating systems, user interface toolkits and versions. Eric Firing has done a lot of work on rationalizing show across backends, with the desired behavior to make show raise all newly created figures and block execution until they are closed. Repeated calls to show should raise newly created figures since the last call. Eric has done a lot of testing on the user interface toolkits and versions and platforms he has access to, but it is not possible to test them all, so please report problems to the mailing list and bug tracker.
This was 'what's new' for version 1.0.1, at time of writing the version in synaptic was still on 0.99.3. I was able to download and build from source v1.0.1. The additional packages I also required to satisfy dependencies were libfreetype6-dev tk-dev tk8.5-dev tcl8.5-dev python-gtk2-dev; your mileage may vary.
Now that i have matplotlib.__version__ == 1.0.1 , the following code works how I would expect:
from matplotlib import pyplot as p
from scipy import eye
p.imshow(eye(3))
p.show()
print 'a'
p.imshow(eye(6))
p.show()
print 'b'
p.imshow(eye(9))
p.show()
print 'c'
def onpick(self,event):
print "click"
print 'you pressed', event.canvas
...
ax.plot(a)
fig.show() # <--- this blocks the entire loop
Try:
def onpick(self,event):
print "click"
print 'you pressed', event.canvas
...
ax.plot(a)
self.draw()
self.update()
My workaround to this problem is to never call close.
I'm pretty sure you can control the transparency of a widget in PyQt. You might try controlling the visibility using Qt instead of matplotlib. I'm sure someone else who knows more about matplotlib can give a better answer than that though :D
You can create a figure instance by:
fig = plt.figure(0)
And draw your stuff by manipulate this fig.
You can use fig.show() for anytime to show your figure.

Categories

Resources