def plot2d(data, variables, filename): # default vales logscale_x = False logscale_y = False logable = True grid_bool = False axistop_bool = False axisright_bool = False axisbottom_bool = True axisleft_bool = True legend_outside = True colors = {} for i in range(0, len(variables)): colors[variables[i]] = ['w', 'b', 'g', 'r', 'c', 'm', 'y', 'k'][i] # intern parameters plot_range = 1. / 8 # means of two data series differ by this factor, # the scalng is defined ill # prepare data ---> # check for appropriate format of input if (isinstance(variables, list)): for i in variables: if (not isinstance(i, str)): print('second argument must be a list of str') return None else: print('second argument must be a list') if (not isinstance(filename, str)): print('third argument must be of type str') return None if (not isinstance(data, list)): print('first argument must be of type list') return None for j in data[0]: if (not isinstance(j, float) and not isinstance(j, int)): print('data must be passed as a list of int or float') return None for i in data[1:]: for j in i: if (not isinstance(j, float) and not isinstance(j, int)): print('data must be passed as list of int or float') return None elif j <= 0 and logable: print('logscale impossible') logable = False # to avoid using logscale #get number of rows and colums of data ncols = len(data) nrows = len(data[0]) #check wether all rows have the same size and create df df = {} # initialize empty dictionary similar to dataframe for i in range(0, ncols): if not len(data[i]) == nrows: print('missing values in data set') return None else: df[variables[i]] = data[i] # <--- prepare data # ---> analyse data # get lower and upper bounds of each variable bounds = {} for i in variables: [bounds['lower', i], bounds['upper', i]] = [min(df[i]), max(df[i])] # check whether magnitudes are approximately equal and use logscale if not means = [0] * (ncols - 1) for i in range(1, ncols): # compute means of the columns means[i - 1] = np.mean(df[variables[i]]) if min(means) / max( means ) < plot_range and logable: # if variables have different magnitudes set print('ill scaling - use logscale') # logscale logscale_y = True for i in range( 1, ncols ): # if one variable changes magnitude drastically set logscale if ((max(df[variables[i]]) - min(df[variables[i]])) != 0): temp = means[i - 1] / (max(df[variables[i]]) - min(df[variables[i]])) if temp < plot_range and logable and not logscale_y: print('very ill scaling - use logscale') logscale_y = True # <--- analyse data # plot ---> fig = plt.figure(1) ax = Subplot(fig, 111) fig.add_subplot(ax) for i in variables[1:]: ax.scatter(df[variables[0]], df[i], label='$' + i + '$', color=colors[i]) # font # labels and legend ax.legend(loc='best') plt.xlabel(variables[0], **hfont) if legend_outside: box = ax.get_position() ax.set_position([box.x0, box.y0, box.width * 0.8, box.height]) ax.legend(loc='center left', bbox_to_anchor=(1, 0.5)) # logscale if logscale_y: plt.yscale('log') if logscale_x: plt.xscale('log') # grid plt.grid(grid_bool) # visible axis ax.axis["right"].set_visible(axisright_bool) ax.axis["top"].set_visible(axistop_bool) ax.axis["bottom"].set_visible(axisbottom_bool) ax.axis["left"].set_visible(axisleft_bool) # <--- plot # save plot ---> pdfdirectory = filename + '.pdf' pgfdirectory = filename + '.pgf' fig.savefig(pdfdirectory) plt.savefig(pgfdirectory) # <--- save plot print('run successful') return None
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self._last_pos = _new_pos self._redraw_later() return False def _redraw_later(self): self.timer = self.figure.canvas.new_timer(interval=10) self.timer.single_shot = True self.timer.add_callback(lambda: self.figure.canvas.draw_idle()) self.timer.start() def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox( [label for label, flag, _ in self.marker_lbls if flag], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + ( bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes(self.plot, self.position_setup, project, specimens) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception( "Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list( map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file( resource_filename("pyxrd.specimen", "glade/save_graph_size.glade") ) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds( 0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
class MainPlotController(object): """ A controller for the main plot canvas. Sets up the widgets and has image exporting functionality. """ file_filters = ("Portable Network Graphics (PNG)", "*.png"), \ ("Scalable Vector Graphics (SVG)", "*.svg"), \ ("Portable Document Format (PDF)", "*.pdf") _canvas = None @property def canvas(self): if not self._canvas: self.setup_figure() self.setup_canvas() self.setup_content() return self._canvas # ------------------------------------------------------------ # View integration getters # ------------------------------------------------------------ def get_toolbar_widget(self, window): return NavigationToolbar(self.canvas, window) def get_canvas_widget(self): return self.canvas # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, status_callback, marker_callback, *args, **kwargs): self.setup_layout_cache() self.setup_figure() self.setup_canvas() self.setup_content(status_callback, marker_callback) def setup_layout_cache(self): self.position_setup = PositionSetup() self.labels = list() self.marker_lbls = list() self._proxies = dict() self.scale = 1.0 self.stats = False self._last_pos = None def setup_figure(self): self.figure = Figure(dpi=72, facecolor="#FFFFFF", linewidth=0) self.figure.subplots_adjust(hspace=0.0, wspace=0.0) def setup_canvas(self): self._canvas = FigureCanvasGTK(self.figure) def setup_content(self, status_callback, marker_callback): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, facecolor=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.mtc = MotionTracker(self, status_callback) self.cc = ClickCatcher(self, marker_callback) #self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def draw(self): self._last_pos = self.fix_before_drawing() self.figure.canvas.draw() def fix_after_drawing(self, *args): _new_pos = self.fix_before_drawing() if _new_pos != self._last_pos: self.figure.canvas.draw() self._last_pos = _new_pos return False def fix_before_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ renderer = get_renderer(self.figure) if not renderer or not self._canvas.get_realized(): return False # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels, renderer) if bbox is not None: self.position_setup.left = self.position_setup.default_left + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox([ label for label, flag, _ in self.marker_lbls if flag ], renderer) if bbox is not None: self.position_setup.top = self.position_setup.default_top - bbox.height # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label], renderer) if bbox is not None: self.position_setup.bottom = self.position_setup.default_bottom + (bbox.ymax - bbox.ymin) * 2.0 # somehow we need this? # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: newy = plot_pos[1] + plot_pos[3] + y_offset - 0.025 label.set_y(newy) _new_pos = self.position_setup.to_string() return _new_pos def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, self.cc, project, specimens ) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes( self.plot, self.position_setup, project, specimens ) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container, renderer): bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception("Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None # ------------------------------------------------------------ # Graph exporting # ------------------------------------------------------------ def save(self, parent=None, current_name="graph", size="auto", num_specimens=1, offset=0.75): """ Displays a save dialog to export an image from the current plot. """ # Parse arguments: width, height = 0, 0 if size == "auto": descr, width, height, dpi = settings.OUTPUT_PRESETS[0] else: width, height, dpi = list(map(float, size.replace("@", "x").split("x"))) # Load gui: builder = Gtk.Builder() builder.add_from_file(resource_filename("pyxrd.specimen", "glade/save_graph_size.glade")) # FIXME move this to this namespace!! size_expander = builder.get_object("size_expander") cmb_presets = builder.get_object("cmb_presets") # Setup combo with presets: cmb_store = Gtk.ListStore(str, int, int, float) for row in settings.OUTPUT_PRESETS: cmb_store.append(row) cmb_presets.clear() cmb_presets.set_model(cmb_store) cell = Gtk.CellRendererText() cmb_presets.pack_start(cell, True) cmb_presets.add_attribute(cell, 'text', 0) def on_cmb_changed(cmb, *args): itr = cmb.get_active_iter() w, h, d = cmb_store.get(itr, 1, 2, 3) entry_w.set_text(str(w)) entry_h.set_text(str(h)) entry_dpi.set_text(str(d)) cmb_presets.connect('changed', on_cmb_changed) # Setup input boxes: entry_w = builder.get_object("entry_width") entry_h = builder.get_object("entry_height") entry_dpi = builder.get_object("entry_dpi") entry_w.set_text(str(width)) entry_h.set_text(str(height)) entry_dpi.set_text(str(dpi)) # What to do when the user wants to save this: def on_accept(dialog): # Get the width, height & dpi width = float(entry_w.get_text()) height = float(entry_h.get_text()) dpi = float(entry_dpi.get_text()) i_width, i_height = width / dpi, height / dpi # Save it all right! self.save_figure(dialog.filename, dpi, i_width, i_height) # Ask the user where, how and if he wants to save: DialogFactory.get_save_dialog( "Save Graph", parent=parent, filters=self.file_filters, current_name=current_name, extra_widget=size_expander ).run(on_accept) def save_figure(self, filename, dpi, i_width, i_height): """ Save the current plot Arguments: filename: the filename to save to (either .png, .pdf or .svg) dpi: Dots-Per-Inch resolution i_width: the width in inch i_height: the height in inch """ # Get original settings: original_dpi = self.figure.get_dpi() original_width, original_height = self.figure.get_size_inches() # Set everything according to the user selection: self.figure.set_dpi(dpi) self.figure.set_size_inches((i_width, i_height)) self.figure.canvas.draw() # replot bbox_inches = matplotlib.transforms.Bbox.from_bounds(0, 0, i_width, i_height) # Save the figure: self.figure.savefig(filename, dpi=dpi, bbox_inches=bbox_inches) # Put everything back the way it was: self.figure.set_dpi(original_dpi) self.figure.set_size_inches((original_width, original_height)) self.figure.canvas.draw() # replot pass # end of class
class MainPlotController (PlotController): """ A controller for the main plot canvas. """ # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, app_controller, *args, **kwargs): self.labels = list() self.marker_lbls = list() self.scale = 1.0 self.stats = False self.position_setup = PositionSetup() self.app_controller = app_controller PlotController.__init__(self, *args, **kwargs) def setup_content(self): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, axisbg=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, project, specimens ) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes( self.plot, self.position_setup, project, specimens ) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container): renderer = self._find_renderer() bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except RuntimeError: logger.exception("Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None def _find_renderer(self): if hasattr(self.figure.canvas, "get_renderer"): #Some backends, such as TkAgg, have the get_renderer method, which #makes this easy. renderer = self.figure.canvas.get_renderer() else: #Others don't, but since this is called in the 'fix_after_drawing', # a renderer has been created, so we can get it like this: renderer = self.figure._cachedRenderer return renderer def fix_after_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels) self.position_setup.left = 0.05 + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox([ label for label, flag, _ in self.marker_lbls if flag ]) if bbox is not None: self.position_setup.top = 1.0 - (0.05 + bbox.height) #Figure top - marker margin # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label]) if bbox is not None: self.position_setup.bottom = 0.10 - min(bbox.ymin, 0.0) # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: label.set_y(plot_pos[1] + plot_pos[3] + y_offset - 0.025) self.figure.canvas.draw() return False pass # end of class
class MainPlotController(PlotController): """ A controller for the main plot canvas. """ # ------------------------------------------------------------ # Initialization and other internals # ------------------------------------------------------------ def __init__(self, app_controller, *args, **kwargs): self.labels = list() self.marker_lbls = list() self.scale = 1.0 self.stats = False self.position_setup = PositionSetup() self.app_controller = app_controller PlotController.__init__(self, *args, **kwargs) def setup_content(self): # Create subplot and add it to the figure: self.plot = Subplot(self.figure, 211, axisbg=(1.0, 1.0, 1.0, 0.0)) self.plot.set_autoscale_on(False) self.figure.add_axes(self.plot) # Connect events: self.canvas.mpl_connect('draw_event', self.fix_after_drawing) self.canvas.mpl_connect('resize_event', self.fix_after_drawing) self.update() # ------------------------------------------------------------ # Update methods # ------------------------------------------------------------ def update(self, clear=False, project=None, specimens=None): """ Updates the entire plot with the given information. """ if clear: self.plot.cla() if project and specimens: self.labels, self.marker_lbls = plot_specimens( self.plot, self.position_setup, project, specimens) # get mixtures for the selected specimens: plot_mixtures(self.plot, project, [ mixture for mixture in project.mixtures if any(specimen in mixture.specimens for specimen in specimens) ]) update_axes(self.plot, self.position_setup, project, specimens) self.draw() # ------------------------------------------------------------ # Plot position and size calculations # ------------------------------------------------------------ def _get_joint_bbox(self, container): renderer = self._find_renderer() bboxes = [] try: for text in container: bbox = text.get_window_extent(renderer=renderer) # the figure transform goes from relative coords->pixels and we # want the inverse of that bboxi = bbox.inverse_transformed(self.figure.transFigure) bboxes.append(bboxi) except (RuntimeError, ValueError): logger.exception( "Caught unhandled exception when joining boundig boxes") return None # don't continue # this is the bbox that bounds all the bboxes, again in relative # figure coords if len(bboxes) > 0: bbox = transforms.Bbox.union(bboxes) return bbox else: return None def _find_renderer(self): if hasattr(self.figure.canvas, "get_renderer"): #Some backends, such as TkAgg, have the get_renderer method, which #makes this easy. renderer = self.figure.canvas.get_renderer() else: #Others don't, but since this is called in the 'fix_after_drawing', # a renderer has been created, so we can get it like this: renderer = self.figure._cachedRenderer return renderer def fix_after_drawing(self, *args): """ Fixes alignment issues due to longer labels or smaller windows Is executed after an initial draw event, since we can then retrieve the actual label dimensions and shift/resize the plot accordingly. """ # Fix left side for wide specimen labels: if len(self.labels) > 0: bbox = self._get_joint_bbox(self.labels) if bbox is not None: self.position_setup.left = 0.05 + bbox.width # Fix top for high marker labels: if len(self.marker_lbls) > 0: bbox = self._get_joint_bbox( [label for label, flag, _ in self.marker_lbls if flag]) if bbox is not None: self.position_setup.top = 1.0 - (0.05 + bbox.height ) #Figure top - marker margin # Fix bottom for x-axis label: bottom_label = self.plot.axis["bottom"].label if bottom_label is not None: bbox = self._get_joint_bbox([bottom_label]) if bbox is not None: self.position_setup.bottom = 0.10 - min(bbox.ymin, 0.0) # Calculate new plot position & set it: plot_pos = self.position_setup.position self.plot.set_position(plot_pos) # Adjust specimen label position for label in self.labels: label.set_x(plot_pos[0] - 0.025) # Adjust marker label position for label, flag, y_offset in self.marker_lbls: if flag: label.set_y(plot_pos[1] + plot_pos[3] + y_offset - 0.025) self.figure.canvas.draw() return False pass # end of class