I am trying to use blitting with matplotlib to improve the performance of my plot, but am running into glitches after resizing the matplotlib window.
I have a matplotlib window showing 8 different lines plotted, along with a table of the values from the plot. The table and plot are updated every ~0.7 seconds.
Here is my main loop:
canvas.draw()
background = canvas.copy_from_bbox(ax.bbox)
while True
canvas.restore_region(background)
[update data for lines, table]
for line in lines:
ax.draw_artist(line)
canvas.blit(ax.bbox)
for line in lines:
line.set_animated(False)
canvas.draw_idle()
canvas.flush_events()
for line in lines:
line.set_animated(True)
time.sleep(0.7)
This updates as I expect it to, and everything looks OK.
But then when I resize the plot window (by clicking and dragging the window edge) I start to see artifacts left over from apparently as the resize was occurring (it appears for maybe 0.1 second, so I can't attach a screenshot). Basically I see what appears to be an old plot and the border of my table for a very short time on each iteration through my main loop, then the plot and table display fine.
What I've Tried:
I registered a callback for 'resize_event' and in that case do:
canvas.restore_region(firstBackground)
canvas.draw_idle()
canvas.flush_events()
Then on the next iteration through the main loop behave like normal. My callback is running, but this doesn't seem to help the problem at all.
So I think I need to do a transform on the background I saved before any data was plotted and call restore_region with the transformed background, but I am not sure if I am missing something simple.
EDIT:
Some more information. The plot I am trying to blit has two axes in the same area(one was created by calling twinx() on the other axis)
So far I have tried:
Calling set_visible(False) on all lines, then drawing and getting a new background image. This shows the same glitch on resize.
Calling cla() on the two axes that overlap. This also shows the same glitch after resizing.
Related
As I am aiming to plot the data into the plot, I encounter 2 problems.
(1) For the Bokeh function, while I am plotting 4 pictures in a row and adding the save as the SVG. Then the browser is shown as figure1. Moreover, as I comment out the code below, it work definitely right as I thought. It's weird.
p1.output_backend = "svg"
export_svgs(p1, filename="./number_like_comment/fig/{}.svg".format(action_str))
(2) For the second question, after I save those files, I realized that the "Label" I put in the plot also disappear. (as figure3).
Thanks so much for reading through. This question bother me quite a long time.
I guess that the data (on the x-axis) starts at 0, and on a logaxis for some reason the svg renderer doesn't like that and refuses to draw the entire line. The canvas renderer (used for the interactive plots) only refuses to draw the line-segment going from or to 0.
If you want to keep the logarithmic axis, you can either remove the 0 values or replace them by something which can be displayed in a logarithmic scale (e.g. 0.1) and then fixate the x-axis.
I know that you can plot multiple figures in the same window by arranging them in a grid layout. However, I want to plot multiple figures, showing only one at a time and be able to move to the next or previous figure with buttons in the UI. I see arrows in the default UI which makes me think there's a way to do this.
I've successfully created a bar chart. However, since there are so many glyphs right above and below each other, whenever a 'click' gets sent to TapTool, multiple glyphs have their tap tool trigger, so multiple images open in new tabs, instead of just a single 'closest to the click' glyph.
As you can see in the screenshots, at the default view, there are so many glyphs that are overlapping that if I simply click my mouse down, I'm likely hitting multiple glyphs, and this is why tap tool triggers multiple times (once per glyph hit). When zoomed in, it is obvious that rather than a bar chart, it is a scatter plot arranged to look like a bar chart, with each glyph representing a different image.
How do I force only the 'closest' glyph (or only take the first glyph from a list of glyphs hit by the taptool) to trigger its taptool?
zoomed_out
zoomed_in
There isn't much documentation online how to do this.
imgs = ['http://1...', 'http://2...', 'http://3...']
url = "#imgs"
click_tool = TapTool(callback=OpenURL(url=url))
p.add_tools(click_tool)
There are simply too many glyphs squeezed per pixel under your mouse cursor so they will all respond to your mouse click. A good solution would be to have a callback function attached to your plot's y-range that will re-draw the chart with less glyphs. So when you zoom in the outer glyphs will disappear from the plot and new one that were previously invisible will be added. At some zoom level you will not have to filter the glyphs anymore and they will all fit in. I mean something like this:
plot.y_range.callback = CustomJS(args=dict(source=source), code=code)
The plot canvas has a fixed height in pixels so in your callback you would need to put no more then that number of glyphs in your plot (number glyphs = canvas pixel height) This means one horizontal strip per pixel height. Then a single click should select just one glyph.
I've got a matplotlib plot in which I've modified the toolbar/status bar information much as in this question. This works just fine and the necessary information is displayed as I need it.
However, I often need to update the plot (which is done by calling draw() on the canvas object) with new data. What happens though is that the toolbar/status bar information won't update until the mouse is moved again. I'd like this information to update as soon as the canvas is redrawn because some of this information is pertinent to the new plot.
My attempt at making this happen was to force a mouse motion event to trigger. I've tried to trigger the event from the canvas via self.canvas.motion_notify_event(0,0) but that doesn't seem to work well. I can see that the toolbar itself has a mouse_move method, but I don't know how to trigger it (or even if it's what I want to trigger).
How can I force the toolbar/status bar information to update during a plot redraw without requiring the user to move their mouse slightly?
Did a lot of digging and figured it out. The motion_notify_event method was what I was looking for, I was just using it wrong. You can fake matplotlib out and trigger a MouseEvent by using this function, which will then make matplotlib call all functions that respond to MouseEvents, including updating the toolbar/status bar information.
The key here is that I needed to trigger the MouseEvent as if it happened within the axes object, not the entire figure. The input to the function is the (x,y) pixel position of the event with respect to the lower left corner of the figure window. By using (0,0) as I did in my question, I was saying the mouse event happened at the lower left corner of the figure window, not on the axes itself. Matplotlib does not show toolbar/status bar information unless the cursor is on the axes.
What you can do then is pick some random pixel position on the axes and use that as the position. A simple way to pick such a pixel position is using matplotlib transformations.
The following now works for me:
canvas.motion_notify_event(*ax.transAxes.transform([0,0]))
Of course in my case I'm not displaying the data coordinates of the mouse, so your use cases may vary.
I'm trying to animate a bunch of constantly updating points over an image (imagine making a plotted dot move diagonally across some image). I've looked at the animate examples here: http://matplotlib.org/examples/animation/dynamic_image.html, but I'm not sure how to keep the same image while clearing out all previous dots, then redrawing them. Any ideas?
You do not need to clear the figure between every frame
#initial data
ln, = ax.plot(x,y)
#...some loop code
ln.set_xdata(new_x)
ln.set_ydata(new_y)
Can you show some code of what you have tried, it will make it easier to give a more concrete answer.
Also see: using matplotlib's quiver in a loop efficiently, Visualization of 3D-numpy-array frame by frame