def addColorbar(mappable, ax): """ Append colorbar to axes Parameters ---------- mappable : a mappable object ax : an axes object Returns ------- cbax : colorbar axes object Notes ----- This is mostly useful for axes created with :func:`curvedEarthAxes`. written by Sebastien, 2013-04 """ from mpl_toolkits.axes_grid1 import SubplotDivider, LocatableAxes, Size import matplotlib.pyplot as plt fig1 = ax.get_figure() divider = SubplotDivider(fig1, *ax.get_geometry(), aspect=True) # axes for colorbar cbax = LocatableAxes(fig1, divider.get_position()) h = [ Size.AxesX(ax), # main axes Size.Fixed(0.1), # padding Size.Fixed(0.2) ] # colorbar v = [Size.AxesY(ax)] _ = divider.set_horizontal(h) _ = divider.set_vertical(v) _ = ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) _ = cbax.set_axes_locator(divider.new_locator(nx=2, ny=0)) _ = fig1.add_axes(cbax) _ = cbax.axis["left"].toggle(all=False) _ = cbax.axis["top"].toggle(all=False) _ = cbax.axis["bottom"].toggle(all=False) _ = cbax.axis["right"].toggle(ticklabels=True, label=True) _ = plt.colorbar(mappable, cax=cbax) return cbax
def addColorbar(mappable, ax): """ Append colorbar to axes Parameters ---------- mappable : a mappable object ax : an axes object Returns ------- cbax : colorbar axes object Notes ----- This is mostly useful for axes created with :func:`curvedEarthAxes`. written by Sebastien, 2013-04 """ from mpl_toolkits.axes_grid1 import SubplotDivider, LocatableAxes, Size import matplotlib.pyplot as plt fig1 = ax.get_figure() divider = SubplotDivider(fig1, *ax.get_geometry(), aspect=True) # axes for colorbar cbax = LocatableAxes(fig1, divider.get_position()) h = [Size.AxesX(ax), # main axes Size.Fixed(0.1), # padding Size.Fixed(0.2)] # colorbar v = [Size.AxesY(ax)] _ = divider.set_horizontal(h) _ = divider.set_vertical(v) _ = ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) _ = cbax.set_axes_locator(divider.new_locator(nx=2, ny=0)) _ = fig1.add_axes(cbax) _ = cbax.axis["left"].toggle(all=False) _ = cbax.axis["top"].toggle(all=False) _ = cbax.axis["bottom"].toggle(all=False) _ = cbax.axis["right"].toggle(ticklabels=True, label=True) _ = plt.colorbar(mappable, cax=cbax) return cbax
def demo_locatable_axes_hard(fig1): from mpl_toolkits.axes_grid1 import SubplotDivider, LocatableAxes, Size divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) # axes for image ax = LocatableAxes(fig1, divider.get_position()) # axes for colorbar ax_cb = LocatableAxes(fig1, divider.get_position()) h = [Size.AxesX(ax), Size.Fixed(0.05), Size.Fixed(0.2)] # main axes # padding, 0.1 inch # colorbar, 0.3 inch v = [Size.AxesY(ax)] divider.set_horizontal(h) divider.set_vertical(v) ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) fig1.add_axes(ax) fig1.add_axes(ax_cb) ax_cb.axis["left"].toggle(all=False) ax_cb.axis["right"].toggle(ticks=True) Z, extent = get_demo_image() im = ax.imshow(Z, extent=extent, interpolation="nearest") plt.colorbar(im, cax=ax_cb) plt.setp(ax_cb.get_yticklabels(), visible=False)
def demo_fixed_pad_axes(): fig = plt.figure(2, (6, 6)) # The first & third items are for padding and the second items are for the # axes. Sizes are in inches. h = [Size.Fixed(1.0), Size.Scaled(1.), Size.Fixed(.2)] v = [Size.Fixed(0.7), Size.Scaled(1.), Size.Fixed(.5)] divider = Divider(fig, (0.0, 0.0, 1., 1.), h, v, aspect=False) # the width and height of the rectangle is ignored. ax = LocatableAxes(fig, divider.get_position()) ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) fig.add_axes(ax) ax.plot([1, 2, 3])
def plot_heatmap(fig2, Z): from mpl_toolkits.axes_grid1 \ import SubplotDivider, LocatableAxes, Size Z = np.flipud(Z) divider = SubplotDivider(fig2, 1, 1, 1, aspect=True) # axes for image ax = LocatableAxes(fig2, divider.get_position()) # axes for colorbar ax_cb = LocatableAxes(fig2, divider.get_position()) h = [ Size.AxesX(ax), # main axes Size.Fixed(0.05), # padding, 0.1 inch Size.Fixed(0.2), # colorbar, 0.3 inch ] v = [Size.AxesY(ax)] divider.set_horizontal(h) divider.set_vertical(v) ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) fig2.add_axes(ax) fig2.add_axes(ax_cb) ax_cb.axis["left"].toggle(all=False) ax_cb.axis["right"].toggle(ticks=True) im = ax.imshow(Z, cmap=cm.coolwarm, extent=(0, 1, 0, 1), interpolation="nearest") plt.colorbar(im, cax=ax_cb) plt.setp(ax_cb.get_yticklabels(), visible=False) mngr = plt.get_current_fig_manager() geom = mngr.window.geometry() x, y, dx, dy = geom.getRect() mngr.window.setGeometry(dx + 200, 100, dx, dy)
def plot2dHeatMap(fig, x, y, z): '''z is a 2d grid; x and y are implicit linspaces''' from mpl_toolkits.axes_grid1 \ import SubplotDivider, LocatableAxes, Size z = np.flipud(z) divider = SubplotDivider(fig, 1, 1, 1, aspect=True) # axes for image ax = LocatableAxes(fig, divider.get_position()) # axes for colorbar ax_cb = LocatableAxes(fig, divider.get_position()) h = [ Size.AxesX(ax), # main axes Size.Fixed(0.05), # padding, 0.1 inch Size.Fixed(0.2), # colorbar, 0.3 inch ] v = [Size.AxesY(ax)] divider.set_horizontal(h) divider.set_vertical(v) ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) fig.add_axes(ax) fig.add_axes(ax_cb) ax_cb.axis["left"].toggle(all=False) ax_cb.axis["right"].toggle(ticks=True) im = ax.imshow(z, cmap=cm.coolwarm, extent=(0, 1, 0, 1), interpolation="nearest") plt.colorbar(im, cax=ax_cb) plt.setp(ax_cb.get_yticklabels(), visible=False) return ax
def __init__(self, parent, file_dialog_service, cbar_height=0.2, v_gap=0.6, cbar_width=0.2, h_gap=0.6, position='bottom', **kwargs): # First element of the tuple correspond to the nx_default or ny_default. # The second element is the nx and ny position for _colorbar_axes. self._nx = { 'bottom': (1, 1), 'top': (1, 1), 'right': (1, 3), 'left': (4, 1) } self._ny = { 'bottom': (3, 1), 'top': (1, 3), 'right': (1, 1), 'left': (1, 1) } super().__init__( parent, file_dialog_service, v_axes=[Size.Scaled(1.0)] if not _is_horizontal(position) else _define_axes(position, cbar_height, v_gap), h_axes=[Size.Scaled(1.0)] if _is_horizontal(position) else _define_axes(position, cbar_width, h_gap), nx_default=self._nx[position][0], ny_default=self._ny[position][0], **kwargs) self._colorbar_axes = LocatableAxes(self._figure, self._divider.get_position()) self._colorbar_axes.set_axes_locator( self._divider.new_locator(nx=self._nx[position][1], ny=self._ny[position][1])) self._figure.add_axes(self._colorbar_axes) self._cbar = None self._points = None self._position = position
def demo_fixed_pad_axes(): fig = plt.figure(2, (6, 6)) # The first & third items are for padding and the second items are for the axes. # sizes are in inch. h = [ Size.Fixed(1.0), Size.Scaled(1.), Size.Fixed(.2), ] v = [ Size.Fixed(0.7), Size.Scaled(1.), Size.Fixed(.5), ] divider = Divider(fig, (0.0, 0.0, 1., 1.), h, v, aspect=False) # the width and height of the rectangle is ignored. ax = LocatableAxes(fig, divider.get_position()) ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) fig.add_axes(ax) ax.plot([1, 2, 3])
def demo_locatable_axes_hard(fig1): from mpl_toolkits.axes_grid1 \ import SubplotDivider, LocatableAxes, Size divider = SubplotDivider(fig1, 2, 2, 2, aspect=True) # axes for image ax = LocatableAxes(fig1, divider.get_position()) # axes for colorbar ax_cb = LocatableAxes(fig1, divider.get_position()) h = [ Size.AxesX(ax), # main axes Size.Fixed(0.05), # padding, 0.1 inch Size.Fixed(0.2), # colorbar, 0.3 inch ] v = [Size.AxesY(ax)] divider.set_horizontal(h) divider.set_vertical(v) ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) ax_cb.set_axes_locator(divider.new_locator(nx=2, ny=0)) fig1.add_axes(ax) fig1.add_axes(ax_cb) ax_cb.axis["left"].toggle(all=False) ax_cb.axis["right"].toggle(ticks=True) Z, extent = get_demo_image() im = ax.imshow(Z, extent=extent, interpolation="nearest") plt.colorbar(im, cax=ax_cb) plt.setp(ax_cb.get_yticklabels(), visible=False)
def _create_secondary_xy_axes(figure, divider, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position()) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) axes.xaxis.tick_top() axes.xaxis.set_label_position('top') axes.yaxis.tick_right() axes.yaxis.set_label_position('right') axes.patch.set_visible(visible) axes.set_zorder(z_order) figure.add_axes(axes) axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) return axes
def __init__(self, ax_size, wspace, dpi, cbar, cbar_width, left, bottom, right, top, fig_func=None): if not cbar: wspace = 0 cbar_width = 0 # convert arguments from pixels to inches ax_size = np.asarray(ax_size) / dpi wspace, cbar_width, left, bottom, right, top = np.array( (wspace, cbar_width, left, bottom, right, top)) / dpi horiz = [left, ax_size[0], wspace, cbar_width, right] vert = [bottom, ax_size[1], top] figsize = (sum(horiz), sum(vert)) fig_func = plt.figure if fig_func is None else fig_func fig = fig_func(figsize=figsize, dpi=dpi) horiz, vert = list(map(Size.Fixed, horiz)), list(map(Size.Fixed, vert)) divider = Divider(fig, (0.0, 0.0, 1., 1.), horiz, vert, aspect=False) ax = LocatableAxes(fig, divider.get_position()) ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) fig.add_axes(ax) if cbar: cax = LocatableAxes(fig, divider.get_position()) cax.set_axes_locator(divider.new_locator(nx=3, ny=1)) fig.add_axes(cax) self.fig = fig self.ax = ax self.cax = cax if cbar else None
def _create_shared_axes(figure, divider, shared_axes, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position(), sharex=shared_axes, sharey=shared_axes, frameon=False) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) for spine in axes.spines.values(): spine.set_visible(False) for axis in axes.axis.values(): axis.set_visible(False) axes.patch.set_visible(False) axes.set_visible(False) axes.set_zorder(z_order) figure.add_axes(axes) return axes
def demo_fixed_size_axes(): fig1 = plt.figure(1, (10, 5)) # The first items are for padding and the second items are for the axes. # sizes are in inch. h = [Size.Fixed(1.0), Size.Fixed(7.5)] v = [Size.Fixed(0.7), Size.Fixed(4.5)] divider = Divider(fig1, (0.0, 0.0, 1., 1.), h, v, aspect=False) # the width and height of the rectangle is ignored. ax = LocatableAxes(fig1, divider.get_position()) ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) fig1.add_axes(ax) ax.plot([1, 2, 3])
class MatPlotColorbar(MatPlotLibBase): def __init__(self, parent, file_dialog_service, cbar_height=0.2, v_gap=0.6, cbar_width=0.2, h_gap=0.6, position='bottom', **kwargs): # First element of the tuple correspond to the nx_default or ny_default. # The second element is the nx and ny position for _colorbar_axes. self._nx = { 'bottom': (1, 1), 'top': (1, 1), 'right': (1, 3), 'left': (4, 1) } self._ny = { 'bottom': (3, 1), 'top': (1, 3), 'right': (1, 1), 'left': (1, 1) } super().__init__( parent, file_dialog_service, v_axes=[Size.Scaled(1.0)] if not _is_horizontal(position) else _define_axes(position, cbar_height, v_gap), h_axes=[Size.Scaled(1.0)] if _is_horizontal(position) else _define_axes(position, cbar_width, h_gap), nx_default=self._nx[position][0], ny_default=self._ny[position][0], **kwargs) self._colorbar_axes = LocatableAxes(self._figure, self._divider.get_position()) self._colorbar_axes.set_axes_locator( self._divider.new_locator(nx=self._nx[position][1], ny=self._ny[position][1])) self._figure.add_axes(self._colorbar_axes) self._cbar = None self._points = None self._position = position def _plot_colorbar(self, label): if self._cbar is None: self._cbar = self._figure.colorbar( self._points, self._colorbar_axes, orientation='horizontal' if _is_horizontal(self._position) else 'vertical') self._cbar.set_label(label) else: self._cbar.update_bruteforce(self._points) self._cbar.set_label(label) def _clear_colorbar(self): for a in self._colorbar_axes.collections: a.remove() @property def colorbar(self): return self._cbar @colorbar.setter def colorbar(self, cbar): self._cbar = cbar
def __init__(self, images, shape): fig = plt.figure(figsize=(10, 6)) # subplot positions h_nav = [Size.Fixed(0.5), Size.Fixed(2.5)] v_nav = [Size.Fixed(0.5), Size.Scaled(1.0), Size.Fixed(0.5)] h_im = [Size.Fixed(0.5), Size.Scaled(1.0), Size.Fixed(0.5)] v_im = [Size.Fixed(0.5), Size.Scaled(1.0), Size.Fixed(0.5)] nav = Divider(fig, (0.0, 0.0, 0.2, 1.), h_nav, v_nav, aspect=False) image = Divider(fig, (0.2, 0.0, 0.8, 1.), h_im, v_im, aspect=True) image.set_anchor('C') # Toolbar menu box ax1 = LocatableAxes(fig, nav.get_position()) ax1.set_axes_locator(nav.new_locator(nx=1, ny=1)) ax1.get_xaxis().set_visible(False) ax1.get_yaxis().set_visible(False) fig.add_axes(ax1, label='toolbar') ax1.text(0.05, 0.45, "Filter", weight='heavy', transform=ax1.transAxes) # Image space ax2 = LocatableAxes(fig, image.get_position()) ax2.set_axes_locator(image.new_locator(nx=1, ny=1)) fig.add_axes(ax2, label='image_space') self.callback = ImageIndex(images, shape, fig) # Navigation ## Go to ax_text_index = plt.axes([0.59, 0.05, 0.1, 0.075]) ip = InsetPosition(ax1, [0.2, 0.84, 0.3, 0.05]) ax_text_index.set_axes_locator(ip) entry_index = TextBox(ax_text_index, 'Go to', initial="0") entry_index.on_submit(self.callback.submit_index) ## Previous ax_prev = plt.axes([0.7, 0.05, 0.075, 0.075]) ip = InsetPosition(ax1, [0.55, 0.84, 0.15, 0.05]) ax_prev.set_axes_locator(ip) bprev = Button(ax_prev, '<<') bprev.on_clicked(self.callback.prev) ## Next ax_next = plt.axes([0.81, 0.05, 0.075, 0.075]) ip = InsetPosition(ax1, [0.75, 0.84, 0.15, 0.05]) ax_next.set_axes_locator(ip) bnext = Button(ax_next, '>>') bnext.on_clicked(self.callback.next) # Bounding Boxes ax_chec = plt.axes([0.1, 0.05, 0.35, 0.075]) ip = InsetPosition(ax1, [0.05, 0.5, 0.9, 0.3]) ax_chec.set_axes_locator(ip) ax_chec.text(0.05, 0.85, "Bounding Boxes", transform=ax_chec.transAxes) check = CheckButtons(ax_chec, ('characters', 'lines'), (False, False)) check.on_clicked(self.callback.update_bboxes) # Filtering ## Image ax_text_image = plt.axes([0.1, 0.1, 0.1, 0.075]) ip = InsetPosition(ax1, [0.26, 0.38, 0.64, 0.05]) ax_text_image.set_axes_locator(ip) entry_image = TextBox(ax_text_image, 'images', initial="image_id,image_id") entry_image.on_submit(self.callback.submit_images) ## Characters ax_text_char = plt.axes([0.1, 0.2, 0.1, 0.075]) ip = InsetPosition(ax1, [0.21, 0.3, 0.69, 0.05]) ax_text_char.set_axes_locator(ip) entry_char = TextBox(ax_text_char, 'chars', initial="U+3055,U+3056") entry_char.on_submit(self.callback.submit_chars) ## Reset ax_reset = plt.axes([0., 0., 1., 1.]) ip = InsetPosition(ax1, [0.05, 0.2, 0.2, 0.05]) ax_reset.set_axes_locator(ip) breset = Button(ax_reset, 'Reset') breset.on_clicked(self.callback.reset) plt.show()
def plot(self, time, beam=None, maxground=2000, maxalt=500, step=1, showrefract=False, nr_cmap='jet_r', nr_lim=[0.8, 1.], raycolor='0.4', fig=None, rect=111): """Plot ray paths **Args**: * **time** (datetime.datetime): time of rays * [**beam**]: beam number * [**maxground**]: maximum ground range [km] * [**maxalt**]: highest altitude limit [km] * [**step**]: step between each plotted ray (in number of ray steps) * [**showrefract**]: show refractive index along ray paths (supersedes raycolor) * [**nr_cmap**]: color map name for refractive index coloring * [**nr_lim**]: refractive index plotting limits * [**raycolor**]: color of ray paths * [**rect**]: subplot spcification * [**fig**]: A pylab.figure object (default to gcf) **Returns**: * **ax**: matplotlib.axes object containing formatting * **aax**: matplotlib.axes object containing data * **cbax**: matplotlib.axes object containing colorbar **Example**: :: # Show ray paths with colored refractive index along path import datetime as dt from models import raydarn sTime = dt.datetime(2012, 11, 18, 5) rto = raydarn.rtRun(sTime, rCode='bks', beam=12) rto.readRays() # read rays into memory ax, aax, cbax = rto.rays.plot(sTime, step=2, showrefract=True, nr_lim=[.85,1]) ax.grid() written by Sebastien, 2013-04 """ from utils import plotUtils from mpl_toolkits.axes_grid1 import make_axes_locatable from matplotlib.collections import LineCollection import matplotlib.pyplot as plt import numpy as np ax, aax = plotUtils.curvedEarthAxes(fig=fig, rect=rect, maxground=maxground, maxalt=maxalt) # make sure that the required time and beam are present assert (time in self.paths.keys()), 'Unkown time %s' % time if beam: assert (beam in self.paths[time].keys()), 'Unkown beam %s' % beam else: beam = self.paths[time].keys()[0] for ir, (el, rays) in enumerate( sorted(self.paths[time][beam].items()) ): if not ir % step: if not showrefract: aax.plot(rays['th'], rays['r']*1e-3, c=raycolor, zorder=2) else: points = np.array([rays['th'], rays['r']*1e-3]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) lcol = LineCollection( segments ) lcol.set_cmap( nr_cmap ) lcol.set_norm( plt.Normalize(*nr_lim) ) lcol.set_array( rays['nr'] ) aax.add_collection( lcol ) # Add a colorbar when plotting refractive index if showrefract: from mpl_toolkits.axes_grid1 import SubplotDivider, LocatableAxes, Size fig1 = ax.get_figure() divider = SubplotDivider(fig1, *ax.get_geometry(), aspect=True) # axes for colorbar cbax = LocatableAxes(fig1, divider.get_position()) h = [Size.AxesX(ax), # main axes Size.Fixed(0.1), # padding Size.Fixed(0.2)] # colorbar v = [Size.AxesY(ax)] divider.set_horizontal(h) divider.set_vertical(v) ax.set_axes_locator(divider.new_locator(nx=0, ny=0)) cbax.set_axes_locator(divider.new_locator(nx=2, ny=0)) fig1.add_axes(cbax) cbax.axis["left"].toggle(all=False) cbax.axis["top"].toggle(all=False) cbax.axis["bottom"].toggle(all=False) cbax.axis["right"].toggle(ticklabels=True, label=True) plt.colorbar(lcol, cax=cbax) cbax.set_ylabel("refractive index") return ax, aax, cbax
def _divide_axes_grid(mpl_figure, divider, layer_name, inp_size, layer_size, mode, target=False): ''' DOCUMENTATION :param mpl_figure: an instance of matplotlib.figure.Figure :param N: number of layers :param i: layer index :param inp_size: number units in the layer :param layer_size: number of sending units to the layer :param target: include target :return: ''' # provide axes-grid coordinates, image sizes, and titles ax_params = { 'input_': ((0, 0), (1, inp_size), 'input'), 'weights': ((0, 2), (layer_size, inp_size), 'W'), 'biases': ((2, 2), (layer_size, 1), 'b'), 'net_input': ((4, 2), (layer_size, 1), 'net'), 'output': ((6, 2), (layer_size, 1), 'a') } if target: ax_params['target'] = ((8, 2), (layer_size, 1), 't') # define padding size _ = Scaled(.8) # define grid column sizes (left to right): weights, biases, net_input, output, gweight, gbiases, gnet_input, goutput mat_w, cvec_w = Scaled(inp_size), Scaled(1) left_panel = [mat_w, _, cvec_w, _, cvec_w, _, cvec_w, _] cols = left_panel + [cvec_w, _] if target else left_panel t = int(target) if mode > 0: right_panel = [_, mat_w, _, cvec_w, _, cvec_w, _, cvec_w] gax_params = { 'gweights': ((9 + 2 * t, 2), (layer_size, inp_size), 'W\''), 'gbiases': ((11 + 2 * t, 2), (layer_size, 1), 'b\''), 'gnet_input': ((13 + 2 * t, 2), (layer_size, 1), 'net\''), 'goutput': ((15 + 2 * t, 2), (layer_size, 1), 'a\'') } for k, v in gax_params.items(): ax_params[k] = v if mode == 2: right_panel += [_, mat_w, _, cvec_w] ax_params['sgweights'] = ((17 + 2 * t, 2), (layer_size, inp_size), 'sW\'') ax_params['sgbiases'] = ((19 + 2 * t, 2), (layer_size, 1), 'sb\'') cols += right_panel # define grid row sizes (top to bottom): weights, input mat_h, rvec_h = Scaled(layer_size), Scaled(1) rows = [rvec_h, _, mat_h] # make divider divider.set_horizontal(cols) divider.set_vertical(rows) # create axes and locate appropriately img_dict = {} for k, (ax_coords, img_dims, ax_title) in ax_params.items(): ax = LocatableAxes(mpl_figure, divider.get_position()) ax.set_axes_locator( divider.new_locator(nx=ax_coords[0], ny=ax_coords[1])) ax.tick_params(labelbottom=False, labelleft=False) ax.set_xticks([]) ax.set_yticks([]) ax.set_xlabel(ax_title) if k == 'input_' else ax.set_title(ax_title) if k == 'weights': ax.set_ylabel(layer_name) mpl_figure.add_axes(ax) img = ax.imshow(np.zeros(img_dims)) img_dict[k] = img return img_dict
def plot_variance_multi(savefile, data, labels, colors, ylabel, ylim, ylim_unit=1, ylim_scale=5, colors2=None, hline=None, vline=None, locator=-1, draw_dist=True, subsample=1): plt.clf() plt.rcParams["font.size"] = 14 colors2 = colors if colors2 is None else colors2 m = len(data) #fig, ax = plt.subplots(1, 1) fig = plt.figure(1, figsize=(4, 4)) # The first items are for padding and the second items are for the axes. # sizes are in inch. h = [Size.Fixed(0.65), Size.Fixed(5.5)] v = [Size.Fixed(0.7), Size.Fixed(4.)] divider = Divider(fig, (0.0, 0.0, 1., 1.), h, v, aspect=False) # the width and height of the rectangle is ignored. ax = LocatableAxes(fig, divider.get_position()) ax.set_axes_locator(divider.new_locator(nx=1, ny=1)) fig.add_axes(ax) if not hasattr(draw_dist, '__getitem__'): v = draw_dist draw_dist = [v for i in range(m)] print(draw_dist) for k in range(m)[::-1]: plot_m = numpy.median(data[k], 0) sorted = numpy.sort(data[k], 0) n = data[k].shape[0] T = data[k].shape[1] t = numpy.arange(0, T) fin = plot_m[-1] best = sorted[0][-1] alpha = 1.0 / (n // 2) * 0.4 if subsample > 1: t = subsample_data(t, subsample) plot_m = subsample_data(plot_m, subsample) sorted = subsample_data(sorted, subsample) if k == 0: if ylim is None: ymax = min([fin * ylim_scale, sorted[-1, 0], 90]) ymin = numpy.floor(numpy.max(best / ylim_unit - 0.5, 0)) * ylim_unit ymin = 0 ylim = [ymin, numpy.ceil(ymax / ylim_unit) * ylim_unit] else: ylim = ylim # 3: 0 if draw_dist[k]: print('k', k) for i in range(n // 2): ax.fill_between(t, sorted[-1 - i], sorted[i], facecolor=colors2[k], alpha=alpha) for k in range(m)[::-1]: plot_m = numpy.median(data[k], 0) t = numpy.arange(0, T) if subsample > 1: t = subsample_data(t, subsample) plot_m = subsample_data(plot_m, subsample) ax.plot(t, plot_m, lw=1, color=colors[k], ls='-', label=labels[k]) if hline is not None: ax.plot((0, T), (hline, hline), color='gray', ls=':') if vline is not None: ax.plot((vline, vline), (ylim[0], ylim[1]), color='gray', ls=':') ax.legend(loc='upper right') ax.set_xlabel('Iterations') ax.set_ylabel(ylabel) ax.set_xlim([0, T]) ax.set_ylim(ylim) if locator >= 0: ax.yaxis.set_major_locator(MultipleLocator(locator)) ax.grid() plt.savefig(savefile + '.png') plt.savefig(savefile + '.pdf')
def __init__(self, parent, file_dialog_service, h_margin=(0.8, 0.1), v_margin=(0.5, 0.15), h_axes=[Size.Scaled(1.0)], v_axes=[Size.Scaled(1.0)], nx_default=1, ny_default=1): QWidget.__init__(self, parent) self._file_dialog_service = file_dialog_service self._figure = Figure() self._canvas = FigureCanvas(self._figure) h = [Size.Fixed(h_margin[0]), *h_axes, Size.Fixed(h_margin[1])] v = [Size.Fixed(v_margin[0]), *v_axes, Size.Fixed(v_margin[1])] self._divider = Divider(self._figure, (0.0, 0.0, 1.0, 1.0), h, v, aspect=False) self._axes = LocatableAxes(self._figure, self._divider.get_position()) self._axes.set_axes_locator( self._divider.new_locator(nx=nx_default, ny=ny_default)) self._axes.set_zorder(2) self._axes.patch.set_visible(False) for spine in ['top', 'right']: self._axes.spines[spine].set_visible(False) self._figure.add_axes(self._axes) self._canvas.setParent(self) self._layout = QVBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._canvas) self.setLayout(self._layout) self._figure.canvas.mpl_connect('scroll_event', self._on_scroll) self._xy_extents = None self._background_cache = None self._decoration_artists = [] self._is_panning = False self._zoom_selector = _RectangleSelector(self._axes, self._zoom_selected) self._zoom_selector.set_active(False) self._x_extent_padding = 0.01 self._y_extent_padding = 0.01 self._axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) self._axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) self._active_tools = {} self._span = _SpanSeletor(self._axes, self._handle_span_select, 'horizontal', rectprops=dict(alpha=0.2, facecolor='red', edgecolor='k'), span_stays=True) self._span.set_on_select_none(self._handle_span_select_none) self.span = self._previous_span = None self._span_center_mouse_event = None self._span_left_mouse_event = None self._span_right_mouse_event = None self._figure.canvas.mpl_connect('button_press_event', self._handle_press) self._figure.canvas.mpl_connect('motion_notify_event', self._handle_move) self._figure.canvas.mpl_connect('button_release_event', self._handle_release) self._figure.canvas.mpl_connect('resize_event', self._handle_resize) self.activateTool(ToolType.span, self.isActiveDefault(ToolType.span)) self._pan_event = None self._pending_draw = None self._pending_artists_draw = None self._other_draw_events = [] self._draw_timer = QTimer(self) self._draw_timer.timeout.connect(self._do_draw_events) self._draw_timer.start(20) self._zoom_skew = None self._menu = QMenu(self) self._copy_image_action = QAction(self.tr('Copy To Clipboard'), self) self._copy_image_action.triggered.connect(self.copyToClipboard) self._copy_image_action.setShortcuts(QKeySequence.Copy) self._save_image_action = QAction(self.tr('Save As Image'), self) self._save_image_action.triggered.connect(self.saveAsImage) self._show_table_action = QAction(self.tr('Show Table'), self) self._show_table_action.triggered.connect(self.showTable) self._menu.addAction(self._copy_image_action) self._menu.addAction(self._save_image_action) self._menu.addAction(self._show_table_action) self.addAction(self._copy_image_action) self._table_view = None self._single_axis_zoom_enabled = True self._cached_label_width_height = None if hasattr(type(self), 'dataChanged'): self.dataChanged.connect(self._on_data_changed) self._options_view = None self._secondary_axes = self._secondary_y_extent = self._secondary_x_extent = None self._legend = None self._draggable_legend = None self._setting_axis_limits = False self.hasHiddenSeries = False
class MatPlotLibBase(QWidget): def __init__(self, parent, file_dialog_service, h_margin=(0.8, 0.1), v_margin=(0.5, 0.15), h_axes=[Size.Scaled(1.0)], v_axes=[Size.Scaled(1.0)], nx_default=1, ny_default=1): QWidget.__init__(self, parent) self._file_dialog_service = file_dialog_service self._figure = Figure() self._canvas = FigureCanvas(self._figure) h = [Size.Fixed(h_margin[0]), *h_axes, Size.Fixed(h_margin[1])] v = [Size.Fixed(v_margin[0]), *v_axes, Size.Fixed(v_margin[1])] self._divider = Divider(self._figure, (0.0, 0.0, 1.0, 1.0), h, v, aspect=False) self._axes = LocatableAxes(self._figure, self._divider.get_position()) self._axes.set_axes_locator( self._divider.new_locator(nx=nx_default, ny=ny_default)) self._axes.set_zorder(2) self._axes.patch.set_visible(False) for spine in ['top', 'right']: self._axes.spines[spine].set_visible(False) self._figure.add_axes(self._axes) self._canvas.setParent(self) self._layout = QVBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._canvas) self.setLayout(self._layout) self._figure.canvas.mpl_connect('scroll_event', self._on_scroll) self._xy_extents = None self._background_cache = None self._decoration_artists = [] self._is_panning = False self._zoom_selector = _RectangleSelector(self._axes, self._zoom_selected) self._zoom_selector.set_active(False) self._x_extent_padding = 0.01 self._y_extent_padding = 0.01 self._axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) self._axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) self._active_tools = {} self._span = _SpanSeletor(self._axes, self._handle_span_select, 'horizontal', rectprops=dict(alpha=0.2, facecolor='red', edgecolor='k'), span_stays=True) self._span.set_on_select_none(self._handle_span_select_none) self.span = self._previous_span = None self._span_center_mouse_event = None self._span_left_mouse_event = None self._span_right_mouse_event = None self._figure.canvas.mpl_connect('button_press_event', self._handle_press) self._figure.canvas.mpl_connect('motion_notify_event', self._handle_move) self._figure.canvas.mpl_connect('button_release_event', self._handle_release) self._figure.canvas.mpl_connect('resize_event', self._handle_resize) self.activateTool(ToolType.span, self.isActiveDefault(ToolType.span)) self._pan_event = None self._pending_draw = None self._pending_artists_draw = None self._other_draw_events = [] self._draw_timer = QTimer(self) self._draw_timer.timeout.connect(self._do_draw_events) self._draw_timer.start(20) self._zoom_skew = None self._menu = QMenu(self) self._copy_image_action = QAction(self.tr('Copy To Clipboard'), self) self._copy_image_action.triggered.connect(self.copyToClipboard) self._copy_image_action.setShortcuts(QKeySequence.Copy) self._save_image_action = QAction(self.tr('Save As Image'), self) self._save_image_action.triggered.connect(self.saveAsImage) self._show_table_action = QAction(self.tr('Show Table'), self) self._show_table_action.triggered.connect(self.showTable) self._menu.addAction(self._copy_image_action) self._menu.addAction(self._save_image_action) self._menu.addAction(self._show_table_action) self.addAction(self._copy_image_action) self._table_view = None self._single_axis_zoom_enabled = True self._cached_label_width_height = None if hasattr(type(self), 'dataChanged'): self.dataChanged.connect(self._on_data_changed) self._options_view = None self._secondary_axes = self._secondary_y_extent = self._secondary_x_extent = None self._legend = None self._draggable_legend = None self._setting_axis_limits = False self.hasHiddenSeries = False enabledToolsChanged = pyqtSignal() spanChanged = pyqtSignal(SpanModel) hasHiddenSeriesChanged = pyqtSignal(bool) span = AutoProperty(SpanModel) hasHiddenSeries = AutoProperty(bool) def setOptionsView(self, options_view): self._options_view = options_view self._options_view.setSecondaryYLimitsEnabled( self._secondary_y_enabled()) self._options_view.setSecondaryXLimitsEnabled( self._secondary_x_enabled()) self._options_view.showGridLinesChanged.connect( self._update_grid_lines) self._options_view.xAxisLowerLimitChanged.connect( self._handle_options_view_limit_changed(x_min_changed=True)) self._options_view.xAxisUpperLimitChanged.connect( self._handle_options_view_limit_changed(x_max_changed=True)) self._options_view.yAxisLowerLimitChanged.connect( self._handle_options_view_limit_changed(y_min_changed=True)) self._options_view.yAxisUpperLimitChanged.connect( self._handle_options_view_limit_changed(y_max_changed=True)) self._options_view.xAxisLimitsChanged.connect( self._handle_options_view_limit_changed(x_min_changed=True, x_max_changed=True)) self._options_view.yAxisLimitsChanged.connect( self._handle_options_view_limit_changed(y_min_changed=True, y_max_changed=True)) self._options_view.secondaryXAxisLowerLimitChanged.connect( self._handle_options_view_secondary_limit_changed( x_min_changed=True)) self._options_view.secondaryXAxisUpperLimitChanged.connect( self._handle_options_view_secondary_limit_changed( x_max_changed=True)) self._options_view.secondaryYAxisLowerLimitChanged.connect( self._handle_options_view_secondary_limit_changed( y_min_changed=True)) self._options_view.secondaryYAxisUpperLimitChanged.connect( self._handle_options_view_secondary_limit_changed( y_max_changed=True)) self._options_view.secondaryXAxisLimitsChanged.connect( self._handle_options_view_secondary_limit_changed( x_min_changed=True, x_max_changed=True)) self._options_view.secondaryYAxisLimitsChanged.connect( self._handle_options_view_secondary_limit_changed( y_min_changed=True, y_max_changed=True)) def setLegendControl(self, legend_control): self._legend_control = legend_control self._legend_control.seriesUpdated.connect(self._legend_series_updated) self._legend_control.showLegendChanged.connect(self._show_legend) self._legend_control.seriesNameChanged.connect( self._handle_series_name_changed) self._legend_control.showSeriesChanged.connect( self._handle_show_series_changed) bind(self._legend_control, self, 'hasHiddenSeries', two_way=False) def _legend_series_updated(self): if self._legend is not None: self._show_legend(self._legend_control.showLegend) def _show_legend(self, show): if self._legend and not show: self._legend.remove() self._legend = None self.draw() elif show: if self._legend: self._legend.remove() show_series = self._legend_control.showSeries handles = [ h for h, s in zip(self._legend_control.seriesHandles, show_series) if s ] names = [ n for n, s in zip(self._legend_control.seriesNames, show_series) if s ] axes = (self._secondary_axes if self._secondary_axes and self._secondary_axes.get_visible() and self._secondary_axes.get_zorder() > self._axes.get_zorder() else self._axes) self._legend = self._create_legend( axes, handles, names, markerscale=self._get_legend_markerscale()) if self._get_legend_text_color() is not None: for text in self._legend.texts: text.set_color(self._get_legend_text_color()) self._draggable_legend = DraggableLegend(self._legend) self.draw() def _get_legend_markerscale(self): return 5 def _create_legend(self, axes, handles, names, **kwargs): return axes.legend(handles, names, **kwargs) def _get_legend_text_color(self): return None def _handle_series_name_changed(self, index, series_name): if self._legend is not None and index < len( self._legend_control.seriesHandles): visible_handles = [ h for h, s in zip(self._legend_control.seriesHandles, self._legend_control.showSeries) if s and h is not None ] try: legend_index = visible_handles.index( self._legend_control.seriesHandles[index]) except ValueError: return if legend_index < len(self._legend.texts): self._legend.texts[legend_index].set_text(series_name) self.draw() def _handle_show_series_changed(self, index, show_series): if index < len(self._legend_control.seriesHandles): self._set_series_visibility( self._legend_control.seriesHandles[index], show_series) if self._legend is not None: self._show_legend(self._legend_control.showLegend) else: self.draw() def _set_series_visibility(self, handle, visible): if not handle: return if hasattr(handle, 'set_visible'): handle.set_visible(visible) elif hasattr(handle, 'get_children'): for child in handle.get_children(): self._set_series_visibility(child, visible) def _update_grid_lines(self): show_grid_lines = False if self._options_view is None else self._options_view.showGridLines gridline_color = self._axes.spines['bottom'].get_edgecolor() gridline_color = gridline_color[0], gridline_color[1], gridline_color[ 2], 0.5 kwargs = dict(color=gridline_color, alpha=0.5) if show_grid_lines else {} self._axes.grid(show_grid_lines, **kwargs) self.draw() def _handle_options_view_limit_changed(self, x_min_changed=False, x_max_changed=False, y_min_changed=False, y_max_changed=False): def _(): if self._options_view is None or self._setting_axis_limits: return (x_min, x_max), (y_min, y_max) = (new_x_min, new_x_max), ( new_y_min, new_y_max) = self._get_xy_extents() (x_opt_min, x_opt_max), (y_opt_min, y_opt_max) = self._get_options_view_xy_extents() if x_min_changed: new_x_min = x_opt_min if x_max_changed: new_x_max = x_opt_max if y_min_changed: new_y_min = y_opt_min if y_max_changed: new_y_max = y_opt_max if [new_x_min, new_x_max, new_y_min, new_y_max ] != [x_min, x_max, y_min, y_max]: self._xy_extents = (new_x_min, new_x_max), (new_y_min, new_y_max) self._set_axes_limits() self.draw() return _ def _get_options_view_xy_extents(self): (x_data_min, x_data_max), (y_data_min, y_data_max) = self._get_data_xy_extents() x_min = x_data_min if np.isnan( self._options_view.xAxisLowerLimit ) else self._options_view.xAxisLowerLimit x_max = x_data_max if np.isnan( self._options_view.xAxisUpperLimit ) else self._options_view.xAxisUpperLimit y_min = y_data_min if np.isnan( self._options_view.yAxisLowerLimit ) else self._options_view.yAxisLowerLimit y_max = y_data_max if np.isnan( self._options_view.yAxisUpperLimit ) else self._options_view.yAxisUpperLimit return (x_min, x_max), (y_min, y_max) def _handle_options_view_secondary_limit_changed(self, x_min_changed=False, x_max_changed=False, y_min_changed=False, y_max_changed=False): def _(): if self._options_view is None or self._setting_axis_limits: return updated = False (x_opt_min, x_opt_max), ( y_opt_min, y_opt_max) = self._get_options_view_secondary_xy_extents() if self._has_secondary_y_extent() and (y_min_changed or y_max_changed): y_min, y_max = new_y_min, new_y_max = self._get_secondary_y_extent( ) if y_min_changed: new_y_min = y_opt_min if y_max_changed: new_y_max = y_opt_max if [new_y_min, new_y_max] != [y_min, y_max]: self._secondary_y_extent = (new_y_min, new_y_max) updated = True if self._has_secondary_x_extent() and (x_min_changed or x_max_changed): x_min, x_max = new_x_min, new_x_max = self._get_secondary_x_extent( ) if x_min_changed: new_x_min = x_opt_min if x_max_changed: new_x_max = x_opt_max if [new_x_min, new_x_max] != [x_min, x_max]: self._secondary_x_extent = (new_x_min, new_x_max) updated = True if updated: self._set_axes_limits() self.draw() return _ def _get_options_view_secondary_xy_extents(self): x_data_min, x_data_max = self._get_data_secondary_x_extent() y_data_min, y_data_max = self._get_data_secondary_y_extent() x_min = x_data_min if np.isnan( self._options_view.secondaryXAxisLowerLimit ) else self._options_view.secondaryXAxisLowerLimit x_max = x_data_max if np.isnan( self._options_view.secondaryXAxisUpperLimit ) else self._options_view.secondaryXAxisUpperLimit y_min = y_data_min if np.isnan( self._options_view.secondaryYAxisLowerLimit ) else self._options_view.secondaryYAxisLowerLimit y_max = y_data_max if np.isnan( self._options_view.secondaryYAxisUpperLimit ) else self._options_view.secondaryYAxisUpperLimit return (x_min, x_max), (y_min, y_max) def _on_data_changed(self): self._cached_label_width_height = None def closeEvent(self, event): QWidget.closeEvent(self, event) if event.isAccepted(): self._zoom_selector.onselect = self._span.onselect = self._span._select_none_handler = None def set_divider_h_margin(self, h_margin): h = [ Size.Fixed(h_margin[0]), Size.Scaled(1.0), Size.Fixed(h_margin[1]) ] self._divider.set_horizontal(h) def set_divider_v_margin(self, v_margin): v = [ Size.Fixed(v_margin[0]), Size.Scaled(1.0), Size.Fixed(v_margin[1]) ] self._divider.set_vertical(v) @property def x_extent_padding(self): return self._x_extent_padding @x_extent_padding.setter def x_extent_padding(self, value): self._x_extent_padding = value @property def y_extent_padding(self): return self._y_extent_padding @y_extent_padding.setter def y_extent_padding(self, value): self._y_extent_padding = value def _in_interval(self, value, interval): return interval[0] <= value <= interval[1] def _interval_skew(self, value, interval): return (value - interval[0]) / (interval[1] - interval[0]) def _in_x_scroll_zone(self, event): return self._in_interval(event.x, self._axes.bbox.intervalx ) and event.y <= self._axes.bbox.intervaly[1] def _in_y_scroll_zone(self, event): return self._in_interval(event.y, self._axes.bbox.intervaly ) and event.x <= self._axes.bbox.intervalx[1] def _on_scroll(self, event): if self._secondary_axes is not None: self._handle_scroll_secondary(event) in_x = self._in_x_scroll_zone(event) in_y = self._in_y_scroll_zone(event) if in_x or in_y and event.button in ['up', 'down']: (x_min, x_max), (y_min, y_max) = self._get_actual_xy_extents() if (in_x and self._single_axis_zoom_enabled) or (in_x and in_y): skew = self._zoom_skew and self._zoom_skew[0] skew = self._interval_skew( event.x, self._axes.bbox.intervalx) if skew is None else skew x_min, x_max = self._zoom(x_min, x_max, skew, event.button) if (in_y and self._single_axis_zoom_enabled) or (in_x and in_y): skew = self._zoom_skew and self._zoom_skew[1] skew = self._interval_skew( event.y, self._axes.bbox.intervaly) if skew is None else skew y_min, y_max = self._zoom(y_min, y_max, skew, event.button) self._xy_extents = (x_min, x_max), (y_min, y_max) self._set_axes_limits() self.draw() def _in_secondary_y_scroll_zone(self, event): return self._in_interval(event.y, self._axes.bbox.intervaly) and \ event.x >= self._axes.bbox.intervalx[1] def _in_secondary_x_scroll_zone(self, event): return self._in_interval(event.x, self._axes.bbox.intervalx) and \ event.y >= self._axes.bbox.intervaly[1] def _handle_scroll_secondary(self, event): if self._has_secondary_y_extent(): in_secondary_y = self._in_secondary_y_scroll_zone(event) if in_secondary_y and event.button in ['up', 'down']: self._secondary_y_extent = self._zoom( *self._get_secondary_y_extent(), self._interval_skew(event.y, self._axes.bbox.intervaly), event.button) if self._has_secondary_x_extent(): in_secondary_x = self._in_secondary_x_scroll_zone(event) if in_secondary_x and event.button in ['up', 'down']: self._secondary_x_extent = self._zoom( *self._get_secondary_x_extent(), self._interval_skew(event.x, self._axes.bbox.intervalx), event.button) def _get_zoom_multiplier(self): return 20 / 19 def _zoom(self, min_, max_, skew, direction): zoom_multiplier = self._get_zoom_multiplier( ) if direction == 'up' else 1 / self._get_zoom_multiplier() range_ = max_ - min_ diff = (range_ * (1 / zoom_multiplier)) - range_ max_ += diff * (1 - skew) min_ -= diff * skew return min_, max_ def _set_axes_limits(self): try: self._setting_axis_limits = True if self._secondary_axes is not None: self._set_secondary_axes_limits() self._update_ticks() (x_min, x_max), (y_min, y_max) = self._get_xy_extents() if self._options_view is not None: if self._options_view.x_limits: self._options_view.setXLimits(float(x_min), float(x_max)) if self._options_view.y_limits: self._options_view.setYLimits(float(y_min), float(y_max)) self._axes.set_xlim(*_safe_limits(x_min, x_max)) self._axes.set_ylim(*_safe_limits(y_min, y_max)) finally: self._setting_axis_limits = False def _set_secondary_axes_limits(self): if self._options_view is not None: if self._options_view.secondary_y_limits: enabled = self._secondary_y_enabled() secondary_y_min, secondary_y_max = self._get_secondary_y_extent( ) if enabled else (float('nan'), float('nan')) self._options_view.setSecondaryYLimitsEnabled(enabled) self._options_view.setSecondaryYLimits(float(secondary_y_min), float(secondary_y_max)) if self._options_view.secondary_x_limits: enabled = self._secondary_x_enabled() secondary_x_min, secondary_x_max = self._get_secondary_x_extent( ) if enabled else (float('nan'), float('nan')) self._options_view.setSecondaryXLimitsEnabled(enabled) self._options_view.setSecondaryXLimits(float(secondary_x_min), float(secondary_x_max)) if self._has_secondary_y_extent(): self._secondary_axes.set_ylim(*_safe_limits( *self._get_secondary_y_extent())) if self._has_secondary_x_extent(): self._secondary_axes.set_xlim(*_safe_limits( *self._get_secondary_x_extent())) def _secondary_y_enabled(self): return True if self._secondary_axes and self._secondary_axes.get_visible( ) and self._has_secondary_y_extent() else False def _secondary_x_enabled(self): return True if self._secondary_axes and self._secondary_axes.get_visible( ) and self._has_secondary_x_extent() else False def _set_axes_labels(self): self._axes.set_xlabel(self.data.xAxisTitle) self._axes.set_ylabel(self.data.yAxisTitle) def _set_center(self, center): if not all(c is not None for c in center): center = (0, 0) x_extent, y_extent = self._get_xy_extents() span = x_extent[1] - x_extent[0], y_extent[1] - y_extent[0] x_extent = center[0] - span[0] / 2, center[0] + span[0] / 2 y_extent = center[1] - span[1] / 2, center[1] + span[1] / 2 self._xy_extents = x_extent, y_extent def _get_xy_extents(self): if self.data is None: return (0, 0), (0, 0) if self._xy_extents is None: return self._get_data_xy_extents() return self._xy_extents def _get_data_xy_extents(self): if self.data is None: return (0, 0), (0, 0) (x_min, x_max), (y_min, y_max) = self.data.get_xy_extents() return self._pad_extent(x_min, x_max, self.x_extent_padding), self._pad_extent( y_min, y_max, self.y_extent_padding) def _has_secondary_y_extent(self): return hasattr(self.data, 'get_secondary_y_extent') def _get_secondary_y_extent(self): if self._secondary_y_extent is not None: return self._secondary_y_extent if self.data is not None: return self._get_data_secondary_y_extent() return (0, 0) def _get_data_secondary_y_extent(self): if self.data is None: return (0, 0) return self._pad_extent(*self.data.get_secondary_y_extent(), self.y_extent_padding) def _has_secondary_x_extent(self): return hasattr(self.data, 'get_secondary_x_extent') def _get_secondary_x_extent(self): if self._secondary_x_extent is not None: return self._secondary_x_extent if self.data is not None: return self._get_data_secondary_x_extent() return (0, 0) def _get_data_secondary_x_extent(self): if self.data is None or not hasattr(self.data, 'get_secondary_x_extent'): return (0, 0) return self._pad_extent(*self.data.get_secondary_x_extent(), self.x_extent_padding) def _get_actual_xy_extents(self): return self._axes.get_xlim(), self._axes.get_ylim() def _pad_extent(self, min_, max_, padding): min_, max_ = self._zero_if_nan(min_), self._zero_if_nan(max_) range_ = max_ - min_ return min_ - padding * range_, max_ + padding * range_ def _zoom_selected(self, start_pos, end_pos): x_min, x_max = min(start_pos.xdata, end_pos.xdata), max(start_pos.xdata, end_pos.xdata) y_min, y_max = min(start_pos.ydata, end_pos.ydata), max(start_pos.ydata, end_pos.ydata) self._xy_extents = (x_min, x_max), (y_min, y_max) self._set_axes_limits() self.draw() def _handle_span_select(self, x_min, x_max): x_min, x_max = self._round_to_bin_width(x_min, x_max) self._update_span_rect(x_min, x_max) self.span = SpanModel(self, x_min, x_max) self.draw() def _handle_span_select_none(self): self.span = None def _handle_press(self, event): if event.button == 1: if self._is_panning: self._pan_event = event elif self._span.active: self._handle_span_press(event) def _handle_move(self, event): if event.xdata and self._pan_event: self._handle_pan_move(event) elif event.xdata and any(self._span_events()): self._handle_span_move(event) def _handle_release(self, event): if self._pan_event: self._pan_event = None elif any(self._span_events()): self._handle_span_release(event) def _handle_pan_move(self, event): from_x, from_y = self._axes.transData.inverted().transform( (self._pan_event.x, self._pan_event.y)) to_x, to_y = self._axes.transData.inverted().transform( (event.x, event.y)) self._pan(from_x - to_x, from_y - to_y) self._pan_event = event def _pan(self, delta_x, delta_y): (x_min, x_max), (y_min, y_max) = self._get_xy_extents() self._xy_extents = (x_min + delta_x, x_max + delta_x), (y_min + delta_y, y_max + delta_y) self._set_axes_limits() self.draw() def _span_events(self): return self._span_center_mouse_event, self._span_left_mouse_event, self._span_right_mouse_event def _handle_span_press(self, event): if not event.xdata: return span_min, span_max = (self.span.left, self.span.right) if self.span else (0, 0) edge_tolerance = self._span_tolerance() if abs(span_min - event.xdata) < edge_tolerance: self._span.active = False self._span_left_mouse_event = event elif abs(span_max - event.xdata) < edge_tolerance: self._span.active = False self._span_right_mouse_event = event elif span_min < event.xdata < span_max: self._span.active = False self._span_center_mouse_event = event def _handle_span_move(self, event): if not self.span: return x_min, x_max = self.span.left, self.span.right last_event = next(x for x in self._span_events() if x) diff_x = event.xdata - last_event.xdata if self._span_center_mouse_event is not None: self._update_span_rect(x_min + diff_x) elif self._span_left_mouse_event is not None: self._update_span_rect(x_min + diff_x, x_max) elif self._span_right_mouse_event is not None: self._update_span_rect(x_min, x_max + diff_x) self.draw([self._span.rect]) def _handle_span_release(self, _event): x_min = self._span.rect.get_x() x_max = x_min + self._span.rect.get_width() x_min, x_max = self._round_to_bin_width(x_min, x_max) self._update_span_rect(x_min, x_max) self.span = SpanModel(self, x_min, x_max) self.draw() self._span.active = True self._span_center_mouse_event = self._span_left_mouse_event = self._span_right_mouse_event = None def _update_span_rect(self, x_min, x_max=None): self._span.rect.set_x(x_min) self._span.stay_rect.set_x(x_min) if x_max: self._span.rect.set_width(x_max - x_min) self._span.stay_rect.set_width(x_max - x_min) def _round_to_bin_width(self, x_min, x_max): return x_min, x_max def _span_tolerance(self): return 5 def toolEnabled(self, _tool_type): return False def toolAvailable(self, _tool_type): return False def activateTool(self, tool_type, active): if tool_type == ToolType.zoom: self._zoom_selector.set_active(active) elif tool_type == ToolType.span: if self._span.active and not active: self._previous_span = self.span self.span = None for r in [self._span.rect, self._span.stay_rect]: self._remove_artist(r) elif not self._span.active and active: self.span = self._previous_span for r in [self._span.rect, self._span.stay_rect]: self._add_artist(r) self._span.active = active self.draw() elif tool_type == ToolType.pan: self._is_panning = active self._active_tools[tool_type] = active def toolActive(self, tool_type): return self._active_tools.get(tool_type, False) def isActiveDefault(self, _tool_type): return False def _add_artist(self, artist): self._axes.add_artist(artist) self._decoration_artists.append(artist) def _remove_artist(self, artist): artist.remove() if artist in self._decoration_artists: self._decoration_artists.remove(artist) def _handle_resize(self, _event): self._update_ticks() return self.draw() def draw(self, artists=None): if artists is None: def _update(): for a in self._decoration_artists: a.remove() self._canvas.draw() self._background_cache = self._canvas.copy_from_bbox( self._figure.bbox) for a in self._decoration_artists: self._axes.add_artist(a) self._axes.draw_artist(a) self._canvas.update() self._pending_draw = _update else: def _update(): if self._background_cache is None: raise RuntimeError('Must run draw before drawing artists!') self._canvas.restore_region(self._background_cache) for a in artists: self._axes.draw_artist(a) self._canvas.update() self._pending_artists_draw = _update def _do_draw_events(self): if self._pending_draw is not None: self._pending_draw() self._pending_draw = None if self._pending_artists_draw is not None: self._pending_artists_draw() self._pending_artists_draw = None if self._other_draw_events: for draw_event in self._other_draw_events: draw_event() self._other_draw_events = [] def addDrawEvent(self, draw_event): self._other_draw_events.append(draw_event) def resetZoom(self): self._secondary_y_extent = self._secondary_x_extent = None self._xy_extents = None self._set_axes_limits() self.draw() def _twinx(self, ylabel): axes = self._axes.twinx() for spine in ['top', 'left']: axes.spines[spine].set_visible(False) axes.set_ylabel(ylabel) axes.set_zorder(1) return axes @property def axes(self): return self._axes @property def secondary_axes(self): if self._secondary_axes is None: self._set_secondary_axes(self._twinx('')) return self._secondary_axes def _set_secondary_axes(self, axes): self._secondary_axes = axes @staticmethod def sizeHint(): """function::sizeHint() Override the default sizeHint to ensure the plot has an initial size """ return QSize(600, 400) def minimumSizeHint(self): """function::sizeHint() Override the default sizeHint to ensure the plot does not shrink below minimum size """ return self.sizeHint() @staticmethod def _zero_if_nan(value): return value if not isinstance(value, float) or not np.isnan(value) else 0 def canShowTable(self): return hasattr(self, 'data') and self.data is not None and hasattr( self.data, 'table') def contextMenuEvent(self, event): self._show_table_action.setEnabled(self.canShowTable()) self._menu.exec_(event.globalPos()) def copyToClipboard(self): with BytesIO() as buffer: self._figure.savefig(buffer, facecolor=self._figure.get_facecolor()) QApplication.clipboard().setImage( QImage.fromData(buffer.getvalue())) def saveAsImage(self): filename = self._file_dialog_service.get_save_filename( self, self.tr('Portable Network Graphics (*.png)')) if filename: self._figure.savefig(filename, facecolor=self._figure.get_facecolor()) def showTable(self): if self.canShowTable(): self._table_view = TableView(None) self._table_view.pasteEnabled = False self._table_view.setModel(self.data.table) self._table_view.setMinimumSize(800, 600) self._table_view.show() def _update_ticks(self): if not self.data: return if hasattr(self.data, 'x_labels'): step = self.data.x_tick_interval if hasattr( self.data, 'x_tick_interval') else None x_ticks, x_labels = self._get_labels(self.data.x_labels, step, horizontal=True) self._axes.set_xticks(x_ticks) self._axes.set_xticklabels(x_labels) if hasattr(self.data, 'y_labels'): step = self.data.y_tick_interval if hasattr( self.data, 'y_tick_interval') else None y_ticks, y_labels = self._get_labels(self.data.y_labels, step, horizontal=False) self._axes.set_yticks(y_ticks) self._axes.set_yticklabels(y_labels) def _get_labels(self, labels, step, horizontal=True): (x0, x1), (y0, y1) = self._get_xy_extents() start, end = (int(x0), int(x1)) if horizontal else (int(y0), int(y1)) visible_points = end - start if not (step and step > 0): width, height = self._get_label_width_height(labels) axes_bbox = self._axes.get_window_extent( self._figure.canvas.get_renderer()).transformed( self._figure.dpi_scale_trans.inverted()) plot_size = (axes_bbox.width if horizontal else axes_bbox.height) * self._figure.dpi size = (width if horizontal else height) if plot_size == 0 or size == 0: n_labels = 16 else: n_labels = int(plot_size / size) if n_labels == 0: n_labels = 16 step = int(visible_points / n_labels) + 1 else: step = int(step) indexes = list(range(len(labels))) display_labels = list(labels) for i in indexes: if i % step: display_labels[i] = '' return indexes, display_labels def _get_label_width_height(self, labels): if not self._cached_label_width_height: font = MatPlotLibFont.default() width = 0 height = 0 for label in labels: next_width, next_height = font.get_size( str(label), matplotlib.rcParams['font.size'], self._figure.dpi) width = max(width, next_width) height = max(height, next_height) self._cached_label_width_height = width, height return self._cached_label_width_height def _create_new_axes(self, nx=1, ny=1) -> LocatableAxes: axes = LocatableAxes(self._figure, self._divider.get_position()) axes.set_axes_locator(self._divider.new_locator(nx=nx, ny=ny)) self._figure.add_axes(axes) return axes @staticmethod def _create_secondary_xy_axes(figure, divider, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position()) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) axes.xaxis.tick_top() axes.xaxis.set_label_position('top') axes.yaxis.tick_right() axes.yaxis.set_label_position('right') axes.patch.set_visible(visible) axes.set_zorder(z_order) figure.add_axes(axes) axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) return axes @staticmethod def _create_shared_axes(figure, divider, shared_axes, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position(), sharex=shared_axes, sharey=shared_axes, frameon=False) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) for spine in axes.spines.values(): spine.set_visible(False) for axis in axes.axis.values(): axis.set_visible(False) axes.patch.set_visible(False) axes.set_visible(False) axes.set_zorder(z_order) figure.add_axes(axes) return axes
def _create_new_axes(self, nx=1, ny=1) -> LocatableAxes: axes = LocatableAxes(self._figure, self._divider.get_position()) axes.set_axes_locator(self._divider.new_locator(nx=nx, ny=ny)) self._figure.add_axes(axes) return axes