Initial commit
This commit is contained in:
parent
e29ab77194
commit
c9d9a1d33d
7 changed files with 573 additions and 0 deletions
|
|
@ -1,2 +1,4 @@
|
|||
# plotter
|
||||
A tool to arrive at some nice looking plots a little quicker.
|
||||
|
||||
Based on what was originally written for the NAFUMA-package (https://www.github.com/rasmusthog/nafuma), and now forked out for more general use.
|
||||
25
plotter/animations.py
Normal file
25
plotter/animations.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import plotter.auxillary as aux
|
||||
|
||||
from PIL import Image
|
||||
import os
|
||||
|
||||
|
||||
def make_animation(paths, options={}):
|
||||
|
||||
default_options = {
|
||||
'save_folder': '.',
|
||||
'save_filename': 'animation.gif',
|
||||
'fps': 5
|
||||
}
|
||||
|
||||
options = aux.update_options(options=options, default_options=default_options)
|
||||
|
||||
|
||||
frames = []
|
||||
for path in paths:
|
||||
frame = Image.open(path)
|
||||
frames.append(frame)
|
||||
|
||||
frames[0].save(os.path.join(options['save_folder'], options['save_filename']), format='GIF', append_images=frames[1:], save_all=True, duration=(1/options['fps'])*1000, loop=0)
|
||||
|
||||
|
||||
44
plotter/auxillary.py
Normal file
44
plotter/auxillary.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
def update_options(options, default_options, required_options=None):
|
||||
''' Takes a dictionary of options along with a list of required options and dictionary of default options, and sets all keyval-pairs of options that is not already defined to the default values'''
|
||||
|
||||
for option in default_options.keys():
|
||||
if option not in options.keys():
|
||||
options[option] = default_options[option]
|
||||
|
||||
|
||||
return options
|
||||
|
||||
def save_options(options, path, ignore=None):
|
||||
''' Saves any options dictionary to a JSON-file in the specified path'''
|
||||
|
||||
options_copy = options.copy()
|
||||
|
||||
if ignore:
|
||||
if not isinstance(ignore, list):
|
||||
ignore = [ignore]
|
||||
|
||||
for i in ignore:
|
||||
options_copy[i] = 'Removed'
|
||||
|
||||
|
||||
if not os.path.isdir(os.path.dirname(path)):
|
||||
if os.path.dirname(path):
|
||||
os.makedirs(os.path.dirname(path))
|
||||
|
||||
|
||||
with open(path, 'w') as f:
|
||||
json.dump(options_copy, f, skipkeys=True, indent=4)
|
||||
|
||||
|
||||
def load_options(path):
|
||||
''' Loads JSON-file into a dictionary'''
|
||||
|
||||
with open(path, 'r') as f:
|
||||
options = json.load(f)
|
||||
|
||||
return(options)
|
||||
|
||||
|
||||
50
plotter/colours.py
Normal file
50
plotter/colours.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import plotter.auxillary as aux
|
||||
|
||||
import numpy as np
|
||||
|
||||
import importlib
|
||||
import itertools
|
||||
|
||||
def generate_colours(palettes, kind=None):
|
||||
|
||||
if kind == 'single':
|
||||
colour_cycle = itertools.cycle(palettes)
|
||||
|
||||
else:
|
||||
# Creates a list of all the colours that is passed in the colour_cycles argument. Then makes cyclic iterables of these.
|
||||
colour_collection = []
|
||||
for palette in palettes:
|
||||
mod = importlib.import_module("palettable.colorbrewer.%s" % palette[0])
|
||||
colour = getattr(mod, palette[1]).mpl_colors
|
||||
colour_collection = colour_collection + colour
|
||||
|
||||
colour_cycle = itertools.cycle(colour_collection)
|
||||
|
||||
|
||||
return colour_cycle
|
||||
|
||||
|
||||
|
||||
def mix_colours(colour1, colour2, options):
|
||||
|
||||
default_options = {
|
||||
'number_of_colours': 10,
|
||||
'weights': None
|
||||
}
|
||||
|
||||
options = aux.update_options(options=options, default_options=default_options)
|
||||
|
||||
if not options['weights']:
|
||||
options['weights'] = [x/options['number_of_colours'] for x in range(options['number_of_colours'])]
|
||||
|
||||
colours = []
|
||||
for weight in options['weights']:
|
||||
colour = []
|
||||
|
||||
for c1, c2 in zip(colour1, colour2):
|
||||
colour.append(np.round(((1-weight)*c1 + weight*c2), 5))
|
||||
|
||||
colours.append(colour)
|
||||
|
||||
|
||||
return colours
|
||||
17
plotter/interactive.py
Normal file
17
plotter/interactive.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
def ipywidgets_update(func, data, options={}, **kwargs):
|
||||
''' A general ipywidgets update function that can be passed to ipywidgets.interactive. To use this, you can run:
|
||||
|
||||
import ipywidgets as widgets
|
||||
import plotter.interactive as plint
|
||||
|
||||
w = widgets.interactive(plint.ipywidgets_update, func=widgets.fixed(my_func), plot_data=widgets.fixed(plot_data), options=widgets.fixed(options), key1=widget1, key2=widget2, key3=widget3)
|
||||
|
||||
where key1, key2, key3 etc. are the values in the options-dictionary you want widget control of, and widget1, widget2, widget3 etc. are widgets to control these values, e.g. widgets.IntSlider(value=1, min=0, max=10)
|
||||
'''
|
||||
|
||||
# Update the options-dictionary with the values from the widgets
|
||||
for key in kwargs:
|
||||
options[key] = kwargs[key]
|
||||
|
||||
# Call the function with the plot_data and options-dictionaries
|
||||
func(data=data, options=options)
|
||||
424
plotter/plots.py
Normal file
424
plotter/plots.py
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
# Helper functions
|
||||
import plotter.auxillary as aux
|
||||
import plotter.colours as col
|
||||
|
||||
# For plotting
|
||||
import matplotlib.pyplot as plt
|
||||
import itertools
|
||||
from matplotlib.lines import Line2D
|
||||
from matplotlib.ticker import (MultipleLocator)
|
||||
from matplotlib.patches import Rectangle
|
||||
|
||||
# To create insets
|
||||
from mpl_toolkits.axes_grid1.inset_locator import (inset_axes, InsetPosition, BboxPatch, BboxConnector)
|
||||
from matplotlib.transforms import TransformedBbox
|
||||
|
||||
def prepare_plot(options={}):
|
||||
''' A general function to prepare a plot based on contents of options['rc_params'] and options['format_params'].
|
||||
|
||||
rc_params is a dictionary with keyval-pairs corresponding to rcParams in matplotlib, to give the user full control over this. Please consult the matplotlib-documentation
|
||||
|
||||
format_params will determine the size, aspect ratio, resolution etc. of the figure. Should be modified to conform with any requirements from a journal.'''
|
||||
|
||||
if 'rc_params' in options.keys():
|
||||
rc_params = options['rc_params']
|
||||
else:
|
||||
rc_params = {}
|
||||
|
||||
|
||||
if 'format_params' in options.keys():
|
||||
format_params = options['format_params']
|
||||
else:
|
||||
format_params = {}
|
||||
|
||||
|
||||
default_format_params = {
|
||||
'single_column_width': 8.3,
|
||||
'double_column_width': 17.1,
|
||||
'column_type': 'single',
|
||||
'width_ratio': '1:1',
|
||||
'aspect_ratio': '1:1',
|
||||
'width': None,
|
||||
'height': None,
|
||||
'compress_width': 1,
|
||||
'compress_height': 1,
|
||||
'upscaling_factor': 1.0,
|
||||
'dpi': 600,
|
||||
'nrows': 1,
|
||||
'ncols': 1,
|
||||
'grid_ratio_height': None,
|
||||
'grid_ratio_width': None
|
||||
}
|
||||
|
||||
format_params = aux.update_options(options=format_params, default_options=default_format_params)
|
||||
|
||||
|
||||
# Reset run commands
|
||||
plt.rcdefaults()
|
||||
|
||||
# Update run commands if any is passed (will pass an empty dictionary if not passed)
|
||||
update_rc_params(rc_params)
|
||||
|
||||
if not format_params['width']:
|
||||
format_params['width'] = determine_width(format_params=format_params)
|
||||
|
||||
if not format_params['height']:
|
||||
format_params['height'] = determine_height(format_params=format_params, width=format_params['width'])
|
||||
|
||||
format_params['width'], format_params['height'] = scale_figure(format_params=format_params, width=format_params['width'], height=format_params['height'])
|
||||
|
||||
if format_params['nrows'] == 1 and format_params['ncols'] == 1:
|
||||
fig, ax = plt.subplots(figsize=(format_params['width'], format_params['height']), dpi=format_params['dpi'])
|
||||
|
||||
return fig, ax
|
||||
|
||||
else:
|
||||
if not format_params['grid_ratio_height']:
|
||||
format_params['grid_ratio_height'] = [1 for i in range(format_params['nrows'])]
|
||||
|
||||
if not format_params['grid_ratio_width']:
|
||||
format_params['grid-ratio_width'] = [1 for i in range(format_params['ncols'])]
|
||||
|
||||
fig, axes = plt.subplots(nrows=format_params['nrows'], ncols=format_params['ncols'], figsize=(format_params['width'],format_params['height']),
|
||||
gridspec_kw={'height_ratios': format_params['grid_ratio_height'], 'width_ratios': format_params['grid_ratio_width']},
|
||||
facecolor='w', dpi=format_params['dpi'])
|
||||
|
||||
return fig, axes
|
||||
|
||||
|
||||
def adjust_plot(fig, ax, options):
|
||||
''' A general function to adjust plot according to contents of the options-dictionary '''
|
||||
|
||||
|
||||
default_options = {
|
||||
'plot_kind': None, # defaults to None, but should be utilised when requiring special formatting for a particular plot
|
||||
'xlabel': None, 'ylabel': None,
|
||||
'xunit': None, 'yunit': None,
|
||||
'xlabel_pad': 4.0, 'ylabel_pad': 4.0,
|
||||
'hide_x_labels': False, 'hide_y_labels': False, # Whether the main labels on the x- and/or y-axes should be hidden
|
||||
'hide_x_ticklabels': False, 'hide_y_ticklabels': False, # Whether ticklabels on the x- and/or y-axes should be hidden
|
||||
'hide_x_ticks': False, 'hide_y_ticks': False, # Whether the ticks on the x- and/or y-axes should be hidden
|
||||
'x_tick_locators': None, 'y_tick_locators': None, # The major and minor tick locators for the x- and y-axes
|
||||
'rotation_x_ticks': 0, 'rotation_y_ticks': 0, # Degrees the x- and/or y-ticklabels should be rotated
|
||||
'xticks': None, 'yticks': None, # Custom definition of the xticks and yticks. This is not properly implemented now.
|
||||
'xlim': None, 'ylim': None, # Limits to the x- and y-axes
|
||||
'xlim_reset': False, 'ylim_reset': False, # For use in setting limits of backgrounds - forcing reset of xlim and ylim, useful when more axes
|
||||
'title': None, # Title of the plot
|
||||
'backgrounds': [],
|
||||
'legend': False, 'legend_position': ['lower center', (0.5, -0.1)], 'legend_ncol': 1, # Toggles on/off legend. Specifices legend position and the number of columns the legend should appear as.
|
||||
'subplots_adjust': {'left': None, 'right': None, 'top': None, 'bottom': None, 'wspace': None, 'hspace': None}, # Adjustment of the Axes-object within the Figure-object. Fraction of the Figure-object the left, bottom, right and top edges of the Axes-object will start.
|
||||
'marker_edges': None,
|
||||
'text': None # Text to show in the plot. Should be a list where the first element is the string, and the second is a tuple with x- and y-coordinates. Could also be a list of lists to show more strings of text.
|
||||
}
|
||||
|
||||
|
||||
options = aux.update_options(options=options, default_options=default_options)
|
||||
|
||||
# Set labels on x- and y-axes
|
||||
if not options['hide_y_labels']:
|
||||
if not options['yunit']:
|
||||
ax.set_ylabel(f'{options["ylabel"]}', labelpad=options['ylabel_pad'])
|
||||
else:
|
||||
ax.set_ylabel(f'{options["ylabel"]} [{options["yunit"]}]', labelpad=options['ylabel_pad'])
|
||||
|
||||
else:
|
||||
ax.set_ylabel('')
|
||||
|
||||
if not options['hide_x_labels']:
|
||||
if not options['xunit']:
|
||||
ax.set_xlabel(f'{options["xlabel"]}', labelpad=options['xlabel_pad'])
|
||||
else:
|
||||
ax.set_xlabel(f'{options["xlabel"]} [{options["xunit"]}]', labelpad=options['xlabel_pad'])
|
||||
else:
|
||||
ax.set_xlabel('')
|
||||
|
||||
# Set multiple locators
|
||||
if options['y_tick_locators']:
|
||||
ax.yaxis.set_major_locator(MultipleLocator(options['y_tick_locators'][0]))
|
||||
ax.yaxis.set_minor_locator(MultipleLocator(options['y_tick_locators'][1]))
|
||||
|
||||
if options['x_tick_locators']:
|
||||
ax.xaxis.set_major_locator(MultipleLocator(options['x_tick_locators'][0]))
|
||||
ax.xaxis.set_minor_locator(MultipleLocator(options['x_tick_locators'][1]))
|
||||
|
||||
|
||||
# FIXME THIS NEEDS REWORK FOR IT TO FUNCTION PROPERLY!
|
||||
#if options['xticks']:
|
||||
# ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1))
|
||||
# ax.set_xticklabels(options['xticks'])
|
||||
# else:
|
||||
# ax.set_xticks(np.arange(plot_data['start'], plot_data['end']+1))
|
||||
# ax.set_xticklabels([x/2 for x in np.arange(plot_data['start'], plot_data['end']+1)])
|
||||
|
||||
# Hide x- and y- ticklabels
|
||||
if options['hide_y_ticklabels']:
|
||||
ax.tick_params(axis='y', direction='in', which='both', labelleft=False, labelright=False)
|
||||
else:
|
||||
plt.xticks(rotation=options['rotation_x_ticks'])
|
||||
#ax.set_xticklabels(ax.get_xticks(), rotation = options['rotation_x_ticks'])
|
||||
|
||||
if options['hide_x_ticklabels']:
|
||||
ax.tick_params(axis='x', direction='in', which='both', labelbottom=False, labeltop=False)
|
||||
else:
|
||||
pass
|
||||
#ax.set_yticklabels(ax.get_yticks(), rotation = options['rotation_y_ticks'])
|
||||
|
||||
|
||||
# Hide x- and y-ticks:
|
||||
if options['hide_y_ticks']:
|
||||
ax.tick_params(axis='y', direction='in', which='both', left=False, right=False)
|
||||
else:
|
||||
ax.tick_params(axis='y', direction='in', which='both', left=True, right=True)
|
||||
|
||||
if options['hide_x_ticks']:
|
||||
ax.tick_params(axis='x', direction='in', which='both', bottom=False, top=False)
|
||||
else:
|
||||
ax.tick_params(axis='x', direction='in', which='both', bottom=True, top=True)
|
||||
|
||||
|
||||
|
||||
# Set title
|
||||
if options['title']:
|
||||
ax.set_title(options['title'], fontsize=plt.rcParams['font.size'])
|
||||
|
||||
|
||||
|
||||
#### DRAW/REMOVE LEGEND ####
|
||||
# Options:
|
||||
# 'legend_position': (default ['lower center', (0.5, -0.1)]) - Follows matplotlib's way of specifying legend position
|
||||
# 'legend_ncol': (default 1) # Number of columns to write the legend in
|
||||
# Also requires options to contain values in colours, markers and labels. (No defaults)
|
||||
|
||||
if ax.get_legend():
|
||||
ax.get_legend().remove()
|
||||
|
||||
|
||||
if options['legend']:
|
||||
# Make palette and linestyles from original parameters
|
||||
if not options['colours']:
|
||||
colours = col.generate_colours(palettes=options['palettes'])
|
||||
else:
|
||||
colours = itertools.cycle(options['colours'])
|
||||
|
||||
|
||||
markers = itertools.cycle(options['markers'])
|
||||
|
||||
# Create legend
|
||||
active_markers = []
|
||||
active_labels = []
|
||||
|
||||
for label in options['labels']:
|
||||
|
||||
|
||||
# Discard next linestyle and colour if label is _
|
||||
if label == '_':
|
||||
_ = next(colours)
|
||||
_ = next(markers)
|
||||
|
||||
else:
|
||||
marker = next(markers)
|
||||
if not marker:
|
||||
active_markers.append(Line2D([], [], color=next(colours)))
|
||||
else:
|
||||
active_markers.append(Line2D([], [], markerfacecolor=next(colours), markeredgecolor=options['marker_edges'], markersize=10, color=(1,1,1,0), marker=marker))
|
||||
|
||||
active_labels.append(label)
|
||||
|
||||
|
||||
|
||||
ax.legend(active_markers, active_labels, frameon=False, loc=options['legend_position'][0], bbox_to_anchor=options['legend_position'][1], ncol=options['legend_ncol'])
|
||||
#fig.legend(handles=patches, loc=options['legend_position'][0], bbox_to_anchor=options['legend_position'][1], frameon=False)
|
||||
|
||||
|
||||
|
||||
# Adjust where the axes start within the figure. Default value is 10% in from the left and bottom edges. Used to make room for the plot within the figure size (to avoid using bbox_inches='tight' in the savefig-command, as this screws with plot dimensions)
|
||||
plt.subplots_adjust(**options['subplots_adjust'])
|
||||
|
||||
|
||||
# If limits for x- and y-axes is passed, sets these.
|
||||
if options['xlim'] is not None:
|
||||
ax.set_xlim(options['xlim'])
|
||||
|
||||
if options['ylim'] is not None:
|
||||
ax.set_ylim(options['ylim'])
|
||||
|
||||
|
||||
#### DRAW BACKGROUNDS ####
|
||||
# options['backgrounds'] should contain a dictionary or a list of dictionaries. Options to be specified are listed below.
|
||||
|
||||
if options['backgrounds']:
|
||||
|
||||
if not isinstance(options['backgrounds'], list):
|
||||
options['backgrounds'] = [options['backgrounds']]
|
||||
|
||||
|
||||
for background in options['backgrounds']:
|
||||
default_background_options = {
|
||||
'colour': (0,0,0),
|
||||
'alpha': 0.2,
|
||||
'xlim': list(ax.get_xlim()),
|
||||
'ylim': list(ax.get_ylim()),
|
||||
'zorder': 0,
|
||||
'edgecolour': None,
|
||||
'linewidth': None
|
||||
}
|
||||
|
||||
|
||||
background = aux.update_options(options=background, default_options=default_background_options)
|
||||
|
||||
if options['xlim_reset']:
|
||||
background['xlim'] = list(ax.get_xlim())
|
||||
if options['ylim_reset']:
|
||||
background['ylim'] = list(ax.get_ylim())
|
||||
|
||||
if not background['xlim'][0]:
|
||||
background['xlim'][0] = ax.get_xlim()[0]
|
||||
if not background['xlim'][1]:
|
||||
background['xlim'][1] = ax.get_xlim()[1]
|
||||
if not background['ylim'][0]:
|
||||
background['ylim'][0] = ax.get_ylim()[0]
|
||||
if not background['ylim'][1]:
|
||||
background['ylim'][1] = ax.get_ylim()[1]
|
||||
|
||||
ax.add_patch(Rectangle(
|
||||
xy=(background['xlim'][0], background['ylim'][0]), # Anchor point
|
||||
width=background['xlim'][1]-background['xlim'][0], # Width of background
|
||||
height=background['ylim'][1]-background['ylim'][0], # Height of background
|
||||
zorder=background['zorder'], # Placement in stack
|
||||
facecolor=(background['colour'][0], background['colour'][1], background['colour'][2], background['alpha']), # Colour
|
||||
edgecolor=background['edgecolour'], # Edgecolour
|
||||
linewidth=background['linewidth']) # Linewidth
|
||||
)
|
||||
|
||||
|
||||
# Add custom text
|
||||
if options['text']:
|
||||
|
||||
# If only a single element, put it into a list so the below for-loop works.
|
||||
if isinstance(options['text'][0], str):
|
||||
options['text'] = [options['text']]
|
||||
|
||||
# Plot all passed texts
|
||||
for text in options['text']:
|
||||
ax.text(x=text[1][0], y=text[1][1], s=text[0])
|
||||
|
||||
return fig, ax
|
||||
|
||||
|
||||
def determine_width(format_params):
|
||||
''' '''
|
||||
|
||||
conversion_cm_inch = 0.3937008 # cm to inch
|
||||
|
||||
if format_params['column_type'] == 'single':
|
||||
column_width = format_params['single_column_width']
|
||||
elif format_params['column_type'] == 'double':
|
||||
column_width = format_params['double_column_width']
|
||||
|
||||
column_width *= conversion_cm_inch
|
||||
|
||||
|
||||
width_ratio = [float(num) for num in format_params['width_ratio'].split(':')]
|
||||
|
||||
|
||||
width = column_width * width_ratio[0]/width_ratio[1]
|
||||
|
||||
|
||||
return width
|
||||
|
||||
|
||||
def determine_height(format_params, width):
|
||||
|
||||
aspect_ratio = [float(num) for num in format_params['aspect_ratio'].split(':')]
|
||||
|
||||
height = width/(aspect_ratio[0] / aspect_ratio[1])
|
||||
|
||||
return height
|
||||
|
||||
|
||||
def scale_figure(format_params, width, height):
|
||||
width = width * format_params['upscaling_factor'] * format_params['compress_width']
|
||||
height = height * format_params['upscaling_factor'] * format_params['compress_height']
|
||||
|
||||
return width, height
|
||||
|
||||
|
||||
def update_rc_params(rc_params):
|
||||
''' Update all passed run commands in matplotlib'''
|
||||
|
||||
if rc_params:
|
||||
for key in rc_params.keys():
|
||||
plt.rcParams.update({key: rc_params[key]})
|
||||
|
||||
def prepare_inset_axes(parent_ax, options):
|
||||
|
||||
default_options = {
|
||||
'hide_inset_x_labels': False, # Whether x labels should be hidden
|
||||
'hide_inset_x_ticklabels': False,
|
||||
'hide_inset_x_ticks': False,
|
||||
'rotation_inset_x_ticks': 0,
|
||||
'hide_inset_y_labels': False, # whether y labels should be hidden
|
||||
'hide_inset_y_ticklabels': False,
|
||||
'hide_inset_y_ticks': False,
|
||||
'rotation_inset_y_ticks': 0,
|
||||
'inset_x_tick_locators': [100, 50], # Major and minor tick locators
|
||||
'inset_y_tick_locators': [10, 5],
|
||||
'inset_position': [0.1,0.1,0.3,0.3],
|
||||
'inset_bounding_box': [0,0,0.1, 0.1],
|
||||
'inset_marks': [None, None],
|
||||
'legend_position': ['upper center', (0.20, 0.90)], # the position of the legend passed as arguments to loc and bbox_to_anchor respectively,
|
||||
'connecting_corners': [1,2]
|
||||
}
|
||||
|
||||
|
||||
options = aux.update_options(options=options, required_options=default_options.keys(), default_options=default_options)
|
||||
|
||||
|
||||
# Create a set of inset Axes: these should fill the bounding box allocated to
|
||||
# them.
|
||||
inset_ax = plt.axes(options["inset_bounding_box"])
|
||||
# Manually set the position and relative size of the inset axes within ax1
|
||||
ip = InsetPosition(parent_ax, options['inset_position'])
|
||||
inset_ax.set_axes_locator(ip)
|
||||
|
||||
if options['connecting_corners'] and len(options["connecting_corners"]) == 2:
|
||||
connect_inset(parent_ax, inset_ax, loc1a=options['connecting_corners'][0], loc2a=options['connecting_corners'][1], loc1b=options['connecting_corners'][0], loc2b=options['connecting_corners'][1], fc='none', ec='black')
|
||||
elif options['connecting_corners'] and len(options['connecting_corners']) == 4:
|
||||
connect_inset(parent_ax, inset_ax, loc1a=options['connecting_corners'][0], loc2a=options['connecting_corners'][1], loc1b=options['connecting_corners'][2], loc2b=options['connecting_corners'][3], fc='none', ec='black', ls='--')
|
||||
|
||||
inset_ax.xaxis.set_major_locator(MultipleLocator(options['inset_x_tick_locators'][0]))
|
||||
inset_ax.xaxis.set_minor_locator(MultipleLocator(options['inset_x_tick_locators'][1]))
|
||||
|
||||
|
||||
inset_ax.yaxis.set_major_locator(MultipleLocator(options['inset_y_tick_locators'][0]))
|
||||
inset_ax.yaxis.set_minor_locator(MultipleLocator(options['inset_y_tick_locators'][1]))
|
||||
|
||||
|
||||
|
||||
|
||||
return inset_ax
|
||||
|
||||
|
||||
|
||||
|
||||
def connect_inset(parent_axes, inset_axes, loc1a=1, loc1b=1, loc2a=2, loc2b=2, **kwargs):
|
||||
rect = TransformedBbox(inset_axes.viewLim, parent_axes.transData)
|
||||
|
||||
pp = BboxPatch(rect, fill=False, **kwargs)
|
||||
parent_axes.add_patch(pp)
|
||||
|
||||
p1 = BboxConnector(inset_axes.bbox, rect, loc1=loc1a, loc2=loc1b, **kwargs)
|
||||
inset_axes.add_patch(p1)
|
||||
p1.set_clip_on(False)
|
||||
p2 = BboxConnector(inset_axes.bbox, rect, loc1=loc2a, loc2=loc2b, **kwargs)
|
||||
inset_axes.add_patch(p2)
|
||||
p2.set_clip_on(False)
|
||||
|
||||
return pp, p1, p2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
11
setup.py
Normal file
11
setup.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name='plotter',
|
||||
version='0.1',
|
||||
description='A plotting tool written on top of matplotlib to arrive at nice looking plots a little quicker.',
|
||||
url='https://github.com/rasmusthog/plotter',
|
||||
author='Rasmus Vester Thøgersen',
|
||||
author_email='github@rasmusthog.me',
|
||||
license='GPLv3',
|
||||
packages=find_packages(),
|
||||
zip_safe=False)
|
||||
Loading…
Add table
Add a link
Reference in a new issue