In my project I'm trying to embed the same matplotlib figure in two places and seem to need to make a copy, as I'm using blitting on the original, and embedding a figure in two places & closing one causes a crash issue w/ blitting an imshow in matplotlib (but not scatterplots). In order to do this I'm using pickle on the matplotlib figure, but I'm getting the error stated in the title. The function where I'm doing the embedding is below, but the first line where dumps/loads from pickle is erroring only my figure w/ scatterplots.
def embedPlot(self, fig):
fig = pickle.loads(pickle.dumps(fig))
temp = self.currentEmbed if self.currentEmbed else None
# set max width/height based on screensize and dpi
fig.set_size_inches(self.root.winfo_screenwidth()/self.root.winfo_fpixels('1i')-1, self.root.winfo_screenheight()/self.root.winfo_fpixels('1i')-1)
self.currentEmbed = Frame(self.root)
canvas = FigureCanvasTkAgg(fig, master = self.currentEmbed)
canvas.draw()
canvas.get_tk_widget().pack()
self.currentEmbed.grid(row=0, column=0, padx=2, pady=2, columnspan=4)
# delete old canvas each time, as creating new FigureCanvasTkAgg's can cause big slowdown when closing window (there's probably a better way to do this)
if temp:
temp.destroy()
I store my different figures in a list and embed the one that is the current "focus" w/ the embed function above. And for reference here is my (messy) figure that is the one that errors.
def blitgenerateTSNEPlots():
def getTSNE(idx):
# get closest points & norms to all points from the example at idx
norms,idxs,prediction = findNearest(exdata,exoutput,getTSNE.advdata,idx,displayEpsilon)
# restore backgrounds, clearing foregound and allowing redrawing of artists
getTSNE.fig.canvas.restore_region(getTSNE.background)
getTSNE.fig.canvas.restore_region(getTSNE.titleBackground)
# change array for scatterplot so it'll recolor, changing offsets of cb so the closest 10 points will be in their new positions, and update title based on new model prediction
getTSNE.scatterPlot.set_array(norms)
getTSNE.cb.set_offsets([ [getTSNE.X_2d[i,0], getTSNE.X_2d[i,1]] for i in idxs])
getTSNE.title.set_text(f"Model Prediction: {prediction}\nAverage Distance: {round(float(sum(norms))/len(norms),2)}")
# redraw artists
getTSNE.ax2.draw_artist(getTSNE.title)
getTSNE.ax2.draw_artist(getTSNE.scatterPlot)
getTSNE.ax2.draw_artist(getTSNE.cb)
# blit bounding boxes of axis and text so figure will update
getTSNE.fig.canvas.blit(getTSNE.ax2.bbox)
getTSNE.fig.canvas.blit(getTSNE.title.get_window_extent())
getTSNE.fig.canvas.flush_events()
return getTSNE.fig
# create figures and turn off all axes ticks
getTSNE.fig, (getTSNE.ax1, getTSNE.ax2) = plt.subplots(1,2,constrained_layout=True)
getTSNE.ax1.set_xticks([])
getTSNE.ax1.set_yticks([])
getTSNE.ax2.set_xticks([])
getTSNE.ax2.set_yticks([])
# load adversarial data for epsilon and get closest points to idx 0 for initial plot creation
getTSNE.advdata = get_data(npys,displayEpsilon)
norms,idxs,prediction = findNearest(exdata,exoutput,getTSNE.advdata,0,displayEpsilon)
# generate tsne embedding based on original set of data
X_2d = []
if os.path.exists("./embedding.npy"):
X_2d = np.load('./embedding.npy').astype(np.float64)
else:
tsne = TSNE(n_components=2, random_state=4, perplexity=100)
origdata = get_data(npys,'e0')
X_2d = tsne.fit_transform(origdata)
np.save('./embedding.npy', X_2d, allow_pickle=False)
getTSNE.X_2d = X_2d
# create scatter of all points colored & labaled by class. this one never needs to be updated, it's static
colors = 'r', 'g', 'b', 'c', 'm', 'y', 'k', 'aquamarine', 'orange', 'purple'
for c, label in zip(colors, labels):
getTSNE.ax1.scatter(X_2d[(testlabels[:] == label), 0], X_2d[(testlabels[:] == label), 1], c=c, label=label, s=3)
getTSNE.ax1.set_title("Test Data")
getTSNE.ax1.legend()
# setting title as blank but with a big size to get background for blitting
getTSNE.title = getTSNE.ax2.set_title(" \n ")
colorLim = (4,13)
# manually create colorbar before second scatterplot has been made
getTSNE.fig.colorbar(matplotlib.cm.ScalarMappable(norm=matplotlib.colors.Normalize(vmin=colorLim[0],vmax=colorLim[1]),cmap='viridis'),ax=[getTSNE.ax1,getTSNE.ax2],label="norm")
# draw figure and store bounding boxes of scatter background & title background
getTSNE.background = getTSNE.fig.canvas.copy_from_bbox(getTSNE.ax2.bbox)
getTSNE.fig.canvas.draw()
getTSNE.titleBackground = getTSNE.fig.canvas.copy_from_bbox(getTSNE.title.get_window_extent())
# create scatter plot of all data colored by example's distance from original data & closest 10 points
getTSNE.scatterPlot = getTSNE.ax2.scatter(X_2d[:,0], X_2d[:,1], c=norms[:], s=3, cmap='viridis', zorder=1)
getTSNE.cb = getTSNE.ax2.scatter(X_2d[idxs,0],X_2d[idxs,1], c='red', s=7, zorder=2)
getTSNE.scatterPlot.set_clim(colorLim[0],colorLim[1])
return getTSNE
generateTSNEPlots = blitgenerateTSNEPlots()
If anyone knows either how to fix the pickling issue w/ the scatterplots, or a different way to work around the total crash with an imshow, either would achieve what I'm trying to do.
CodePudding user response:
Turns out the issue was setting constrained_layout
. Setting it to false allows figure to be successfully pickled without any errors.