def test_selector_clear_method(selector): ax = get_ax() def onselect(*args): pass if selector == 'span': tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, ignore_event_outside=True) else: tool = widgets.RectangleSelector(ax, onselect, interactive=True) click_and_drag(tool, start=(10, 10), end=(100, 120)) assert tool._selection_completed assert tool.visible if selector == 'span': assert tool.extents == (10, 100) tool.clear() assert not tool._selection_completed assert not tool.visible # Do another cycle of events to make sure we can click_and_drag(tool, start=(10, 10), end=(50, 120)) assert tool._selection_completed assert tool.visible if selector == 'span': assert tool.extents == (10, 50)
def check_span(*args, **kwargs): fig, ax = plt.subplots(1, 1) ax.plot([0, 200], [0, 200]) ax.figure.canvas.draw() def onselect(vmin, vmax): ax._got_onselect = True assert vmin == 100 assert vmax == 150 def onmove(vmin, vmax): assert vmin == 100 assert vmax == 125 ax._got_on_move = True if 'onmove_callback' in kwargs: kwargs['onmove_callback'] = onmove tool = widgets.SpanSelector(ax, onselect, *args, **kwargs) event = get_event(ax, xdata=100, ydata=100, button=1) tool.press(event) event = get_event(ax, xdata=125, ydata=125, button=1) tool.onmove(event) event = get_event(ax, xdata=150, ydata=150, button=1) tool.release(event) assert ax._got_onselect if 'onmove_callback' in kwargs: assert ax._got_on_move
def test_span_selector_onselect(interactive): # check when press and release events take place at the same position ax = get_ax() def onselect(vmin, vmax): ax._got_onselect = True tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=interactive) do_event(tool, 'press', xdata=100, ydata=100, button=1) # move outside of axis do_event(tool, 'onmove', xdata=150, ydata=100, button=1) do_event(tool, 'release', xdata=150, ydata=100, button=1) assert tool.ax._got_onselect assert tool.extents == (100, 150) # Reset tool.ax._got_onselect tool.ax._got_onselect = False do_event(tool, 'press', xdata=10, ydata=100, button=1) do_event(tool, 'release', xdata=10, ydata=100, button=1) assert tool.ax._got_onselect
def span_selection_init(self): self.span = mwidgets.SpanSelector(self.axes, self.on_select_span, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red'))
def test_span_selector_drag(drag_from_anywhere): ax = get_ax() def onselect(*args): pass # Create span tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, drag_from_anywhere=drag_from_anywhere) click_and_drag(tool, start=(10, 10), end=(100, 120)) assert tool.extents == (10, 100) # Drag inside span # # If drag_from_anywhere == True, this will move the span by 10, # giving new value extents = 20, 110 # # If drag_from_anywhere == False, this will create a new span with # value extents = 25, 35 click_and_drag(tool, start=(25, 15), end=(35, 25)) if drag_from_anywhere: assert tool.extents == (20, 110) else: assert tool.extents == (25, 35) # Check that in both cases, dragging outside the span draws a new span click_and_drag(tool, start=(175, 185), end=(185, 195)) assert tool.extents == (175, 185)
def test_span_selector_set_props_handle_props(): ax = get_ax() def onselect(epress, erelease): pass tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, props=dict(facecolor='b', alpha=0.2), handle_props=dict(alpha=0.5)) # Create rectangle do_event(tool, 'press', xdata=0, ydata=10, button=1) do_event(tool, 'onmove', xdata=100, ydata=120, button=1) do_event(tool, 'release', xdata=100, ydata=120, button=1) artist = tool._selection_artist assert artist.get_facecolor() == mcolors.to_rgba('b', alpha=0.2) tool.set_props(facecolor='r', alpha=0.3) assert artist.get_facecolor() == mcolors.to_rgba('r', alpha=0.3) for artist in tool._handles_artists: assert artist.get_color() == 'b' assert artist.get_alpha() == 0.5 tool.set_handle_props(color='r', alpha=0.3) for artist in tool._handles_artists: assert artist.get_color() == 'r' assert artist.get_alpha() == 0.3
def test_span_selector_ignore_outside(ignore_event_outside): ax = get_ax() def onselect(vmin, vmax): ax._got_onselect = True def onmove(vmin, vmax): ax._got_on_move = True tool = widgets.SpanSelector(ax, onselect, 'horizontal', onmove_callback=onmove, ignore_event_outside=ignore_event_outside) click_and_drag(tool, start=(100, 100), end=(125, 125)) assert ax._got_onselect assert ax._got_on_move assert tool.extents == (100, 125) # Reset ax._got_onselect = False ax._got_on_move = False # Trigger event outside of span click_and_drag(tool, start=(150, 150), end=(160, 160)) if ignore_event_outside: # event have been ignored and span haven't changed. assert not ax._got_onselect assert not ax._got_on_move assert tool.extents == (100, 125) else: # A new shape is created assert ax._got_onselect assert ax._got_on_move assert tool.extents == (150, 160)
def test_span_selector_ignore_outside(ignore_event_outside): ax = get_ax() def onselect(vmin, vmax): ax._got_onselect = True def onmove(vmin, vmax): ax._got_on_move = True tool = widgets.SpanSelector(ax, onselect, 'horizontal', onmove_callback=onmove, ignore_event_outside=ignore_event_outside) do_event(tool, 'press', xdata=100, ydata=100, button=1) do_event(tool, 'onmove', xdata=125, ydata=125, button=1) do_event(tool, 'release', xdata=125, ydata=125, button=1) assert ax._got_onselect assert ax._got_on_move assert tool.extents == (100, 125) # Reset ax._got_onselect = False ax._got_on_move = False # Trigger event outside of span do_event(tool, 'press', xdata=150, ydata=150, button=1) do_event(tool, 'onmove', xdata=160, ydata=160, button=1) do_event(tool, 'release', xdata=160, ydata=160, button=1) if ignore_event_outside: # event have been ignored and span haven't changed. assert not ax._got_onselect assert not ax._got_on_move assert tool.extents == (100, 125) else: # A new shape is created assert ax._got_onselect assert ax._got_on_move assert tool.extents == (150, 160)
def test_span_selector_drag(drag_from_anywhere): ax = get_ax() def onselect(*args): pass # Create span tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, drag_from_anywhere=drag_from_anywhere) do_event(tool, 'press', xdata=10, ydata=10, button=1) do_event(tool, 'onmove', xdata=100, ydata=120, button=1) do_event(tool, 'release', xdata=100, ydata=120, button=1) assert tool.extents == (10, 100) # Drag inside span # # If drag_from_anywhere == True, this will move the span by 10, # giving new value extents = 20, 110 # # If drag_from_anywhere == False, this will create a new span with # value extents = 25, 35 do_event(tool, 'press', xdata=25, ydata=15, button=1) do_event(tool, 'onmove', xdata=35, ydata=25, button=1) do_event(tool, 'release', xdata=35, ydata=25, button=1) if drag_from_anywhere: assert tool.extents == (20, 110) else: assert tool.extents == (25, 35) # Check that in both cases, dragging outside the span draws a new span do_event(tool, 'press', xdata=175, ydata=185, button=1) do_event(tool, 'onmove', xdata=185, ydata=195, button=1) do_event(tool, 'release', xdata=185, ydata=195, button=1) assert tool.extents == (175, 185)
def test_span_selector_bound(direction): fig, ax = plt.subplots(1, 1) ax.plot([10, 20], [10, 30]) ax.figure.canvas.draw() x_bound = ax.get_xbound() y_bound = ax.get_ybound() tool = widgets.SpanSelector(ax, print, direction, interactive=True) assert ax.get_xbound() == x_bound assert ax.get_ybound() == y_bound bound = x_bound if direction == 'horizontal' else y_bound assert tool._edge_handles.positions == list(bound) press_data = [10.5, 11.5] move_data = [11, 13] # Updating selector is done in onmove release_data = move_data do_event(tool, 'press', xdata=press_data[0], ydata=press_data[1], button=1) do_event(tool, 'onmove', xdata=move_data[0], ydata=move_data[1], button=1) assert ax.get_xbound() == x_bound assert ax.get_ybound() == y_bound index = 0 if direction == 'horizontal' else 1 handle_positions = [press_data[index], release_data[index]] assert tool._edge_handles.positions == handle_positions
def test_selector_clear_method(selector): ax = get_ax() def onselect(*args): pass if selector == 'span': tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, ignore_event_outside=True) else: tool = widgets.RectangleSelector(ax, onselect, interactive=True) do_event(tool, 'press', xdata=10, ydata=10, button=1) do_event(tool, 'onmove', xdata=100, ydata=120, button=1) do_event(tool, 'release', xdata=100, ydata=120, button=1) assert tool._selection_completed assert tool.visible if selector == 'span': assert tool.extents == (10, 100) tool.clear() assert not tool._selection_completed assert not tool.visible # Do another cycle of events to make sure we can do_event(tool, 'press', xdata=10, ydata=10, button=1) do_event(tool, 'onmove', xdata=50, ydata=120, button=1) do_event(tool, 'release', xdata=50, ydata=120, button=1) assert tool._selection_completed assert tool.visible if selector == 'span': assert tool.extents == (10, 50)
def check_span(*args, **kwargs): ax = get_ax() def onselect(vmin, vmax): ax._got_onselect = True assert vmin == 100 assert vmax == 150 def onmove(vmin, vmax): assert vmin == 100 assert vmax == 125 ax._got_on_move = True if 'onmove_callback' in kwargs: kwargs['onmove_callback'] = onmove tool = widgets.SpanSelector(ax, onselect, *args, **kwargs) do_event(tool, 'press', xdata=100, ydata=100, button=1) do_event(tool, 'onmove', xdata=125, ydata=125, button=1) do_event(tool, 'release', xdata=150, ydata=150, button=1) assert ax._got_onselect if 'onmove_callback' in kwargs: assert ax._got_on_move
def test_span_selector_direction(): ax = get_ax() def onselect(*args): pass tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True) assert tool.direction == 'horizontal' assert tool._edge_handles.direction == 'horizontal' with pytest.raises(ValueError): tool = widgets.SpanSelector(ax, onselect, 'invalid_direction') tool.direction = 'vertical' assert tool.direction == 'vertical' assert tool._edge_handles.direction == 'vertical' with pytest.raises(ValueError): tool.direction = 'invalid_string'
def add_widgets(self): rect_props = dict(facecolor='blue', alpha=0.5) self.span = mwidgets.SpanSelector(self.plot_ax, self._on_interval_select, 'horizontal', rectprops=rect_props, useblit=True) all_lr, _ = list(zip(*sorted(self.values[-1][0]))) init_lr_min = all_lr[0] init_lr_max = self.init_lr_max or all_lr[-1] init_wd = self.values[-1][1] # add the plot controls self.plot_lr_min_text: str = '' self.plot_lr_max_text: str = '' self.plot_lr_min_text = str(init_lr_min) self.plot_lr_max_text = str(init_lr_max) self.plot_wd_text = str(init_wd) self.rerun_wd = init_wd self.rerun_lr_min_box = mwidgets.TextBox(self.plot_lr_min_ax, label='LR min', initial=str(init_lr_min)) self.rerun_lr_max_box = mwidgets.TextBox(self.plot_lr_max_ax, label='LR max', initial=str(init_lr_max)) self.rerun_wd_box = mwidgets.TextBox(self.plot_wd_ax, label='Weight decay', initial=str(init_wd)) self.rerun_btn = mwidgets.Button(self.plot_btn_ax, label='PLOT') # add event handling self.rerun_lr_min_box.on_text_change(self._on_plot_lr_min_change) self.rerun_lr_max_box.on_text_change(self._on_plot_lr_max_change) self.rerun_wd_box.on_text_change(self._on_plot_wd_change) self.rerun_btn.on_clicked(self._on_rerun_click) # add the value input controls self.lr_min_text = '' self.lr_max_text = '' self.wd_text = str(init_wd) self.lr_min_box = mwidgets.TextBox(self.lr_min_ax, label='LR min') self.lr_max_box = mwidgets.TextBox(self.lr_max_ax, label='LR max') self.wd_box = mwidgets.TextBox(self.wd_ax, label='Weight decay', initial=str(init_wd)) self.save_btn = mwidgets.Button(self.btn_ax, label='SAVE') # add event handling self.rerun_lr_min_box.on_text_change(self._on_lr_min_change) self.rerun_lr_max_box.on_text_change(self._on_lr_max_change) self.rerun_wd_box.on_text_change(self._on_wd_change) self.save_btn.on_clicked(self._on_save_click)
def add_zoomed_axes(self, from_ax_nr=1, to_ax_nr=2, rect_props=dict(alpha=0.5, facecolor='red')): self.ax[from_ax_nr]['span'] = widgets.SpanSelector( self.ax[from_ax_nr][1], lambda xmin, xmax, from_ax=from_ax_nr, to_ax=to_ax_nr: self. _on_span_select(xmin, xmax, from_ax, to_ax), 'horizontal', useblit=True, rectprops=rect_props)
def subplot(): fig, ax = plt.subplots() ax.plot([1, 2, 3], [10, 50, 100]) span = mwidgets.SpanSelector(ax, onselect_span, "horizontal", rectprops=dict(alpha=1, facecolor='red'), span_stays=True) # choosing span_stays = True will make the selected area stay on the image. # lasso = mwidgets.LassoSelector(ax, onselect=onselect_lasso) plt.show()
def Plot(filename=None, dataset=None, timemarks=None, events=None, eventfile=None, ylim=None, columns=1, battery="DREA160", autoscale=True): """Plot from ipython. Args: filename (string): name of a data file to plot. This will be loaded into a DataSet object. dataset (DataSet): pre-existing dataset to plot. Mutually exclusive with filename parameter. timemarks (string): a time spec indicating a span of time to slice. eventfile (string): name of data file containing event marks. events (DataSet): A pre-existing event dataset. ylim (tuple of (min, max): minimum and maximum Y values to plot. columns (int, or sequence of ints): The column number, or numbers, starting from zero that will be extracted out (vertical slice). battery (string): Name of battery model. Default is "DREA160" autoscale (bool): If True, automatically fit graph scale to data. False means use a fixed scale (2.5 amp max). """ if filename is not None: dataset = analyze.DataSet(filename=filename, timespec=timemarks) if eventfile is not None: events = analyze.DataSet(filename=eventfile) if dataset is None: print "You should supply a filename or a dataset." return analyze.MakeCharts(dataset, ylim=ylim, events=events, columns=columns, autoscale=autoscale, interactive=True) pylab.gcf().set_size_inches((9,7)) plotaxes = pylab.gca() pylab.subplots_adjust(bottom=0.15) capacity = analyze.BATTERIES[battery][0] reporter = DataSampleReporter(plotaxes, dataset, capacity) span = widgets.SpanSelector(plotaxes, reporter.StatSelected, "horizontal") capaxes = pylab.axes([0.20, 0.025, 0.65, 0.03]) capslider = widgets.Slider(capaxes, "Batt (mA-h)", 800, 1350, capacity.inUnitsOf("mA*h").value) capslider.on_changed(reporter.SetCapacity) pylab.ion() pylab.show()
def test_span_selector_add_state(): ax = get_ax() def onselect(*args): pass tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True) with pytest.raises(ValueError): tool.add_state('unsupported_state') with pytest.raises(ValueError): tool.add_state('center') with pytest.raises(ValueError): tool.add_state('square') tool.add_state('move')
def test_span_selector_onselect(interactive): # check when press and release events take place at the same position ax = get_ax() def onselect(vmin, vmax): ax._got_onselect = True tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=interactive) # move outside of axis click_and_drag(tool, start=(100, 100), end=(150, 100)) assert tool.ax._got_onselect assert tool.extents == (100, 150) # Reset tool.ax._got_onselect tool.ax._got_onselect = False click_and_drag(tool, start=(10, 100), end=(10, 100)) assert tool.ax._got_onselect
def __init__(self, dataframe, xfield, yfield, ax=None, **kwargs): """ make a plot of xfield vs yfield in pandas dataframe kwargs are passed on to pyplot.plot """ self.xfield = xfield self.yfield = yfield self.dataframe = dataframe # create an axes if none is provided if ax is None: import matplotlib.pyplot as plt fig, ax = plt.subplots(1) self.fig = ax.figure self.ax = ax self.line, = ax.plot(self.dataframe[self.xfield], self.dataframe[self.yfield], **kwargs) # set useblit True on gtkagg for enhanced performance import matplotlib.widgets as widgets self.span = widgets.SpanSelector(ax, self.on_select, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red')) x = self.dataframe[self.xfield] # mpl converts dates to floats runder the hood so we'll have # to special case some stuff below if dates are involved self._is_xdate = (isinstance(x[0], datetime.date) or isinstance(x[0], datetime.datetime)) if self._is_xdate: # this func rotates the x tick labels to make some room # for long date labels self.fig.autofmt_xdate() self.fig.tight_layout()
def show_data(self, filename): self.ax_data_1.clear() self.ax_data_2.clear() self.ax_data_2.grid() self.df = analyse_igc(read_igc(filename)) self.df[["vs_smooth"]].plot(ax=self.ax_data_1) self.df[["RPM"]].plot(ax=self.ax_data_2, color="green", linewidth=1) self.span = mwidgets.SpanSelector(self.ax_data_2, self.onselect, 'horizontal', rectprops=dict(facecolor='blue', alpha=0.5), useblit=True) # print(df[df.isna().any(axis=1)].head(50)) # plt.show() self.fig_canvas.draw()
def test_span_selector_set_props_handle_props(): ax = get_ax() def onselect(epress, erelease): pass tool = widgets.SpanSelector(ax, onselect, 'horizontal', interactive=True, props=dict(facecolor='b', alpha=0.2), handle_props=dict(alpha=0.5)) # Create rectangle click_and_drag(tool, start=(0, 10), end=(100, 120)) artist = tool._selection_artist assert artist.get_facecolor() == mcolors.to_rgba('b', alpha=0.2) tool.set_props(facecolor='r', alpha=0.3) assert artist.get_facecolor() == mcolors.to_rgba('r', alpha=0.3) for artist in tool._handles_artists: assert artist.get_color() == 'b' assert artist.get_alpha() == 0.5 tool.set_handle_props(color='r', alpha=0.3) for artist in tool._handles_artists: assert artist.get_color() == 'r' assert artist.get_alpha() == 0.3
#---- Find files within root folder according to log files ".log" pv.FilesList = pv.FindAndOrganize_dazer(pv.Pattern_PlotFiles, pv.Catalogue_Folder, unpack=True, CheckComputer=False) #---- Generate Initial Frame pv.FigConf('night') pv.Axis.text(0.95, 0.05, 'Initiating visualizer', verticalalignment='bottom', horizontalalignment='right', transform=pv.Axis.transAxes, fontsize=15) GUI = Tk_GUI(PlottingVector=pv) pv.FigCanvas.show() pv.FigCanvas.mpl_connect('key_press_event', Key_Manager) Span = widgets.SpanSelector(pv.Axis, Span_Manager, 'horizontal', useblit=False, rectprops=dict(alpha=1, facecolor='Blue')) GUI.mainloop() print "Dazer Closed"
def __init__(self, counts, positions, fig=None, pos_order=None, norm=None): if pos_order is None: pos_order = {"x": 0, "y": 1} # extract x/y data self.x_pos = xpos = positions[pos_order["x"]] self.y_pos = ypos = positions[pos_order["y"]] self.points = np.transpose((xpos.ravel(), ypos.ravel())) # sort ouf the normalization if norm is None: norm = np.ones_like(self.x_pos) norm = np.atleast_3d(norm[:]) self.counts = counts[:] / norm # compute values we will use for extents below dx = np.diff(xpos.mean(axis=0)).mean() dy = np.diff(ypos.mean(axis=1)).mean() left = xpos[:, 0].mean() - dx / 2 right = xpos[:, -1].mean() + dx / 2 top = ypos[0].mean() - dy / 2 bot = ypos[-1].mean() + dy / 2 # create a figure if we must if fig is None: import matplotlib.pyplot as plt fig = plt.figure(tight_layout=True) else: # clear the figure fig.clf() # set the window title (look at the tool bar) fig.canvas.set_window_title("XRF map") self.fig = fig # set up the figure layout gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1], figure=fig) # set up the top panel (the map) self.ax_im = fig.add_subplot(gs[0, 0], gid="imgmap") self.ax_im.set_xlabel("x [?]") self.ax_im.set_ylabel("y [?]") self.ax_im.set_title("shift-click to select pixel\n" "alt-drag to draw region\n" "right-click to reset") # set up the lower axes (the average spectrum of the ROI) self.ax_spec = fig.add_subplot(gs[1, 0], gid="spectrum") self.ax_spec.set_ylabel("counts [?]") self.ax_spec.set_xlabel("bin number") self.ax_spec.set_yscale("log") self.ax_spec.set_title("click-and-drag to select energy ROI") self._EROI_txt = self.ax_spec.annotate( "ROI: all", xy=(0, 1), xytext=(0, 5), xycoords="axes fraction", textcoords="offset points", ) self._pixel_txt = self.ax_spec.annotate( "map average", xy=(1, 1), xytext=(0, -5), xycoords="axes fraction", textcoords="offset points", ha="right", va="top", ) # show the initial image self.im = self.ax_im.imshow( self.counts[:, :, :].sum(axis=2), cmap="viridis", interpolation="nearest", extent=[left, right, bot, top], ) # and colorbar self.cb = self.fig.colorbar(self.im, ax=self.ax_im) # and the ROI mask (overlay in red) self.mask = np.ones(self.x_pos.shape, dtype="bool") self.mask_im = self.ax_im.imshow( self._overlay_image, interpolation="nearest", extent=[left, right, bot, top], zorder=self.im.get_zorder(), ) self.mask_im.mouseover = False # do not consider for mouseover text (self.overlay_plot, ) = self.ax_im.plot( [], [], marker="o", markersize=5, markerfacecolor="none", markeredgecolor="red", ) # set up the spectrum, to start average everything (self.spec, ) = self.ax_spec.plot(self.counts.mean(axis=(0, 1)), lw=2) # set up the selector widget for the specturm self.selector = mwidgets.SpanSelector( self.ax_spec, self._on_span, "horizontal", useblit=True, minspan=2, span_stays=True, ) # placeholder for the lasso selector self.lasso = None # hook up the mouse events for the XRF map self.cid = self.fig.canvas.mpl_connect("button_press_event", self._on_click)
import matplotlib.pyplot as plt import matplotlib.widgets as mwidgets fig, ax = plt.subplots() ax.plot([1, 2, 3], [10, 50, 100]) def onselect(vmin, vmax): print(vmin, vmax) rectprops = dict(facecolor='blue', alpha=0.5) span = mwidgets.SpanSelector(ax, onselect, 'horizontal', rectprops=rectprops) fig.show()
def __init__(self, inst, controls=True, xlim=None, ylim=None, zlim=None, focus=None, style='dark_background', **kwargs): plt.style.use(style) self.inst = inst self.controls = controls self._populate_offset_dict() self.fig = plt.figure(**kwargs) self.ax_zx = self.fig.add_subplot(2, 2, 1) self.ax_zy = self.fig.add_subplot(2, 2, 2) self.ax_xy = self.fig.add_subplot(2, 2, 3) self.ax_or = self.fig.add_subplot(2, 2, 4, projection='3d') self._draw_labels() self.offset = [0, 0, 0] if self.controls: plt.subplots_adjust(left=0.05, right=0.7) rectprops = dict(facecolor='blue', alpha=0.5) self.xlim_span_ax = plt.axes([0.72, 0.3, 0.25, 0.03]) self.xlim_span_ax.set_yticks([]) self.xlim_span = wid.SpanSelector(self.xlim_span_ax, self._inst_xlim_change, 'horizontal', rectprops=rectprops) xlim_span_label = self._create_text("xlim", [0.72, 0.25, 0.25, 0.03]) self.ylim_span_ax = plt.axes([0.72, 0.2, 0.25, 0.03]) self.ylim_span_ax.set_yticks([]) self.ylim_span = wid.SpanSelector(self.ylim_span_ax, self._inst_ylim_change, 'horizontal', rectprops=rectprops) ylim_span_label = self._create_text("ylim", [0.72, 0.15, 0.25, 0.03]) self.zlim_span_ax = plt.axes([0.72, 0.1, 0.25, 0.03]) self.zlim_span_ax.set_yticks([]) self.zlim_span = wid.SpanSelector(self.zlim_span_ax, self._inst_zlim_change, 'horizontal', rectprops=rectprops) zlim_span_label = self._create_text("zlim", [0.72, 0.05, 0.25, 0.03]) self.comp_focus_textbox_ax = plt.axes([0.72, 0.35, 0.25, 0.63], facecolor='grey') self.comp_focus_buttons = wid.RadioButtons( self.comp_focus_textbox_ax, tuple(kr.comp_name for kr in self.inst.kernel_refs)) self.comp_focus_buttons.on_clicked(self._inst_comp_focus) #self.comp_focus_textbox = wid.TextBox(self.comp_focus_textbox_ax, "", color='.1', hovercolor='.15') #self.comp_focus_textbox.on_text_change(self._inst_comp_focus) #comp_focus_label = self._create_text("Focussed component", [0.72, 0.45, 0.25, 0.03] if focus: self._inst_comp_focus(focus) if xlim: self._inst_xlim_change(xlim[0], xlim[1]) if ylim: self._inst_ylim_change(ylim[0], ylim[1]) if zlim: self._inst_zlim_change(zlim[0], zlim[1])
def __init__(self, counts, positions, fig=None, pos_order=None, norm=None): if pos_order is None: pos_order = {'x': 0, 'y': 1} # extract x/y data self.x_pos = xpos = positions[pos_order['x']] self.y_pos = ypos = positions[pos_order['y']] self.points = np.transpose((xpos.ravel(), ypos.ravel())) # sort ouf the normalization if norm is None: norm = np.ones_like(self.x_pos) norm = np.atleast_3d(norm[:]) self.counts = counts[:] / norm # compute values we will use for extents below dx = np.diff(xpos.mean(axis=0)).mean() dy = np.diff(ypos.mean(axis=1)).mean() left = xpos[:, 0].mean() - dx / 2 right = xpos[:, -1].mean() + dx / 2 top = ypos[0].mean() - dy / 2 bot = ypos[-1].mean() + dy / 2 # create a figure if we must if fig is None: import matplotlib.pyplot as plt fig = plt.figure(tight_layout=True) # clear the figure fig.clf() # set the window title (look at the tool bar) fig.canvas.set_window_title('XRF map') self.fig = fig # set up the figure layout gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1]) # set up the top panel (the map) self.ax_im = fig.add_subplot(gs[0, 0], gid='imgmap') self.ax_im.set_xlabel('x [?]') self.ax_im.set_ylabel('y [?]') self.ax_im.set_title('shift-click to select pixel, ' 'alt-drag to draw region, ' 'right-click to reset') # set up the lower axes (the average spectrum of the ROI) self.ax_spec = fig.add_subplot(gs[1, 0], gid='spectrum') self.ax_spec.set_ylabel('counts [?]') self.ax_spec.set_xlabel('bin number') self.ax_spec.set_yscale('log') self.ax_spec.set_title('click-and-drag to select energy region') self._EROI_txt = self.ax_spec.annotate('ROI: all', xy=(0, 1), xytext=(0, 5), xycoords='axes fraction', textcoords='offset points') self._pixel_txt = self.ax_spec.annotate('map average', xy=(1, 1), xytext=(0, 5), xycoords='axes fraction', textcoords='offset points', ha='right') # show the initial image self.im = self.ax_im.imshow(self.counts[:, :, :].sum(axis=2), cmap='viridis', interpolation='nearest', extent=[left, right, bot, top]) # and colorbar self.cb = self.fig.colorbar(self.im, ax=self.ax_im) # and the ROI mask (overlay in red) self.mask = np.ones(self.x_pos.shape, dtype='bool') self.mask_im = self.ax_im.imshow(self._overlay_image, interpolation='nearest', extent=[left, right, bot, top], zorder=self.im.get_zorder()) self.mask_im.mouseover = False # do not consider for mouseover text # set up the spectrum, to start average everything self.spec, = self.ax_spec.plot(self.counts.mean(axis=(0, 1)), lw=2) # set up the selector widget for the specturm self.selector = mwidgets.SpanSelector(self.ax_spec, self._on_span, 'horizontal', useblit=True, minspan=2, span_stays=True) # placeholder for the lasso selector self.lasso = None # hook up the mouse events for the XRF map self.cid = self.fig.canvas.mpl_connect('button_press_event', self._on_click)
def polypatch(figure, axes, line2D, verbose=False): ''' Description: Given a plot of Y vs X, select an interval of X and fit a polynomial to the data. Optionally, select a sub-interval of X to be ignored by the fit. IMPORTANT: The code forces the fit to pass through the two last and two first data point in the selected interval. This in order to achieve a smoother mathing between the data and the fit. As a result, the degree of the fit must be 3 or higher. This is done by means of Lagrange multipliers. The figure given as input can contain a legend, title, labels, etc. Inputs: figure: It refers to the figure object from Matplotlib. axes: It refers to the axes object from Matplotlib. line2D: It refers to the line2D object from Matplotlib. Optional inputs: verbose: Print additional information on the terminal. Often useful for debugging. Output: The program returns a tupe where the first element is a mask which contains the indices of the orifinal X corresponding to the fitted interval. The second element are the new values of that interval. Example: import numpy as np import matplotlib.pyplot as plt from polypatch import polypatch # Sample data x = np.arange(0.25,1.50,0.01) sigma=0.05; mu=1 gaussian = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x-mu)**2/(2*sigma**2) ) y = 1 + 0.5*x - x**2 + 0.5*x**3 + 0.1*gaussian # Making a plot figure = plt.figure() axes = plt.axes() axes.set_title('Example') axes.set_ylabel('Y data') axes.set_xlabel('X data') line2d, = plt.plot(x,y) # Mind the comma! # Fitting a polynomial interactively mask, fit = polypatch(figure, axes, line2d) # Integration of the fit values into the original data y_modified = y.copy() y_modified[mask] = fit # Plot original data and modified data figure = plt.figure() axes = plt.axes() axes.set_title('Example') axes.set_ylabel('Y data') axes.set_xlabel('X data') plt.plot(x,y,label='original data', color='black') plt.plot(x,y_modified,label='modified data', color='red') plt.legend(loc='best') plt.show() ''' def window_info(_): '''Display information about how to use''' # Text separators l1 = '--------------------------------------------------------------------------' l2 = '==========================================================================' # Info text title = 'Info' t0 = '>> To enable recognition of the left and right buttons, first hit enter <<' t1 = '> The left button selects the interval for the fit by dragging on the plot.' t2 = '> The right button selects an interval excluded to by excluded from the fit.' t3 = '> Press the button [Add fit] to generate the fit.' t4 = '> Mark the checkbox [Not refresh fit] to overplot different fits.' t5 = '> When closing plot, you will be asked whether to save the changes.' # Message to display message = ('\n\n' + '\n\n'.join( [l2, t0, l2, t1, l1, t2, l1, t3, l1, t4, l1, t5, l1]) + '\n\n') # Create blank windows window = tkinter.Tk() # Create title window.title(title) # Add content and organize its geometry tkinter.Label(window, text=message, justify='left').pack() # Modify canvas and organize its geometry tkinter.Canvas(window, width=600, height=0).pack() # Display the window window.mainloop() def window_error(): '''Display information about how to use''' title = 'Error' text = 'Please input an integer equal or greater than 3.' message = ('\n\n' + text + '\n\n') window = tkinter.Tk() window.title(title) tkinter.Label(window, text=message, justify='center').pack() tkinter.Canvas(window, width=600, height=0).pack() window.mainloop() def read_keystroke(event): '''Get the pressed key over the axes during plot visualization''' ivar['keystroke'] = event.key if verbose: print("ivar['keystroke'] = {}".format(ivar['keystroke'])) def read_button(event): '''Get the pressed button over the axes during plot visualization''' ivar['pressed_button'] = event.button if verbose: print("ivar['pressed_button']", ivar['pressed_button']) def read_polynomial_degree(text): '''Get the input text in the textbox during plot visualization''' if text.isdigit() and np.int(text) > 2: ivar['polynomial_degree'] = np.int(text) else: window_error() # Keep the previous value and display it on the check box. interface['textbox_polynomial_degree'].textbox.set_val( ivar['polynomial_degree']) if verbose: print("ivar['polynomial_degree'] = {}".format( ivar['polynomial_degree'])) def button_add_fit(event): '''Add the fit to the plot''' nonlocal fit_function if fit_interval_ready: # If a previous fit, clear it clear_line2D(figure, lines2D['fit'], axes, redraw=False) if not ivar['not_refresh_flag']: clear_line2D(figure, lines2D['fit_denser'], axes, redraw=False) # Get current colors in the axis to not repeat them colors = [l.get_color() for l in axes.get_lines()] # Fit if mask['nonfit'] is not None: if interval['xmin_nonfit'] > interval['xmin_fit'] and interval[ 'xmax_nonfit'] < interval['xmax_fit']: mask_fit_minus_nonfit = np.logical_xor( mask['fit'], mask['nonfit']) else: print( '**The non fit interval is outside the fit interval. It will be ignored.**' ) mask_fit_minus_nonfit = mask['fit'].copy() else: mask_fit_minus_nonfit = mask['fit'].copy() xfit = x[mask_fit_minus_nonfit] yfit = y[mask_fit_minus_nonfit] # Force the fit to match the fist two data points and the last two data points to promote a smoother match with the original xfit_fixed = np.concatenate([[xfit[0]], [xfit[1]], [xfit[-2]], [xfit[-1]]]) yfit_fixed = np.concatenate([[yfit[0]], [yfit[1]], [yfit[-2]], [yfit[-1]]]) # Fit fit_function = np.poly1d( polyfit_with_fixed_points(ivar['polynomial_degree'], xfit, yfit, xfit_fixed, yfit_fixed)) color = 'lime' if not 'lime' in colors else None lines2D['fit'], = axes.plot(x[mask['fit']], fit_function(x[mask['fit']]), linestyle='None', marker='o', color=color, markerfacecolor=color, markeredgecolor='None') # Plot a denser x to evidence possible wiggles between the original data points_in_between = np.arange(0.0, 1.0, 0.1) x_mask_diff = np.diff(x[mask['fit']]) x_mask_denser = np.array([]) for shift in points_in_between: x_mask_denser = np.concatenate( [x_mask_denser, x[mask['fit']][:-1] + x_mask_diff * shift]) # Add the last point x_mask_denser = np.concatenate( [x_mask_denser, np.array([x[mask['fit']][-1]])]) x_mask_denser = np.sort(x_mask_denser) color = lines2D['fit'].get_color() lines2D['fit_denser'], = axes.plot( x_mask_denser, fit_function(x_mask_denser), linestyle='solid', marker=None, color=color, label='Polynomial fit n={}'.format(ivar['polynomial_degree'])) list_fit_denser_line2D.append(lines2D['fit_denser']) # Redraw axes.legend(loc='best', ncol=1, framealpha=0.5, fontsize=10) figure.canvas.draw() def button_clear_fit(event): '''Clear the fits curves from the plot''' nonlocal list_fit_denser_line2D nonlocal fit_function clear_line2D(figure, lines2D['fit'], axes, redraw=True) for line2D in list_fit_denser_line2D: clear_line2D(figure, line2D, axes, redraw=True) fit_function = None def check_box_switch(label): '''Invert the flag of not refresh''' ivar['not_refresh_flag'] = not ivar['not_refresh_flag'] interface['checkbox'].checkbox.rectangles[0].set_fill( ivar['not_refresh_flag']) figure.canvas.draw() def onselect(vmin, vmax): '''Select the interval for the fit and the interval to be excluded in the fit''' nonlocal fit_interval_ready # Interval to fit # Activate by pressing Enter and then using the left button if ivar['keystroke'] == 'enter' and ivar['pressed_button'] == 1: clear_line2D(figure, lines2D['fit_interval'], axes, redraw=False) # Store the values interval['xmin_fit'] = vmin interval['xmax_fit'] = vmax # Print the interval print('Interval for the fit:') print('xmin = {:.3},\t xmax = {:.3}\n'.format(vmin, vmax)) # Get the indices of the values within the selected span condition1_fit = interval['xmin_fit'] < x condition2_fit = x < interval['xmax_fit'] mask['fit'] = np.logical_and(condition1_fit, condition2_fit) # Plot in red the selected span as an aditional Line2D object in lines if interval['xmin_fit'] != interval['xmax_fit']: lines2D['fit_interval'], = axes.plot(x[mask['fit']], y[mask['fit']], linestyle='None', marker='o', markerfacecolor='red', markeredgecolor='None', label='Fit interval') fit_interval_ready = True else: fit_interval_ready = False # Interval to exclude # Activate by pressing Enter and then using the right button OR pressing Shift+Enter and the using left button if (ivar['keystroke'] == 'shift+enter' and ivar['pressed_button'] == 1) or (ivar['keystroke'] == 'enter' and ivar['pressed_button'] == 3): clear_line2D(figure, lines2D['v1'], axes, redraw=False) clear_line2D(figure, lines2D['v2'], axes, redraw=False) interval['xmin_nonfit'] = vmin interval['xmax_nonfit'] = vmax # Print the interval print('Interval to be excluded in the fit:') print('xmin = {:.3},\t xmax = {:.3}\n'.format(vmin, vmax)) # Get the indices of the values within the selected span condition1_nonfit = interval['xmin_nonfit'] < x condition2_nonfit = x < interval['xmax_nonfit'] mask['nonfit'] = np.logical_and(condition1_nonfit, condition2_nonfit) # Plot in black the selected span if interval['xmin_nonfit'] != interval['xmax_nonfit']: lines2D['v1'] = axes.axvline(interval['xmin_nonfit'], label='Nonfit interval', linestyle='dashed', color='black') lines2D['v2'] = axes.axvline(interval['xmax_nonfit'], linestyle='dashed', color='black') # Redraw axes.legend(loc='best', ncol=1, framealpha=0.5, fontsize=10) figure.canvas.draw() ### Initialize variables # Curves in the plot lines2D = { 'fit': None, 'fit_interval': None, 'fit_denser': None, 'v1': None, 'v2': None } # Masks mask = {'fit': None, 'nonfit': None} # Variable to store the fit fit_function = None # Interval's extrema interval = { 'xmin_fit': None, 'xmax_fit': None, 'xmin_nonfit': None, 'xmax_nonfit': None } # Interactive variables ivar = { 'keystroke': None, 'pressed_button': None, 'polynomial_degree': 3, 'not_refresh_flag': False } # List where to store curves of different fits list_fit_denser_line2D = list() # Flags fit_interval_ready = False # Make space for the interface of buttons plt.subplots_adjust(bottom=0.2) # Get the data x = line2D.get_xdata() y = line2D.get_ydata() # Connect ID to the plot visualization cid_key = figure.canvas.mpl_connect('key_press_event', read_keystroke) cid_button = figure.canvas.mpl_connect('button_press_event', read_button) # Biuld interface: Buttons, checkbox and textbox height = 0.04 width = 0.1 position = [(0.65, 0.07)] button_addfit = Button( function=button_add_fit,\ text='Add fit',\ coords=[0.65, 0.07, width, height] ) button_clearfit = Button( function=button_clear_fit,\ text='Clear fit',\ coords=[0.76, 0.07, width, height] ) button_help = Button( function=window_info,\ text='Help',\ coords=[0.76, 0.02, width, height] ) checkbox = Checkbox(function=check_box_switch,\ label='Not refresh fit',\ coords=[0.65, 0.02, width, 0.040],\ type=2) textbox_polynomial_degree = Textbox( function=read_polynomial_degree,\ prompt_text='Polynomial degree for the fit = ',\ initial_text='{}'.format(ivar['polynomial_degree']),\ coords=[0.45, 0.05, 0.05, 0.05]) interface = { 'button_addfit': button_addfit, 'button_clearfit': button_clearfit, 'button_help': button_help, 'checkbox': checkbox, 'textbox_polynomial_degree': textbox_polynomial_degree } # Properties of the rectangle-span area-selector rect_props = dict(facecolor='cyan', alpha=0.20) # Area selector span = mwidgets.SpanSelector(axes, onselect, 'horizontal', rectprops=rect_props) # Display plot in a maximazed window mng = plt.get_current_fig_manager() mng.full_screen_toggle() plt.show() # Disconnect from the plot visuzlization for cid in [cid_key, cid_button]: figure.canvas.mpl_disconnect(cid) # Print the selected intervals print('\n') print('=========================================') print('Interval for the fit:') print('xmin = {:.3}, \txmax = {:.3}'.format(interval['xmin_fit'], interval['xmax_fit'])) print('=========================================') print('Interval excluded from the fit:') print('xmin = {:.3}, \txmax = {:.3}'.format(interval['xmin_nonfit'], interval['xmax_nonfit'])) print('=========================================') print('Polynomial degree of the fit = {}'.format( ivar['polynomial_degree'])) print('=========================================') print('\n') # Return velues if fit_function != None: return mask['fit'], fit_function(x[mask['fit']]) else: return None, None
def __init__(self, counts, positions, fig=None, pos_order=None, norm=None): if pos_order is None: pos_order = {'x': 0, 'y': 1} self.x_pos = xpos = positions[pos_order['x']] self.y_pos = ypos = positions[pos_order['y']] self.points = np.transpose((xpos.ravel(), ypos.ravel())) if norm is None: norm = np.ones_like(self.x_pos) norm = np.atleast_3d(norm[:]) # TODO do not normalize up from? self.counts = counts[:] / norm dx = np.diff(xpos.mean(axis=0)).mean() dy = np.diff(ypos.mean(axis=1)).mean() left = xpos[:, 0].mean() - dx/2 right = xpos[:, -1].mean() + dx/2 top = ypos[0].mean() - dy/2 bot = ypos[-1].mean() + dy/2 if fig is None: import matplotlib.pyplot as plt fig = plt.figure(tight_layout=True) fig.clf() fig.canvas.set_window_title('XRF map') self.fig = fig gs = gridspec.GridSpec(2, 1, height_ratios=[4, 1]) self.ax_im = fig.add_subplot(gs[0, 0], gid='imgmap') self.ax_im.set_xlabel('x [?]') self.ax_im.set_ylabel('y [?]') self.ax_spec = fig.add_subplot(gs[1, 0], gid='spectrum') self.ax_spec.set_ylabel('counts [?]') self.ax_spec.set_xlabel('bin number') self.ax_spec.set_yscale('log') self._EROI_txt = self.ax_spec.annotate('ROI: all', xy=(0, 1), xytext=(0, 5), xycoords='axes fraction', textcoords='offset points') self._pixel_txt = self.ax_spec.annotate('map average', xy=(1, 1), xytext=(0, 5), xycoords='axes fraction', textcoords='offset points', ha='right') self.im = self.ax_im.imshow(self.counts[:, :, :].sum(axis=2), cmap='viridis', interpolation='nearest', extent=[left, right, bot, top] ) self.xy_plt = self.ax_im.plot(xpos.ravel(), ypos.ravel(), 'wo', visible=False) self.cb = self.fig.colorbar(self.im, ax=self.ax_im) self.mask = np.ones(self.x_pos.shape, dtype='bool') self.mask_im = self.ax_im.imshow(self._make_overlay, interpolation='nearest', extent=[left, right, bot, top], zorder=self.im.get_zorder()) # do not consider for mouseover text self.mask_im.mouseover = False self.spec, = self.ax_spec.plot( self.counts.mean(axis=(0, 1)), lw=2) self.cid = self.fig.canvas.mpl_connect('button_press_event', self._on_click) self.selector = mwidgets.SpanSelector(self.ax_spec, self._on_span, 'horizontal', useblit=True, minspan=2, span_stays=True) self.lasso = None
def analyze(self, norm: bool = True, portrait: bool = True, blit: bool = False): if self.freq_arr is None: raise RuntimeError if self.resp_arr is None: raise RuntimeError import matplotlib.pyplot as plt try: from resonator_tools import circuit import matplotlib.widgets as mwidgets _do_fit = True except ImportError: _do_fit = False nr_amps = len(self.amp_arr) self._AMP_IDX = nr_amps // 2 if norm: resp_scaled = np.zeros_like(self.resp_arr) for jj in range(nr_amps): resp_scaled[jj] = self.resp_arr[jj] / self.amp_arr[jj] else: resp_scaled = self.resp_arr resp_dB = 20. * np.log10(np.abs(resp_scaled)) amp_dBFS = 20 * np.log10(self.amp_arr / 1.0) # choose limits for colorbar cutoff = 1. # % lowlim = np.percentile(resp_dB, cutoff) highlim = np.percentile(resp_dB, 100. - cutoff) # extent x_min = 1e-9 * self.freq_arr[0] x_max = 1e-9 * self.freq_arr[-1] dx = 1e-9 * (self.freq_arr[1] - self.freq_arr[0]) y_min = amp_dBFS[0] y_max = amp_dBFS[-1] dy = amp_dBFS[1] - amp_dBFS[0] if portrait: fig1 = plt.figure(tight_layout=True, figsize=(6.4, 9.6)) ax1 = fig1.add_subplot(2, 1, 1) # fig1 = plt.figure(tight_layout=True) # ax1 = fig1.add_subplot(1, 1, 1) else: fig1 = plt.figure(tight_layout=True, figsize=(12.8, 4.8)) ax1 = fig1.add_subplot(1, 2, 1) im = ax1.imshow( resp_dB, origin='lower', aspect='auto', interpolation='none', extent=(x_min - dx / 2, x_max + dx / 2, y_min - dy / 2, y_max + dy / 2), vmin=lowlim, vmax=highlim, ) line_sel = ax1.axhline(amp_dBFS[self._AMP_IDX], ls="--", c="k", lw=3, animated=blit) # ax1.set_title(f"amp = {amp_arr[AMP_IDX]:.2e}") ax1.set_xlabel("Frequency [GHz]") ax1.set_ylabel("Drive amplitude [dBFS]") cb = fig1.colorbar(im) if portrait: cb.set_label("Response amplitude [dB]") else: ax1.set_title("Response amplitude [dB]") fig1.show() # return fig1 if portrait: ax2 = fig1.add_subplot(4, 1, 3) ax3 = fig1.add_subplot(4, 1, 4, sharex=ax2) else: ax2 = fig1.add_subplot(2, 2, 2) ax3 = fig1.add_subplot(2, 2, 4, sharex=ax2) ax2.yaxis.set_label_position("right") ax2.yaxis.tick_right() ax3.yaxis.set_label_position("right") ax3.yaxis.tick_right() line_a, = ax2.plot(1e-9 * self.freq_arr, resp_dB[self._AMP_IDX], label="measured", animated=blit) line_p, = ax3.plot(1e-9 * self.freq_arr, np.angle(self.resp_arr[self._AMP_IDX]), animated=blit) if _do_fit: line_fit_a, = ax2.plot(1e-9 * self.freq_arr, np.full_like(self.freq_arr, np.nan), ls="--", label="fit", animated=blit) line_fit_p, = ax3.plot(1e-9 * self.freq_arr, np.full_like(self.freq_arr, np.nan), ls="--", animated=blit) f_min = 1e-9 * self.freq_arr.min() f_max = 1e-9 * self.freq_arr.max() f_rng = f_max - f_min a_min = resp_dB.min() a_max = resp_dB.max() a_rng = a_max - a_min p_min = -np.pi p_max = np.pi p_rng = p_max - p_min ax2.set_xlim(f_min - 0.05 * f_rng, f_max + 0.05 * f_rng) ax2.set_ylim(a_min - 0.05 * a_rng, a_max + 0.05 * a_rng) ax3.set_xlim(f_min - 0.05 * f_rng, f_max + 0.05 * f_rng) ax3.set_ylim(p_min - 0.05 * p_rng, p_max + 0.05 * p_rng) ax3.set_xlabel("Frequency [GHz]") ax2.set_ylabel("Response amplitude [dB]") ax3.set_ylabel("Response phase [rad]") ax2.legend(loc="lower right") def onbuttonpress(event): if event.inaxes == ax1: self._AMP_IDX = np.argmin(np.abs(amp_dBFS - event.ydata)) update() def onkeypress(event): if event.inaxes == ax1: if event.key == "up": self._AMP_IDX += 1 if self._AMP_IDX >= len(amp_dBFS): self._AMP_IDX = len(amp_dBFS) - 1 update() elif event.key == "down": self._AMP_IDX -= 1 if self._AMP_IDX < 0: self._AMP_IDX = 0 update() def update(): line_sel.set_ydata( [amp_dBFS[self._AMP_IDX], amp_dBFS[self._AMP_IDX]]) # ax1.set_title(f"amp = {amp_arr[AMP_IDX]:.2e}") print( f"drive amp {self._AMP_IDX:d}: {self.amp_arr[self._AMP_IDX]:.2e} FS = {amp_dBFS[self._AMP_IDX]:.1f} dBFS" ) line_a.set_ydata(resp_dB[self._AMP_IDX]) line_p.set_ydata(np.angle(self.resp_arr[self._AMP_IDX])) if _do_fit: line_fit_a.set_ydata(np.full_like(self.freq_arr, np.nan)) line_fit_p.set_ydata(np.full_like(self.freq_arr, np.nan)) # ax2.set_title("") if blit: fig1.canvas.restore_region(self._bg) ax1.draw_artist(line_sel) ax2.draw_artist(line_a) ax3.draw_artist(line_p) fig1.canvas.blit(fig1.bbox) fig1.canvas.flush_events() else: fig1.canvas.draw() if _do_fit: def onselect(xmin, xmax): port = circuit.notch_port(self.freq_arr, self.resp_arr[self._AMP_IDX]) port.autofit(fcrop=(xmin * 1e9, xmax * 1e9)) if norm: line_fit_a.set_data( 1e-9 * port.f_data, 20 * np.log10( np.abs(port.z_data_sim / self.amp_arr[self._AMP_IDX]))) else: line_fit_a.set_data(1e-9 * port.f_data, 20 * np.log10(np.abs(port.z_data_sim))) line_fit_p.set_data(1e-9 * port.f_data, np.angle(port.z_data_sim)) # print(port.fitresults) print("----------------") print(f"fr = {port.fitresults['fr']}") print(f"Qi = {port.fitresults['Qi_dia_corr']}") print(f"Qc = {port.fitresults['Qc_dia_corr']}") print(f"Ql = {port.fitresults['Ql']}") print( f"kappa = {port.fitresults['fr'] / port.fitresults['Qc_dia_corr']}" ) print("----------------") # ax2.set_title( # f"fr = {1e-6*fr:.0f} MHz, Ql = {Ql:.0f}, Qi = {Qi:.0f}, Qc = {Qc:.0f}, kappa = {1e-3*kappa:.0f} kHz") if blit: fig1.canvas.restore_region(self._bg) ax1.draw_artist(line_sel) ax2.draw_artist(line_a) ax2.draw_artist(line_fit_a) ax3.draw_artist(line_p) ax3.draw_artist(line_fit_p) fig1.canvas.blit(fig1.bbox) fig1.canvas.flush_events() else: fig1.canvas.draw() rectprops = dict(facecolor='tab:gray', alpha=0.5) fig1._span_a = mwidgets.SpanSelector(ax2, onselect, 'horizontal', rectprops=rectprops, useblit=blit) fig1._span_p = mwidgets.SpanSelector(ax3, onselect, 'horizontal', rectprops=rectprops, useblit=blit) fig1.canvas.mpl_connect('button_press_event', onbuttonpress) fig1.canvas.mpl_connect('key_press_event', onkeypress) fig1.show() if blit: fig1.canvas.draw() fig1.canvas.flush_events() self._bg = fig1.canvas.copy_from_bbox(fig1.bbox) ax1.draw_artist(line_sel) ax2.draw_artist(line_a) ax3.draw_artist(line_p) fig1.canvas.blit(fig1.bbox) return fig1