Commit c2cd1b3b authored by Yori Fournier's avatar Yori Fournier
Browse files

Merge branch '95-feature-alias-groupping' into 'dev'

Resolve "FEATURE: alias groupping"

See merge request !61
parents 549dd326 1a5a114f
Pipeline #612 passed with stage
in 11 minutes and 48 seconds
from .mpl_Data import MplData
from .mpl_data import MplData
from matplotlib import rcParams
# FORM -----------------------------------------------------------------
......@@ -9,6 +9,8 @@ DBUG = " > dbug-: "
SPCE = " > -----: "
# CONFIGURATION --------------------------------------------------------
CONTEXT= 'CLIENT'
# myFig
D_FIGNUM = 0 # default figure number
D_FIGSIZE = (8., 6.) # default figure size
......
......@@ -169,6 +169,7 @@ class MplFig():
self.reformat = kwargs.pop('reformat', D_REFORMAT)
self.debug = kwargs.pop('debug', D_DEBUG)
self.formatted = kwargs.pop('formatted', D_FORMATTED)
self._tagged_keywords = {}
self.aliases = {}
self.FIGSIZE = kwargs.pop('figsize', self.FIGSIZE)
......@@ -195,7 +196,10 @@ class MplFig():
self.addAxes()
# This will throw a pep8 error but its needed to prevent
# confusion with matplotlib -- need another name
# automatically create keys for the axes keywords to provide a 1 to 1 mapping
self._generate_tagged_keywords()
# declare the aliases
self.declare_aliases()
......@@ -205,6 +209,8 @@ class MplFig():
# PRIVATE UPDATE ---------------------------------------------------
def _update(self, **kwargs):
forax = {}
# check attributes in keywords
for keyword in kwargs.keys():
......@@ -223,37 +229,62 @@ class MplFig():
# update value
setattr(self, keyword, kwargs[keyword])
# For each axes update the keywords
# it is a keyword
else:
if keyword in self.aliases.keys():
alias_value = kwargs[keyword]
key_group = self.aliases[keyword]
for key in key_group:
# if the key doe not already exists (case of explicit keyword)
if not forax.get(key, False):
forax.update({key: alias_value})
else:
forax.update({keyword: kwargs[keyword]})
# For each axes collect the keywords
for ax in self.get_axes():
forax = {}
ax_specific_keys = {}
for keyword in kwargs.keys():
for keyword in forax.keys():
# ignore figure attributes
if keyword in ['rawdata'] + self._attributes_to_update_keys:
pass
if keyword in self._tagged_keywords.keys():
# Check if a key of kwargs has an alias for this axes
elif keyword in self.aliases.keys():
alax, alkey = self.aliases[keyword]
tag_ax, tag_key = self._tagged_keywords[keyword]
# If an alias is found then update axkwargs
if ax == alax:
forax.update(**{alkey: kwargs[keyword]})
if ax == tag_ax:
ax_specific_keys.update({tag_key: forax[keyword]})
# use keyword as it is for the axes
else:
forax.update(**{keyword: kwargs[keyword]})
# if the keyword was not yet updated by a specific keyword.
if (not ax_specific_keys.get(keyword, False)) and (keyword in ax.keywords):
ax_specific_keys.update({keyword: forax[keyword]})
# Then eventually all collected Keywords are updated in one go
if (forax):
if (ax_specific_keys):
if self.debug:
print (DBUG + ' fig.update ', ax, 'keywords: ', forax)
ax.update(**forax)
print (DBUG+' fig.update ', ax, 'keywords: ', ax_specific_keys)
ax.update(**ax_specific_keys)
return(True)
# GENERATE TAGGED KEYWORDS -----------------------------------------
def _generate_tagged_keywords(self):
""" make the axes keywords accessible in the figure context by inserting
a generated name to _tagged_keywords. An auto-naming scheme is
applied like so:
<axes_name>_<keyword_from_axes_context>"""
for ax in self.axes:
for key,val in ax.keywords.items():
self._tagged_keywords.update({ax.name+"_"+key : (ax , key) })
# GET AXES BY NAME -------------------------------------------------
def get_axes_by_name(self, name):
......@@ -274,11 +305,31 @@ class MplFig():
if unknownax == ax :
return ax.name
# LIST KEYWORDS AVAILABLE IN THE FIGURE ----------------------------
def list_keywords(self):
""" list all keywords (_tagged_keywords) and aliases in readable form.
use get_state() for info on value."""
for ax in self.get_axes():
print(INFO+'Keywords for axes "{}":'.format(self.get_axes_name(ax)))
for key in ax.keywords:
#~ print(self._tagged_keywords.items())
for tk,axkey in self._tagged_keywords.items():
#~ print(tk,axkey)
if (axkey[1] == key) and (axkey[0] == ax):
print(INFO+' "{}": Access by "{}"'.format(key, tk))
for alias,repace_keys in self.aliases.items():
#~ print(alias,repace_keys)
for rk in repace_keys:
#~ print(rk)
if rk == tk:
print(INFO+' or via figure .alias: "{}"'.format(alias))
# GET AXES ---------------------------------------------------------
def get_axes(self):
return(self.axes)
# DEBUG --------------------------------------------------------------
# DEBUG ------------------------------------------------------------
def print_debug(self):
class_name = str(self.__class__.__name__)
print('\n' + DBUG + " {0} PARAMETERS: ".format(class_name))
......@@ -295,6 +346,26 @@ class MplFig():
def layout(self):
self.grid.layout()
# GET STATE -----------------------------------------------------------
def get_state(self):
""" this function collects the current state of in the figure
by creating a dict that holds all keyword,value pairs
(taking the aliases into account)
Warning: myplotlib allows in a transparent manner to modify
the axes directly. Those changes can not be covert.
"""
state = dict()
# test for double keywords and print a waring
for tagged_key, key_value in self._tagged_keywords.items():
ax, ax_key = key_value
state.update({tagged_key : ax.keywords[ax_key]})
return state
# DEF METHOD (DECORATOR) -------------------------------------------
def def_method(self, f):
name = f.__name__
......@@ -304,6 +375,7 @@ class MplFig():
raise KeyError('Fig has no method called {name}'.format(name=name))
return(f)
# === INTERFACE METHODS ============================================
#
# The interfaces methods are the one that the user supposely
......@@ -485,16 +557,6 @@ class MplFig():
"""
pass
# GET STATE --------------------------------------------------------
def get_state(self):
"""Extract the state of the MplFig used to tests synchronisation.
This method make sens only in the client context, and is therefore
implemented in the ClassFactory.
"""
pass
# RE-SYNC FIGURE ---------------------------------------------------
def re_sync_fig(self):
"""Re-Synchronise a client figure with the server side.
......@@ -923,47 +985,6 @@ class MplFig():
'The client is not connected to a server yet.'
'Please use the client to set up a sync conection.')
def _get_state_client(self):
""" this function collects the current state of in the figure
by creating a dict that holds all keyword,value pairs
(taking the aliases into account)
Warning: myplotlib allows in a transparent manner to modify
the axes directly. Those changes can not be covert.
"""
state = dict()
# test for double keywords and print a waring
for ax in self.axes:
# find aliases for this specific axes
masks = dict()
for alias in self.aliases.keys():
axes_for_alias, keyword = self.aliases[alias]
if axes_for_alias == ax:
masks.update({keyword: alias})
# collect all keywords of the axes and replace the keys with the
# aliases
ax_keywords = ax.keywords.copy()
for mask in masks:
if mask in ax_keywords:
ax_keywords[masks[mask]] = ax_keywords.pop(mask)
# Test if kewords are already set by an other axes
for key in ax_keywords.keys():
if key in state.keys():
print(
'Warning: The keyword \"',
key,
'\" appears in multiple axes. The Defaults will'
' be overwritten. Prevent this by using aliases')
# update the global keyword index
state.update(ax_keywords)
return state
def _re_sync_fig_client(self):
if self.client is not None:
......
from .. import D_HIERARCHY
#~ from .. import D_HIERARCHY
from .myIOs import readStupidData, readStupidData2
if D_HIERARCHY in ('CLIENT', 'client'):
#~ if D_HIERARCHY in ('CLIENT', 'client'):
from .axTestClient import AxTestClient
from .figTestClient import FigTestClient as FigTest
#~ from .axTestClient import AxTestClient
#~ from .figTestClient import FigTestClient as FigTest
# from .tests_client import myTest
# from .tests_client import test100
# from .tests import myTest
......@@ -12,50 +12,50 @@ if D_HIERARCHY in ('CLIENT', 'client'):
# testList = [test100,
# ]
elif D_HIERARCHY in ('SERVER', 'server'):
from .axTestServer import AxTestServer
from .figTestServer import FigTestServer as FigTest
#~ elif D_HIERARCHY in ('SERVER', 'server'):
#~ from .axTestServer import AxTestServer
#~ from .figTestServer import FigTestServer as FigTest
# from .tests_server import myTest
# from .tests_server import test100
# testList = [test100,
# ]
elif D_HIERARCHY in ('local', 'LOCAL'):
#~ elif D_HIERARCHY in ('local', 'LOCAL'):
from .axTest1 import AxTest1
from .axTest1 import AxTestPlot2D, AxTestPlots2D
from .figTest import FigTest1, FigTest2
from .axTest1 import AxTest1
#~ from .axTest1 import AxTestPlot2D, AxTestPlots2D
from .figTest import FigTest1, FigTest2
from .tests import myTest
#~ from .tests import myTest
# import the tests
from .tests import test100
from .tests import test200, test201, test202, test203, test204
from .tests import test300, test301, test302, test303, test304, test305
from .tests import test306, test307, test308, test309, test310, test311
from .tests import test312
from .tests import test400, test401, test402, test403
from .tests import test500, test501, test502, test503, test504
from .tests import test505, test506, test507, test508, test509
from .tests import test510, test511
from .tests import test600
# the tests to run
testList = [test100,
test201, test202, test203, test204,
test300, test301, test302, test303, test304, test305,
test306, test307, test308, test309, test310, test311,
test312,
test400, test401, test402,
test503, test506, test500, test507, test501, test502,
test504, test505, test508, test509, test510, test511,
test600,
]
# for the demos
from ..mytool import print2screen
else:
raise ImportError
#~ from .tests import test100
#~ from .tests import test200, test201, test202, test203, test204
#~ from .tests import test300, test301, test302, test303, test304, test305
#~ from .tests import test306, test307, test308, test309, test310, test311
#~ from .tests import test312
#~ from .tests import test400, test401, test402, test403
#~ from .tests import test500, test501, test502, test503, test504
#~ from .tests import test505, test506, test507, test508, test509
#~ from .tests import test510, test511
#~ from .tests import test600
#~
#~ # the tests to run
#~ testList = [test100,
#~ test201, test202, test203, test204,
#~ test300, test301, test302, test303, test304, test305,
#~ test306, test307, test308, test309, test310, test311,
#~ test312,
#~ test400, test401, test402,
#~ test503, test506, test500, test507, test501, test502,
#~ test504, test505, test508, test509, test510, test511,
#~ test600,
#~ ]
#~
#~ # for the demos
#~ from ..mytool import print2screen
#~
#~ else:
#~
#~ raise ImportError
......@@ -43,8 +43,8 @@
#
# IMPORT ---------------------------------------------------------------
from .. import SEVR, DBUG, INFO
from .. import MyAxes
from ..mytool import AxPlot2D, AxPlots2D
from .. import MplAxes
#~ from ..mytool import AxPlot2D, AxPlots2D
D_XRANGE = None
D_YRANGE = None
......@@ -53,9 +53,9 @@ D_LOGX = False
# Class MyAxes Overwriting Matplotlib.figure.Axes
class AxTest1(MyAxes):
class AxTest1(MplAxes):
def declareKeywords(self):
def declare_keywords(self):
self.keywords = {'xRange': D_XRANGE,
'yRange': D_YRANGE}
......@@ -63,7 +63,7 @@ class AxTest1(MyAxes):
return(True)
# FORMATTING ---------------------------------------------------------
def formatRawData(self, rawdata):
def format_rawdata(self, rawdata):
# give value to data a dict
# with xdata, ydata1, zdata, ydata2 ...
......@@ -98,51 +98,51 @@ class AxTest1(MyAxes):
return(True)
# Class MyAxes Overwriting Matplotlib.figure.Axes
class AxTestPlots2D(AxPlots2D):
# FORMATTING ---------------------------------------------------------
def formatRawData(self, rawdata):
# give value to data a dict
# with xdata, ydata1, zdata, ydata2 ...
try:
self.data = {'xdata': [rawdata.data[0], rawdata.data[1]],
'ydatas': ([rawdata.data[2], rawdata.data[3]],
[rawdata.data[3], rawdata.data[2]]),
'xlabel': 'Some X Label',
'ylabel': 'Some Y Label',
'labels': ('curve 1', 'curve 2')}
except(TypeError, KeyError, IndexError):
print(SEVR + 'The Raw Data could not be formatted --> EXIT')
return(False)
if(self.fig.debug):
print(DBUG + 'I formatted the raw data!')
return(True)
# Class MyAxes Overwriting Matplotlib.figure.Axes
class AxTestPlot2D(AxPlot2D):
# FORMATTING ---------------------------------------------------------
def formatRawData(self, rawdata):
# give value to data a dict
# with xdata, ydata1, zdata, ydata2 ...
try:
self.data = {'xdata': [rawdata.data[0], rawdata.data[1]],
'ydata': [rawdata.data[2], rawdata.data[3]],
'xlabel': 'Some X Label',
'ylabel': 'Some Y Label'}
except(TypeError, KeyError, IndexError):
print(SEVR + 'The Raw Data could not be formatted --> EXIT')
return(False)
if(self.fig.debug):
print(DBUG + 'I formatted the raw data!')
return(True)
#~ # Class MyAxes Overwriting Matplotlib.figure.Axes
#~ class AxTestPlots2D(AxPlots2D):
#~
#~ # FORMATTING ---------------------------------------------------------
#~ def formatRawData(self, rawdata):
#~
#~ # give value to data a dict
#~ # with xdata, ydata1, zdata, ydata2 ...
#~ try:
#~ self.data = {'xdata': [rawdata.data[0], rawdata.data[1]],
#~ 'ydatas': ([rawdata.data[2], rawdata.data[3]],
#~ [rawdata.data[3], rawdata.data[2]]),
#~ 'xlabel': 'Some X Label',
#~ 'ylabel': 'Some Y Label',
#~ 'labels': ('curve 1', 'curve 2')}
#~
#~ except(TypeError, KeyError, IndexError):
#~ print(SEVR + 'The Raw Data could not be formatted --> EXIT')
#~ return(False)
#~
#~ if(self.fig.debug):
#~ print(DBUG + 'I formatted the raw data!')
#~
#~ return(True)
#~
#~
#~ # Class MyAxes Overwriting Matplotlib.figure.Axes
#~ class AxTestPlot2D(AxPlot2D):
#~
#~ # FORMATTING ---------------------------------------------------------
#~ def formatRawData(self, rawdata):
#~
#~ # give value to data a dict
#~ # with xdata, ydata1, zdata, ydata2 ...
#~ try:
#~ self.data = {'xdata': [rawdata.data[0], rawdata.data[1]],
#~ 'ydata': [rawdata.data[2], rawdata.data[3]],
#~ 'xlabel': 'Some X Label',
#~ 'ylabel': 'Some Y Label'}
#~
#~ except(TypeError, KeyError, IndexError):
#~ print(SEVR + 'The Raw Data could not be formatted --> EXIT')
#~ return(False)
#~
#~ if(self.fig.debug):
#~ print(DBUG + 'I formatted the raw data!')
#~
#~ return(True)
......@@ -67,13 +67,13 @@
# IMPORT ---------------------------------------------------------------
from .. import os
from .. import DBUG, SEVR, INFO, SPCE, WARN
from .. import MyFig
from .. import MplFig
from .axTest1 import AxTest1
# Class MyFig Overwriting Matplotlib.figure.Figure
class FigTest1(MyFig): # Fig with one Axes
class FigTest1(MplFig): # Fig with one Axes
# Set the size of the Figure in inch
# (private variable can not be updated)
......@@ -85,7 +85,7 @@ class FigTest1(MyFig): # Fig wi
self.add_axes(AxTest1(self, frame1), "p1")
class FigTest2(MyFig): # Fig with two Axes
class FigTest2(MplFig): # Fig with two Axes
# Set the size of the Figure in inch
# (private variable can not be updated)
......@@ -99,14 +99,8 @@ class FigTest2(MyFig): # Fig wi
print(self.add_axes(AxTest1(self, frame1), "p1"))
print(self.add_axes(AxTest1(self, frame2), "p2"))
def declareAliases(self):
def declare_aliases(self):
# it is important to not hold additional references to an axes in the figure to avoid memory leaks. use the this function
# get the plot added above
p1 = self.getAxesByName("p1")
p2 = self.getAxesByName("p2")
self.aliases = {'xRange_p1': (p1, "xRange"),
'xRange_p2': (p2, "xRange")}
self.aliases = {'xRange': ("p1_xRange","p2_xRange")}
return(True)
......@@ -98,6 +98,7 @@ class AxTest(MplAxes):
return(True)
class AxBroken(MplAxes):
def FormatRawdata(self, rawdata):
......@@ -106,6 +107,7 @@ class AxBroken(MplAxes):
def Plottting(self):
return(True)
class AxMinimumImplementation(MplAxes):
def format_rawdata(self, rawdata):
......@@ -114,14 +116,20 @@ class AxMinimumImplementation(MplAxes):
def plotting(self):
return(True)
class AxWithKeywords(MplAxes):
def declare_keywords(self):
self.keywords = {'x_range': [-1., 1.],
'y_range': [-1., 1.]}
'y_range': [-1., 1.],
'title': None}
def format_rawdata(self, rawdata):
return(True)
def plotting(self):
title = self.keywords.get('title',None)
if title:
self.set_title(title)
return(True)
# IMPORT ---------------------------------------------------------------
from myplotlib import MplFig
from axes_tests import AxTest
from axes_tests import AxWithKeywords
class FigTest(MplFig):
......@@ -10,6 +12,7 @@ class FigTest(MplFig):
self.add_axes(AxTest(self), "p1")
class EmptyFig(MplFig):
FIGSIZE = (8., 6.)
......@@ -17,6 +20,7 @@ class EmptyFig(MplFig):
def addAxes(self):
pass
class BrokenFigure(MplFig):
FIGSIZE = (12., 6.)
......@@ -24,6 +28,7 @@ class BrokenFigure(MplFig):
def AddAxes(self):
pass
class FigTestWithAliases(MplFig):
FIGSIZE = (8., 6.)
......@@ -38,7 +43,23 @@ class FigTestWithAliases(MplFig):
def declare_aliases(self):
self.aliases.update({'x_range_p1': (self.axes[0], 'x_range'),
'y_range_p1': (self.axes[0], 'y_range'),
'x_range_p2': (self.axes[1], 'x_range'),
'y_range_p2': (self.axes[1], 'y_range')})
self.aliases.update({'x_range': ('p1_x_range', 'p2_x_range'),
'y_range': ('p1_y_range', 'p2_y_range')})
class FigTestAxesAliases(MplFig):
FIGSIZE = (8., 6.)
def addAxes(self):
frame1 = [0.1, 0.1, 0.4, 0.8]
frame2 = [0.6, 0.1, 0.4, 0.8]
self.add_axes(AxWithKeywords(self, frame=frame1), "p1")
self.add_axes(AxWithKeywords(self, frame=frame2), "p2")
def declare_aliases(self):
self.aliases.update({'x_range_alias': ('p1_x_range', 'p2_x_range'),
'y_range_alias': ('p1_y_range', 'p2_y_range')})
......@@ -39,9 +39,13 @@ class TestMyAxes(unittest.TestCase):
'''Verify the keywords assignments of the constructor.
'''
ax1 = AxWithKeywords(self.fig)
self.assertEqual(ax1.keywords, {'x_range': [-1., 1.], 'y_range': [-1., 1.]})
self.assertEqual(ax1.keywords, {'x_range': [-1., 1.],