from . import navbar as navbar\r
from . import panel as panel\r
from .panel.propertyeditor import prop_from_argument, prop_from_setting\r
-from . import prettyformat as prettyformat\r
from . import statusbar as statusbar\r
\r
\r
Setting(section=self.setting_section, option='plot legend',\r
value=True,\r
help='Enable/disable the plot legend.'),\r
- Setting(section=self.setting_section, option='plot x format',\r
- value='None',\r
- help='Display format for plot x values.'),\r
- Setting(section=self.setting_section, option='plot y format',\r
- value='None',\r
- help='Display format for plot y values.'),\r
- Setting(section=self.setting_section, option='plot zero',\r
- value=0,\r
- help='Select "0" vs. e.g. "0.00" for plot axes?'),\r
+ Setting(section=self.setting_section, option='plot SI format',\r
+ value='True',\r
+ help='Enable/disable SI plot axes numbering.'),\r
+ Setting(section=self.setting_section, option='plot decimals',\r
+ value=2,\r
+ help='Number of decimal places to show if "plot SI format" is enabled.'),\r
Setting(section=self.setting_section, option='folders-workdir',\r
value='.',\r
help='This should probably go...'),\r
+++ /dev/null
-#!/usr/bin/env python\r
-\r
-'''\r
-curve.py\r
-\r
-Curve and related classes for Hooke.\r
-\r
-Copyright 2010 by Dr. Rolf Schmidt (Concordia University, Canada)\r
-\r
-This program is released under the GNU General Public License version 2.\r
-'''\r
-\r
-from matplotlib.ticker import Formatter\r
-import lib.prettyformat\r
-\r
-class Curve(object):\r
-\r
- def __init__(self):\r
- self.color = 'blue'\r
- self.decimals = Decimals()\r
- self.destination = Destination()\r
- self.label = ''\r
- self.legend = False\r
- self.linewidth = 1\r
- self.prefix = Prefix()\r
- self.size = 0.5\r
- self.style = 'plot'\r
- self.title = ''\r
- self.units = Units()\r
- self.visible = True\r
- self.x = []\r
- self.y = []\r
-\r
-\r
-class Data(object):\r
-\r
- def __init__(self):\r
- self.x = []\r
- self.y = []\r
-\r
-\r
-class Decimals(object):\r
-\r
- def __init__(self):\r
- self.x = 2\r
- self.y = 2\r
-\r
-\r
-class Destination(object):\r
-\r
- def __init__(self):\r
- self.column = 1\r
- self.row = 1\r
-\r
-\r
-class Prefix(object):\r
-\r
- def __init__(self):\r
- self.x = 'n'\r
- self.y = 'p'\r
-\r
-\r
-class PrefixFormatter(Formatter):\r
- '''\r
- Formatter (matplotlib) class that uses power prefixes.\r
- '''\r
- def __init__(self, decimals=2, prefix='n', use_zero=True):\r
- self.decimals = decimals\r
- self.prefix = prefix\r
- self.use_zero = use_zero\r
-\r
- def __call__(self, x, pos=None):\r
- 'Return the format for tick val *x* at position *pos*'\r
- if self.use_zero:\r
- if x == 0:\r
- return '0'\r
- multiplier = lib.prettyformat.get_exponent(self.prefix)\r
- decimals_str = '%.' + str(self.decimals) + 'f'\r
- return decimals_str % (x / (10 ** multiplier))\r
-\r
-\r
-class Units(object):\r
-\r
- def __init__(self):\r
- self.x = ''\r
- self.y = ''\r
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas\r
from matplotlib.backends.backend_wx import NavigationToolbar2Wx as NavToolbar\r
from matplotlib.figure import Figure\r
+from matplotlib.ticker import Formatter\r
import wx\r
\r
from ....util.callback import callback, in_callback\r
+from ....util.si import ppSI, split_data_label\r
from . import Panel\r
\r
\r
+class HookeFormatter (Formatter):\r
+ """:class:`matplotlib.ticker.Formatter` using SI prefixes.\r
+ """\r
+ def __init__(self, unit='', decimals=2):\r
+ self.decimals = decimals\r
+ self.unit = unit\r
+\r
+ def __call__(self, x, pos=None):\r
+ """Return the format for tick val `x` at position `pos`.\r
+ """\r
+ if x == 0:\r
+ return '0'\r
+ return ppSI(value=x, unit=self.unit, decimals=self.decimals)\r
+\r
+\r
class PlotPanel (Panel, wx.Panel):\r
"""UI for graphical curve display.\r
"""\r
#self.SetStatusText(coordinateString)\r
\r
def _resize_canvas(self):\r
- print 'resizing'\r
w,h = self.GetClientSize()\r
tw,th = self._c['toolbar'].GetSizeTuple()\r
dpi = float(self._c['figure'].get_dpi())\r
self.update(config=config)\r
\r
def update(self, config={}):\r
- print 'updating'\r
- x_format = config['plot x format']\r
- y_format = config['plot y format']\r
- zero = config['plot zero']\r
+ x_name = 'z piezo (m)'\r
+ y_name = 'deflection (m)'\r
\r
self._c['figure'].clear()\r
self._c['figure'].suptitle(\r
fontsize=12)\r
axes = self._c['figure'].add_subplot(1, 1, 1)\r
\r
-# if x_format != 'None':\r
-# f = lib.curve.PrefixFormatter(curve.decimals.x, curve.prefix.x, use_zero)\r
-# axes.xaxis.set_major_formatter(f)\r
-# if y_format != 'None':\r
-# f = lib.curve.PrefixFormatter(curve.decimals.y, curve.prefix.y, use_zero)\r
-# axes.yaxis.set_major_formatter(f)\r
-\r
- x_name = 'z piezo (m)'\r
- y_name = 'deflection (m)'\r
- #axes.set_xlabel(x_name)\r
- #axes.set_ylabel(y_name)\r
+ if config['plot si format'] == 'True': # TODO: config should convert\r
+ d = int(config['plot decimals']) # TODO: config should convert\r
+ x_n, x_unit = split_data_label(x_name)\r
+ y_n, y_unit = split_data_label(y_name)\r
+ fx = HookeFormatter(decimals=d, unit=x_unit)\r
+ axes.xaxis.set_major_formatter(fx)\r
+ fy = HookeFormatter(decimals=d, unit=y_unit)\r
+ axes.yaxis.set_major_formatter(fy)\r
+ axes.set_xlabel(x_n)\r
+ axes.set_ylabel(y_n)\r
+ else:\r
+ axes.set_xlabel(x_name)\r
+ axes.set_ylabel(y_name)\r
\r
self._c['figure'].hold(True)\r
for i,data in enumerate(self._curve.data):\r
if config['plot legend'] == 'True': # HACK: config should convert\r
axes.legend(loc='best')\r
self._c['canvas'].draw()\r
+\r
+# LocalWords: matplotlib\r
+++ /dev/null
-# Copyright\r
-\r
-"""Simple Python function to format values with nice prefixes.\r
-"""\r
-\r
-import math\r
-from numpy import isnan\r
-\r
-\r
-def pretty_format(value, unit='', decimals=-1, multiplier=0, leading_spaces=False):\r
- if value == 0:\r
- return '0'\r
- if isnan(value):\r
- return 'NaN'\r
-\r
- output_str = ''\r
- leading_spaces_int = 0\r
- if leading_spaces:\r
- leading_spaces_int = 5\r
- #automatic setting of multiplier\r
- if multiplier == 0:\r
- multiplier=get_multiplier(value)\r
- unit_str = ''\r
- if unit:\r
- unit_str = ' ' + get_prefix(multiplier) + unit\r
- if decimals >= 0:\r
- format_str = '% ' + repr(leading_spaces_int + decimals) + '.' + repr(decimals) + 'f'\r
- output_str = format_str % (value / multiplier) + unit_str\r
- else:\r
- output_str = str(value / multiplier) + unit_str\r
-\r
- if decimals < 0:\r
- output_str = str(value / multiplier) + ' ' + get_prefix(value / multiplier) + unit\r
-\r
- if leading_spaces_int == 0:\r
- output_str = output_str.lstrip()\r
-\r
- return output_str\r
-\r
-def get_multiplier(value):\r
- return pow(10, get_power(value))\r
-\r
-def get_power(value):\r
- if value != 0 and not isnan(value):\r
- #get the log10 from value (make sure the value is not negative)\r
- value_temp = math.floor(math.log10(math.fabs(value)))\r
- #reduce the log10 to a multiple of 3 and return it\r
- return value_temp - (value_temp % 3)\r
- else:\r
- return 0\r
-\r
-def get_exponent(prefix):\r
- #set up a dictionary to find the exponent\r
- exponent = {\r
- 'Y': 24,\r
- 'Z': 21,\r
- 'E': 18,\r
- 'P': 15,\r
- 'T': 12,\r
- 'G': 9,\r
- 'M': 6,\r
- 'k': 3,\r
- '': 0,\r
- 'm': -3,\r
- u'\u00B5': -6,\r
- 'n': -9,\r
- 'p': -12,\r
- 'f': -15,\r
- 'a': -18,\r
- 'z': -21,\r
- 'y': -24,\r
- }\r
- if exponent.has_key(prefix):\r
- return exponent[prefix]\r
- else:\r
- return 1\r
-\r
-def get_prefix(value):\r
- #set up a dictionary to find the prefix\r
- prefix = {\r
- 24: lambda: 'Y',\r
- 21: lambda: 'Z',\r
- 18: lambda: 'E',\r
- 15: lambda: 'P',\r
- 12: lambda: 'T',\r
- 9: lambda: 'G',\r
- 6: lambda: 'M',\r
- 3: lambda: 'k',\r
- 0: lambda: '',\r
- -3: lambda: 'm',\r
- -6: lambda: u'\u00B5',\r
- -9: lambda: 'n',\r
- -12: lambda: 'p',\r
- -15: lambda: 'f',\r
- -18: lambda: 'a',\r
- -21: lambda: 'z',\r
- -24: lambda: 'y',\r
- }\r
- if value != 0 and not isnan(value):\r
- #get the log10 from value\r
- value_temp = math.floor(math.log10(math.fabs(value)))\r
- else:\r
- value_temp = 0\r
- #reduce the log10 to a multiple of 3 and create the return string\r
- return prefix.get(value_temp - (value_temp % 3))()\r
-\r
-'''\r
-test_value=-2.4115665714484597e-008\r
-print 'Value: '+str(test_value)+')'\r
-print 'pretty_format example (value, unit)'\r
-print pretty_format(test_value, 'N')\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals)'\r
-print pretty_format(test_value, 'N', 3)\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals, multiplier)'\r
-print pretty_format(test_value, 'N', 5, 0.000001)\r
-print'-----------------------'\r
-print 'pretty_format example (value, unit, decimals, multiplier, leading spaces)'\r
-print pretty_format(0.0166276297705, 'N', 3, 0.001, True)\r
-print pretty_format(0.00750520813323, 'N', 3, 0.001, True)\r
-print pretty_format(0.0136453282825, 'N', 3, 0.001, True)\r
-'''\r
-'''\r
-#to output a scale:\r
-#choose any value on the axis and find the multiplier and prefix for it\r
-#use those to format the rest of the scale\r
-#as values can span several orders of magnitude, you have to decide what units to use\r
-\r
-#tuple of values:\r
-scale_values=0.000000000985, 0.000000001000, 0.000000001015\r
-#use this element (change to 1 or 2 to see the effect on the scale and label)\r
-index=0\r
-#get the multiplier from the value at index\r
-multiplier=get_multiplier(scale_values[index])\r
-print '\nScale example'\r
-decimals=3\r
-#print the scale\r
-for aValue in scale_values: print decimalFormat(aValue/multiplier, decimals),\r
-#print the scale label using the value at index\r
-print '\n'+get_prefix(scale_values[index])+'N'\r
-'''\r
--- /dev/null
+# Copyright\r
+\r
+"""Define functions for handling numbers in SI notation.\r
+\r
+Notes\r
+-----\r
+To output a scale, choose any value on the axis and find the\r
+multiplier and prefix for it. Use those to format the rest of the\r
+scale. As values can span several orders of magnitude, you have\r
+to decide what units to use.\r
+\r
+>>> xs = (985e-12, 1e-9, 112358e-12)\r
+\r
+Get the power from the first (or last, or middle, ...) value\r
+\r
+>>> p = get_power(xs[0])\r
+>>> for x in xs:\r
+... print ppSI(x, decimals=2, power=p)\r
+985.00 p\r
+1000.00 p\r
+112358.00 p\r
+>>> print prefix_from_value(xs[0]) + 'N'\r
+pN\r
+"""\r
+\r
+import math\r
+from numpy import isnan\r
+import re\r
+\r
+\r
+PREFIX = {\r
+ 24: 'Y',\r
+ 21: 'Z',\r
+ 18: 'E',\r
+ 15: 'P',\r
+ 12: 'T',\r
+ 9: 'G',\r
+ 6: 'M',\r
+ 3: 'k',\r
+ 0: '',\r
+ -3: 'm',\r
+ -6: u'\u00B5',\r
+ -9: 'n',\r
+ -12: 'p',\r
+ -15: 'f',\r
+ -18: 'a',\r
+ -21: 'z',\r
+ -24: 'y',\r
+ }\r
+"""A dictionary of SI prefixes from 10**24 to 10**-24.\r
+\r
+Examples\r
+--------\r
+>>> PREFIX[0]\r
+''\r
+>>> PREFIX[6]\r
+'M'\r
+>>> PREFIX[-9]\r
+'n'\r
+"""\r
+\r
+_DATA_LABEL_REGEXP = re.compile('^([^(]*[^ ]) ?\(([^)]*)\)$')\r
+"""Used by :func:`data_label_unit`.\r
+"""\r
+\r
+\r
+def ppSI(value, unit='', decimals=None, power=None, pad=False):\r
+ """Pretty-print `value` in SI notation.\r
+\r
+ The current implementation ignores `pad` if `decimals` is `None`.\r
+\r
+ Examples\r
+ --------\r
+ >>> x = math.pi * 1e-8\r
+ >>> print ppSI(x, 'N')\r
+ 31.415927 nN\r
+ >>> print ppSI(x, 'N', 3)\r
+ 31.416 nN\r
+ >>> print ppSI(x, 'N', 4, power=-12)\r
+ 31415.9265 pN\r
+ >>> print ppSI(x, 'N', 5, pad=True)\r
+ 31.41593 nN\r
+\r
+ If you want the decimal indented by six spaces with `decimal=2`,\r
+ `pad` should be the sum of\r
+\r
+ * 6 (places before the decimal point)\r
+ * 1 (length of the decimal point)\r
+ * 2 (places after the decimal point)\r
+\r
+ >>> print ppSI(-x, 'N', 2, pad=(6+1+2))\r
+ -31.42 nN\r
+ """\r
+ if value == 0:\r
+ return '0'\r
+ if isnan(value):\r
+ return 'NaN'\r
+\r
+ if power == None: # auto-detect power\r
+ power = get_power(value)\r
+\r
+ if decimals == None:\r
+ format = lambda n: '%f' % n\r
+ else:\r
+ if pad == False: # no padding\r
+ format = lambda n: '%.*f' % (decimals, n) \r
+ else:\r
+ if pad == True: # auto-generate pad\r
+ # 1 for ' ', 1 for '-', 3 for number, 1 for '.', and decimals.\r
+ pad = 6 + decimals\r
+ format = lambda n: '%*.*f' % (pad, decimals, n)\r
+ return '%s %s%s' % (format(value / pow(10,power)), PREFIX[power], unit)\r
+\r
+\r
+def get_power(value):\r
+ """Return the SI power for which `0 <= |value|/10**pow < 1000`. \r
+ \r
+ Exampes\r
+ -------\r
+ >>> get_power(0)\r
+ 0\r
+ >>> get_power(123)\r
+ 0\r
+ >>> get_power(-123)\r
+ 0\r
+ >>> get_power(1e8)\r
+ 6\r
+ >>> get_power(1e-16)\r
+ -18\r
+ """\r
+ if value != 0 and not isnan(value):\r
+ # get log10(|value|)\r
+ value_temp = math.floor(math.log10(math.fabs(value)))\r
+ # reduce the log10 to a multiple of 3\r
+ return int(value_temp - (value_temp % 3))\r
+ else:\r
+ return 0\r
+\r
+def prefix_from_value(value):\r
+ """Determine the SI power of `value` and return its prefix.\r
+\r
+ Examples\r
+ --------\r
+ >>> prefix_from_value(0)\r
+ ''\r
+ >>> prefix_from_value(1e10)\r
+ 'G'\r
+ """\r
+ return PREFIX[get_power(value)]\r
+\r
+def split_data_label(label):\r
+ """Split `curve.data[i].info['name']` labels into `(name, unit)`.\r
+\r
+ Examples\r
+ --------\r
+ >>> split_data_label('z piezo (m)')\r
+ ('z piezo', 'm')\r
+ >>> split_data_label('deflection (N)')\r
+ ('deflection', 'N')\r
+ """\r
+ m = _DATA_LABEL_REGEXP.match(label)\r
+ assert m != None, label\r
+ return m.groups()\r