mcetl.plot_utils

Provides utility functions, classes, and constants for plotting.

Separated from utils.py to reduce import load time since matplotlib imports are not needed for base usage. Useful functions are put here in order to prevent circular importing within the other files.

@author: Donald Erb Created on Nov 11, 2020

mcetl.plot_utils.CANVAS_SIZE

A tuple specifying the size (in pixels) of the figure canvas used in various GUIs for mcetl. This can be modified if the user wishes a larger or smaller canvas. The default is (800, 800).

Type

tuple(int, int)

Module Contents

Classes

EmbeddedFigure

Class defining a PySimpleGUI window with an embedded matplotlib Figure.

PlotToolbar

Custom toolbar without the subplots and save figure buttons.

Functions

determine_dpi

Gives the correct dpi to fit the figure within the GUI canvas.

draw_figure_on_canvas

Places the figure and toolbar onto the canvas.

get_dpi_correction

Calculates the correction factor needed to create a figure with the desired dpi.

scale_axis

Calculates the new bounds to scale the current axis bounds.

class mcetl.plot_utils.EmbeddedFigure(x, y, click_list=None, enable_events=True, enable_keybinds=True, toolbar_class=NavigationToolbar2Tk)

Class defining a PySimpleGUI window with an embedded matplotlib Figure.

Class to be used to define BackgroundSelector and PeakSelector classes, which share common functions.

Parameters
  • x (array-like) -- The x-values to be plotted.

  • y (array-like) -- The y-values to be plotted.

  • click_list (list, optional) -- A list of selected points on the plot.

  • enable_events (bool, optional) -- If True (default), will connect self.events (defaults to self._on_click, self._on_pick, and self._on_key) to the figure when it is drawn on the canvas. If False, the figure will have no connected events.

  • enable_keybinds (bool, optional) -- If True (default), will connect the matplotlib keybind events to the figure.

  • toolbar_class (NavigationToolbar2Tk, optional) -- The class of the toolbar to place in the window. The default is NavigationToolbar2Tk.

x

The x-values to be plotted.

Type

array-like

y

The y-values to be plotted.

Type

array-like

click_list

A list of selected points on the plot.

Type

list

figure

The figure embedded in the window.

Type

plt.Figure

axis

The main axis on the figure which owns the events.

Type

plt.Axes

canvas

The PySimpleGUI canvas element in the window which contains the figure.

Type

sg.Canvas

toolbar_canvas

The PySimpleGUI canvas element that contains the toolbar for the figure.

Type

sg.Canvas

picked_object

The selected Artist objected on the figure. Useful for pick events.

Type

plt.Artist

xaxis_limits

The x axis limits when the figure is first created. The values are used to determine the size of the Ellipse place by the _create_circle function. The initial limits are used so that zooming on the figure does not change the size of the Ellipse.

Type

tuple

yaxis_limits

The y axis limits when the figure is first created.

Type

tuple

events

A list containing the events for the figure. Each item in the list is a list or tuple with the first item being the matplotlib event, such as 'pick_event', and the second item is a callable (function) to be executed when the event occurs.

Type

list(tuple(str, Callable))

canvas_size

The size, in pixels, of the figure to be created. Default is (CANVAS_SIZE[0], CANVAS_SIZE[1] - 100), which is (800, 700).

Type

tuple(float, float)

toolbar_class

The class of the toolbar to place in the window. The default is NavigationToolbar2Tk.

Type

NavigationToolbar2Tk

window

The PySimpleGUI window containing the figure.

Type

sg.Window

enable_keybinds

If True, will allow using matplotlib's keybinds to trigger events on the figure.

Type

bool

Notes

This class allows easy subclassing to create simple windows with embedded matplotlib figures.

A typical __init__ for a subclass should create the figure and axes, create the window, and then place the figure within the window's canvas. For example (note that plt designates matplotlib.pyplot)

>>> class SimpleEmbeddedFigure(EmbeddedFigure):
        def __init__(x, y, **kwargs):
            super().__init__(x, y, **kwargs)
            self.figure, self.axis = plt.subplots()
            self.axis.plot(self.x, self.y)
            self._create_window()
            self._place_figure_on_canvas()

The only function that should be publically available is the event_loop method, which should return the desired output.

To close the window, use the self._close() method, which ensures that both the window and the figure are correctly closed.

Initialize self. See help(type(self)) for accurate signature.

event_loop(self)

Handles the event loop for the GUI.

Notes

This function should typically be overwritten by a subclass, and should typically return any desired values from the embedded figure.

This simple implementation makes the window visible, and closes the window as soon as anything in the window is clicked.

class mcetl.plot_utils.PlotToolbar(fig_canvas, canvas, **kwargs)

Bases: matplotlib.backends.backend_tkagg.NavigationToolbar2Tk

Custom toolbar without the subplots and save figure buttons.

Ensures that saving is done through the save menu in the window, which gives better options for output image quality and ensures the figure dimensions are correct. The subplots button is removed so that the user does not mess with the plot layout since it is handled by using matplotlib's tight layout.

Parameters
  • fig_canvas (matplotlib.FigureCanvas) -- The figure canvas on which to operate.

  • canvas (tkinter.Canvas) -- The Canvas element which owns this toolbar.

  • **kwargs -- Any additional keyword arguments to pass to NavigationToolbar2Tk.

mcetl.plot_utils.determine_dpi(fig_height, fig_width, dpi, canvas_size=CANVAS_SIZE)

Gives the correct dpi to fit the figure within the GUI canvas.

Parameters
  • fig_height (float) -- The figure height.

  • fig_width (float) -- The figure width.

  • dpi (float) -- The desired figure dpi.

  • canvas_size (tuple(int, int), optional) -- The size of the canvas that the figure will be placed on. Defaults to CANVAS_SIZE.

Returns

The correct dpi to fit the figure onto the GUI canvas.

Return type

float

Notes

When not saving, the dpi needs to be scaled to fit the figure on the GUI's canvas, and that scaling is called size_scale. For example, if the desired size was 1600 x 1200 pixels with a dpi of 300, the figure would be scaled down to 800 x 600 pixels to fit onto the canvas, so the dpi would be changed to 150, with a size_scale of 0.5.

A dpi_scale correction is needed because the qt5Agg backend will change the dpi to 2x the specified dpi when the display scaling in Windows is not 100%. I am not sure how it works on non-Windows operating systems.

The final dpi when not saving is equal to dpi * size_scale * dpi_scale.

mcetl.plot_utils.draw_figure_on_canvas(canvas, figure, toolbar_canvas=None, toolbar_class=NavigationToolbar2Tk, kwargs=None)

Places the figure and toolbar onto the canvas.

Parameters
  • canvas (tkinter.Canvas) -- The tkinter Canvas element for the figure.

  • figure (plt.Figure) -- The figure to be place on the canvas.

  • toolbar_canvas (tkinter.Canvas, optional) -- The tkinter Canvas element for the toolbar.

  • toolbar_class (NavigationToolbar2Tk, optional) -- The toolbar class used to create the toolbar for the figure. The default is NavigationToolbar2Tk.

  • kwargs (dict, optional) -- Keyword arguments designating how to pack the figure into the window. Relevant keys are 'canvas' and 'toolbar', with values being dictionaries containing keyword arguments to pass to pack.

Returns

  • figure_canvas (FigureCanvasTkAgg or None) -- The canvas containing the figure. Is None if drawing the canvas caused an exception.

  • toolbar (NavigationToolbar2Tk or None) -- The created toolbar. Is None if toolbar_canvas is None, or if there was an error when drawing figure_canvas.

Notes

The canvas children are destroyed after drawing the figure canvas so that there is a seamless transition from the old canvas to the new canvas.

The toolbar is packed before the figure canvas because the figure canvas shoulud be more flexible to resizing if they are on the same canvas.

mcetl.plot_utils.get_dpi_correction(dpi)

Calculates the correction factor needed to create a figure with the desired dpi.

Necessary because some matplotlib backends (namely qt5Agg) will adjust the dpi of the figure after creation.

Parameters

dpi (float or int) -- The desired figure dpi.

Returns

dpi_correction -- The scaling factor needed to create a figure with the desired dpi.

Return type

float

Notes

The matplotlib dpi correction occurs when the operating system display scaling is set to any value not equal to 100% (at least on Windows, other operating systems are unknown). This may cause issues when using UHD monitors, but I cannot test.

To get the desired dpi, simply create a figure with a dpi equal to dpi * dpi_correction.

mcetl.plot_utils.scale_axis(axis_bounds, lower_scale=None, upper_scale=None)

Calculates the new bounds to scale the current axis bounds.

The new bounds are calculated by multiplying the desired scale factor by the current difference of the upper and lower bounds, and then adding (for upper bound) or subtracting (for lower bound) from the current bound.

Parameters
  • axis_bounds (tuple(float, float)) -- The current lower and upper axis bounds.

  • lower_scale (float, optional) -- The desired fraction by which to scale the current lower bound. If None, will not scale the current lower bounds.

  • upper_scale (float, optional) -- The desired fraction by which to scale the current upper bound. If None, will not scale the current upper bounds.

Returns

  • lower_bound (float) -- The lower bound after scaling.

  • upper_bound (float) -- The upper bound after scaling.