Repository navigation
[Bug]: Subplots aren't updating with fig.canvas.draw_idle(). #27216
Copy link
Copy link
Closed
Labels
status: needs clarificationIssues that need more information to resolve.Issues that need more information to resolve.
Description
Bug summary
fig.canvas.draw_idle() in a key event callback doesn't update all subplots (in the example there're 9). No matter what function I used. I've tried fig.canvas.draw(), plt.pause(), fig.canvas.flush_events().
Code for reproduction
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.widgets import LassoSelector
from matplotlib.path import Path
import matplotlib
print(matplotlib.get_backend())
class SelectFromSharedCollection:
mask = None
def __init__(self, ax, collection, alpha_other=0.3):
self.ax, self.canvas = ax, ax.figure.canvas
self.collection = collection
self.alpha_other = alpha_other
self.xys = collection.get_offsets()
self.Npts = len(self.xys)
# Ensure that we have separate colors for each object
self.fc = collection.get_facecolors()
if len(self.fc) == 0:
raise ValueError("Collection must have a facecolor")
elif len(self.fc) == 1:
self.fc = np.tile(self.fc, (self.Npts, 1))
self.lasso = LassoSelector(ax, onselect=self.onselect)
self.ind = []
def onselect(self, verts):
path = Path(verts)
SelectFromSharedCollection.last_path = path
SelectFromSharedCollection.mask = path.contains_points(self.xys)
self.ind = np.nonzero(SelectFromSharedCollection.mask)[0]
self.fc[:, -1] = self.alpha_other
self.fc[self.ind, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
def disconnect(self):
self.lasso.disconnect_events()
self.fc[:, -1] = 1
self.collection.set_facecolors(self.fc)
self.canvas.draw_idle()
def accept(self, event):
if event.key == "enter":
print(SelectFromSharedCollection.mask)
self.fc[~SelectFromSharedCollection.mask] = [0.0, 0.5, 0, 0.1]
self.fc[SelectFromSharedCollection.mask] = [0.5, 0, 0, 1]
self.canvas.draw_idle()
fig, axes = plt.subplots(3, 3)
axes = axes.flatten()
figures = {}
def accept(event):
match event.key:
case "enter":
for perplexity, figure in figures.items():
print(f"Perplexity: {perplexity}")
figure["selector"].accept(event)
plt.pause(0.5)
fig.canvas.draw()
fig.canvas.flush_events()
case _:
pass
for ax, perplexity in zip(axes, range(20, 200, 20)):
X_embedded = np.random.rand(50, 2)
pts = ax.scatter(X_embedded[:, 0], X_embedded[:, 1], s=1)
figures[perplexity] = {
"ax": ax,
"selector": SelectFromSharedCollection(ax, pts),
}
fig.canvas.mpl_connect("key_press_event", accept)
plt.show()Actual outcome
After selecting points from one of the subplots, pressing enter doesn't do anything. Until individual subplots are clicked on, then the plots update properly. But every subplots has to be clicked to update.
Expected outcome
The plots just update themselves after hitting the "Enter" keys.
Additional information
No response
Operating system
macOS Ventura 13.5
Matplotlib Version
3.7.2, 3.8.0 (I tried both)
Matplotlib Backend
MacOSX
Python version
3.11.4
Jupyter version
wasn't using jupyter
Installation
pip
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
status: needs clarificationIssues that need more information to resolve.Issues that need more information to resolve.
Activity
greglucas commentedon Oct 29, 2023
This is a pretty complicated example to follow. I believe the issue is that you aren't updating the data in the other subplots, so there is no new data for your events to "draw". But, it is a bit unclear what you're expecting to see with this example. I'd suggest trying to narrow it down to just two subplots with minimal data if you can which might show the issue more clearly.
Jacfger commentedon Oct 29, 2023
So in short my code basically highlight lasso selected point with the enter key. And assume there's an index for eacb points, other plots highlight the corresponding points as well, which is done by updating the attribute facecolor of the collection (return object from scatter).
I'll try it tmr but what do you mean by data update? Is updating facecolor also a data update to the subplot?
greglucas commentedon Oct 29, 2023
There is a lot going on in that example, which makes it hard for someone without any context to follow what is going on. I'm just suggesting to boil it down to a simpler example to follow, which might make the analysis easier. I think what is going on is that you aren't updating the data in the other plots how you think you are, but again that is not easy to follow without some added effort of digging into it.
Here is a more minimal example that simply swaps facecolors of items in two collections when pressing enter. You should see the blue and red scatter points change every time you hit enter.
tacaswell commentedon Oct 30, 2023
90% sure that the issue is each of the lasso selectors is using blitting (
useblit=Trueis the default) and because there are naive to each other the last one "wins".Jacfger commentedon Oct 31, 2023
After comparing your example and mine I found out what I missed was
set_facecolors(). It was called inonselect(), that's why clicking the subplots would trigger it, but not right after I press enter. Somehow I had the impression thatset_facecolors()wasn't needed as the returned object fromget_facecolors()seemed to be modifiable.Sorry for my stupid mistakes. Yikes.
Thank you very much for your kind help and examples. This would probably be much harder to find out especially without @greglucas example.