Can python make Matlab style GUIs [closed] - python

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I'm really trying to fall in love with Python and move away from Matlab. Is it possible to make Matlab style GUIs in Python? How easy is it by comparison? (I make matlab GUIs programmatically, wouldn't dare use GUIDE) Can I put matplotlib graphics in these GUIs? Is tk or wx (or something else) better for this?

wxPython has tabs, grids or ListCtrls (i.e. tables) and supports matplotlib and PyPlot for graphs. You can read about using matplotlib at the following links:
http://eli.thegreenplace.net/2008/08/01/matplotlib-with-wxpython-guis/
Embedding a matplotlib figure inside a WxPython panel
http://www.scipy.org/Cookbook/Matplotlib/EmbeddingInWx
http://wiki.wxpython.org/MatplotlibFourierDemo
To see all the widgets that are included with wxPython, go to www.wxpython.org and click the download link on the left. You'll find that they have a standalone Docs & Demo package that can show you almost every widget and how it works.

Haven't used Matlab before, not sure about it's GUI. But if you tend to use Python interactively, you may want to give iPython a try. Ipython with Qt can render you an elegant GUI.

For simple interfaces you might want to check out the GUI neutral widgets that matplotlib provides, documented here -- http://matplotlib.org/api/widgets_api.html
Here's a simple example of using these GUI neutral widgets to draw a function with variable parameters that are controlled by three slider widgets:
import functools
import numpy as np
import pylab
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons, MultiCursor
def hillfxn(x, B, K, n):
xn = float(x**n)
return (B * xn)/(K**n + xn)
def draw_function(x, y, xlabel="Activator concentration (X)",
ylabel="Promoter activity"):
fig, ax = plt.subplots(1, 1, sharex=True)
plt.subplots_adjust(left=0.15, bottom=0.25)
lines = ax.plot(x, y)
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.set_ylim(0,max(y)*1.1)
return fig, ax, lines
def draw_interactive_controls(n, B, K):
axcolor = 'lightgoldenrodyellow'
axK = plt.axes([0.1, 0.01, 0.75, 0.03], axisbg=axcolor)
axB = plt.axes([0.1, 0.06, 0.75, 0.03], axisbg=axcolor)
axN = plt.axes([0.1, 0.11, 0.75, 0.03], axisbg=axcolor)
Nslider = Slider(axN, "$n$", 1, 10, valinit=n, valfmt='%1.3f')
Bslider = Slider(axB, "$\\beta$", 0, 20, valinit=B, valfmt='%1.3f')
Kslider = Slider(axK, "$K$", 0.01, 20, valinit=K, valfmt='%1.3f')
return Nslider, Bslider, Kslider
def update_plot(val, x=None, lines=None, ax=None,
Nslider=None, Bslider=None, Kslider=None):
n = Nslider.val
B = Bslider.val
K = Kslider.val
y = [hillfxn(i, B, K, n) for i in x]
lines[0].set_ydata(y)
ax.set_ylim(0,max(y)*1.1)
pylab.draw()
if __name__ == "__main__":
# initial values
B, K, n = 5, 5, 1
x= np.linspace(0,30,250)
y = [hillfxn(i, B, K, n) for i in x]
# setup initial graph and control settings
fig, ax, lines = draw_function(x,y)
Nslider, Bslider, Kslider = draw_interactive_controls(n, B, K)
# specify updating function for interactive controls
updatefxn = functools.partial(update_plot, x=x, lines=lines, ax=ax,
Nslider=Nslider, Bslider=Bslider, Kslider=Kslider)
Nslider.on_changed(updatefxn)
Bslider.on_changed(updatefxn)
Kslider.on_changed(updatefxn)
pylab.show()
This will produce an interface like the following:

Related

UserWarning: Animation was deleted without rendering anything

Note: only the last code chunk brings an error. The earlier chunks are to give context to the animation that I want. This is all in Jupyter for Windows.
I have a matplotlib pyplot with two line segments, one is "wrong" and the other is "right." I want to animate the graph to start with both lines where the blue "wrong" line is,and have the red one pivot and move to be in the right place. The "wrong" line goes from (x,y) = (-1.25,9.1) to (0.75,8.0). The "right" line goes from (-1.25,9.7) to (0.75,7.5)
Here is the code for the static comparison:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
boring_fig = plt.figure()
blue = plt.plot([-1.25,.75], [9.1,8], color = 'b', label = 'wrong')
red = plt.plot([-1.25,.75], [9.7,7.5], color = 'r', label = 'right')
plt.show()
Now I want to start them both where the blue line is, and then have the red line incrementally move to the correct position. I made these two arrays for y coordinates to incrementally change between lines. Then I graph everything but the red line, in hopes of adding it as an animation after this.
y_left = np.array([9.1, 9.16, 9.22, 9.28, 9.34, 9.4, 9.46, 9.52, 9.58, 9.64, 9.7])
y_right = np.array([8.0, 7.95, 7.9, 7.85, 7.8, 7.75, 7.7, 7.65, 7.6, 7.55, 7.5])
fig = plt.figure()
blue = plt.plot([-1.25,.75], [9.1,8], color = 'b', label = 'wrong')
plt.show()
And then I try to animate the red line segment shifting along those incremented y values. I get the error somewhere in here:
def animate_1(i):
return plt.plot([-1.25,.75], [y_left[i],y_right[i]], color = 'r'),
anim = FuncAnimation(fig = fig, func = animate_1, interval = 100, frames = 10)
plt.show(anim)
And I get this message: "UserWarning: Animation was deleted without rendering anything. This is most likely unintended. To prevent deletion, assign the Animation to a variable that exists for as long as you need the Animation."
I spent hours trying to figure this out, but I am too much of a noob. Please help.
It turns out I had to install ffmpeg for windows:
https://www.wikihow.com/Install-FFmpeg-on-Windows
Now the graph shows up but I can't see it animating. Oh well. That is an entirely different question.
here is the end graph I made (with a couple of extra details)
Add this line to the beginning of your code in order to see the animation work:
%matplotlib notebook
In Jupyter Notebook (at least on Windows) specifying the GUI toolkit will allow you to see the animation updating, not just a static graph. You can read more about toolkits and user interfaces here.
If there is a message that says:
Warning: Cannot change to a different GUI toolkit: ...
then restart the Jupyter kernel and run the cell again to update the GUI. For troubleshooting the GUI, see this answer.
In regards to the original question, if you set blit = False in FuncAnimation then the animation should display. Otherwise, you can add a return blue, statement to the animate function. See my code below and feel free to ask questions!
Complete Code
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
def move_line(num_frames):
x_vals = [-1.25,.75]
y_left_array = np.linspace(9.1, 9.7, num=num_frames, endpoint=True)
y_right_array = np.linspace(8.0, 7.5, num=num_frames, endpoint=True)
fig = plt.figure()
red = plt.plot(x_vals, [9.7,7.5], color = 'r', label = 'right')
blue, = plt.plot([], [], color = 'b', label = 'wrong')
def animate(I):
y_vals = [y_left_array[i],y_right_array[I]]
blue.set_data(x_vals,y_vals)
return blue,
anim = FuncAnimation(
fig,
animate,
frames = num_frames,
interval = 1000/30,
repeat = False,
blit = True
)
plt.show()
return anim
animate_lines = move_line(100)

I couldn't find the way to use "valinit" in the rangeslider widget of matpoltlib

I'm trying to use Matplotlib RangeSlider and everything is ok except "valinit" value. I just couldn't find how to use it.
valinit : tuple of float or None, default: None
The initial positions of the slider. If None the initial positions will be at the 25th and 75th percentiles of the range.
https://matplotlib.org/stable/api/widgets_api.html#matplotlib.widgets.RangeSlider
I want to make initial position of the slider as 0%-100%, in my case (0, totaltimest) not the default 25%-75%, but I couldn't figure it out. Can somebody help me please? An example would be great.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import RangeSlider
import soundfile as sf
file='A.wav' # mono audio file
data,fs=sf.read(file)
totaltimest=len(data)/fs
x = np.linspace(0, totaltimest, len(data))
fig, ax= plt.subplots(figsize=(10, 5))
ax.plot(x,data, label='Reference', color='red',alpha=0.5,linewidth=0.6)
ax.set_facecolor('#EFEFEF')
(yl1,yh1)=ax.get_ylim()
scroll1 = plt.axes([0.22, 0.01, 0.60, 0.03], facecolor='#EFEFEF')
scroll1v = RangeSlider(scroll1, 'Window', 0, totaltimest, valinit=None) # PROBLEM
def update(val):
pos1=scroll1v.val[0]
pos2=scroll1v.val[1]
ax.axis([pos1, pos2,yl1,yh1])
fig.canvas.draw_idle()
scroll1v.on_changed(update)
plt.tight_layout
plt.show()
Figured someone else might find this useful so better late than never, but give this a shot:
scroll1v = RangeSlider(scroll1, 'Window', valinit=(0,totaltimest))
Here's an example of how I used it in conjunction with Numpy arrays for my own project. I was previously working with two separate sliders, but recognizing that the Slider widget works differently than a RangeSlider (float vs. tuple), we have to format the argument for valinit as a tuple as well. Check it out:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import RangeSlider
# Creating axis and slider:
ax_rgb = plt.axes([0.23, 0.18, 0.10, 0.70]) # X-cord, Y-cord, Width, Height
sl_rgb = RangeSlider(ax_rgb,
label = 'RGB Array',
valinit = (np.max(rgb),np.min(rgb)),
valmax = np.max(rgb),
valmin = np.min(rgb),
orientation = 'vertical')
Depending on how comfortable you are with Matplotlib widgets, might not be a bad idea to indent and label your Slider attributes. Cheers!

Is it possible to put a mathematical formula (using Latex) next to the Slider widget in Matplotlib?

My question is in the title, as you can see in the image below my variable is M2/M1 and I am wondering if it is possible to replace it by $\dfrac{M_2}{M_1}$.
I tried:
axmass = plt.axes([0.18, 0.1, 0.65, 0.03])
smass = Slider(axmass, '$\dfrac{M_2}{M_1}$', 1e-5, 1, 0)
But I get the following error:
ValueError:
\dfrac{M_2}{M_1}
^
Unknown symbol: \dfrac (at char 0), (line:1, col:1)
You can, this should work, it adds an r before the string, it uses \frac instead of \dfrac (display mode is not supported), it uses rc (if not, the result is ugly).
After request I also added a line to vertically center the fraction (if not the fraction line is not aligned with the slider), I am open to suggestion for a better way to position correctly the legend !
Here is a minimal working exemple adapted from this STACK OVERFLOW question:
import matplotlib.pyplot as plt
import matplotlib.widgets
fig = plt.figure()
plt.rc('text', usetex=True)
E0_slider_ax = fig.add_axes([0.6, 0.2, 0.25, 0.03], facecolor="skyblue")
E0_slider = matplotlib.widgets.Slider(E0_slider_ax, r'$\frac{M_3}{M_1}$', 1, 100, valinit = 50)
E0_slider.label.set_size(40)
E0_slider.label.set_position((-0.1,0)) #Probably not the best solution
plt.show()
You can take a look at matplotlib official documentation on the subject: https://matplotlib.org/users/usetex.html

Decreasing animation speed in matplotlib using patches

The following codes works correctly, but during animation time the process becomes slower and slower. I feel it has something to do with the fact that more and more patches accumulate. But I am not really an expert concerning animations with matplotlib and do not have a clue to keep the speed constant for longer sequences.
Before someone asks: My problems seem similar to this question "Matplotlib animation slows down when adding patches", but the suggested solution -- which is not finaly anyway -- does not work out in my case. Among other problems I really need a kind of class design like the one in the example code.
Thanks for any suggestions
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
class myMovie:
def __init__(self):
self.fig = plt.figure()
self.ax = plt.axes(xlim=(0, 100), ylim=(0, 100))
self.toDraw = []
for i in range(10):
agent = plt.Circle((10, -10), 0.75, fc=(i/10,0,0))
self.toDraw.append(agent)
def init(self):
for i in range(10):
self.toDraw[i].center = (50,50)
self.ax.add_patch(self.toDraw[i])
return []
def animationManage(self,i):
for j in range(10):
d = np.random.rand(2) - 0.5
c = self.toDraw[j].center + d
self.toDraw[j].center = c
self.ax.add_patch(self.toDraw[j])
return []
def justPlot(self):
self.anim = animation.FuncAnimation(self.fig, self.animationManage,
init_func=self.init,
interval=20,
repeat=True)
self.fig.show()
justATest = myMovie()
justATest.justPlot()

Interactive matplotlib plot with two sliders

I used matplotlib to create some plot, which depends on 8 variables. I would like to study how the plot changes when I change some of them. I created some script that calls the matplotlib one and generates different snapshots that later I convert into a movie, it is not bad, but a bit clumsy.
I wonder if somehow I could interact with the plot regeneration using keyboard keys to increase / decrease values of some of the variables and see instantly how the plot changes.
What is the best approach for this?
Also if you can point me to interesting links or a link with a plot example with just two sliders?
In addition to what #triplepoint mentioned, have a look at the slider widget.
There's an example on the matplotlib examples page. It's a graphical slider bar rather than keyboard bindings, but it works quite well for what you want to do.
Also note that to guarantee the sliders and buttons remain responsive and not garbage-collected, references to the objects (amp_slider, freq_slider, etc.) should be maintained by yourself.
(I'm making this community wiki, as I'm just copy-pasting from the example. This particular example teaches bad habits (e.g. from pylab import *), but it gets the point across. The example has been fixed to avoid the use of pylab.)
from numpy import pi, sin
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
def signal(amp, freq):
return amp * sin(2 * pi * freq * t)
axis_color = 'lightgoldenrodyellow'
fig = plt.figure()
ax = fig.add_subplot(111)
# Adjust the subplots region to leave some space for the sliders and buttons
fig.subplots_adjust(left=0.25, bottom=0.25)
t = np.arange(0.0, 1.0, 0.001)
amp_0 = 5
freq_0 = 3
# Draw the initial plot
# The 'line' variable is used for modifying the line later
[line] = ax.plot(t, signal(amp_0, freq_0), linewidth=2, color='red')
ax.set_xlim([0, 1])
ax.set_ylim([-10, 10])
# Add two sliders for tweaking the parameters
# Define an axes area and draw a slider in it
amp_slider_ax = fig.add_axes([0.25, 0.15, 0.65, 0.03], facecolor=axis_color)
amp_slider = Slider(amp_slider_ax, 'Amp', 0.1, 10.0, valinit=amp_0)
# Draw another slider
freq_slider_ax = fig.add_axes([0.25, 0.1, 0.65, 0.03], facecolor=axis_color)
freq_slider = Slider(freq_slider_ax, 'Freq', 0.1, 30.0, valinit=freq_0)
# Define an action for modifying the line when any slider's value changes
def sliders_on_changed(val):
line.set_ydata(signal(amp_slider.val, freq_slider.val))
fig.canvas.draw_idle()
amp_slider.on_changed(sliders_on_changed)
freq_slider.on_changed(sliders_on_changed)
# Add a button for resetting the parameters
reset_button_ax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
reset_button = Button(reset_button_ax, 'Reset', color=axis_color, hovercolor='0.975')
def reset_button_on_clicked(mouse_event):
freq_slider.reset()
amp_slider.reset()
reset_button.on_clicked(reset_button_on_clicked)
# Add a set of radio buttons for changing color
color_radios_ax = fig.add_axes([0.025, 0.5, 0.15, 0.15], facecolor=axis_color)
color_radios = RadioButtons(color_radios_ax, ('red', 'blue', 'green'), active=0)
def color_radios_on_clicked(label):
line.set_color(label)
fig.canvas.draw_idle()
color_radios.on_clicked(color_radios_on_clicked)
plt.show()
I followed the advice to check widgets in jupyter, and they work very well.
The example script is uploaded in GitHub https://github.com/LeonidBystrykh/course-python-for-beginners/blob/master/Interactive_dots.ipynb
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib.pyplot as plt, random
def series(dots, colr):
a,b=[],[]
for i in range(dots):
a.append(random.randint(1,100))
b.append(random.randint(1,100))
plt.scatter(a,b, c=colr)
return()
interact(series, dots=(1,100,1), colr=["red","orange","brown"]);
The image copy is below
Matplotlib has some fairly nice gui functionality. There are some documentation examples in the source tarball of matplotlib, in /examples/user_interfaces and matplotlib>/examples/event_handling. Specifically on keyhandling is: http://matplotlib.sourceforge.net/examples/event_handling/keypress_demo.html
I have done something kind of similar to what you are aiming for:
import numpy as np
import pylab
class plotter:
def __init__(self, initial_values):
self.values
self.fig = pylab.figure()
pylab.gray()
self.ax = self.fig.add_subplot(111)
self.draw()
self.fig.canvas.mpl_connect('key_press_event',self.key)
def draw(self):
im = your_function(self.values)
pylab.show()
self.ax.imshow(im)
def key(self, event):
if event.key=='right':
self.values = modify()
elif event.key == 'left':
self.values = modify()
self.draw()
self.fig.canvas.draw()
I was using this to shift through displaying different images in a stack on keypresses, but you should be able to put logic in to modify your values given keyboard input.
If you want to do things like have the user input values, I think the examples have options for dialog boxes, but if you just want to increment/decrement a number of variables, just defining keyboard pairs for them in this manner might work well
For ipython or jupyter notebooks you can use ipywidgets:
from ipywidgets import *
def update(w=0,h=0):
print(h+w)
interact(update, w= widgets.IntSlider(value=1, min=0, max=7, step=1) ,
h= widgets.IntSlider(value=1, min=0, max=7, step=1) );
See documentation here:
https://ipywidgets.readthedocs.io/en/stable/examples/Using%20Interact.html
Use waitforbuttonpress(timeout=0.001) then plot will see your mouse ticks.
You could also consider using the Quibbler package.
Quibbler automatically makes your normal matplotlib graphics interactive. There is no need to write callback functions.
See some examples in the Quibbler docs. In particular, see a specific example of a plot with two sliders.
from pyquibbler import iquib, initialize_quibbler
initialize_quibbler()
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
%matplotlib tk
# Figure setup:
fig = plt.figure(figsize=(4, 6))
axs = fig.add_axes([0, 0.36, 1, 0.64])
axs.axis('square')
axs.axis((-10, 10, -10, 10))
axs.axis('off')
axs_slider1 = fig.add_axes([0.3, 0.16, 0.55, 0.03])
axs_slider2 = fig.add_axes([0.3, 0.06, 0.55, 0.03])
# Defining quib input variables:
nPoints = iquib(120)
nCycles = iquib(30)
# Define downstream function quib calculations based on the quib inputs:
phi = np.linspace(0, 2 * np.pi * nCycles, nPoints)
r = np.linspace(0, 10, nPoints)
# Plot the data:
axs.plot(r * np.cos(phi), r * np.sin(phi), 'r-')
# Create quib-linked slider widgets:
Slider(ax=axs_slider1, label='nCycles', valmin=0, valmax=200, valstep=1, valinit=nCycles)
Slider(ax=axs_slider2, label='nPoints', valmin=0, valmax=200, valstep=1, valinit=nPoints)
For transparency: I am one of the developers of Quibbler.
I don't think that simply plotting graphs using plt.plot will allow you to do this. You will need to yourself make a custom GUI script/app by embedding Matplotlib into it. Currently, Matplotlib supports all the major GUI toolkits - PyGTK+, PyQt4 and wxPython.
I use wxPython and embedding matplotlib in it is fairly easy. Similar should be case with the other GUI toolkits. You can get all the information you need for this in the book -
It is available on amazon here.

Categories