content: import re
  import os
  import json
  import importlib
  import timeit
  import inspect
  import collections
  from enum import Enum
  from pathlib import Path
  
  import numpy as np
  from matplotlib.cm import cmap_d
  import wx
  
  import app
  import fileio
  from classes.om.base.manager import ObjectManager
  from app import log
  
  
  class GripyBitmap(wx.Bitmap):
  
      def __init__(self, path_to_bitmap=None):
          if path_to_bitmap is None:
              super().__init__()
              return
          if os.path.exists(path_to_bitmap):
              full_file_name = path_to_bitmap
          elif os.path.exists(os.path.join(app.ICONS_PATH, \
                                           path_to_bitmap)):
              full_file_name = os.path.join(app.ICONS_PATH, path_to_bitmap)
          else:
              raise Exception('ERROR: Wrong bitmap path [{}, {}].'.format( \
                  app.ICONS_PATH, path_to_bitmap)
              )
          super().__init__(full_file_name)
  
  
  class GripyIcon(wx.Icon):
  
      def __init__(self, path_to_bitmap=None, type_=wx.BITMAP_TYPE_ANY):
  
          # print(PurePath(app.ICONS_PATH, path_to_bitmap), 'r')
  
          if path_to_bitmap is not None:
              if Path(path_to_bitmap).exists():
                  pass
              elif Path(app.ICONS_PATH, path_to_bitmap).exists():
                  path_to_bitmap = Path(app.ICONS_PATH, path_to_bitmap)
              else:
                  raise Exception('ERROR: Wrong bitmap path.')
          super().__init__(path_to_bitmap, type_)
  
  
  def calc_well_time_from_depth(event, well_uid):
      OM = ObjectManager()
      well = OM.get(well_uid)
      vp = None
      for log_obj in OM.list('log', well.uid):
          if log_obj.datatype == 'Velocity':
              vp = log_obj
              break
      if vp is None:
          raise Exception('ERROR [calc_prof_tempo]: Vp log not found.')
      index_set = OM.get(vp.index_set_uid)
      md = index_set.get_z_axis_indexes_by_type('MD')[0]
      #
      if md.data[0] != 0.0:
          return
      owt = [0.0]
      #
      for idx in range(1, len(md.data)):
          if vp.data[idx - 1] == np.nan:
              raise Exception('ERROR [calc_prof_tempo]: Found np.nan on Vp[{}] '.format(idx - 1))
          diff_prof = md.data[idx] - md.data[idx - 1]
          value = (float(diff_prof) / vp.data[idx - 1]) * 1000.0  # To milliseconds
          value = owt[idx - 1] + value
          owt.append(value)
      #    
      owt = np.array(owt)
      twt = owt * 2.0
      #
      print('\nOWT:', owt)
      #
      owt_index = OM.new('data_index', 0, 'One Way Time', 'TIME', 'ms', data=owt)
      OM.add(owt_index, index_set.uid)
      #
      twt_index = OM.new('data_index', 0, 'Two Way Time', 'TWT', 'ms', data=twt)
      OM.add(twt_index, index_set.uid)
      #          
  
  
  def load_segy(event, filename, new_obj_name='', comparators_list=None,
                iline_byte=9, xline_byte=21, offset_byte=37, tid='seismic',
                datatype='amplitude', parentuid=None):
      OM = ObjectManager()
      disableAll = wx.WindowDisabler()
      wait = wx.BusyInfo("Loading SEG-Y file...")
      #
      try:
          print("\nLoading SEG-Y file...")
          segy_file = fileio.segy.SEGYFile(filename)
          # segy_file.print_dump()
          # """
          segy_file.read(comparators_list)
          segy_file.organize_3D_data(iline_byte, xline_byte, offset_byte)
          #
          print('segy_file.traces.shape:', segy_file.traces.shape)
          #
  
          #
          seis_like_obj = OM.new(tid, segy_file.traces, name=new_obj_name,
                                 datatype=datatype
                                 )
          if not OM.add(seis_like_obj, parentuid):
              raise Exception('Object was not added. tid={}'.format(tid))
          #
  
          #
          z_index = OM.new('data_index',
                           name='Time',
                           datatype='TWT',
                           unit='ms',
                           start=0.0,
                           step=(segy_file.sample_rate * 1000),
                           samples=segy_file.number_of_samples
                           )
          OM.add(z_index, seis_like_obj.uid)
          #
  
          try:
              offset_index = OM.new('data_index',
                                    segy_file.dimensions[2],
                                    name='Offset',
                                    datatype='OFFSET',
                                    unit='m'
                                    )
              OM.add(offset_index, seis_like_obj.uid)
              next_dim = 2
          except Exception as e:
              next_dim = 1
  
              #
          xline_index = OM.new('data_index',
                               segy_file.dimensions[1],
                               name='X Line',
                               datatype='X_LINE'
                               )
          if OM.add(xline_index, seis_like_obj.uid):
              next_dim += 1
          #
  
          iline_index = OM.new('data_index',
                               segy_file.dimensions[0],
                               name='I Line',
                               datatype='I_LINE'
                               )
          OM.add(iline_index, seis_like_obj.uid)
          #  
          seis_like_obj._create_data_index_map(
              [iline_index.uid],
              [xline_index.uid],
              [offset_index.uid],
              [z_index.uid]
          )
  
          print('seis_like_obj.traces.shape:', seis_like_obj.data.shape)
  
          # """
      except Exception as e:
          raise e
      finally:
          del wait
          del disableAll
  
  
  #
  # TODO: Verificar melhor opcao no Python 3.6
  #   
  
  CallerInfo = collections.namedtuple('CallerInfo',
                                      ['object_', 'class_', 'module', 'function_name',
                                       'filename', 'line_number', 'line_code'
                                       ]
                                      )
  
  
  def get_callers_stack():
      """
          Based on: https://gist.github.com/techtonik/2151727 with some 
          changes.
      
          Get a list with caller modules, objects and functions in the stack
          with list index 0 being the latest call.
          
          Returns:
              list(collections.namedtuple('CallerInfo', 
                          ['object_', 'class_', 'module', 'function_name', 
                           'filename', 'line_number', 'line_code']))
      """
  
      ret_list = []
  
      print('app_utils.get_callers_stack')
  
      try:
          stack = inspect.stack()
          for i in range(1, len(stack)):
              fi = stack[i]
              module_ = None
              obj = fi.frame.f_locals.get('self', None)
              class_ = fi.frame.f_locals.get('__class__', None)
              if obj:
                  module_ = inspect.getmodule(obj)
              if not class_ and obj:
                  class_ = obj.__class__
              ret_list.append(
                  CallerInfo(object_=obj, class_=class_, module=module_,
                             function_name=fi.function, filename=fi.filename,
                             line_number=fi.lineno, line_code=fi.code_context,
                             # index=fi.index,
                             # traceback=traceback, f_locals=fi.frame.f_locals
                             )
              )
              if fi.frame.f_locals.get('__name__') == '__main__':
                  break
      except Exception as e:
          print(e)
          raise
  
      return ret_list
  
  
  def get_class_full_name(obj):
      try:
          full_name = obj.__class__.__module__ + "." + obj.__class__.__name__
      except Exception as e:
          msg = 'ERROR in function app.app_utils.get_class_full_name().'
          log.exception(msg)
          raise e
      return full_name
  
  
  def get_string_from_function(function_):
      if not callable(function_):
          msg = 'ERROR: Given input is not a function: {}.'.format(str(function_))
          log.error(msg)
          raise Exception(msg)
      return function_.__module__ + '.' + function_.__name__
  
  
  def get_function_from_filename(full_filename, function_name):
      try:
          # print ('\nget_function_from_filename', full_filename, function_name)
          if function_name == '<module>':
              return None
  
          rel_path = os.path.relpath(full_filename, app.BASE_PATH)
          module_rel_path = os.path.splitext(rel_path)[0]
          # print (module_rel_path)
          module_str = '.'.join(module_rel_path.split(os.path.sep))
          # print (module_str)
          module_ = importlib.import_module(module_str)
          # print (module_, function_name)
          function_ = getattr(module_, function_name)
          return function_
      except:
          raise
  
  
  def get_function_from_string(fullpath_function):
      try:
          # print ('\nget_function_from_string:', fullpath_function)
          module_str = '.'.join(fullpath_function.split('.')[:-1])
          function_str = fullpath_function.split('.')[-1]
          # print ('importing module:', module_str)
          module_ = importlib.import_module(module_str)
          # print ('getting function:', function_str, '\n')
          function_ = getattr(module_, function_str)
          return function_
      except Exception as e:
          msg = 'ERROR in function app.app_utils.get_function_from_string({}).'.format(fullpath_function)
          log.exception(msg)
          print(msg)
          raise e
  
  
  class Chronometer(object):
  
      def __init__(self):
          self.start_time = timeit.default_timer()
  
      def end(self):
          self.total = timeit.default_timer() - self.start_time
          return 'Execution in {:0.3f}s'.format(self.total)
  
      # Phoenix DropTarget code
  
  
  class DropTarget(wx.DropTarget):
  
      def __init__(self, _test_func, callback=None):
          wx.DropTarget.__init__(self)
          self.data = wx.CustomDataObject('obj_uid')
          self.SetDataObject(self.data)
          self._test_func = _test_func
          self._callback = callback
  
      def OnDrop(self, x, y):
          return True
  
      def OnData(self, x, y, defResult):
          obj_uid = self._get_object_uid()
          if self._callback:
              wx.CallAfter(self._callback, obj_uid)
          return defResult
  
      def OnDragOver(self, x, y, defResult):
          obj_uid = self._get_object_uid()
          if obj_uid:
              if self._test_func(obj_uid):
                  return defResult
          return wx.DragNone
  
      def _get_object_uid(self):
          if self.GetData():
              obj_uid_bytes = self.data.GetData().tobytes()
              obj_uid_str = obj_uid_bytes.decode()
              if obj_uid_str:
                  obj_uid = parse_string_to_uid(obj_uid_str)
                  return obj_uid
          return None
  
  
  class GripyEnum(Enum):
  
      def __repr__(self):
          # return '{} object, name: {}, value: {}'.format(self.__class__, self.name, self.value)
          return str(self.value)
  
      def __eq__(self, other):
          if type(other) is self.__class__:
              return self.value is other.value
          return self.value is other
  
      def __lt__(self, other):
          if type(other) is self.__class__:
              return self.value < other.value
          return self.value < other
  
      def __le__(self, other):
          return self.__eq__(other) or self.__lt__(other)
  
      def __gt__(self, other):
          if type(other) is self.__class__:
              return self.value > other.value
          return self.value > other
  
      def __ge__(self, other):
          return self.__eq__(other) or self.__gt__(other)
  
  
  class GripyEnumBitwise(GripyEnum):
  
      def __or__(self, other):
          if type(other) is self.__class__:
              return self.value | other.value
          return self.value | other
  
      def __ror__(self, other):
          return self.__or__(other)
  
  
  class WellPlotState(GripyEnum):
      NORMAL_TOOL = 0
      SELECTION_TOOL = 1
  
  
  FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
  WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
  WHITESPACE_STR = ' \t\n\r'
  NUMBER_RE = re.compile(
      r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
      (re.VERBOSE | re.MULTILINE | re.DOTALL)
  )
  
  
  class GripyJSONEncoder(json.JSONEncoder):
  
      def default(self, obj):
          if isinstance(obj, wx.Point):
              return 'wx.Point' + str(obj)
          elif isinstance(obj, wx.Size):
              return 'wx.Size' + str(obj)
          elif isinstance(obj, GripyEnum):
              return str(obj.value)
          elif callable(obj):
              return get_string_from_function(obj)
          try:
              return str(obj)
          except:
              return super(GripyJSONDecoder, self).default(self, obj)
  
  
  def clean_path_str(path):
      # path = path.replace('\\' ,'/')
      path = path.encode('ascii', 'ignore')  # in order to save unicode characters
      path = path.encode('string-escape')
      return path
  
  
  def write_json_file(py_object, fullfilename):
      fullfilename = clean_path_str(fullfilename)
      fullfilename = os.path.normpath(fullfilename)
      directory = os.path.dirname(fullfilename)
      if not os.path.exists(directory):
          os.makedirs(directory)
          msg = 'App.app_utils.write_json_file has created directory: {}'.format(directory)
          # log.debug(msg)
          print(msg)
      f = open(fullfilename, 'w')
      f.write(json.dumps(py_object, indent=4, cls=GripyJSONEncoder))
      f.close()
  
  
  class GripyJSONDecoder(json.JSONDecoder):
  
      def decode(self, s, _w=WHITESPACE.match):
          self.scan_once = gripy_make_scanner(self)
          return super(GripyJSONDecoder, self).decode(s, _w)
  
  
  def gripy_make_scanner(context):
      parse_object = context.parse_object
      parse_array = context.parse_array
      parse_string = context.parse_string
      match_number = NUMBER_RE.match
      # encoding = context.encoding
      strict = context.strict
      parse_float = context.parse_float
      parse_int = context.parse_int
      parse_constant = context.parse_constant
      object_hook = context.object_hook
      object_pairs_hook = context.object_pairs_hook
  
      def _scan_once(string, idx):
          try:
              nextchar = string[idx]
          except IndexError:
              raise StopIteration
          if nextchar == '"':
              if string[idx:idx + 10] == '"wx.Point(':
                  return GripyJSONParser((string, idx + 10), _scan_once, wx.Point)
              elif string[idx:idx + 9] == '"wx.Size(':
                  return GripyJSONParser((string, idx + 9), _scan_once, wx.Size)
              return parse_string(string, idx + 1, strict)
          elif nextchar == '{':
              return parse_object((string, idx + 1), strict,
                                  _scan_once, object_hook, object_pairs_hook)
          elif nextchar == '[':
              return parse_array((string, idx + 1), _scan_once)
          elif nextchar == 'n' and string[idx:idx + 4] == 'null':
              return None, idx + 4
          elif nextchar == 't' and string[idx:idx + 4] == 'true':
              return True, idx + 4
          elif nextchar == 'f' and string[idx:idx + 5] == 'false':
              return False, idx + 5
          m = match_number(string, idx)
          if m is not None:
              integer, frac, exp = m.groups()
              if frac or exp:
                  res = parse_float(integer + (frac or '') + (exp or ''))
              else:
                  res = parse_int(integer)
              return res, m.end()
          elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
              return parse_constant('NaN'), idx + 3
          elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
              return parse_constant('Infinity'), idx + 8
          elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
              return parse_constant('-Infinity'), idx + 9
          else:
              raise StopIteration
  
      return _scan_once
  
  
  def GripyJSONParser(s_and_end, scan_once, _class, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
      s, end = s_and_end
      values = []
      nextchar = s[end:end + 1]
      if nextchar in _ws:
          end = _w(s, end + 1).end()
          nextchar = s[end:end + 1]
      # Look-ahead for trivial empty array
      if nextchar == ']':
          return values, end + 1
      _append = values.append
  
      while True:
          try:
              value, end = scan_once(s, end)
          except StopIteration:
              raise ValueError("Expecting object {}, {}".format(s, end))
          _append(value)
          nextchar = s[end:end + 1]
          if nextchar in _ws:
              end = _w(s, end + 1).end()
              nextchar = s[end:end + 1]
          end += 1
          if nextchar == ')' and s[end:end + 1] == '"':
              end += 1
              break
          elif nextchar != ',':
              raise ValueError("Expecting ',' delimiter {}, {}".format(s, end))
          try:
              if s[end] in _ws:
                  end += 1
                  if s[end] in _ws:
                      end = _w(s, end + 1).end()
          except IndexError:
              pass
      return _class(int(values[0]), int(values[1])), end
  
  
  def read_json_file(full_filename):
      # print ('\nread_json_file:', fullfilename, type(fullfilename))
      # fullfilename = fullfilename.replace('\\' ,'/')
      # fullfilename = fullfilename.encode('ascii', 'ignore') # in order to save unicode characters
      # fullfilename = fullfilename.encode('string-escape')
      json_file = open(full_filename, 'r')
      state = json.load(json_file, cls=GripyJSONDecoder)
      json_file.close()
      return state
  
  
  def parse_string_to_uid(obj_uid_string):
      """
      Parse a uid String (which may contains non uid characters like " and \) to
      a uid tuple in a format (tid, oid).
      
      Parameters
      ----------
      obj_uid_string : str
          The uid String.
      
      Returns
      -------
      tuple
          A pair (tid, oid) which can be a Gripy object identifier.
      """
      try:
          #        print ('parse_string_to_uid:', obj_uid_string)
          left_index = obj_uid_string.find('(')
          right_index = obj_uid_string.rfind(')')
          if left_index == -1 or right_index == -1:
              return None
          elif right_index < left_index:
              return None
          obj_uid_string = obj_uid_string[left_index + 1:right_index]
          tid, oid = obj_uid_string.split(',')
          tid = tid.strip('\'\" ')
          oid = int(oid.strip('\'\" '))
          return tid, oid
      except:
          raise
  
  
  def get_wx_colour_from_seq_string(seq_str):
      # tuple or list
      if seq_str.startswith('(') or seq_str.startswith('['):
          seq_str = seq_str[1:-1]
          val = tuple([int(c.strip()) for c in seq_str.split(',')])
          color = wx.Colour(val)
          print('_get_wx_colour:', color,
                color.GetAsString(wx.C2S_HTML_SYNTAX))
          return color
      return None
  
  
  # Have colormaps separated into categories:
  # http://matplotlib.org/examples/color/colormaps_reference.html
  """
  # MPL 1.4/1.5 COLORS
  
  MPL_CATS_CMAPS = [('Perceptually Uniform Sequential', [
              'viridis', 'plasma', 'inferno', 'magma']),
           ('Sequential', [
              'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
              'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
              'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']),
           ('Sequential (2)', [
              'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
              'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
              'hot', 'afmhot', 'gist_heat', 'copper']),
           ('Diverging', [
              'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
              'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']),
           ('Qualitative', [
              'Pastel1', 'Pastel2', 'Paired', 'Accent',
              'Dark2', 'Set1', 'Set2', 'Set3',
              'tab10', 'tab20', 'tab20b', 'tab20c']),
           ('Miscellaneous', [
              'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
              'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'hsv',
              'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])]
  """
  
  # MPL 2.0 COLORS
  MPL_CATS_CMAPS = [
      ('Perceptually Uniform Sequential',
       ['viridis', 'inferno', 'plasma', 'magma']
       ),
      ('Sequential',
       ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys',
        'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples',
        'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd'
        ]
       ),
      ('Sequential (2)',
       ['afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat',
        'gray', 'hot', 'pink', 'spring', 'summer', 'winter'
        ]
       ),
      ('Diverging',
       ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr',
        'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic'
        ]
       ),
      ('Qualitative',
       ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1',
        'Set2', 'Set3', 'Vega10', 'Vega20', 'Vega20b', 'Vega20c'
        ]
       ),
      ('Miscellaneous',
       ['gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg',
        'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar',
        'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv',
        'flag', 'prism'
        ]
       )
  ]
  
  # MPL_COLORMAPS = [value for (key, values) in MPL_CATS_CMAPS for value in values]
  
  
  MPL_COLORMAPS = sorted(cmap_d)
  
  """
  MPL_COLORMAPS = ['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 
                   'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r',
                   'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 
                   'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r',
                   'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r',
                   'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBu_r',
                   'PuBuGn', 'PuBuGn_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r',
                   'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 
                   'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r',
                   'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 
                   'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Vega10', 'Vega10_r',
                   'Vega20', 'Vega20_r', 'Vega20b', 'Vega20b_r', 'Vega20c', 'Vega20c_r',
                   'Wistia', 'Wistia_r', 'YlGn', 'YlGn_r', 'YlGnBu', 'YlGnBu_r',
                    'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 
                    'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r',
                    'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 
                    'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r',
                    'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 
                    'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r',
                    'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r',
                    'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r',
                    'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r',
                    'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r',
                    'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 
                    'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 
                    'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 
                    'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 
                    'spectral', 'spectral_r', 'spring', 'spring_r', 
                    'summer', 'summer_r', 'terrain', 'terrain_r', 
                    'viridis', 'viridis_r', 'winter', 'winter_r']
    
  """
  
  ###############################################################################
  ###############################################################################  
  
  MPL_COLORS = collections.OrderedDict()
  MPL_COLORS['Black'] = None
  MPL_COLORS['Maroon'] = None
  MPL_COLORS['Green'] = wx.Colour(0, 100, 0)  # Dark Green
  MPL_COLORS['Olive'] = wx.Colour(128, 128, 0)
  MPL_COLORS['Navy'] = None
  MPL_COLORS['Purple'] = None
  MPL_COLORS['Teal'] = wx.Colour(0, 128, 128)
  MPL_COLORS['Gray'] = None
  MPL_COLORS['Silver'] = wx.Colour(192, 192, 192)
  MPL_COLORS['Red'] = None
  MPL_COLORS['Lime'] = wx.Colour(0, 255, 0)  # Green
  MPL_COLORS['Yellow'] = None
  MPL_COLORS['Blue'] = None
  MPL_COLORS['Fuchsia'] = wx.Colour(255, 0, 255)
  MPL_COLORS['Aqua'] = wx.Colour(0, 255, 255)
  MPL_COLORS['White'] = None
  MPL_COLORS['SkyBlue'] = wx.Colour(135, 206, 235)
  MPL_COLORS['LightGray'] = wx.Colour(211, 211, 211)
  MPL_COLORS['DarkGray'] = wx.Colour(169, 169, 169)
  MPL_COLORS['SlateGray'] = wx.Colour(112, 128, 144)
  MPL_COLORS['DimGray'] = wx.Colour(105, 105, 105)
  MPL_COLORS['BlueViolet'] = wx.Colour(138, 43, 226)
  MPL_COLORS['DarkViolet'] = wx.Colour(148, 0, 211)
  MPL_COLORS['Magenta'] = None
  MPL_COLORS['DeepPink'] = wx.Colour(148, 0, 211)
  MPL_COLORS['Brown'] = None
  MPL_COLORS['Crimson'] = wx.Colour(220, 20, 60)
  MPL_COLORS['Firebrick'] = None
  MPL_COLORS['DarkRed'] = wx.Colour(139, 0, 0)
  MPL_COLORS['DarkSlateGray'] = wx.Colour(47, 79, 79)
  MPL_COLORS['DarkSlateBlue'] = wx.Colour(72, 61, 139)
  MPL_COLORS['Wheat'] = None
  MPL_COLORS['BurlyWood'] = wx.Colour(222, 184, 135)
  MPL_COLORS['Tan'] = None
  MPL_COLORS['Gold'] = None
  MPL_COLORS['Orange'] = None
  MPL_COLORS['DarkOrange'] = wx.Colour(255, 140, 0)
  MPL_COLORS['Coral'] = None
  MPL_COLORS['DarkKhaki'] = wx.Colour(189, 183, 107)
  MPL_COLORS['GoldenRod'] = None
  MPL_COLORS['DarkGoldenrod'] = wx.Colour(184, 134, 11)
  MPL_COLORS['Chocolate'] = wx.Colour(210, 105, 30)
  MPL_COLORS['Sienna'] = None
  MPL_COLORS['SaddleBrown'] = wx.Colour(139, 69, 19)
  MPL_COLORS['GreenYellow'] = wx.Colour(173, 255, 47)
  MPL_COLORS['Chartreuse'] = wx.Colour(127, 255, 0)
  MPL_COLORS['SpringGreen'] = wx.Colour(0, 255, 127)
  MPL_COLORS['MediumSpringGreen'] = wx.Colour(0, 250, 154)
  MPL_COLORS['MediumAquamarine'] = wx.Colour(102, 205, 170)
  MPL_COLORS['LimeGreen'] = wx.Colour(50, 205, 50)
  MPL_COLORS['LightSeaGreen'] = wx.Colour(32, 178, 170)
  MPL_COLORS['MediumSeaGreen'] = wx.Colour(60, 179, 113)
  MPL_COLORS['DarkSeaGreen'] = wx.Colour(143, 188, 143)
  MPL_COLORS['SeaGreen'] = wx.Colour(46, 139, 87)
  MPL_COLORS['ForestGreen'] = wx.Colour(34, 139, 34)
  MPL_COLORS['DarkOliveGreen'] = wx.Colour(85, 107, 47)
  MPL_COLORS['DarkGreen'] = wx.Colour(1, 50, 32)
  MPL_COLORS['LightCyan'] = wx.Colour(224, 255, 255)
  MPL_COLORS['Thistle'] = None
  MPL_COLORS['PowderBlue'] = wx.Colour(176, 224, 230)
  MPL_COLORS['LightSteelBlue'] = wx.Colour(176, 196, 222)
  MPL_COLORS['LightSkyBlue'] = wx.Colour(135, 206, 250)
  MPL_COLORS['MediumTurquoise'] = wx.Colour(72, 209, 204)
  MPL_COLORS['Turquoise'] = None
  MPL_COLORS['DarkTurquoise'] = wx.Colour(0, 206, 209)
  MPL_COLORS['DeepSkyBlue'] = wx.Colour(0, 191, 255)
  MPL_COLORS['DodgerBlue'] = wx.Colour(30, 144, 255)
  MPL_COLORS['CornflowerBlue'] = wx.Colour(100, 149, 237)
  MPL_COLORS['CadetBlue'] = wx.Colour(95, 158, 160)
  MPL_COLORS['DarkCyan'] = wx.Colour(0, 139, 139)
  MPL_COLORS['SteelBlue'] = wx.Colour(70, 130, 180)
  MPL_COLORS['RoyalBlue'] = wx.Colour(65, 105, 225)
  MPL_COLORS['SlateBlue'] = wx.Colour(106, 90, 205)
  MPL_COLORS['DarkBlue'] = wx.Colour(0, 0, 139)
  MPL_COLORS['MediumBlue'] = wx.Colour(0, 0, 205)
  MPL_COLORS['SandyBrown'] = wx.Colour(244, 164, 96)
  MPL_COLORS['DarkSalmon'] = wx.Colour(233, 150, 122)
  MPL_COLORS['Salmon'] = None
  MPL_COLORS['Tomato'] = wx.Colour(255, 99, 71)
  MPL_COLORS['Violet'] = wx.Colour(238, 130, 238)
  MPL_COLORS['HotPink'] = wx.Colour(255, 105, 180)
  MPL_COLORS['RosyBrown'] = wx.Colour(188, 143, 143)
  MPL_COLORS['MediumVioletRed'] = wx.Colour(199, 21, 133)
  MPL_COLORS['DarkMagenta'] = wx.Colour(139, 0, 139)
  MPL_COLORS['DarkOrchid'] = wx.Colour(153, 50, 204)
  MPL_COLORS['Indigo'] = wx.Colour(75, 0, 130)
  MPL_COLORS['MidnightBlue'] = wx.Colour(25, 25, 112)
  MPL_COLORS['MediumSlateBlue'] = wx.Colour(123, 104, 238)
  MPL_COLORS['MediumPurple'] = wx.Colour(147, 112, 219)
  MPL_COLORS['MediumOrchid'] = wx.Colour(186, 85, 211)
  
  MPL_COLORS = collections.OrderedDict(sorted(MPL_COLORS.items()))
  
  ###############################################################################
  ###############################################################################        
  
  # Based on https://matplotlib.org/3.1.0/gallery/lines_bars_and_markers/linestyles.html
  # 10/September/2019 - Adriano Santana
  
  MPL_LINESTYLES = collections.OrderedDict()
  MPL_LINESTYLES['Solid'] = (0, ())
  MPL_LINESTYLES['Dotted'] = (0, (1, 1))
  MPL_LINESTYLES['Loosely dotted'] = (0, (1, 10))
  MPL_LINESTYLES['Densely dotted'] = (0, (1, 1))
  MPL_LINESTYLES['Dashed'] = (0, (5, 5))
  MPL_LINESTYLES['Loosely dashed'] = (0, (5, 10))
  MPL_LINESTYLES['Densely dashed'] = (0, (5, 1))
  MPL_LINESTYLES['Dashdotted'] = (0, (3, 5, 1, 5))
  MPL_LINESTYLES['Loosely dashdotted'] = (0, (3, 10, 1, 10))
  MPL_LINESTYLES['Densely dashdotted'] = (0, (3, 1, 1, 1))
  MPL_LINESTYLES['Dashdotdotted'] = (0, (3, 5, 1, 5, 1, 5))
  MPL_LINESTYLES['Loosely dashdotdotted'] = (0, (3, 10, 1, 10, 1, 10))
  MPL_LINESTYLES['Densely dashdotdotted'] = (0, (3, 1, 1, 1, 1, 1))
  