I am new to Gui programming, so if this question has been repeated elsewhere using proper terminology I apologize.
Is it possible to make interactive geometric animations with matplotlib? In particular, I want to embed an interactive Matplotlib window within a Tkinter frame to test out several variants of the same algorithm.
Here is a typical usage scenario for which I want to write an interactive
program as described above: Say I want to test a triangulation algorithm for point-sets in the 2-d plane.
When I run my new_triangulation_algorithm.py script, I would like to open up an interactive tkinter frame having an embedded matplotlib window, where the user "mouses-in" his/her points by clicking on, say, 20 different points -- every time a point is selected, a thick blue dot appears at that position. I know Matplotlib "knows" the coordinates of the point when I hover my mouse cursor over it since I always see it displayed in the status bar of my plots.
But I don't know how to input a point at that position with a mouse.
The second panel of my Tkinter frame would then contain several buttons, each button corresponding a different triangulation algorithm which I would like to animate on a given point set.
As the algorithm proceeds, I would like the matplotlib window to get refreshed to show the current state of the algorithm. I presume I would have to use Matplotlib's animation feature to do this, but I am not sure about
this since Matplotlib will have to be embedded in a Tkinter frame.
In future, I would like not only to input points, but also segments, circles, rectangles, but I would want to master a basic example with points
as described above.
This example might be a start. Its backend independend. To add your own buttons you might want to have a look at matplotlibs NavigationToolbar class. There are backend specific implementations like NavigationToolbar2QTAgg. You could inherit the Tk version and add some controls.
from matplotlib import pyplot
import numpy
x_pts = []
y_pts = []
fig, ax = pyplot.subplots()
line, = ax.plot(x_pts, y_pts, marker="o")
def onpick(event):
m_x, m_y = event.x, event.y
x, y = ax.transData.inverted().transform([m_x, m_y])
x_pts.append(x)
y_pts.append(y)
line.set_xdata(x_pts)
line.set_ydata(y_pts)
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', onpick)
pyplot.show()
Related
For the lab where I am working, I am currently developing a software that, basically, takes a 3D matrix, where the three axis are intensity as a function of time, and perform some FT over them, obtaining a 3D matrix with intensity as a function of frequency. By slicing and plotting that 3D matrix over the Z axis, it is possible to visualize some 2D maps that contain useful information for my experiments, like this one:
beating_map
A different source of information is obtained when summing over the X and Y axis, and plotting the results as intensity as a function of the Z axis. In our experiment, not every X and Y coordinate contains the information that we need. Therefore, making the sum over specific x-y coordinates of that 3D matrix is necessary. Example of this windows setting can be found here:
sum_spectra
Question is, is there a way that, with the cursor, tkinter can interact with matplotlib graphs and extract information about coordinates? A way in such, making some clicks, the user could select these coordinates without having to write them manually in some input box to make the sum. This is only one example of windows setting but, in fact, I have many other graphs where this interaction cursor-matplotlib-tkinter would be amazing to simplify the work.
I add the code of the map where I would like to have this interaction, just an example to work with:
self.fig = Figure(figsize=(4.5, 4.5))
self.ax = self.fig.add_subplot(111) # create axis
self.canvas = FigureCanvasTkAgg(self.fig, master=frame) # A tk.DrawingArea.
self.canvas.get_tk_widget().pack(side="top")
self.map = self.ax.contourf(x, y, intensity,
cmap=cmap, levels=15) # draw board
self.toolbar = NavigationToolbar2Tk(self.canvas, frame)
self.toolbar.update()
self.canvas.get_tk_widget().pack(side="bottom")
self.canvas.draw_idle() # update matplotlib figure
Simple code to include would be:
Trigger: click on the graph
Event: print the coordinates
With that tool is enough for me to start developing the code. If you need more of my code, please let me know. I leave one example of a MatLab software that does what I need:
matlab_example
It is possible to see that the cursor is now an arrow that, after clicking, save the x coordinates of the point where the click has been made.
Thank you very much for your time.
I have a plotting routine and I show the result with
plt.show()
as this is the easiest way for me to see my data. Once I have found and area which I want to look at i want to plot the exact same area again for a different set of data which has the same xy dimensions. The easies way for me would be to see the coordinates of the corners after the zoom or the origin of the zoom and the size in xy direction.
I found this solution . If I understand it correctly I have to manually change the plotting script with the new x y limits for each subsequent plot I want to make.
Is there a possibility to show the limit of the manual zoom in the panel itself and have the possibility to perform and actual input in dedicated fields such that I can recreate this zoom?
Do you mean something like this?
This "Figure options" dialog is present in the Qt backend. To use it,
import matplotlib
matplotlib.use("Qt4Agg") # or "Qt5Agg" depending on the version
I want to make an animated barchart in Python and save this animation in mp4 format. My problem is that the frames in the saved video overlay, although I use "blit=True" to tell the animation that only the things that change from frame to frame are drawn. Surprisingly, this problem does not occur in the built-in preview of Python.
Here is a minimal that reflects my situation:
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n): #That's the list of bars I want to display
C=[]
for k in range(1,6):
C.append(1/float(n*k))
return C
fig=plt.figure()
n=100 #Number of frames
def animate(i):
x=range(1,6)
y=barlist(i+1)
return plt.bar(x,y)
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=True,frames=n,
interval=50)
anim.save('barchart_animated_'+str(n)+'.mp4')
plt.show()
I must admit that I'm not pretty sure what I should do to remove this flaw. The only example I know of where the bars do not overlay in the frames is here (more exactly, I'm referring to the code of the first answer of the following link):
Dynamically updating a bar plot in matplotlib
It seems that I somehow have to tell the animation how it should set the height of each bar at each frame with the set_height-method. But as I said, I don't really know what's wrong in the above example. Thanks for any help!
Martin
The problem you have here is that you create a new barplot in every iteration of the animation. They will one by one be added to the plot, but since their height is shrinking over time, it may look as though only the first bar is present.
There are two ways to overcome this. First option is to clear the axes before plotting a new bar plot. This however will rescale the axis limits, which should then be constantly set to the same value.
The other option is to manipulate the one and only bar plot in the axes and adapt it's height for every frame. This is shown in the code below.
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n):
return [1/float(n*k) for k in range(1,6)]
fig=plt.figure()
n=100 #Number of frames
x=range(1,6)
barcollection = plt.bar(x,barlist(1))
def animate(i):
y=barlist(i+1)
for i, b in enumerate(barcollection):
b.set_height(y[i])
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=False,frames=n,
interval=100)
anim.save('mymovie.mp4',writer=animation.FFMpegWriter(fps=10))
plt.show()
Answers to the questions from the comments:
Blotting is a technique where all the parts of the figure which do not change are stored as a background. Then for each animated frame, only the changing parts are redrawn. This avoids the background to be redrawn from scratch and thus allows for much faster animations. Blitting will only affect the on-screen animation, because saving the animation to a file is not performed in real-time (and doesn't need to anyways).
Using blit=False here allows to make the code more simple because we do not need to care about the differences between the animation on screen and the one saved - they are just the same.
The enumerate function yields both the index as well as the object from the enumerated sequence. I did use it here, because it is a convenient way to obtain both in the same loop. It is not at all important here, you could alternatively do something like
for i in range(len(barcollection)):
barcollection[i].set_height(y[i])
I have created a little GUI with QT which set's up a single matplotlib figure and axes.
The GUI has controls to change just about everything to do with the appearance of the axes.
Basically, it does this by each widget emitting signals back up to the main frame and it calls the appropriate matplotlib setters on the axes and figure objects.
However, it is possible for the axes (and therefore the image displayed on the FigureCanvas) to change without input from the GUI (e.g. when autoscaling, or adding certain plots which adjust the axes automatically).
In this case, a widget controlling e.g. the limits of the x axis will now be displaying the wrong values.
I would like all the relevant widgets to update when the axes updates....how could I possible achieve this?
I'm thinking that this is a problem that has been solved before - how to enable a two-way communication between distinct objects?
fig.canvas.draw()
time.sleep(1e-2)
whenever anything writes to the plot? however it's hard to help with no code.
Showing an example of how your code is not working would help a lot.
EDIT:
I'll try this again then:
What about getting the state of the plot you are updating? I guess its what #Ajean means by updater method. I know that Artists in matplotlib have an Artist.properties() method that returns all of the properties and values.
I imagine Axes would have a similar method.
A quick look at the matplotlib docs yielded 2 interesting methods of axes:
ax.get_autoscale_on()
and
ax.set_autoscale_on().
ax.set_autoscale_on(False) will prevent plots from updating the state of the axes.
Is there already a python package allowing to graphically edit the graph of a function?
Chaco is designed to be very interactive, and is significantly more so than matplotlib. For example, the user can use the mouse to drag the legend to different places on a plot, or lasso data, or move a point around on one plot and change the results in another, or change the color of a plot by clicking on a swatch, etc.