replace Figure in FigureCanvas? - python

Is it possible to replace Figure in FigureCanvas (see funcs plot & plot_data)?
I thought, it's possible to use fig.set_canvas(self) (see plot func), but it doesn't work.
PS: I know, it's possible to clear figure with self.axes.clear(), but it doesn't fit me. I'd like to save Figure in some map in the future (caching)
class Canvas(FigureCanvas):
def __init__(self, parent = None, width = 5, height = 5, dpi = 100):
self.fig_width = width
self.fig_height = height
self.fig_dpi = dpi
fig = Figure(figsize=(self.fig_width, self.fig_height), dpi=self.fig_dpi)
self.axes = fig.subplots(2,3)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
def plot(self, datatest_data):
fig = self.plot_data(new_data)
fig.set_canvas(self)
def plot_data(self, new_data):
len_keys = len(new_data.index)
fig = Figure(figsize=(30, 5*len_keys), dpi=self.fig_dpi)
self.axes = fig.subplots(len_keys, 2)
.....
return fig

Related

list reference to mpl.figure.Figure does not show plot

I am trying to handle a list of figures with an object.
Unfortunately there seems to be a problem with plotting from a list of figures.
Please comment out the line in the example below and you see how the plotting breaks:
import matplotlib as mpl
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg, FigureManagerQT
class Test:
def __init__(self):
self.figs = [mpl.figure.Figure(),mpl.figure.Figure()]
self.fig = mpl.figure.Figure()
ax = self.fig.subplots()
ax.plot([1,2],[3,4])
def show(self):
fig = self.fig # works
# fig = self.figs[0] # does not work
canvas = FigureCanvasQTAgg(fig)
figManager = FigureManagerQT(canvas, 0)
a=Test()
a.show()
Result (this is what I want):
Result with line uncommented:
In some other tests I found it might be connected with destructing the object. As a list is a mutable object, this might be the connection.
I also tried (unsuccessfully) several workarounds to copy the figure object for plotting:
I used something like fig = myCopy(self.figs[0])
in combination with a pickle-copy.
Can you please give me some explanation of what is happening and what might be a workaround?
In __init__, you give axes to self.fig and plot to this Axes object:
class Test:
def __init__(self):
self.figs = [mpl.figure.Figure(),mpl.figure.Figure()]
self.fig = mpl.figure.Figure()
ax = self.fig.subplots()
ax.plot([1,2],[3,4])
The figure objects in self.figs have no Axes object attached to them, so they're basically empty.
As a result, what you see is an empty figure:
def show(self):
fig = self.figs[0] # This is a figure with no axes
canvas = FigureCanvasQTAgg(fig)
figManager = FigureManagerQT(canvas, 0)
The problem with your logic is that it's not really meaningful to plot data in the __init__ method.
Your workflow should be:
Initialization
Figure selection
Plot
Show
I suggest that you add two methods, select_figure and plot, so as to improve the overall usability of your figure manager:
class Test:
def __init__(self):
self.fig = None
self.figures = [mpl.figure.Figure(), mpl.figure.Figure()]
def select_figure(self, index):
self.fig = self.figures[index]
def plot(self, x, y):
ax = self.fig.subplots()
ax.plot(x, y)
def show(self):
canvas = FigureCanvasQTAgg(self.fig)
figManager = FigureManagerQT(canvas, 0)
Then you can implement the workflow I described above:
test = Test()
test.select_figure(0)
test.plot([1, 2], [3, 4])
test.show()
test.select_figure(1)
test.plot([3, 4], [5, 6])
test.show()

How to aesthetically show a generic number of axes in matplotlib?

I want to do a simple GUI that allows the user to add or remove traces from a plot for any number of traces. It looks like this:
The problems I'm having:
I don't know how to make the axes not to superpose with each other for a generic number of plots.
When I plot more than one trace, and then delete all but one, there are two axes showing for some reason. There should always be one axis per trace being shown.
Is there a way to fix these issues? You can find my code below. The only function that should be changed is update_canvas(), I believe. To try it out, just modify the list name_vars in the main with the number of variables you want. The rest of the example code is self-contained.
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(ApplicationWindow, self).__init__(parent)
global name_vars
self.x = np.array([1,2,3,4,5])
self.y = np.random.random((5, len(name_vars)))
self.num_vars = np.size(self.y,1)
self.name_vars = name_vars
self.tags_on = [0] * self.num_vars
self.colors = ['#1F77B4','#FF7F0E','#2CA02C','#D62728','#9467BD',
'#8C564B','#E377C2','#F7F7F7','#BCBD22','#17BECF']
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
canvas = FigureCanvas(Figure(figsize=(10, 10)))
self.canvas_ax = canvas.figure.subplots()
self.canvas_ax.set_xlabel("Time")
self.canvas_ax_twin = []
self.list_tags = QtWidgets.QComboBox(self)
for name in self.name_vars:
self.list_tags.addItem(name)
button_add = QtWidgets.QPushButton('Add', self)
button_remove = QtWidgets.QPushButton('Remove', self)
button_add.clicked.connect(self.add_plot)
button_remove.clicked.connect(self.remove_plot)
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(canvas, 0, 0)
dropdown_layout = QtWidgets.QHBoxLayout()
dropdown_layout.addWidget(self.list_tags)
dropdown_layout.addWidget(button_add)
dropdown_layout.addWidget(button_remove)
layout.addLayout(dropdown_layout, 1, 0)
self.show()
def add_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 1
self.update_canvas()
def remove_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 0
self.update_canvas()
def update_canvas(self):
# Delete all traces
self.canvas_ax.clear()
[i.clear() for i in self.canvas_ax_twin]
self.canvas_ax_twin = []
num_plots = 0
for ii in range(self.num_vars):
if self.tags_on[ii] == 1:
# If it's not the first trace, create a twin axis
if num_plots != 0:
self.canvas_ax_twin.append(self.canvas_ax.twinx())
self.canvas_ax_twin[-1].plot(self.x, self.y[:,ii], self.colors[num_plots])
self.canvas_ax_twin[-1].set_ylabel(self.name_vars[ii])
self.canvas_ax_twin[-1].yaxis.label.set_color(self.colors[num_plots])
self.canvas_ax_twin[-1].tick_params(axis='y', colors=self.colors[num_plots])
num_plots += 1
# If it's the first trace, use the original axis
else:
self.canvas_ax.plot(self.x, self.y[:,ii], self.colors[num_plots])
self.canvas_ax.set_ylabel(self.name_vars[ii])
self.canvas_ax.yaxis.label.set_color(self.colors[num_plots])
self.canvas_ax.tick_params(axis='y', colors=self.colors[num_plots])
num_plots += 1
# Show the final plot
self.canvas_ax.figure.canvas.draw()
if __name__ == '__main__':
# Edit the number of elements in name_vars to try the code
name_vars = ['V1','V2','V3','V4']
app = QtWidgets.QApplication([])
ex = ApplicationWindow()
ex.show()
app.exec_()
I would suggest to separate the logic from the actual plotting. This makes it easier to follow through. This solves the second question about not removing all axes.
The question about not letting the axes superimpose may be solved by setting the position of additional twin axes to some distance from the axes, depending on how many axes you have.
ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))
where n is the axes number starting from 0. The main axes (n=0) should be excluded, and the first axes will stay at position 1. Further axes are positionned in steps of 0.1.
Then it makes sense to also adjust the right margin of the main axes to give enough space for the extra spines.
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None, name_vars=[]):
super(ApplicationWindow, self).__init__(parent)
self.x = np.array([1,2,3,4,5])
self.y = np.random.random((5, len(name_vars)))
self.num_vars = np.size(self.y,1)
self.name_vars = name_vars
self.tags_on = [0] * self.num_vars
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.figure = Figure(figsize=(10, 10))
canvas = FigureCanvas(self.figure)
self.left = self.figure.subplotpars.left
self.right = self.figure.subplotpars.right
self.canvas_ax = canvas.figure.subplots()
self.canvas_ax.set_xlabel("Time")
self.axes = [self.canvas_ax]
self.list_tags = QtWidgets.QComboBox(self)
for name in self.name_vars:
self.list_tags.addItem(name)
button_add = QtWidgets.QPushButton('Add', self)
button_remove = QtWidgets.QPushButton('Remove', self)
button_add.clicked.connect(self.add_plot)
button_remove.clicked.connect(self.remove_plot)
layout = QtWidgets.QGridLayout(self._main)
layout.addWidget(canvas, 0, 0)
dropdown_layout = QtWidgets.QHBoxLayout()
dropdown_layout.addWidget(self.list_tags)
dropdown_layout.addWidget(button_add)
dropdown_layout.addWidget(button_remove)
layout.addLayout(dropdown_layout, 1, 0)
self.show()
def add_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 1
self.update_canvas()
def remove_plot(self):
selected_tag = self.list_tags.currentIndex()
self.tags_on[selected_tag] = 0
self.update_canvas()
def create_nth_axes(self, n, dataset):
if n == 0:
ax = self.canvas_ax
else:
ax = self.canvas_ax.twinx()
ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))
for direction in ["left", "bottom", "top"]:
ax.spines[direction].set_visible(False)
# adjust subplotparams to make space for new axes spine
new_right = (self.right-self.left)/(1+(n-1)*0.1)+self.left
self.figure.subplots_adjust(right=new_right)
color = next(self.canvas_ax._get_lines.prop_cycler)['color']
ax.set_ylabel(self.name_vars[dataset], color=color)
ax.plot(self.x, self.y[:,dataset], color=color)
return ax
def clear_canvas(self):
# Clear main axes
self.canvas_ax.clear()
# clear and remove other axes
for ax in self.axes[1:]:
ax.clear()
ax.remove()
self.axes = [self.canvas_ax]
self.figure.subplots_adjust(right=0.9)
def update_canvas(self):
self.clear_canvas()
k = 0
for i, tag in enumerate(self.tags_on):
if tag:
ax = self.create_nth_axes(k, i)
if k > 0:
self.axes.append(ax)
k += 1
self.canvas_ax.figure.canvas.draw()
if __name__ == '__main__':
# Edit the number of elements in name_vars to try the code
name_vars = ['V1','V2','V3','V4']
app = QtWidgets.QApplication([])
ex = ApplicationWindow(name_vars=name_vars)
ex.show()
app.exec_()

Dynamically update multiple axis in matplotlib

I want to display sensor data on a PyQT GUI with a matplotlib animation.
I already have a working Plot which gets updates every time I receive new sensor value from an external source with this code:
def __init__(self):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = []
self.ydata = []
self.entry_limit = 50
self.line, = self.axes.plot([0], [0], 'r')
def update_figure_with_new_value(self, xval: float, yval: float):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
I want now to extend the plot to show another data series with the same x-axis. I tried to achieve this with the following additions to the init-code above:
self.axes2 = self.axes.twinx()
self.y2data = []
self.line2, = self.axes2.plot([0], [0], 'b')
and in the update_figure_with_new_value() function (for test purpose I just tried to add 1 to yval, I will extend the params of the function later):
self.y2data.append(yval+1)
if len(self.y2data) > self.entry_limit:
self.y2data.pop(0)
self.line2.set_data(self.xdata, self.ydata)
self.axes2.relim()
self.axes2.autoscale_view()
But instead of getting two lines in the plot which should have the exact same movement but just shifted by one I get vertical lines for the second plot axis (blue). The first axis (red) remains unchanged and is ok.
How can I use matplotlib to update multiple axis so that they display the right values?
I'm using python 3.4.0 with matplotlib 2.0.0.
Since there is no minimal example available, it's hard to tell the reason for this undesired behaviour. In principle ax.relim() and ax.autoscale_view() should do what you need.
So here is a complete example which works fine and updates both scales when being run with python 2.7, matplotlib 2.0 and PyQt4:
import numpy as np
import matplotlib.pyplot as plt
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = Figure(figsize=(5,4), dpi=100)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = [0]
self.ydata = [0]
self.entry_limit = 50
self.line, = self.axes.plot([], [], 'r', lw=3)
self.axes2 = self.axes.twinx()
self.y2data = [0]
self.line2, = self.axes2.plot([], [], 'b')
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.show()
self.ctimer = QtCore.QTimer()
self.ctimer.timeout.connect(self.update)
self.ctimer.start(150)
def update(self):
y = np.random.rand(1)
self.update_figure_with_new_value(self.xdata[-1]+1,y)
def update_figure_with_new_value(self, xval,yval):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.y2data.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.y2data.append(yval+np.random.rand(1)*0.17)
self.line2.set_data(self.xdata, self.y2data)
self.axes2.relim()
self.axes2.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
if __name__ == "__main__":
qapp = QtGui.QApplication([])
a = Window()
exit(qapp.exec_())
You may want to test this and report back if it is working or not.

Connect Qslider and MatplotlibWidget

I am a real beginner in python and try to connect a qslider to a matplotlibwidget.
That means if I change the value of the slider the graph should change. It seems that the value changes correctly, while the graph stays the same. Can anyone tell me how to connect the change of the slider with the graph? Here is my algorithm so far:
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 04 16:48:12 2014
#author: Christoph
"""
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure
from matplotlib import rcParams
import numpy as np
import scipy.constants as const
import sys
rcParams['font.size'] = 9
class MatplotlibWidget(Canvas):
"""
MatplotlibWidget inherits PyQt4.QtGui.QWidget
and matplotlib.backend_bases.FigureCanvasBase
Options: option_name (default_value)
-------
parent (None): parent widget
title (''): figure title
xlabel (''): X-axis label
ylabel (''): Y-axis label
xlim (None): X-axis limits ([min, max])
ylim (None): Y-axis limits ([min, max])
xscale ('linear'): X-axis scale
yscale ('linear'): Y-axis scale
width (4): width in inches
height (3): height in inches
dpi (100): resolution in dpi
hold (False): if False, figure will be cleared each time plot is called
Widget attributes:
-----------------
figure: instance of matplotlib.figure.Figure
axes: figure axes
Example:
-------
self.widget = MatplotlibWidget(self, yscale='log', hold=True)
from numpy import linspace
x = linspace(-10, 10)
self.widget.axes.plot(x, x**2)
self.wdiget.axes.plot(x, x**3)
"""
def __init__(self, parent=None, title='', xlabel='', ylabel='',
xlim=None, ylim=None, xscale='linear', yscale='linear',
width=4, height=3, dpi=100, hold=False):
self.figure = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.figure.add_subplot(111)
self.axes.set_title(title)
self.axes.set_xlabel(xlabel)
self.axes.set_ylabel(ylabel)
if xscale is not None:
self.axes.set_xscale(xscale)
if yscale is not None:
self.axes.set_yscale(yscale)
if xlim is not None:
self.axes.set_xlim(*xlim)
if ylim is not None:
self.axes.set_ylim(*ylim)
self.axes.hold(hold)
Canvas.__init__(self, self.figure)
self.setParent(parent)
Canvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
Canvas.updateGeometry(self)
def sizeHint(self):
w, h = self.get_width_height()
return QtGui.QSize(w, h)
def minimumSizeHint(self):
return QtGui.QSize(10, 10)
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# Graphics Window
self.mpl = MatplotlibWidget(self, title='Graph',
xlabel='x',
ylabel='y',
hold=True)
self.mpl.setGeometry(0,0,1300,800)
self.setGeometry(0, 30, 1680, 800)
# Slider Resistance
title1=QtGui.QLabel(self)
title1.setText('R')
title1.move(1400,10)
self.value1=QtGui.QLabel(self)
self.value1.setText('1')
self.value1.move(1550,40)
cb=QtGui.QSlider(QtCore.Qt.Horizontal, self)
cb.setGeometry(1400,40,100,30)
cb.setMinimum(1)
cb.setMaximum(10000)
cb.valueChanged.connect(self.Rout)
self.plot(1, self.mpl.axes)
def Rout(self, position):
self.value1.setText('%i' %position)
self.plot(position, self.mpl.axes)
def plot(self, R, axes):
x=np.linspace(0,5,1001)
B=0.035
n1=0.115
H=2.06227451e-15
n2=1.37040209e-01
gamma=0.001*const.e
C=0.13
x=np.array(x)
diodetheo = H*(np.exp((const.e*x*n2)/(const.k*300))-1)
zaehler = 1+np.exp((B-C+n1*x)*const.e/(const.k*300))
nenner = 1+np.exp((B-C-n1*x)*const.e/(const.k*300))
A=8.7476434*10**(29)*gamma
D=gamma/2
klammer2 = (const.pi/2)+np.arctan((C-n1*x)/D)
y1 = A*np.log(zaehler/nenner)*klammer2
# plt.figure()
# plt.plot(x, diodetheo, 'g')
# plt.show()
indup=[]
inddown=[]
iup=[]
idown=[]
theo = (y1+diodetheo)*(10**(-12))*(100)/4
for i, Volt in enumerate(x):
xup=np.linspace(0,Volt,i+1)
last=Volt/R-xup/R
diff=np.array(last)-np.array(theo[0:i+1])
inter=np.where(np.diff(np.sign(diff)))[0]
if inter.size==0:
inter=np.array([0])
indup.append(inter[0])
inddown.append(inter[-1])
iup.append(theo[inter[0]])
idown.append(theo[inter[-1]])
up = np.array(iup)
down = np.array(idown)
down=np.flipud(down)
ytotal=np.concatenate((up, down))
xneg=np.flipud(x)
xtotal=np.concatenate((x,xneg))
#plt.figure()
#plt.plot(xtotal, ytotal, 'g')
#plt.show()
axes.plot(xtotal, ytotal, 'r')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = ApplicationWindow()
win.show()
sys.exit(app.exec_())
Greetings
Christoph
OK I've made some changes to your code in order to make it works:
Change the function definition def plot(self, R, axes): for def plot(self, R):
Change the call self.plot(position, self.mpl.axes) on Rout accordingly to self.plot(position)
Change the final line axes.plot(xtotal, ytotal, 'r') on the previous plot function by:
self.mpl.axes.clear() #clear the previous plot
self.mpl.axes.plot(xtotal, ytotal, 'r') #replot
self.mpl.figure.canvas.draw() #redraw the canvas
Explanations:
First, you don't need to pass the axes to the plot function if it's already an attribute of self.mpl and is easy accessible. Second, when you make another plot you need to clear the previous one and also refresh the canvas.
Next all the code with modifications for easy use of copy-paste:
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as Canvas
from matplotlib.figure import Figure
from matplotlib import rcParams
import numpy as np
import scipy.constants as const
import sys
rcParams['font.size'] = 9
class MatplotlibWidget(Canvas):
"""
MatplotlibWidget inherits PyQt4.QtGui.QWidget
and matplotlib.backend_bases.FigureCanvasBase
Options: option_name (default_value)
-------
parent (None): parent widget
title (''): figure title
xlabel (''): X-axis label
ylabel (''): Y-axis label
xlim (None): X-axis limits ([min, max])
ylim (None): Y-axis limits ([min, max])
xscale ('linear'): X-axis scale
yscale ('linear'): Y-axis scale
width (4): width in inches
height (3): height in inches
dpi (100): resolution in dpi
hold (False): if False, figure will be cleared each time plot is called
Widget attributes:
-----------------
figure: instance of matplotlib.figure.Figure
axes: figure axes
Example:
-------
self.widget = MatplotlibWidget(self, yscale='log', hold=True)
from numpy import linspace
x = linspace(-10, 10)
self.widget.axes.plot(x, x**2)
self.wdiget.axes.plot(x, x**3)
"""
def __init__(self, parent=None, title='', xlabel='', ylabel='',
xlim=None, ylim=None, xscale='linear', yscale='linear',
width=4, height=3, dpi=100, hold=False):
self.figure = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.figure.add_subplot(111)
self.axes.set_title(title)
self.axes.set_xlabel(xlabel)
self.axes.set_ylabel(ylabel)
if xscale is not None:
self.axes.set_xscale(xscale)
if yscale is not None:
self.axes.set_yscale(yscale)
if xlim is not None:
self.axes.set_xlim(*xlim)
if ylim is not None:
self.axes.set_ylim(*ylim)
self.axes.hold(hold)
Canvas.__init__(self, self.figure)
self.setParent(parent)
Canvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
Canvas.updateGeometry(self)
def sizeHint(self):
w, h = self.get_width_height()
return QtGui.QSize(w, h)
def minimumSizeHint(self):
return QtGui.QSize(10, 10)
class ApplicationWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# Graphics Window
self.mpl = MatplotlibWidget(self, title='Graph',
xlabel='x',
ylabel='y',
hold=True)
self.mpl.setGeometry(0,0,1300,800)
self.setGeometry(0, 30, 1680, 800)
# Slider Resistance
title1=QtGui.QLabel(self)
title1.setText('R')
title1.move(1400,10)
self.value1=QtGui.QLabel(self)
self.value1.setText('1')
self.value1.move(1550,40)
cb=QtGui.QSlider(QtCore.Qt.Horizontal, self)
cb.setGeometry(1400,40,100,30)
cb.setMinimum(1)
cb.setMaximum(10000)
cb.valueChanged.connect(self.Rout)
self.plot(1)
def Rout(self, position):
self.value1.setText('%i' %position)
self.plot(position)
def plot(self, R):
x=np.linspace(0,5,1001)
B=0.035
n1=0.115
H=2.06227451e-15
n2=1.37040209e-01
gamma=0.001*const.e
C=0.13
x=np.array(x)
diodetheo = H*(np.exp((const.e*x*n2)/(const.k*300))-1)
zaehler = 1+np.exp((B-C+n1*x)*const.e/(const.k*300))
nenner = 1+np.exp((B-C-n1*x)*const.e/(const.k*300))
A=8.7476434*10**(29)*gamma
D=gamma/2
klammer2 = (const.pi/2)+np.arctan((C-n1*x)/D)
y1 = A*np.log(zaehler/nenner)*klammer2
# plt.figure()
# plt.plot(x, diodetheo, 'g')
# plt.show()
indup=[]
inddown=[]
iup=[]
idown=[]
theo = (y1+diodetheo)*(10**(-12))*(100)/4
for i, Volt in enumerate(x):
xup=np.linspace(0,Volt,i+1)
last=Volt/R-xup/R
diff=np.array(last)-np.array(theo[0:i+1])
inter=np.where(np.diff(np.sign(diff)))[0]
if inter.size==0:
inter=np.array([0])
indup.append(inter[0])
inddown.append(inter[-1])
iup.append(theo[inter[0]])
idown.append(theo[inter[-1]])
up = np.array(iup)
down = np.array(idown)
down=np.flipud(down)
ytotal=np.concatenate((up, down))
xneg=np.flipud(x)
xtotal=np.concatenate((x,xneg))
#plt.figure()
#plt.plot(xtotal, ytotal, 'g')
#plt.show()
self.mpl.axes.clear()
self.mpl.axes.plot(xtotal, ytotal, 'r')
self.mpl.figure.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = ApplicationWindow()
win.show()
sys.exit(app.exec_())

Scaling embedded matplotlib widget in qt application written in python problem

I am writing a simple digital image processing program. To do this I have embedded a mpl widget in my qt application. The user can perform some simple analysis on the image such as box car filter, FFT etc. Every thing is working fine until I would like to switch from displaying an image to displaying a plot.
If I display a plot first, the axis are fine (see bottom plot in image). But if I display an image first, followed by a plot (top plot in image), the scale compresses.
https://picasaweb.google.com/105163945296073520628/Temp <-- sorry I can't post images yet
The code is hosted here https://code.launchpad.net/~marrabld/pymi/trunk
I am using imshow() to display the image. and plot(x,y) for the plots.
This is the main update method
def updateImage(self):
self.ui.mplWidget.canvas.PlotTitle = self.plotTitle
self.ui.mplWidget.canvas.xtitle = self.xTitle
self.ui.mplWidget.canvas.ytitle = self.yTitle
#self.ui.mplWidget.canvas.ax.visible(False)
self.ui.mplWidget.canvas.format_labels()
if self.projectProperty == globals.IMAGE:
if self.lastProjectProperty == globals.PLOT:
self.myImage = imageFuncs.basic(self.imageFileName)
self.imPlot = self.ui.mplWidget.canvas.ax.imshow(self.myImage.image,cmap=matplotlib.cm.gray,origin='upper')
elif self.projectProperty == globals.PLOT:
if self.lastProjectProperty == globals.IMAGE: # we need to reload the GUI
self.ui.mplWidget.canvas.ax.hold(False)
self.ui.mplWidget.canvas.ax.plot(self.xData,self.yData)
self.ui.mplWidget.canvas.draw()
And the mpl widget I am using
#!/usr/bin/env python
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#from matplotlib.backends.backend_qt4 import NavigationToolbar2QT as NavigationToolbar
from matplotlib.backend_bases import NavigationToolbar2
from matplotlib.figure import Figure
from matplotlib import rc
import numpy as N
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width = 10, height = 12, dpi = 125, sharex = None, sharey = None):
rc('text', usetex=True)
rc('font', family='sans-serif')
rc('legend',fontsize='small' )
rc('legend',shadow='true')
self.fig = Figure(figsize = (width, height), dpi=dpi, facecolor = '#FFFFFF')
self.ax = self.fig.add_subplot(111, sharex = sharex, sharey = sharey)
self.fig.subplots_adjust(left=0.15, bottom=0.15, right=0.9, top=0.9)
self.fig.add_axes(yscale='symlog')
self.xtitle=r"x-Axis"
self.ytitle=r"y-Axis"
self.PlotTitle = r"Title"
self.grid_status = True
self.xaxis_style = 'linear'
self.yaxis_style = 'linear'
#self.fig.yscale = 'log'
self.format_labels()
self.ax.hold(True)
FigureCanvas.__init__(self, self.fig)
#self.fc = FigureCanvas(self.fig)
#FigureCanvas.setSizePolicy(self,
# QSizePolicy.Expanding,
# QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def format_labels(self):
self.ax.set_title(self.PlotTitle)
self.ax.title.set_fontsize(5)
self.ax.set_xlabel(self.xtitle, fontsize = 4)
self.ax.set_ylabel(self.ytitle, fontsize = 4)
labels_x = self.ax.get_xticklabels()
labels_y = self.ax.get_yticklabels()
for xlabel in labels_x:
xlabel.set_fontsize(4)
for ylabel in labels_y:
ylabel.set_fontsize(4)
ylabel.set_color('b')
def sizeHint(self):
w, h = self.get_width_height()
return QSize(w, h)
def minimumSizeHint(self):
return QSize(10, 10)
def sizeHint(self):
w, h = self.get_width_height()
return QSize(w, h)
def minimumSizeHint(self):
return QSize(10, 10)
#mouseClick = pyqtProperty("QPoint",mouseClick,click)
class mplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.canvas = MyMplCanvas()
#self.toolbar = MyNavigationToolbar(self.canvas, self.canvas, direction = 'v')
self.hbox = QHBoxLayout()
#self.hbox.addWidget(self.toolbar)
self.hbox.addWidget(self.canvas)
self.setLayout(self.hbox)
def savePlot(self,filePath):
self.canvas.fig.savefig(filePath)
def setLegend(self,handle, label):
self.canvas.fig.legend(handle,label,'upper right')
def clearPlot(self):
self.canvas.fig.clear()
width = 10
height = 12
dpi = 125
sharex = None
sharey = None
self.canvas.fig = Figure(figsize = (width, height), dpi=dpi, facecolor = '#FFFFFF')
self.canvas.ax = self.canvas.fig.add_subplot(111, sharex = sharex, sharey = sharey)
self.canvas.fig.subplots_adjust(left=0.15, bottom=0.15, right=0.9, top=0.9)
self.canvas.fig.add_axes(yscale='symlog')
self.canvas.xtitle=r"x-Axis"
self.canvas.ytitle=r"y-Axis"
self.canvas.PlotTitle = r"Title"
self.canvas.grid_status = True
self.canvas.xaxis_style = 'linear'
self.canvas.yaxis_style = 'linear'
#self.fig.yscale = 'log'
self.canvas.format_labels()
self.canvas.ax.hold(True)
FigureCanvas.__init__(self.canvas, self.canvas.fig)
#self.fc = FigureCanvas(self.fig)
FigureCanvas.setSizePolicy(self,
QSizePolicy.Expanding,
QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
Any Help would be greatly appreciated.
you can use aspect parameter of imshow() to adjust the ratio between the height & weight:
from pylab import *
a = np.zeros((100,10)) # height=100, weight=10
subplot(211)
imshow(a) # ratio = 10
subplot(212)
imshow(a, aspect=0.1) # ratio = 1
show()
but it will stretch the image.
or you can use xlim(), ylim() the set the range of x-y axis.
imshow(a)
xlim(-50,50)
EDIT:
imshow() will set the aspect property of axe to "equal". you need reset it before calling plot():
self.ui.mplWidget.canvas.ax.set_aspect("auto")
self.ui.mplWidget.canvas.ax.plot(self.xData,self.yData)

Categories

Resources