Force update to matplotlib toolbar/status bar information - python

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.

Related

How to move canvas drawn shapes to have a fixed position on screen?

I have a GUI that I've made in tkinter using a canvas that has a bar along the left side and a bar along the top containing information. In the remaining area various rectangles are drawn corresponding to the information in those bars I described. The issue is, when you scroll away on the canvas, those events will leave as well.
Basically, I want to create a visual effect like position: fixed is in CSS where these bars on the side and the top stay in place relative to the rest of the canvas so that they don't move from their relative position while scrolling.
I tried making use of the scrollbar commands, but have had trouble making my own function there. I also tried to see if there was an event I could bind to the canvas to track the movement so that I could move the bars myself but I could not find anything.
There is no way to do this directly in the canvas unless you write the code to move the items in the top and left whenever the user scrolls. It would probably require a fair amount of work.
However, it's trivial to have separate canvases for the side and top that do not scroll, and a third canvas that has all of the scrolled data.
I was able to end up solving my own issue by adapting the solution from: How can I add Unscrollable Image to a frame/Canvas?
Simply put, I made my own yview and xview functions that would scroll my desired "bars" with the window by moving the objects.
Special thanks to #jasonharper for his help.

Matplotlib Glitches After Window Resize using Blitting

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.

Updating matplotlib annotation position without redrawing all data

I've essentially recreated the matlab data cursor on my custom pyplot figure tool. It allows dragging and left/right arrow key presses to update an annotation box's position along a line, etc. My problem comes from when there are a large number of lines. The data cursor still works, but dragging its position around is a nightmare and is extremely slow. The problem stems, I'm rather certain, from my canvas.draw() calls since it's redrawing the entire figure every time the data cursor moves.
I'm working towards implementing options like blit() and "restore_region()", but I'm having great difficulty implementing them. Can anyone provide even a shell of how to make this work? I can't include actual code due to the classification of the code itself, but I can give a small pseudo-code example.
def create_anno(self):
# Do annotation creation stuff here
self.background = self.figure.canvas.copy_from_bbox(self.axis.bbox)
def key_press(self):
self.figure.canvas.restore_region(self.background)
# Do update position/text stuff here
self.figure.canvas.blit(self.axis.bbox)
self.figure.canvas.flush_events()
The key_press function is called normally, but the annotation box doesn't move anymore. Most examples use draw_artist() between their restore_region() and blit() calls, but annotation doesn't have that. I just want to update a little annotation box's position across a figure without having to redraw the entire axis. Any ideas?
Thanks!

How to display a binary state (ON/OFF) in Matplotlib?

I have built a GUI with matplotlib and it contains several plots of values versus time. Now I need a special plot which just shows if a value is on or off (binary state).
Kinda like a control lamp on an analog control panel. I have 5 of those on/off values and I dont know how to do it the best way.
The "lamps" must be updateable because I stream the data from serial and analyze it in real time in my GUI.
I attached a picture where you see my current GUI. In the bottom right corner is now a bar chart, I tried to visualize the ON/OFF state with a bar, but it didn't work well and I wasn't able to animate it.
So yeah, how could I display 5 values with each an ON/OFF state in that area?
Instead of passing via bar charts I would directly plot a number of rectangles and then dynamically change their color.
You can find the documentation for rectangular patches here: http://matplotlib.org/api/patches_api.html#matplotlib.patches.Rectangle
If you need some pointers on how to animate such a patch have a look here:
https://nickcharlton.net/posts/drawing-animating-shapes-matplotlib.html

matplotlib gui respond to axes changes

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.

Categories

Resources