Force background of matplotlib figure to be transparent - python

I'm trying to include a matplotlib figure in a Python Gtk3 application I'm writing. I'd like to set the background colour of the figure to be transparent, so that the figure just shows up against the natural grey background of the application, but nothing I've tried so far seems to be working.
Here's an MWE:
from gi.repository import Gtk
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import numpy as np
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
fig, ax = plt.subplots()
#fig.patch.set_alpha(0.0)
x,y = np.array([[0, 1], [0, 0]])
line = mlines.Line2D(x, y, c='#729fcf')
ax.add_line(line)
plt.axis('equal')
plt.axis('off')
fig.tight_layout()
sw = Gtk.ScrolledWindow()
sw.set_border_width(50)
canvas = FigureCanvas(fig)
sw.add_with_viewport(canvas)
layout = Gtk.Grid()
layout.add(sw)
self.add(layout)
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
If the fig.patch.set_alpha(0.0) line is uncommented, the colour of the background just changes to white, rather than grey. All suggestions greatly appreciated!

It seems to me that it's the axes background that needs to be hidden. You might try using ax.patch.set_facecolor('None') or ax.patch.set_visible(False).
Alternatively, have you tried setting both the figure and axes patches off? This may be accomplished by:
for item in [fig, ax]:
item.patch.set_visible(False)

I solved it this way. It's not ideal, but it works.
from gi.repository import Gtk
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import numpy as np
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
from matplotlib.colors import ColorConverter
class MyWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
fig, ax = plt.subplots()
#fig.patch.set_alpha(0.0)
x,y = np.array([[0, 1], [0, 0]])
line = mlines.Line2D(x, y, c='#729fcf')
ax.add_line(line)
plt.axis('equal')
plt.axis('off')
fig.tight_layout()
win = Gtk.Window()
style = win.get_style_context()
bg_colour = style.get_background_color(
Gtk.StateType.NORMAL).to_color().to_floats()
cc = ColorConverter()
cc.to_rgba(bg_colour)
fig.patch.set_facecolor(bg_colour)
sw = Gtk.ScrolledWindow()
sw.set_border_width(50)
canvas = FigureCanvas(fig)
sw.add_with_viewport(canvas)
layout = Gtk.Grid()
layout.add(sw)
self.add(layout)
win = MyWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

A combination of
fig.patch.set_visible(False)
self.setStyleSheet("background-color:transparent;")
works for me (self is a subclass of Canvas).
Any of these two alone do not work.

Related

PyQt5 with NavigationToolbar2QT missing change of color option for plt.fill_between()

how can I change the color of a plt.fill_between() in the according navigation toolbar? so basically add one more selection for the fill between curves additional to _line0 (see picture). alternatively, it would also be possible to change the error of fill_between according to the color of the line.
I tried the fill_between with facecolor=pl[0].get_color() but it did not change dynamically with a different color selection in the toolbar.
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import sys
import pandas as pd
import numpy as np
from scipy import stats
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.draw()
def initUI(self):
self.canvasFigure = plt.figure(1)
self.canvasWidget = FigureCanvas(self.canvasFigure)
self.toolbar = NavigationToolbar(self.canvasWidget, self)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.canvasWidget)
lay.addWidget(self.toolbar)
self.resize(500, 400)
def draw(self):
### generate random data
df = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df_group_error = stats.sem(df, axis=1, nan_policy='omit')
df_group = df.mean(axis=1)
### show in canvas
self.canvasFigure.clear()
fig = plt.figure(1)
pl = plt.plot(df_group)
plt.fill_between(df_group.index.tolist(), df_group - df_group_error, df_group + df_group_error, facecolor='red', alpha=0.2)
self.canvasWidget.draw()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
where change of fill_between color should be possible
You can write a callback that, whenever the figure is drawn, matches the color of the fill with the color of the line.
import matplotlib
matplotlib.use("Qt5Agg")
import numpy as np; np.random.seed(42)
from matplotlib.colors import to_hex
import matplotlib.pyplot as plt
x = np.linspace(0,100)
y = np.cumsum(np.random.randn(len(x)))+4
err = np.random.rand(len(x))*0.5 + 1
fig, ax = plt.subplots()
fillb = ax.fill_between(x, y+err, y-err, alpha=0.4, facecolor="red", edgecolor="none")
line, = ax.plot(x,y, color="blue")
def update(evnt=None):
c1 = line.get_color()
c2 = fillb.get_facecolor().ravel()
if to_hex(c1) != to_hex(c2):
fillb.set_facecolor(line.get_color())
fig.canvas.draw_idle()
cid = fig.canvas.mpl_connect("draw_event", update)
update()
plt.show()

unable to set matplotlib figure facecolor to none

As per matplotlib documentation, I am trying to set transparent background to figure and subplot using patch.set_facecolor('none')
It is working for subplot but not for figure.
def create_plot_win(self,label_list,set_prop=1,box=None):
ax_list=[]
fig1 = Figure(tight_layout=True)
fig1.patch.set_facecolor('none')
l=len(label_list)
for i in range(len(label_list)):
ax_list.append(fig1.add_subplot(l,1,i+1))
ax_list[-1].patch.set_facecolor('none')
canvas = FigureCanvas(fig1)
toolbar=NavigationToolbar(canvas,box)
box.pack_start(toolbar,False,False,0)
if l ==1:return canvas,fig1,toolbar,ax_list[0]
How can I set figure background as transparent?
I am working on windows-8.1 with python-2.7.
if i set figure facecolor to 'none', i am getting figure with a black background and my application background is having image.
For screen shot, click here screen shot
import pygtk
pygtk.require('2.0')
import gtk
import matplotlib
matplotlib.use('GtkAgg')
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as Canvas
import pylab
import cairo
class PyApp(gtk.Window):
def __init__(self):
super(PyApp, self).__init__()
self.set_position(gtk.WIN_POS_CENTER)
self.fig = matplotlib.pyplot.figure()
self.ax = self.fig.add_subplot(1,1,1)
self.canvas = Canvas(self.fig)
self.ax.plot([0,1,2,3,4],[0,1,4,9,16])
self.ax.patch.set_facecolor('none')
self.fig.patch.set_facecolor('none')
self.pltbox = gtk.VBox(False, 0)
self.pltbox.pack_start(self.canvas)
self.add(self.pltbox)
self.connect("destroy",gtk.main_quit)
self.show_all()
PyApp()
gtk.main()

Python: Animating a vector using mplot3d and animation

I'm trying to make a 3d plot with Matplotlib and the animation package from matplotlib. In addition, the animation should be a part of a Gui generated using PyQt and Qt-Designer. Currently I'm stuck on using the "animation.Funcnimation()" correctly, at least i think so...
So here is my code:
import sys
from PyQt4.uic import loadUiType
from PyQt4 import QtGui
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation
import numpy as np
import Quaternion as qt
Ui_MainWindow, QMainWindow = loadUiType('Newsphere.ui')
class Kinematic(Ui_MainWindow, QMainWindow):
def __init__(self):
super(Kinematic, self).__init__()
self.setupUi(self)
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.fig.tight_layout()
self.ani = animation.FuncAnimation(self.fig, self.update,
init_func=self.setup_plot, blit=True)
self.canvas = FigureCanvas(self.fig)
self.mplvl.addWidget(self.canvas)
self.canvas.draw()
def setup_plot(self):
self.ax.view_init(40, 45)
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.ax.set_zlabel('Z')
self.ax.set_xlim3d(-1,1)
self.ax.set_ylim3d(-1,1)
self.ax.set_zlim3d(-1,1)
g_x = np.matrix([[1.0],[0.0],[0.0]])
g_y = np.matrix([[0.0],[1.0],[0.0]])
g_z = np.matrix([[0.0],[0.0],[1.0]])
self.ax.plot([0,g_x[0]], [0,g_x[1]], [0,g_x[2]], label='$X_0$')
self.ax.plot([0,g_y[0]], [0,g_y[1]], [0,g_y[2]], label='$Y_0$')
self.ax.plot([0,g_z[0]], [0,g_z[1]], [0,g_z[2]], label='$Z_0$')
self.vek, = self.ax.plot([0,-1], [0,0], [0,0], label='$g \cdot R$', animated=True)
self.ax.legend(loc='best')
self.ax.scatter(0,0,0, color='k')
return self.vek,
def update(self, i):
b = self.bslider.value() / 100
g = np.matrix([[1.0],[0.0],[0.0]])
q = np.array([0,b,0.5,0])
R = qt.QtoR(q)
x, y, z = R*g
self.vek, = self.ax.plot([0,x], [0,y], [0,z], label='$g \cdot R$', animated=True) #the rotated vector
return self.vek,
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Kinematic()
main.show()
sys.exit(app.exec_())
You won't be able to run it by just copy-paste because you don't have the file "Newsphere.ui" (Line 13) and the Quaternion.py (Line 11). So when I run it, I get the following (actually like I wish!):
Coordinate system
My goal is now to draw a vector (Line 50) and animate it (Line 66) using data which I get from the Gui-slider (Line 58). Can anyone help me with this? I'm stuck with this for days!
Since your problem is with the animation part, below you can see a snippet that animate an arrow that is rotating.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def data_gen(num):
"""Data generation"""
angle = num * np.pi/36
vx, vy, vz = np.cos(angle), np.sin(angle), 1
ax.cla()
ax.quiver(0, 0, 0, vx, vy, vz, pivot="tail", color="black")
ax.quiver(0, 0, 0, vx, vy, 0, pivot="tail", color="black",
linestyle="dashed")
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.view_init(elev=30, azim=60)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
data_gen(0)
ani = animation.FuncAnimation(fig, data_gen, range(72), blit=False)
plt.show()
The documentation on animations might not be the best. But there are several examples out there, for example, this one animates the Lorenz attractor.
So if someone is interested in a solution of the mentioned problem, here we go: (again it is not a code for copy-paste because of the missing 'Newsphere.ui', but I try to explain the important snippets)
import sys
from PyQt4.uic import loadUiType
from PyQt4 import QtGui
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import animation
import numpy as np
Ui_MainWindow, QMainWindow = loadUiType('Newsphere.ui')
class Kinematic(Ui_MainWindow, QMainWindow):
def __init__(self):
super(Kinematic, self).__init__()
self.setupUi(self)
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111,projection = '3d')
self.fig.tight_layout()
self.ax.view_init(40, -45)
# dashed coordinate system
self.ax.plot([0,1], [0,0], [0,0], label='$X_0$', linestyle="dashed")
self.ax.plot([0,0], [0,-1], [0,0], label='$Y_0$', linestyle="dashed")
self.ax.plot([0,0], [0,0], [0,1], label='$Z_0$', linestyle="dashed")
self.ax.set_xlim3d(-1,1)
self.ax.set_ylim3d(-1,1)
self.ax.set_zlim3d(-1,1)
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.ax.set_zlabel('Z')
self.ax.scatter(0,0,0, color='k') # black origin dot
self.canvas = FigureCanvas(self.fig)
self.mplvl.addWidget(self.canvas)
self.ani = animation.FuncAnimation(self.fig, self.data_gen, init_func=self.setup_plot, blit=True)
def setup_plot(self):
self.ax.legend(loc='best')
self.vek = self.ax.quiver(0, 0, 0, 0, 0, 0, label='$g \cdot R$', pivot="tail", color="black")
return self.vek,
def data_gen(self, i):
b = self.bslider.value() / 100
vx, vy, vz = np.cos(b), np.sin(b), 0
self.vek = self.ax.quiver(0, 0, 0, vx, vy, vz, label='$g \cdot R$', pivot="tail", color="black")
self.canvas.draw()
return self.vek,
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Kinematic()
main.show()
sys.exit(app.exec_())
By running the code I get
(These are two pictures combined in one showing the same process)
I generated a GUI file named Newsphere.ui following this tutorial. Basically it contains just a Widget, a QSlider and a QSpinBox. The Widget has the layout-name "mplvl", which occures in line 41. This adds the generated figure to the widget (I think so...). The QSlider is connected with the QSpinBox (done in QtDesigner) and has the name "bslider", line 55. So in this line the slider-value gets divided by 100 because I didn't found a slider that generates me a float-value. The key-line for me was line 61, where the canvas is drawn. Now the animation.FuncAnimation (line 43) draws a new vector when I change the slider value, compare the pics. Also it is important to draw the changing vector as a ax.quiver and not as a ax.plot like in my previous attempt.
If there are questions or suggestions for improvement please ask/post.

How can I improve scrolling speed in matplotlib when a figure contains many axes?

I have a matplotlib figure with many axes, and the scrolling/zooming becomes unusably slow. Is there anyway to speed it up?
As an example, try scrolling one of the axes produced with this code:
import matplotlib.pyplot as plt
fig,plts = plt.subplots(10,10)
plt.show()
(I am on a Mac, using the macosx backend. The QT4Agg backend seemed similarly sluggish.)
I think the slowdown comes from matplotlib redrawing the entire figure, rather than just the subplot you want to zoom. I have found that you can speed things up by creating multiple figures and embedding them in a PyQt widget.
Here's a quick proof of concept using 'figure_enter_event' and a bit of ugly hackery to allow the use of a single navigation toolbar across all figures. Note that I have only attempted to make the pan and zoom features work properly. By peeking at the source of NavigationToolbar2 in backend_bases.py some more I'm sure you could adapt it to your needs.
import sys
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import pyqtSlot
import matplotlib
matplotlib.use('Qt5Agg')
matplotlib.rcParams['backend.qt5'] = 'PyQt5'
matplotlib.rcParams.update({'figure.autolayout': True})
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, **kwargs):
super(MainWindow, self).__init__(**kwargs)
# Construct the plots
playout = QtWidgets.QGridLayout()
playout.setContentsMargins(0, 0, 0, 0)
for row in range(0, 10):
for col in range(0, 10):
fig = Figure()
ax = fig.add_subplot(111)
canvas = FigureCanvas(fig)
canvas.mpl_connect('figure_enter_event', self.enterFigure)
playout.addWidget(canvas, row, col, 1, 1)
t = np.arange(-2*np.pi, 2*np.pi, step=0.01)
ax.plot(t, np.sin(row*t) + np.cos(col*t))
# Assign toolbar to first plot
self.navbar = NavigationToolbar(playout.itemAtPosition(0, 0).widget(), self)
cwidget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(cwidget)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.navbar)
layout.addLayout(playout)
self.setCentralWidget(cwidget)
def enterFigure(self, event):
self.navbar.canvas = event.canvas
event.canvas.toolbar = self.navbar
self.navbar._idDrag = event.canvas.mpl_connect('motion_notify_event', self.navbar.mouse_move)
# Toggle control off and then on again for the current canvas
if self.navbar._active:
if self.navbar._active == 'PAN':
self.navbar.pan()
self.navbar.pan()
elif self.navbar._active == 'ZOOM':
self.navbar.zoom()
self.navbar.zoom()
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
app.exec_()

How to make Matplotlib redraw faster?

I have to plot a grid of subplots (16x16 ones, for example). I use matplotlib but It is a bit too slow, especially when I have to redraw the plots (for example, when their size is changed). How can I make it faster?
P.S. Here is an example of code:
import sys
import matplotlib
matplotlib.use('Qt4Agg')
import pylab
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import PyQt4
from PyQt4 import QtCore, QtGui, Qt
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
# generate the plot
fig = Figure(figsize=(800, 800), facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
for i in range(256):
ax = fig.add_subplot(16, 16, i)
ax.plot([0, 1])
ax.set_xticks([])
ax.set_yticks([])
# ax.set_title("Mega %i" % (i,))
# generate the canvas to display the plot
canvas = FigureCanvas(fig)
canvas.setMinimumWidth(640)
canvas.setMinimumHeight(640)
# generate layout
layout = QtGui.QVBoxLayout();
layout.addWidget(canvas)
layout.setGeometry(QtCore.QRect(0, 0, 1000, 1000))
# generate widget
widget = QtGui.QWidget()
widget.setLayout(layout)
# generate scroll area
win = QtGui.QScrollArea()
win.setWidget(widget)
win.setMinimumWidth(100)
win.setWidgetResizable(True)
win.show()
canvas.draw()
sys.exit(app.exec_())
I don't have an environment to test your code but I these steps work for me:
Using cProfile to profile the whole thing (f.e. How can you profile a python script?).
Usually its one or two functions which slow down all.
Search stackoverflow/the internet for these function names. Usually 1-2 people solved the performance issue already.
Greetings Kuishi

Categories

Resources