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.
Advertisement
Answer
Turns out the issue was setting constrained_layout
. Setting it to false allows figure to be successfully pickled without any errors.