Initial project version
This commit is contained in:
49
pygrace/README.md
Normal file
49
pygrace/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
# Pygrace: A tool to convert matplotlib figure to xmgrace plots
|
||||
|
||||
After plotting the matplotlib figure, call
|
||||
pygrace.saveagr('myfig.agr')
|
||||
to save it as a xmgrace plot.
|
||||
|
||||
## Current state of supperted matplotlib porperties
|
||||
|
||||
The conversion is done by the appropriate classes (`AgrFigure`, `AgrAxis`, ...) defined in the pygrace module.
|
||||
They each only support a small subset of matplotlib properties, see below.
|
||||
|
||||
### Figures
|
||||
|
||||
- canvas size (page size), controllable with `AgrFigure.dpi`
|
||||
- font size scaling, controllable with `AgrFigure.fontscale`
|
||||
- map colors
|
||||
- x/y offset: adds an offset (as fraction of the according edge) to the xmgrace figure. Helpfull if x labels get pushed out of the page.
|
||||
|
||||
### Axes
|
||||
|
||||
- size, axis limits
|
||||
- title
|
||||
- linear or log scale
|
||||
- labels and ticklabels:
|
||||
- turn on/off
|
||||
- position (left, right, bottom, top)
|
||||
- fontsize
|
||||
- legend:
|
||||
- turn on/off
|
||||
- location
|
||||
- fontsize
|
||||
|
||||
### Lines
|
||||
|
||||
- label (for legend)
|
||||
- linestyle
|
||||
- line color
|
||||
- markers:
|
||||
- shape
|
||||
- color
|
||||
- edgewidth
|
||||
- fill (appart from full and none, this is kind of random)
|
||||
|
||||
### Texts:
|
||||
|
||||
- location
|
||||
- fontsize
|
||||
- color
|
||||
|
1
pygrace/__init__.py
Normal file
1
pygrace/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .pygrace import saveagr
|
114
pygrace/_load_stuff.py
Normal file
114
pygrace/_load_stuff.py
Normal file
@ -0,0 +1,114 @@
|
||||
from __future__ import print_function
|
||||
import os.path
|
||||
import os
|
||||
import imp
|
||||
import sys
|
||||
import configparser as ConfigParser
|
||||
|
||||
if os.name == 'nt':
|
||||
path_to_log = os.path.expanduser('~\.auswerten')
|
||||
else:
|
||||
path_to_log = os.path.expanduser('~/.auswerten')
|
||||
path_to_module = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def reload_(filename):
|
||||
(path, name) = os.path.split(filename)
|
||||
(name, ext) = os.path.splitext(name)
|
||||
|
||||
try:
|
||||
file, filename, data = imp.find_module(name, [path])
|
||||
except ImportError:
|
||||
print('No module {} found'.format(name))
|
||||
try:
|
||||
mod = imp.load_module(name, file, filename, data)
|
||||
return mod
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
if file:
|
||||
file.close()
|
||||
|
||||
|
||||
def import_(filename):
|
||||
"""
|
||||
Tries to add random file to sys.modules. Insecure as fuck and predestined to break something
|
||||
(it will overload other modules)
|
||||
"""
|
||||
(path, name) = os.path.split(filename)
|
||||
(name, ext) = os.path.splitext(name)
|
||||
try:
|
||||
return sys.modules[name]
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
file, filename, data = imp.find_module(name, [path])
|
||||
except ImportError:
|
||||
print('No module {} found'.format(name))
|
||||
try:
|
||||
mod = imp.load_module(name, file, filename, data)
|
||||
return mod
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
finally:
|
||||
# Since we may exit via an exception, close fp explicitly.
|
||||
try:
|
||||
if file:
|
||||
file.close()
|
||||
except UnboundLocalError:
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
from shutil import copyfile
|
||||
if os.name == 'nt':
|
||||
copyfile(os.path.join(path_to_module, 'models\myfitmodels.py'), filename)
|
||||
else:
|
||||
copyfile(os.path.join(path_to_module, './models/myfitmodels.py'), filename)
|
||||
# open(filename, 'a').close()
|
||||
|
||||
user_model_path = os.path.join(path_to_log, 'myfitmodels.py')
|
||||
userfitmodels = import_(user_model_path)
|
||||
|
||||
|
||||
def read_grace_conf(mode):
|
||||
__path_to_config = os.path.join(path_to_log, 'grace.conf')
|
||||
config = ConfigParser.SafeConfigParser()
|
||||
config.read(__path_to_config)
|
||||
|
||||
if config.has_section(mode):
|
||||
width = config.getfloat(mode, 'width')
|
||||
height = config.getfloat(mode, 'height')
|
||||
bottom_m = config.getfloat(mode, 'bottom margin')
|
||||
left_m = config.getfloat(mode, 'left margin')
|
||||
top_m = config.getfloat(mode, 'top margin')
|
||||
right_m = config.getfloat(mode, 'right margin')
|
||||
title = config.getint(mode, 'title')
|
||||
haxis_t = config.getint(mode, 'hor axis tick')
|
||||
vaxis_t = config.getint(mode, 'ver axis tick')
|
||||
haxis_l = config.getint(mode, 'hor axis label')
|
||||
vaxis_l = config.getint(mode, 'ver axis label')
|
||||
leg = config.getint(mode, 'legend')
|
||||
else:
|
||||
width = round(11 * 2.54, 2)
|
||||
height = round(8.5 * 2.54, 2)
|
||||
bottom_m = round(0.15 * 8.5 * 2.54, 2)
|
||||
left_m = round(0.15 * 8.5 * 2.54, 2)
|
||||
top_m = round(0.15 * 8.5 * 2.54, 2)
|
||||
right_m = round((11 / 8.5 - 1.15) * 8.5 * 2.54, 2)
|
||||
haxis_l = 100
|
||||
haxis_t = 100
|
||||
vaxis_l = 100
|
||||
vaxis_t = 100
|
||||
leg = 100
|
||||
title = 100
|
||||
if config.has_section('sizes'):
|
||||
sym = config.getfloat('sizes', 'symbol')
|
||||
line = config.getfloat('sizes', 'line')
|
||||
f_line = config.getfloat('sizes', 'fit_line')
|
||||
else:
|
||||
sym = 10
|
||||
line = 1
|
||||
f_line = 1
|
||||
|
||||
return (width, height), (bottom_m, top_m, left_m, right_m), \
|
||||
((haxis_t, haxis_l), (vaxis_t, vaxis_l), title, leg), \
|
||||
(sym, line, f_line)
|
||||
|
158
pygrace/colors.py
Normal file
158
pygrace/colors.py
Normal file
@ -0,0 +1,158 @@
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from collections import OrderedDict
|
||||
from itertools import cycle
|
||||
|
||||
|
||||
def make_cmap(colors, mapping=True):
|
||||
if isinstance(colors, dict):
|
||||
colors = np.array(colors.values())
|
||||
bit_rgb = np.linspace(0, 1, 256)
|
||||
position = np.linspace(0, 1, len(colors))
|
||||
_cols = colors[:] + 0
|
||||
for i in range(len(colors)):
|
||||
for j in xrange(3):
|
||||
_cols[i][j] = bit_rgb[colors[i][j]]
|
||||
if mapping:
|
||||
cdict = {'red': [], 'green':[], 'blue':[]}
|
||||
for pos, color in zip(position, _cols):
|
||||
cdict['red'].append((pos, color[0], color[0]))
|
||||
cdict['green'].append((pos, color[1], color[1]))
|
||||
cdict['blue'].append((pos, color[2], color[2]))
|
||||
|
||||
cmap = mpl.colors.LinearSegmentedColormap('my_colormap', cdict, 256)
|
||||
else:
|
||||
cmap = mpl.colors.ListedColormap(_cols, 'my_colormap')
|
||||
return cmap
|
||||
|
||||
cm_bw = OrderedDict([('white', (255, 255, 255, 1)),
|
||||
('black', (0, 0, 0, 1))])
|
||||
|
||||
cm_grace = OrderedDict([
|
||||
('red', (255, 0, 0, 255)),
|
||||
('green', (0, 255, 0, 255)),
|
||||
('blue', (0, 0, 255, 255)),
|
||||
('yellow', (255, 255, 0, 255)),
|
||||
('brown', (188, 143, 143, 255)),
|
||||
('grey', (220, 220, 220, 255)),
|
||||
('violet', (148, 0, 211, 255)),
|
||||
('cyan', (0, 255, 255, 255)),
|
||||
('magenta', (255, 0, 255, 255)),
|
||||
('orange', (255, 165, 0, 255)),
|
||||
('indigo', (114, 33, 188, 255)),
|
||||
('maroon', (103, 7, 72, 255)),
|
||||
('turquoise', (64, 224, 208, 255)),
|
||||
('green4', (0, 139, 0, 255))
|
||||
])
|
||||
|
||||
|
||||
cm_tuda = OrderedDict([
|
||||
('tud1a', (93, 133, 195, 255)),
|
||||
('tud2a', (0, 156, 218, 255)),
|
||||
('tud3a', (80, 182, 149, 255)),
|
||||
('tud4a', (175, 204, 80, 255)),
|
||||
('tud5a', (221, 223, 72, 255)),
|
||||
('tud6a', (255, 224, 92, 255)),
|
||||
('tud7a', (248, 186, 60, 255)),
|
||||
('tud8a', (238, 122, 52, 255)),
|
||||
('tud9a', (233, 80, 62, 255)),
|
||||
('tud10a', (201, 48, 142, 255)),
|
||||
('tud11a', (128, 69, 151, 255))
|
||||
])
|
||||
|
||||
cm_tudb = OrderedDict([
|
||||
('tud1b', (0, 90, 169, 255)),
|
||||
('tud2b', (0, 131, 204, 255)),
|
||||
('tud3b', (0, 157, 129, 255)),
|
||||
('tud4b', (153, 192, 0, 255)),
|
||||
('tud5b', (201, 212, 0, 255)),
|
||||
('tud6b', (253, 202, 0, 255)),
|
||||
('tud7b', (245, 163, 0, 255)),
|
||||
('tud8b', (236, 101, 0, 255)),
|
||||
('tud9b', (230, 0, 26, 255)),
|
||||
('tud10b', (166, 0, 132, 255)),
|
||||
('tud11b', (114, 16, 133, 255))
|
||||
])
|
||||
|
||||
cm_tudc = OrderedDict([
|
||||
('tud1c', (0, 78, 138, 255)),
|
||||
('tud2c', (0, 104, 157, 255)),
|
||||
('tud3c', (0, 136, 119, 255)),
|
||||
('tud4c', (127, 171, 22, 255)),
|
||||
('tud5c', (177, 189, 0, 255)),
|
||||
('tud6c', (215, 172, 0, 255)),
|
||||
('tud7c', (210, 135, 0, 255)),
|
||||
('tud8c', (204, 76, 3, 255)),
|
||||
('tud9c', (185, 15, 34, 255)),
|
||||
('tud10c', (149, 17, 105, 255)),
|
||||
('tud11c', (97, 28, 115, 255))
|
||||
])
|
||||
|
||||
|
||||
cm_tudd = OrderedDict([
|
||||
('tud1d', (36, 53, 114, 255)),
|
||||
('tud2d', (0, 78, 115, 255)),
|
||||
('tud3d', (0, 113, 94, 255)),
|
||||
('tud4d', (106, 139, 55, 255)),
|
||||
('tud5d', (153, 166, 4, 255)),
|
||||
('tud6d', (174, 142, 0, 255)),
|
||||
('tud7d', (190, 111, 0, 255)),
|
||||
('tud8d', (169, 73, 19, 255)),
|
||||
('tud9d', (156, 28, 38, 255)),
|
||||
('tud10d', (115, 32, 84, 255)),
|
||||
('tud11d', (76, 34, 106, 255))
|
||||
])
|
||||
|
||||
|
||||
def combine_maps(*args):
|
||||
color_map = OrderedDict()
|
||||
for d in args:
|
||||
for k, v in d.items():
|
||||
color_map[k] = v
|
||||
return color_map
|
||||
|
||||
cm_tud = combine_maps(cm_tuda, cm_tudb, cm_tudc, cm_tudd)
|
||||
|
||||
all_colors = combine_maps(cm_bw, cm_grace, cm_tud)
|
||||
|
||||
colors = cm_tud.values()
|
||||
|
||||
symbols = cycle(['o', 't', 'd', 's', '+'])
|
||||
|
||||
sym_map = {None: '0', 'o': '1', 's': '2', 'd': '3', 't': '6', '+': '9'}
|
||||
|
||||
|
||||
class ColorDict(OrderedDict):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ColorDict, self).__init__(*args, **kwargs)
|
||||
for d in [cm_bw, cm_tuda, cm_tudb, cm_tudc, cm_tudd, cm_grace]:
|
||||
old_length = len(self)
|
||||
for k, c in enumerate(d.keys()):
|
||||
rgb = d[c][:3]
|
||||
self[rgb] = (k + old_length, c)
|
||||
self._new = OrderedDict()
|
||||
|
||||
def __contains__(self, item):
|
||||
if isinstance(item, tuple):
|
||||
return item in self.keys()
|
||||
elif isinstance(item. basestring):
|
||||
return any([item == v[1] for v in self.values()])
|
||||
else:
|
||||
return False
|
||||
|
||||
def convert_color(self, color):
|
||||
c = tuple(color[0:3])
|
||||
if c not in self.keys():
|
||||
self[c] = (str(self.__len__()), ''.join('{:02X}'.format(a) for a in c))
|
||||
self._new[c] = (str(self.__len__()-1), ''.join('{:02X}'.format(a) for a in c))
|
||||
return self[c][0]
|
||||
|
||||
def has_index(self, idx):
|
||||
return any([idx == v[0] for v in self.values()])
|
||||
|
||||
def new_colors(self):
|
||||
return self._new
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
355
pygrace/pygrace.py
Normal file
355
pygrace/pygrace.py
Normal file
@ -0,0 +1,355 @@
|
||||
import numpy as np
|
||||
from matplotlib.colors import ColorConverter
|
||||
from matplotlib.backends import backend_agg
|
||||
import matplotlib.pyplot as plt
|
||||
import textwrap
|
||||
from itertools import zip_longest
|
||||
from collections import defaultdict
|
||||
|
||||
from . import tex2grace
|
||||
from .tex2grace import latex_to_xmgrace
|
||||
|
||||
|
||||
def update_labels(labels, axis=None):
|
||||
if axis is None:
|
||||
axis = plt.gca()
|
||||
for line, lb in zip_longest(axis.lines, labels, fillvalue=''):
|
||||
line.set_label(lb)
|
||||
|
||||
|
||||
def sanitize_strings(dic):
|
||||
"""Do some sanitization for strings."""
|
||||
for k in dic:
|
||||
if isinstance(dic[k], str):
|
||||
dic[k].replace('{', '{{')
|
||||
dic[k].replace('}', '}}')
|
||||
|
||||
|
||||
def get_world_coords(artist):
|
||||
"""Get the world coordinates of an artist."""
|
||||
trans = artist.axes.transData.inverted()
|
||||
return trans.transform(artist.get_window_extent())
|
||||
|
||||
|
||||
class AgrText:
|
||||
fmt = """string on
|
||||
string loctype view
|
||||
string {position}
|
||||
string char size {size:.2f}
|
||||
string color {color}
|
||||
string def "{value}"
|
||||
"""
|
||||
|
||||
def get_position(self):
|
||||
trans = self.agr_axis.agr_figure.figure.transFigure.inverted()
|
||||
pos = trans.transform(self.text.get_window_extent())[0]
|
||||
pos = (pos + self.agr_figure.offset) * self.agr_figure.pagescale
|
||||
self.position = '{:.5f}, {:.5f}'.format(*pos)
|
||||
|
||||
def __init__(self, text, agr_axis):
|
||||
self.text = text
|
||||
self.agr_axis = agr_axis
|
||||
self.agr_figure = agr_axis.agr_figure
|
||||
|
||||
self.value = latex_to_xmgrace(text.get_text())
|
||||
self.size = text.get_fontsize() * self.agr_figure.fontscale
|
||||
self.color = AgrLine.color_index(text.get_color())
|
||||
self.get_position()
|
||||
sanitize_strings(self.__dict__)
|
||||
|
||||
def __str__(self):
|
||||
return self.fmt.format(**self.__dict__)
|
||||
|
||||
|
||||
class AgrLine:
|
||||
fmt = """hidden {hidden}
|
||||
type {type}
|
||||
legend "{label}"
|
||||
line linestyle {linestyle}
|
||||
line linewidth {linewidth}
|
||||
line color {color}
|
||||
symbol {marker}
|
||||
symbol color {markeredgecolor}
|
||||
symbol fill color {markerfacecolor}
|
||||
symbol fill pattern {markerfill}
|
||||
symbol linewidth {linewidth}
|
||||
"""
|
||||
width_scale = 2
|
||||
cc = ColorConverter()
|
||||
linestyles = {'None': 0, '-': 1, ':': 2, '--': 3, '-.': 5}
|
||||
markers = defaultdict(
|
||||
lambda: 1,
|
||||
{'': 0, 'None': 0, 'o': 1, 's': 2, 'd': 3, '^': 4, '<': 5, 'v': 6, '>': 7, '+': 8, 'x': 9, '*': 10}
|
||||
)
|
||||
fillstyles = ('none', 'full', 'right', 'left', 'bottom', 'top')
|
||||
colors = ['white', 'black']
|
||||
|
||||
def color_index(col):
|
||||
if col not in AgrLine.colors:
|
||||
AgrLine.colors.append(col)
|
||||
return AgrLine.colors.index(col)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
o = '@type xy\n'
|
||||
for x, y in self.line.get_xydata():
|
||||
o += '{} {}\n'.format(x, y)
|
||||
o += '&'
|
||||
return o
|
||||
|
||||
def get_label(self):
|
||||
lbl = self.line.get_label()
|
||||
self.label = latex_to_xmgrace(lbl) if not lbl.startswith('_line') else ''
|
||||
|
||||
def get_linestyle(self):
|
||||
self.linestyle = self.linestyles[self.line.get_linestyle()]
|
||||
|
||||
def get_linewidth(self):
|
||||
self.linewidth = self.line.get_linewidth() * self.width_scale
|
||||
|
||||
def get_color(self):
|
||||
self.color = AgrLine.color_index(self.line.get_color())
|
||||
|
||||
def get_marker(self):
|
||||
mk = self.line.get_marker()
|
||||
self.marker = self.markers[mk] if mk in self.markers else 1
|
||||
mfc = self.line.get_markerfacecolor()
|
||||
self.markerfacecolor = AgrLine.color_index(mfc)
|
||||
mec = self.line.get_markeredgecolor()
|
||||
self.markeredgecolor = AgrLine.color_index(mec)
|
||||
self.markeredgewidth = self.line.get_markeredgewidth() * self.width_scale
|
||||
self.markerfill = self.fillstyles.index(self.line.get_fillstyle())
|
||||
|
||||
def __init__(self, line, agr_axis):
|
||||
self.agr_axis = agr_axis
|
||||
self.line = line
|
||||
self.hidden = 'false'
|
||||
self.type = 'xy'
|
||||
|
||||
# run all get_ methods
|
||||
for d in dir(self):
|
||||
if d.startswith('get_'):
|
||||
getattr(self, d)()
|
||||
|
||||
sanitize_strings(self.__dict__)
|
||||
|
||||
def __str__(self):
|
||||
return self.fmt.format(**self.__dict__)
|
||||
|
||||
|
||||
class AgrAxis:
|
||||
fmt = """world {world}
|
||||
view {view}
|
||||
title {title}
|
||||
yaxes scale {yscale}
|
||||
yaxis tick major {yticks}
|
||||
xaxis label "{xlabel}"
|
||||
xaxis label place {xlabelpos}
|
||||
xaxis label char size {labelsize}
|
||||
xaxis tick major {xticks}
|
||||
xaxis ticklabel {xticklabel}
|
||||
xaxis ticklabel place {xticklabelpos}
|
||||
xaxis ticklabel char size {ticklabelsize}
|
||||
xaxes scale {xscale}
|
||||
yaxis label "{ylabel}"
|
||||
yaxis label place {ylabelpos}
|
||||
yaxis label char size {labelsize}
|
||||
yaxis ticklabel {yticklabel}
|
||||
yaxis ticklabel place {yticklabelpos}
|
||||
yaxis ticklabel char size {ticklabelsize}
|
||||
legend {legend}
|
||||
legend loctype world
|
||||
legend {legend_pos}
|
||||
legend char size {legend_fontsize}
|
||||
"""
|
||||
|
||||
def get_world(self):
|
||||
xmin, xmax = self.axis.get_xlim()
|
||||
ymin, ymax = self.axis.get_ylim()
|
||||
self.world = '{:.3}, {:.3}, {:.3}, {:.3}'.format(xmin, ymin, xmax, ymax)
|
||||
box = self.axis.get_position()
|
||||
fx, fy = self.axis.figure.get_size_inches()
|
||||
sx, sy = self.agr_figure.pagescale
|
||||
offx, offy = self.agr_figure.offset
|
||||
self.view = '{:.3}, {:.3}, {:.3}, {:.3}'.format(
|
||||
(box.xmin + offx)*sx, (box.ymin + offy)*sy,
|
||||
(box.xmax + offx)*sx, (box.ymax + offy)*sy
|
||||
)
|
||||
|
||||
def get_title(self):
|
||||
self.title = latex_to_xmgrace(self.axis.get_title())
|
||||
|
||||
def get_xyaxis(self):
|
||||
self.xlabel = latex_to_xmgrace(self.axis.get_xlabel())
|
||||
xpos = self.axis.xaxis.get_label_position()
|
||||
self.xlabelpos = 'normal' if xpos == 'bottom' else 'opposite'
|
||||
|
||||
self.ylabel = latex_to_xmgrace(self.axis.get_ylabel())
|
||||
ypos = self.axis.yaxis.get_label_position()
|
||||
self.ylabelpos = 'normal' if ypos == 'left' else 'opposite'
|
||||
|
||||
xsc = self.axis.get_xscale()
|
||||
self.xscale = 'Logarithmic' if 'log' in xsc else 'Normal'
|
||||
xticks = self.axis.get_xticks()
|
||||
if xsc == 'log':
|
||||
self.xticks = (xticks[1:] / xticks[:-1]).mean()
|
||||
else:
|
||||
self.xticks = (xticks[1:] - xticks[:-1]).mean()
|
||||
|
||||
self.xticklabel = 'on' if any([x.get_visible() for x in self.axis.get_xticklabels()]) else 'off'
|
||||
xpos = self.axis.xaxis.get_ticks_position()
|
||||
if xpos == 'both':
|
||||
self.xticklabelpos = 'both'
|
||||
elif xpos == 'top':
|
||||
self.xticklabelpos = 'opposite'
|
||||
else:
|
||||
self.xticklabelpos = 'normal'
|
||||
|
||||
xtlpos = set([x.get_position()[1] for x in self.axis.get_xticklabels() if x.get_visible()])
|
||||
if len(xtlpos) == 0:
|
||||
self.xticklabel = 'off'
|
||||
self.xticklabelpos = 'normal'
|
||||
elif len(xtlpos) == 1:
|
||||
self.xticklabel = 'on'
|
||||
self.xticklabelpos = 'opposite' if 1 in xtlpos else 'normal'
|
||||
else:
|
||||
self.xticklabel = 'on'
|
||||
self.xticklabelpos = 'both'
|
||||
|
||||
ytlpos = set([x.get_position()[0] for x in self.axis.get_yticklabels() if x.get_visible()])
|
||||
if len(ytlpos) == 0:
|
||||
self.yticklabel = 'off'
|
||||
self.yticklabelpos = 'normal'
|
||||
elif len(ytlpos) == 1:
|
||||
self.yticklabel = 'on'
|
||||
self.yticklabelpos = 'opposite' if 1 in ytlpos else 'normal'
|
||||
else:
|
||||
self.yticklabel = 'on'
|
||||
self.yticklabelpos = 'both'
|
||||
|
||||
ysc = self.axis.get_yscale()
|
||||
self.yscale = 'Logarithmic' if 'log' in ysc else 'Normal'
|
||||
yticks = self.axis.get_yticks()
|
||||
if ysc == 'log':
|
||||
self.yticks = (yticks[1:] / yticks[:-1]).mean()
|
||||
else:
|
||||
self.yticks = (yticks[1:] - yticks[:-1]).mean()
|
||||
|
||||
self.labelsize = self.axis.xaxis.get_label().get_fontsize() * self.agr_figure.fontscale
|
||||
fs = self.axis.xaxis.get_ticklabels()[0].get_fontsize()
|
||||
self.ticklabelsize = fs * self.agr_figure.fontscale
|
||||
|
||||
def get_legend(self):
|
||||
leg = self.axis.get_legend()
|
||||
if leg is None:
|
||||
self.legend = 'off'
|
||||
self.legend_pos = '0, 0'
|
||||
self.legend_fontsize = 0
|
||||
else:
|
||||
self.legend = 'on'
|
||||
for lbl, line in zip(leg.get_texts(), leg.get_lines()):
|
||||
pass
|
||||
pos = get_world_coords(leg)
|
||||
self.legend_pos = '{:.3f}, {:.3f}'.format(*pos.diagonal())
|
||||
self.legend_fontsize = leg.get_texts()[0].get_fontsize() * self.agr_figure.fontscale
|
||||
|
||||
def __init__(self, axis, agr_fig):
|
||||
self.agr_figure = agr_fig
|
||||
self.axis = axis
|
||||
|
||||
# run all get_ methods
|
||||
for d in dir(self):
|
||||
if d.startswith('get_'):
|
||||
getattr(self, d)()
|
||||
|
||||
sanitize_strings(self.__dict__)
|
||||
self.lines = {'s{}'.format(i): AgrLine(l, self) for i, l in enumerate(axis.lines)}
|
||||
self.texts = [AgrText(t, self) for t in self.axis.texts]
|
||||
|
||||
def __str__(self):
|
||||
o = self.fmt.format(**self.__dict__)
|
||||
for k, l in self.lines.items():
|
||||
o += textwrap.indent(str(l), prefix=k + ' ')
|
||||
for txt in self.texts:
|
||||
o += 'with string\n'
|
||||
o += textwrap.indent(str(txt), prefix=' ')
|
||||
return o
|
||||
|
||||
|
||||
class AgrFigure:
|
||||
dpi = 96
|
||||
fontscale = 0.5
|
||||
fmt = """@version 50125
|
||||
@page size {page}
|
||||
"""
|
||||
data_fmt = "@target {target}\n{data}\n"""
|
||||
|
||||
def collect_data(self):
|
||||
d = {}
|
||||
for ia, ax in self.axes.items():
|
||||
for il, ln in ax.lines.items():
|
||||
d['{}.{}'.format(ia.upper(), il.upper())] = ln.data
|
||||
return d
|
||||
|
||||
def get_figprops(self):
|
||||
fx, fy = self.figure.get_size_inches()
|
||||
scx, scy = (1 + self.offset)
|
||||
# scy = (1 + self.offset_vertical)
|
||||
self.page = '{}, {}'.format(int(scx * self.dpi * fx), int(scy * self.dpi * fy))
|
||||
self.fontscale = AgrFigure.fontscale / min(fx * scx, fy * scy)
|
||||
|
||||
self.pagescale = np.array([fx, fy]) / min(fx * scx, fy * scy)
|
||||
|
||||
def __init__(self, figure, offset_horizontal=0, offset_vertical=0, convert_latex=True):
|
||||
tex2grace.do_latex_conversion = convert_latex
|
||||
self.figure = figure
|
||||
self.offset = np.array([offset_horizontal, offset_vertical])
|
||||
# make sure to draw the figure...
|
||||
canv = backend_agg.FigureCanvasAgg(figure)
|
||||
canv.draw()
|
||||
|
||||
# run all get_ methods
|
||||
for d in dir(self):
|
||||
if d.startswith('get_'):
|
||||
getattr(self, d)()
|
||||
sanitize_strings(self.__dict__)
|
||||
|
||||
self.axes = {'g{}'.format(i): AgrAxis(ax, self) for i, ax in enumerate(self.figure.axes)}
|
||||
|
||||
def __str__(self):
|
||||
o = self.fmt.format(**self.__dict__)
|
||||
|
||||
for i, col in enumerate(AgrLine.colors):
|
||||
# in matplotlib-1.5 to_rgb can not handle 'none', this was fixed in 2.0
|
||||
rgb = [int(x * 255) for x in AgrLine.cc.to_rgba(col)[:3]]
|
||||
o += '@map color {i} to ({rgb[0]}, {rgb[1]}, {rgb[2]}), "{col}"\n'.format(i=i, rgb=rgb, col=col)
|
||||
|
||||
for k, ax in self.axes.items():
|
||||
o += textwrap.indent("on\nhidden false\ntype XY\nstacked false\n", prefix='@{} '.format(k))
|
||||
o += '@with {}\n'.format(k)
|
||||
o += textwrap.indent(str(ax), prefix='@ ')
|
||||
|
||||
for k, d in self.collect_data().items():
|
||||
o += self.data_fmt.format(target=k, data=d)
|
||||
|
||||
return o
|
||||
|
||||
|
||||
def saveagr(fname, figure=None, offset_x=0, offset_y=0, convert_latex=True):
|
||||
"""
|
||||
Save figure as xmgrace plot.
|
||||
|
||||
If no figure is provided, this will save the current figure.
|
||||
|
||||
Args:
|
||||
fname: Filename of the agr plot
|
||||
figure (opt.): Matplotlib figure to save, if None gcf() is used.
|
||||
offset_x, offset_y (opt.): Add an offest in x or y direction to the xmgrace plot.
|
||||
convert_latex (opt.): If latex strings will be converted to xmgrace.
|
||||
"""
|
||||
if figure is None:
|
||||
figure = plt.gcf()
|
||||
with open(fname, 'w') as f:
|
||||
af = AgrFigure(figure, offset_horizontal=offset_x, offset_vertical=offset_y,
|
||||
convert_latex=convert_latex)
|
||||
f.write(str(af))
|
201
pygrace/str2format.py
Normal file
201
pygrace/str2format.py
Normal file
@ -0,0 +1,201 @@
|
||||
from __future__ import print_function
|
||||
import re
|
||||
|
||||
small_greek = {'alpha': u'\u03b1',
|
||||
'beta': u'\u03b2',
|
||||
'gamma': u'\u03b3',
|
||||
'delta': u'\u03b4',
|
||||
'epsilon': u'\u03b5',
|
||||
'zeta': u'\u03b6',
|
||||
'eta': u'\u03b7',
|
||||
'theta': u'\u03b8',
|
||||
'iota': u'\u03b9',
|
||||
'kappa': u'\u03ba',
|
||||
'lambda': u'\u03bb',
|
||||
'mu': u'\u03bc',
|
||||
'nu': u'\u03bd',
|
||||
'xi': u'\u03be',
|
||||
'omicron': u'\u03bf',
|
||||
'pi': u'\u03c0',
|
||||
'rho': u'\u03c1',
|
||||
'varsigma': u'\u03c2',
|
||||
'sigma': u'\u03c3',
|
||||
'tau': u'\u03c4',
|
||||
'ypsilon': u'\u03c5',
|
||||
'phi': u'\u03c6',
|
||||
'chi': u'\u03c7',
|
||||
'psi': u'\u03c8',
|
||||
'omega': u'\u03c9'
|
||||
}
|
||||
|
||||
big_greek = {'Alpha': u'\u0391',
|
||||
'Beta': u'\u0392',
|
||||
'Gamma': u'\u0393',
|
||||
'Delta': u'\u0394',
|
||||
'Epsilon': u'\u0395',
|
||||
'Zeta': u'\u0396',
|
||||
'Eta': u'\u0397',
|
||||
'Theta': u'\u0398',
|
||||
'Iota': u'\u0399',
|
||||
'Kappa': u'\u039a',
|
||||
'Lambda': u'\u039b',
|
||||
'Mu': u'\u039c',
|
||||
'Nu': u'\u039d',
|
||||
'Xi': u'\u039e',
|
||||
'Omicron': u'\u039f',
|
||||
'Pi': u'\u03a0',
|
||||
'Rho': u'\u03a1',
|
||||
'Varsigma': u'\u03a2',
|
||||
'Sigma': u'\u03a3',
|
||||
'Tau': u'\u03a4',
|
||||
'Ypsilon': u'\u03a5',
|
||||
'Phi': u'\u03a6',
|
||||
'Chi': u'\u03a7',
|
||||
'Psi': u'\u03a8',
|
||||
'Omega': u'\u03a9'
|
||||
}
|
||||
|
||||
special_chars = {'infty': u'\u221e',
|
||||
'int': u'\u222b',
|
||||
'perp': u'\u27c2',
|
||||
'para': u'\u2225',
|
||||
'leftarrow': u'\u21d0',
|
||||
'rightarrow': u'\u21d2',
|
||||
'leftrightarrow': u'\u21d4',
|
||||
'\n': '<br>',
|
||||
'*': u'\u00b7',
|
||||
'plmin': u'\u00b1'}
|
||||
|
||||
small_greek_grace = {'alpha': r'\f{Symbol}a\f{}',
|
||||
'beta': r'\f{Symbol}b\f{}',
|
||||
'gamma': r'\f{Symbol}g\f{}',
|
||||
'delta': r'\f{Symbol}d\f{}',
|
||||
'epsilon': r'\f{Symbol}e\f{}',
|
||||
'zeta': r'\f{Symbol}z\f{}',
|
||||
'eta': r'\f{Symbol}h\f{}',
|
||||
'theta': r'\f{Symbol}q\f{}',
|
||||
'iota': r'\f{Symbol}i\f{}',
|
||||
'kappa': r'\f{Symbol}k\f{}',
|
||||
'lambda': r'\f{Symbol}l\f{}',
|
||||
'mu': r'\f{Symbol}m\f{}',
|
||||
'nu': r'\f{Symbol}n\f{}',
|
||||
'xi': r'\f{Symbol}x\f{}',
|
||||
'omicron': r'\f{Symbol}o\f{}',
|
||||
'pi': r'\f{Symbol}p\f{}',
|
||||
'rho': r'\f{Symbol}r\f{}',
|
||||
'sigma': r'\f{Symbol}s\f{}',
|
||||
'tau': r'\f{Symbol}t\f{}',
|
||||
'ypsilon': r'\f{Symbol}u\f{}',
|
||||
'phi': r'\f{Symbol}f\f{}',
|
||||
'chi': r'\f{Symbol}c\f{}',
|
||||
'psi': r'\f{Symbol}y\f{}',
|
||||
'omega': r'\f{Symbol}w\f{}'
|
||||
}
|
||||
|
||||
big_greek_grace = {'Alpha': r'\f{Symbol}A\f{}',
|
||||
'Beta': r'\f{Symbol}B\f{}',
|
||||
'Gamma': r'\f{Symbol}g\f{}',
|
||||
'Delta': r'\f{Symbol}D\f{}',
|
||||
'Epsilon': r'\f{Symbol}E\f{}',
|
||||
'Zeta': r'\f{Symbol}Z\f{}',
|
||||
'Eta': r'\f{Symbol}H\f{}',
|
||||
'Theta': r'\f{Symbol}Q\f{}',
|
||||
'Iota': r'\f{Symbol}I\f{}',
|
||||
'Kappa': r'\f{Symbol}K\f{}',
|
||||
'Lambda': r'\f{Symbol}L\f{}',
|
||||
'Mu': r'\f{Symbol}M\f{}',
|
||||
'Nu': r'\f{Symbol}N\f{}',
|
||||
'Xi': r'\f{Symbol}X\f{}',
|
||||
'Omicron': r'\f{Symbol}O\f{}',
|
||||
'Pi': r'\f{Symbol}P\f{}',
|
||||
'Rho': r'\f{Symbol}R\f{}',
|
||||
'Sigma': r'\f{Symbol}S\f{}',
|
||||
'Tau': r'\f{Symbol}T\f{}',
|
||||
'Ypsilon': r'\f{Symbol}U\f{}',
|
||||
'Phi': r'\f{Symbol}F\f{}',
|
||||
'Chi': r'\f{Symbol}C\f{}',
|
||||
'Psi': r'\f{Symbol}U\f{}',
|
||||
'Omega': r'\f{Symbol}Q\f{}'
|
||||
}
|
||||
|
||||
special_chars_grace = {'infty': r'\f{Symbol}\c%\f{}'}
|
||||
|
||||
|
||||
full_chars = dict()
|
||||
for d in [big_greek, small_greek, special_chars]:
|
||||
full_chars.update(d)
|
||||
|
||||
full_char_reverse = {y: x for x, y in full_chars.items()}
|
||||
|
||||
|
||||
def _replace_unicode(text):
|
||||
for k, v in full_chars.items():
|
||||
text = text.replace(k, v)
|
||||
return text
|
||||
|
||||
|
||||
def _replacesub(text):
|
||||
sub_pattern_1 = re.compile('(_\{\S*?\})')
|
||||
sub_pattern_2 = re.compile('_\S')
|
||||
for s in sub_pattern_1.findall(text):
|
||||
text = text.replace(s, u'<sub>{0:}</sub>'.format(s[2:-1]))
|
||||
for s in sub_pattern_2.findall(text):
|
||||
text = text.replace(s, u'<sub>{0:}</sub>'.format(s[1:]))
|
||||
return text
|
||||
|
||||
|
||||
def _replacesup(text):
|
||||
sup_pattern_1 = re.compile('\^\{\S*?\}')
|
||||
sup_pattern_2 = re.compile('\^\S')
|
||||
for s in sup_pattern_1.findall(text):
|
||||
text = text.replace(s, u'<sup>{0:}</sup>'.format(s.strip()[2:-1]))
|
||||
for s in sup_pattern_2.findall(text):
|
||||
text = text.replace(s, u'<sup>{0:}</sup>'.format(s.strip()[1:]))
|
||||
return text
|
||||
|
||||
|
||||
def str2format(text):
|
||||
text = _replacesub(text)
|
||||
text = _replacesup(text)
|
||||
text = _replace_unicode(text)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def format2str(text):
|
||||
for k, v in full_char_reverse.items():
|
||||
text = text.replace(k, v)
|
||||
for k, v in [('<sub>', '_{'), ('</sub>', '}'), ('<sup>', '^{'), ('</sup>', '}')]:
|
||||
text = text.replace(k, v)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def format2grace(text):
|
||||
text = format2str(text)
|
||||
for k, v in [('_{', r'\s'), ('}', r'\N'), ('^{', r'\S'), ('}', r'\N')]:
|
||||
text = text.replace(k, v)
|
||||
for k, v in big_greek_grace.items():
|
||||
text = text.replace(k, v)
|
||||
for k, v in small_greek_grace.items():
|
||||
text = text.replace(k, v)
|
||||
for k, v in special_chars_grace.items():
|
||||
text = text.replace(k, v)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def grace2str(text):
|
||||
text = text.replace(r'\s', '_{').replace('\S', '^{')
|
||||
text = text.replace(r'\N', '}')
|
||||
text = text.replace(r'\f{}', '')
|
||||
|
||||
return text
|
||||
|
||||
if __name__ == '__main__':
|
||||
stringtext = 'M_{infty}[1-alpha exp(-x/T_{1,1})^{beta_1}]'
|
||||
print('Input:\t{}'.format(stringtext))
|
||||
print(u'Output:\t{}'.format(str2format(stringtext)))
|
||||
print('Input 2:\t{}'.format(format2str(str2format(stringtext))))
|
||||
print('Grace:\t{}'.format(format2grace(str2format(stringtext))))
|
||||
print(u'Output 2:\t{}'.format(str2format(format2str(str2format(stringtext)))))
|
37
pygrace/tex2grace.py
Normal file
37
pygrace/tex2grace.py
Normal file
@ -0,0 +1,37 @@
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import re
|
||||
|
||||
patterns = OrderedDict()
|
||||
patterns['\$'] = ''
|
||||
patterns[r'\\math(?:tt|sf|it)({.+})'] = r'\1'
|
||||
patterns[r'\^({.+}|.)'] = r'\S\1\N'
|
||||
patterns[r'\_({.+}|.)'] = r'\s\1\N'
|
||||
# Remove any left over latex groups as the last step
|
||||
patterns[r'[{}]'] = ''
|
||||
|
||||
# Greek letters in xmgrace are written by switching to symbol-font:
|
||||
# "\x a\f{}" will print an alpha and switch back to normal font
|
||||
greek = {
|
||||
'alpha': 'a', 'beta': 'b', 'gamma': 'g', 'delta': 'd', 'epsilon': 'e', 'zeta': 'z',
|
||||
'eta': 'h', 'theta': 'q', 'iota': 'i', 'kappa': 'k', 'lambda': 'l', 'mu': 'm',
|
||||
'nu': 'n', 'xi': 'x', 'omicron': 'o', 'pi': 'p', 'rho': 'r', 'sigma': 's',
|
||||
'tau': 't', 'upsilon': 'u', 'phi': 'f', 'chi': 'c', 'psi': 'y', 'omega': 'w',
|
||||
'varphi': 'j', 'varepsilon': 'e', 'vartheta': 'J', 'varrho': 'r'
|
||||
}
|
||||
for latex, xmg in greek.items():
|
||||
patt = r'\\{}'.format(latex)
|
||||
repl = r'\\x {}\\f{{}}'.format(xmg)
|
||||
patterns[patt] = repl
|
||||
|
||||
|
||||
do_latex_conversion = True
|
||||
|
||||
|
||||
def latex_to_xmgrace(string):
|
||||
logging.debug('Convert to xmgrace: {}'.format(string))
|
||||
if do_latex_conversion and '$' in string:
|
||||
for patt, repl in patterns.items():
|
||||
string = re.sub(patt, repl, string)
|
||||
logging.debug('To -> {}'.format(string))
|
||||
return string
|
Reference in New Issue
Block a user