From f883756338d10fb28f775adc1962307592eba944 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Sun, 27 Aug 2017 22:13:58 +0200 Subject: [PATCH 01/11] Add myFig_client FormatRawData not yet programmed --- myFig_client.py | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 myFig_client.py diff --git a/myFig_client.py b/myFig_client.py new file mode 100644 index 0000000..ee5ceea --- /dev/null +++ b/myFig_client.py @@ -0,0 +1,329 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# ================= FILE HEADER ======================================== +# +# myplotlib v3.0.1, +# +# @file myFig.py +# @author Yori 'AGy' Fournier +# @licence CC-BY-SA +# +# MyFig class: Overlay of matplotlib Figure class +# It is done such that the user can concentrate on +# the important aspect of the figure and not the +# technical part. +# +# @Class MyFig +# +# @Constructor +# +# fignum: number of the Figure. +# reformat: flag for reComputing plotted data +# from rawData. +# debug: flag for debugging, more verbatile. +# (kw)args: user defined arguments and keywords. +# +# @section Functions +# +# - printDebug: print some debug information. +# +# - plot: overlay of Figure.plot() function, +# should be overwrited by the user. +# Consists of +# - creating axes, +# - formating the data, +# - adding the axis to the figure +# - plotting the axis +# +# - update: update the parameters in the +# keywords dictionary. +# +# @section History +# +# v 0.1.1 - MyFig class for the myplotlib module. +# +# v 1.0.0 - Changed rawdata with inputarg to allows any type +# of rawdata, can be an array. +# +# replace the attribute format (reserved word) with +# formatted. +# +# v 1.0.1 - Suppress the explicit definitions of the keywords. +# by the introduction of the keywords dictionary. +# All parameters in this dictionary are automatically +# updated. +# +# suppress the default value of fignum and hardcode it +# to be -1. +# +# add the attribute boundedToWin required for the +# interactive mode of myplotlib. +# +# v 2.2.3 - Allow the possibility of giving a unique rawdata format +# all axes. /!\ self.rawdata is a generator. /!\ +# +# ====================================================================== +# +# +# IMPORT --------------------------------------------------------------- +from . import os +from . import D_FIGSIZE, D_INPUTARG, D_DEBUG, D_REFORMAT, D_FORMATTED +from . import D_OFORMAT, D_OPATH +from . import DBUG, SEVR, INFO, SPCE, WARN +from . import MyData +from . import MyAxes +from . import Figure + + +# Class MyFig Overwriting Matplotlib.figure.Figure +class MyFig_client(Figure): + + # Set the size of the Figure in inch + # (private variable can not be updated) + FIGSIZE = D_FIGSIZE + + # CONSTRUCTOR -------------------------------------------------------- + def __init__(self, rawdata, *args, **kwargs): + + # for the update function + self._attributesToUpdateKeys = ['fignum', 'reformat', 'debug', 'formatted', 'aliases'] + + # initialise the attribute default values and remove them from kwargs + self.fignum = kwargs.pop('fignum', -1) # need to be hard coded for interactive mode + self.reformat = kwargs.pop('reformat', D_REFORMAT) + self.debug = kwargs.pop('debug', D_DEBUG) + self.formatted = kwargs.pop('formatted', D_FORMATTED) + self.aliases = {} + self.FIGSIZE = kwargs.pop('figsize', self.FIGSIZE) + + # parent constructor + Figure.__init__(self, figsize=self.FIGSIZE) + + # This is required for the interactive mode + # it ensures that a figure can not be bounded + # to several windows and prevent the loss of canvas. + self.boundedToWin = False + + # initialise + self._initialize(*args, **kwargs) + + # INITIALIZE ------------------------------------------------------- + def _initialize(self, *args, **kwargs): + + # add the axes + self.addAxes() + + # declare the aliases + self.declareAliases() + + # update the attributes and keywords + self.update(**kwargs) + + # UPDATE ---------------------------------------------------------- + def update(self, **kwargs): + + # check attributes in keywords + for keyword in kwargs.keys(): + + # if it is an attribute + if keyword in self._attributesToUpdateKeys: + # update value + setattr(self, keyword, kwargs[keyword]) + + # For each axes update the keywords + for ax in self.get_axes(): + + forax = {} + + for keyword in kwargs.keys(): + + # ignore figure attributes + if keyword in self._attributesToUpdateKeys : + pass + + # Check if a key of kwargs has an alias for this axes + elif keyword in self.aliases.keys(): + alax, alkey = self.aliases[keyword] + + # If an alias is found then update axkwargs + if ax == alax: + forax.update(**{alkey: kwargs[keyword]}) + + # use keyword as it is for the axes + else : + forax.update(**{keyword : kwargs[keyword]}) + + # Then eventually all collected Keywords are updated in one go + if (forax) : + if self.debug: + print (DBUG+' fig.update ', ax, 'keywords: ', forax) + ax.update(**forax) + + return(True) + + # DECLARE ALIASES ------------------------------------------------- + def declareAliases(self): + pass + + # ADD AXES --------------------------------------------------------- + def addAxes(self, *arg, **kwargs): + pass + + # This overwrites MPL add_axes. It gives a name to the axis added so that it is easier to refer to + def add_axes(self, ax, name) : + +# if isinstance(name, str) and issubclass(ax.__class__, MyAxes): # This lead to an error is ax is not in the same namespace + ax.name = name +# else: +# print(SEVR + " there is an error with the input type of add_axes()") +# return False + + # test if an axes with that name is already present + for pax in self.get_axes() : + if pax.name == name : + print(SEVR + " an axes with that name is already present") + return False + + Figure.add_axes(self, ax) + + return True + + # GET AXES BY NAME ------------------------------------------------- + def getAxesByName(self, name): + + for ax in self.get_axes() : + if ax.name == name : + return ax + + #if the name is correct we should never get here. + print(SEVR + "The axes name ", name, " was not found") + return None + + + # FORMAT RAW DATA -------------------------------------------------- + def formatRawData(self): + # HEre comes a function that send a signal to the server + # and tells him to execute the format rawdata of the + # synchronized figure (server-side) + pass + + # PLOT ------------------------------------------------------------- + def plot(self): + + status = True + + if(self.debug): + print(DBUG + "currently formatting the data...") + + if((self.reformat) or (not self.formatted)): + status = self.formatRawData() + if(not status): return(False) + + if(self.debug): + print(DBUG + "currently plotting the axes...") + + # For all axes in the figure reformat if needed and plot + for ax in self.get_axes(): + + # clean the axes + ax.cla() + + # plot + plottingStatus = ax.plotting() + status = status and plottingStatus + + return(status) + + # RESET ------------------------------------------------------------ + def reset(self, *args, **kwargs): + self.clf() + self._initialize(*args, **kwargs) + self.plot() + + # DEBUG -------------------------------------------------------------- + def printDebug(self): + className = str(self.__class__.__name__) + print('\n' + DBUG + " {0} PARAMETERS: ".format(className)) + print(SPCE + " ID the figure: " + str(self.fignum)) + print(SPCE + "Size of the figure: " + str(self.FIGSIZE) + ' [inch] \n') + + # PRINT 2 FILE ------------------------------------------------------- + def print2file(self, filename, *args, **kwargs): + + # get the keywords + debug = kwargs.get('debug', D_DEBUG) + oformat = kwargs.get('oformat', D_OFORMAT) + opath = kwargs.get('opath', D_OPATH) + as_seen = kwargs.get('as_seen', False) + + # set the dpi + if(oformat == 'png'): + dpi = 300. + addMetaData = True # NOT YET IMPLEENTED + else: + dpi = 100. + addMetaData = False # NOT YET IMPLEMENTED + + from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas + # if the figure is boundedToWin + # save to canvas in a local variable and restore after saving to file. + if self.boundedToWin: + winCanvas = self.canvas + else: + winCanvas = None + + canvas = FigureCanvas(self) # the canvas of the figure changed + + # Plot the figure + if not as_seen: + status = self.plot() + if not status: + print(SEVR + 'Can not plot the figure. sorry --> EXIT') + return(False) + + # draw the figure on the new canvas + canvas.draw() # not really needed (savefig does that) + + # check if the extention is in the name + if '.png' in str(filename): + oformat = 'png' + filename = filename[:-4] + elif '.eps' in str(filename): + oformat = 'eps' + filename = filename[:-4] + + # check if the path is in the name + if '/' in str(filename): + if(debug): + print(WARN + "I detect that the path is in the name of the file, will ignore D_OPATH.") + arg = filename + filename = os.path.basename(arg) + opath = os.path.dirname(arg) + + # take care of the . in the extention + if oformat[0] == '.': + oformat = oformat[1:] + + # clean the output path + if str(opath)[-1] == '/': + opath = str(opath)[:-1] + else: + opath = str(opath) + + # print into a file + try: + self.savefig(str(opath) + '/' + str(filename) + '.' + str(oformat), dpi=dpi) # ! this not the save from plt but from fig + except IOError: + print(SEVR + 'the path to the file does not exists:') + print(SPCE + str(opath) + '/' + str(filename) + '.' + str(oformat) + ' --> EXIT') + return(False) + except: + print(SEVR + 'Can not save file. sorry --> EXIT') + return(False) + + # restore the former canvas + if winCanvas is not None: + self.canvas = winCanvas + + return(True) -- GitLab From aabf73b26c1eb20ce681a8a5d78539adbb0f33e0 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Sun, 27 Aug 2017 22:17:26 +0200 Subject: [PATCH 02/11] Add myAxes_client should be ok like that, let's see. --- myAxes_client.py | 123 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 myAxes_client.py diff --git a/myAxes_client.py b/myAxes_client.py new file mode 100644 index 0000000..f172114 --- /dev/null +++ b/myAxes_client.py @@ -0,0 +1,123 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# ================= FILE HEADER ======================================== +# +# myplotlib v3.0.1, +# +# @file myAxes.py +# @author Yori 'AGy' Fournier +# @licence CC-BY-SA +# +# MyAxes class: Overlay of matplotlib Axes class +# It is done such that the user can concentrate on +# the important aspect of the figure and not the +# technical part. It consists of a constructor, +# that requires a Figure, the ratio of the xaxis over +# the yaxis, and the frame in which the figure +# should be plotted. +# +# @Class MyAxes +# +# @Constructor(self, fig, ratio, frame, +user defined kw): +# +# @section Functions +# +# - plotting: this is the overwritten function +# from Axes. It is called by it's +# parent-figure's function .plot() +# +# - formatRawData: it computes out of the rawData +# given as argument some quantities that +# are returned as a dictionary and +# become accessable for plotting. +# +# @section History +# +# v 0.0.0 - MyAxes class for the myplotlib module, consists +# of a constructor, a pltting function and +# formatRawData. +# +# v 2.2.3 - Add testRawData function +# +# ====================================================================== +# +# +# IMPORT --------------------------------------------------------------- +from . import SEVR, DBUG +from . import Axes +from . import rcParams + + +# Class MyAxes Overwriting Matplotlib.figure.Axes +class MyAxes(Axes): + + # CONSTRUCTOR -------------------------------------------------------- + def __init__(self, fig, ratio, frameRect, *args, **kwargs): + + self.fig = fig + self.declareKeywords() + + rect = self.computeRect(ratio, frameRect, *args, **kwargs) + kwargs.pop('forceRatioOnWidth', None) + kwargs.pop('forceRatioOnHeight', None) + + # parent constructor + Axes.__init__(self, fig, rect, **kwargs) + + # set a default name. Should be individualized by instance + self.name = 'default' + + + # COMPUTE RECT ------------------------------------------------------- + def computeRect(self, ratio, frameRect, *args, **kwargs): + + # Get the optional keyword + forceRatioOnWidth = kwargs.pop('forceRatioOnWidth', None) + forceRatioOnHeight = kwargs.pop('forceRatioOnHeight', None) + + # get the frame allowed + framePosX, framePosY, frameWidth, frameHeight = frameRect + + # get the size of the figure + figWidth = self.fig.get_figwidth() + figHeight = self.fig.get_figheight() + + if(forceRatioOnWidth): + frameWidth = ratio * figWidth / (frameHeight * figHeight) + elif(forceRatioOnHeight): + frameHeight = ratio * (frameWidth * figWidth) / figHeight + + rect = [framePosX, framePosY, frameWidth, frameHeight] + + return(rect) + + # DECLARE KEYWORDS ------------------------------------------------- + def declareKeywords(self): + self.keywords = {} + + # PLOTTING ----------------------------------------------------------- + def plotting(self): + return(True) + + # UPDATE ------------------------------------------------------------- + def update(self, *args, **kwargs): + + # Because matplotlib.axes.update expect kwargs and not **kwargs ... (stupid!!) + if args: # catch matplotlib kwargs + kwargs = args[0] + # kw_for_axes = {key: value for (key, value) in args[0].items() if key not in self.keywords} # Not compatible with python2.6 + + kw_for_axes = {} + for (key, value) in args[0].items(): + if key not in self.keywords: + kw_for_axes.update({key: value}) + + Axes.update(self, kw_for_axes) # update matplotlib.Axes + + # myplotlib update + # self.keywords.update({key: value for (key, value) in kwargs.items() if key in self.keywords}) # Not compatible with python2.6 + + for (key, value) in kwargs.items(): + if key in self.keywords: + self.keywords.update({key: value}) -- GitLab From b8e02dec44d84977a250c396bafacdb1785e39d8 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Sun, 27 Aug 2017 22:30:11 +0200 Subject: [PATCH 03/11] modify the __init__ and add some axTestClient --- __init__.py | 40 ++++++++++++++-------- test/__init__.py | 30 ++++++++++++----- test/axTestClient.py | 80 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 test/axTestClient.py diff --git a/__init__.py b/__init__.py index c0e72d3..acaa9c2 100644 --- a/__init__.py +++ b/__init__.py @@ -90,11 +90,37 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): # BACKEND: u'TKAgg', u'GTKAgg', u'WXAgg', u'Qt4Agg', u'MacOSX' rcParams['backend'] = u'GTKAgg' +if D_HIERARCHY in ('CLIENT', 'client'): + + # MyAxes: Overlay on matplotlib.Axes class + from .myAxes import MyAxes_client as MyAxes + + # MyFig: Overlay on matplotlib.Figure class + from .myFig import MyFig_client as MyFig + +elif(D_HIERARCHY in ('SERVER', 'server')): + + # MyAxes: Overlay on matplotlib.Axes class + from .myAxes_server import MyAxes_server as MyAxes + + # MyFig: Overlay on matplotlib.Figure class + from .myFig_server import MyFig_server as MyFig + +elif D_HIERARCHY in ('LOCAL', 'local'): + # MyAxes: Overlay on matplotlib.Axes class from .myAxes import MyAxes # MyFig: Overlay on matplotlib.Figure class from .myFig import MyFig + +else: + + print(SEVR+'the value of D_HIERARCHY has to be SERVER, CLIENT or LOCAL') + raise ImportError + + +if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): _G_WINDOWS = [] @@ -112,20 +138,6 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): else: print(SEVR + "The backend you choosed is not supported interactive mode not available") -elif(D_HIERARCHY in ('SERVER', 'server')): - - # MyAxes: Overlay on matplotlib.Axes class - from .myAxes_server import MyAxes_server as MyAxes - - # MyFig: Overlay on matplotlib.Figure class - from .myFig_server import MyFig_server as MyFig - -else: - - print(SEVR+'the value of D_HIERARCHY has to be SERVER, CLIENT or LOCAL') - raise ImportError - - # myTool.*: interface functions to use myplotlib interactively #from .mytool import print2file # need to make tools for server ?? or just put them in client/local #from .mytool import FigOneAxes diff --git a/test/__init__.py b/test/__init__.py index 9c11c0e..7e08abd 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,7 +1,27 @@ from .. import D_HIERARCHY from .myIOs import readStupidData, readStupidData2 -if D_HIERARCHY in ('CLIENT', 'client', 'local', 'LOCAL'): +if D_HIERARCHY in ('CLIENT', 'client'): + + from .axTestClient import AxTestClient +# from .figTestClient import FigTestClient +# from .tests_client import myTest +# from .tests_client import test100 + +# testList = [test100, +# ] + +elif D_HIERARCHY in ('SERVER', 'server'): + + from .axTestServer import AxTestServer + from .figTestServer import FigTestServer + from .tests_server import myTest + from .tests_server import test100 + + testList = [test100, + ] + +elif D_HIERARCHY in ('local', 'LOCAL'): from .axTest1 import AxTest1 from .axTest1 import AxTestPlot2D, AxTestPlots2D @@ -38,10 +58,4 @@ if D_HIERARCHY in ('CLIENT', 'client', 'local', 'LOCAL'): else: - from .axTestServer import AxTestServer - from .figTestServer import FigTestServer - from .tests_server import myTest - from .tests_server import test100 - - testList = [test100, - ] + raise ImportError diff --git a/test/axTestClient.py b/test/axTestClient.py new file mode 100644 index 0000000..2187297 --- /dev/null +++ b/test/axTestClient.py @@ -0,0 +1,80 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# ================= FILE HEADER ======================================== +# +# myplotlib v3.0.1, +# +# @file myAxes.py +# @author Yori 'AGy' Fournier +# @licence CC-BY-SA +# +# MyAxes class: Overlay of matplotlib Axes class +# It is done such that the user can concentrate on +# the important aspect of the figure and not the +# technical part. It consists of a constructor, +# that requires a Figure, the ratio of the xaxis over +# the yaxis, and the frame in which the figure +# should be plotted. +# +# @Class MyAxes +# +# @Constructor(self, fig, ratio, frame, +user defined kw): +# +# @section Functions +# +# - plotting: this is the overwritten function +# from Axes. It is called by it's +# parent-figure's function .plot() +# +# - formatRawData: it computes out of the rawData +# given as argument some quantities that +# are returned as a dictionary and +# become accessable for plotting. +# +# @section History +# +# v 0.0.0 - MyAxes class for the myplotlib module, consists +# of a constructor, a pltting function and +# formatRawData. +# +# ====================================================================== +# +# +# IMPORT --------------------------------------------------------------- +from .. import SEVR, DBUG, INFO +from .. import MyAxes + +D_XRANGE = None +D_YRANGE = None +D_LOGY = False +D_LOGX = False + + +# Class MyAxes Overwriting Matplotlib.figure.Axes +class AxTestClient(MyAxes): + + def declareKeywords(self): + + self.keywords = {'xRange': D_XRANGE, + 'yRange': D_YRANGE} + + return(True) + + # PLOTTING ----------------------------------------------------------- + # the plotting function (need to be overwrite from child + def plotting(self): + + xRange = self.keywords.get('xRange') + yRange = self.keywords.get('yRange') + + try: + self.plot(self.data['xdata'], self.data['ydata']) + except KeyError: + print(SEVR + 'The formatting of the data was apparently wrong. --> EXIT') + return(False) + + if (xRange): self.set_xlim(xRange) + if (yRange): self.set_ylim(yRange) + + return(True) -- GitLab From d3f06a03fe938d0a162dc1b5e04e1d6ddebc5012 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Mon, 28 Aug 2017 17:36:48 +0200 Subject: [PATCH 04/11] add some idea of tests for the serverInterface add the serverInterface modified server modified __init__ under testing phase --- __init__.py | 6 ++ server.py | 46 ++++++++++-- serverInterface.py | 62 ++++++++++++++++ test/tests_remote.py | 164 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 serverInterface.py create mode 100644 test/tests_remote.py diff --git a/__init__.py b/__init__.py index acaa9c2..98a4a46 100644 --- a/__init__.py +++ b/__init__.py @@ -63,6 +63,9 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): from matplotlib.figure import Figure from matplotlib import is_interactive from matplotlib import use + + from .config import D_HOST, D_PORT + from socket import socket, AF_INET, SOCK_STREAM elif D_HIERARCHY in ('SERVER', 'server'): print(INFO+'Import myplotlib as a server.') @@ -99,6 +102,9 @@ if D_HIERARCHY in ('CLIENT', 'client'): from .myFig import MyFig_client as MyFig elif(D_HIERARCHY in ('SERVER', 'server')): + + # for testing purpose + from .test.myIOs import readStupidData # MyAxes: Overlay on matplotlib.Axes class from .myAxes_server import MyAxes_server as MyAxes diff --git a/server.py b/server.py index ead9474..f2a6830 100644 --- a/server.py +++ b/server.py @@ -33,12 +33,50 @@ class Server(): conn.close() break else: - self.treatAnswer() + answer = self.treatAnswer() + self.soc.sendall(answer) def treatAnswer(self): + answer = None + try: - exec(str(self.answer)) + (header, content) = exec(str(self.answer)) except: - print(INFO+"I could not execute the command") - print(INFO+'Received: '+str(self.answer)) + print(WARN+"I could not execute the command") + print(SPCE+'Received: '+str(self.answer)) + print(SPCE+"I expect '(header, content)'") + answer = ('readData', None, 'answer not correct format') + + if header in ('readData', 'READATA', 'readdata'): + try: + (dataName, functionName, args, kwargs) = exec(str(content)) + except: + print(WARN+"The content of readData could not be extracted") + print(SPCE+'Received: '+str(content)) + print(SPCE+"I expect '(dataName, functionName, args, kwargs)'") + answer = ('readData', None, 'could not extract content') + + try: + + if args is not None: + if kwargs is not None: + G_RAWDATA[dataName] = exec(str(functionName)+'('+str(args)+', '+str(kwargs)+')') + else: + G_RAWDATA[dataName] = exec(str(functionName)+'('+str(args)+')') + else: + if kwargs is not None: + G_RAWDATA[dataName] = exec(str(functionName)+'('+str(kwargs)+')') + else: + G_RAWDATA[dataName] = exec(str(functionName)+'()') + + answer = ('readData', 'dataName', 'no error') + + except: + print(WARN+"The read function could not be executed") + answer = ('readData', None, 'function could not be executed') + + else: + print(WARN+"I don't know ths signal, sorry") + + return(answer) diff --git a/serverInterface.py b/serverInterface.py new file mode 100644 index 0000000..1a70789 --- /dev/null +++ b/serverInterface.py @@ -0,0 +1,62 @@ +from socket import socket, AF_INET, SOCK_STREAM + +class ServerInterface(object): + + def __init__(self, ip, port): + + self.ip = ip + self.port = port + # just a list of names + self.currentRemoteData = [] + + def readData(self, ioFunction, dataName, *args, **kwargs): + + + content = '['+str(dataName)+', '+ioFunction.__name__+', '+str(args)+', '+str(kwargs)+']' + + signal = "("+'readData'+', '+str(content)+')' + + # create signal + sock = self.sendSignal(signal) + (answer, errmsg) = self.waitForAnswer(sock) + dataName = answer + # add data in the currentSyncData interface + if dataName is None: + print(SEVR+errmsg) + status=False + else: + self.currentSyncData.append(dataName) + status=True + + return(True) + + def newSyncFigure(self, figClass, symbolicRawdata, **kwargs): + + syncFigure = figClass(symbolicRawdata, **kwargs) + + # create a socket + # connect to ip:port + # send signal with parameters + # wait for answer: + # response is [ID, errormsg] + # ID = -1 no figure were created + # maybe error msg? + ID = 1 + # if ok then create syncFigure. + # add the identifier of the server-side figure + syncFigure.syncID = ID + # return the syncFigure + + return(syncFigure) + + def sendSignal(self, signal): + sock = socket(AF_INET, SOCK_STREAM) + sock.connect((self.ip, self.port)) + sock.sendall(str(signal)) + + return(sock) + + def waitForAnswer(self, sock): + answer = sock.recv(1024) + + return(answer) diff --git a/test/tests_remote.py b/test/tests_remote.py new file mode 100644 index 0000000..6388754 --- /dev/null +++ b/test/tests_remote.py @@ -0,0 +1,164 @@ +from .. import os +from .. import D_OFORMAT, D_OPATH +from .. import MyData +from .. import SEVR, WARN, DBUG, SPCE + +# TESTER +def myTest(function, expected, failed, debug): + + if debug: + status = function(debug) + if status is not expected: + failed.append(str(function.__name__)) + return(False) + else: + return(True) + else: + try: + status = function(debug) + if status is not expected: + failed.append(str(function.__name__)) + return(False) + else: + return(True) + except: + print(WARN + str(function.__name__) + " caught exception.") + failed.append(str(function.__name__)) + return(False) + + return(True) + + +# TEST 1.* for testing MyFig_server +# TEST 2.* for testing MyAxes_server + +# TEST 1.00: Test server.requestServerInterface +def test100(debug): + + status = True + + wrongIP = 15.12.12.12.42 + wrongPort = 22 + correctIP = 127.0.0.1 + correctPort = 50824 + + server = requestServerInterface(wrongIP, correctPORT) + + if (server is None) or (not isinstance(server, ServerInterface)): + status = True + else: + status = False + + server = requestServerInterface(correctIP, wrongPORT) + + if (server is None) or (not isinstance(server, ServerInterface)): + status = status and True + else: + status = False + + server = requestServerInterface(correctIP, correctPORT) + + if (server is None) or (not isinstance(server, ServerInterface)): + status = False + else: + status = status and True + + return(status) + + +# Test readData +def test101(debug): + + status = True + + IP = 127.0.0.1 + Port = 50824 + + server = requestServerInterface(IP, PORT) + + status = server.readData(readStupidData, 'data-name', 42, option=True) + + return(status) + + +# Test listCurrentData +def test102(debug): + + status = True + + IP = 127.0.0.1 + Port = 50824 + + server = requestServerInterface(IP, PORT) + + server.readData(readStupidData, 'data-name', 42, option=True) + listOfData = server.listCurrentData() + + if listOfData != ['data-name',]: + status = False + + return(status) + + +# test newFigure +def test103(debug): + + status = True + + IP = 127.0.0.1 + Port = 50824 + + server = requestServerInterface(IP, PORT) + + server.readData(readStupidData, 'data-name', 42, option=True) + listOfData = server.listCurrentData() + + myTestFig = server.newFigure(FigTestRemote, ('data-name', ), xRange = [2.,3.]) + + return(status) + +# test listCurrentFigures +def test104(debug): + + status = True + + IP = 127.0.0.1 + Port = 50824 + + server = requestServerInterface(IP, PORT) + + server.readData(readStupidData, 'data-name', 42, option=True) + listOfData = server.listCurrentData() + + mySyncFig = newFigure(FigTestRemote, ('data-name', ), xRange = [2.,3.]) + + listOfCurrentFigures = server.listCurrentFigures() + + if listOfCurrentFigures != [mySyncFig,]: + status = False + + return(status) + +# test plotting +def test105(debug): + + status = True + + IP = 127.0.0.1 + Port = 50824 + + server = requestServerInterface(IP, PORT) + + server.readData(readStupidData, 'data-name', 42, option=True) + listOfData = server.listCurrentData() + + mySyncFig = newFigure(FigTestRemote, ('data-name', ), xRange = [2.,3.]) + + listOfCurrentFigures = server.listCurrentFigures() + + status = server.listCurrentFigures()[0].plot() + + # this last command should be equivalent to + # mySyncFig.plot() + + return(status) -- GitLab From 26ee0ed16b1cf25e1641d2cdae0cc99ca9f6609a Mon Sep 17 00:00:00 2001 From: Yori Fournier Date: Mon, 28 Aug 2017 17:40:40 +0200 Subject: [PATCH 05/11] Some minor changes --- config-server.py | 2 +- server.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/config-server.py b/config-server.py index e918e20..7e9e47e 100644 --- a/config-server.py +++ b/config-server.py @@ -4,7 +4,7 @@ from .myData import MyData D_VERSION='v5' D_HIERARCHY='SERVER' D_HOST='localhost' -D_PORT=50228 +D_PORT=50803 # FORM ----------------------------------------------------------------- INFO = " > info-: " diff --git a/server.py b/server.py index ead9474..0886c10 100644 --- a/server.py +++ b/server.py @@ -1,6 +1,7 @@ from . import socket, AF_INET, SOCK_STREAM from . import D_HOST from . import D_PORT +from . import INFO, DBUG, WARN, SEVR, SPCE class Server(): @@ -8,10 +9,11 @@ class Server(): def __init__(self): self.soc = socket(AF_INET, SOCK_STREAM) +# self.soc.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # requires some extra import self.host = D_HOST self.port = D_PORT - self.answer = None +# self.answer = False def init(self): @@ -20,25 +22,27 @@ class Server(): def run(self): + answer = False + while True: - if not self.answer: + if not answer: conn, addr = self.soc.accept() print(INFO + 'Connected by ' + str(addr)) - self.answer = conn.recv(1024) + answer = conn.recv(1024) - if self.answer in ('KILL', 'kill', 'Kill'): + if answer in ('KILL', 'kill', 'Kill'): print(INFO+"Recieved 'Kill' signal.") conn.close() break else: - self.treatAnswer() + self.treatAnswer(answer) - def treatAnswer(self): + def treatAnswer(self, answer): try: - exec(str(self.answer)) + exec(str(answer)) except: print(INFO+"I could not execute the command") - print(INFO+'Received: '+str(self.answer)) + print(INFO+'Received: '+str(answer)) -- GitLab From 35beaaeb940eb7c0ca596d3f07e39a28b3d5771e Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Mon, 28 Aug 2017 22:39:53 +0200 Subject: [PATCH 06/11] Correct serverInterface --- serverInterface.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/serverInterface.py b/serverInterface.py index 1a70789..66e151f 100644 --- a/serverInterface.py +++ b/serverInterface.py @@ -11,21 +11,21 @@ class ServerInterface(object): def readData(self, ioFunction, dataName, *args, **kwargs): - - content = '['+str(dataName)+', '+ioFunction.__name__+', '+str(args)+', '+str(kwargs)+']' + content = "['"+str(dataName)+"', '"+ioFunction.__name__+"', '"+str(args)+"', '"+str(kwargs)+"']" - signal = "("+'readData'+', '+str(content)+')' + signal = "("+"'readData'"+', '+str(content)+')' # create signal sock = self.sendSignal(signal) - (answer, errmsg) = self.waitForAnswer(sock) - dataName = answer + answer = self.waitForAnswer(sock) + header, content, errmsg = eval(answer) + dataName = content # add data in the currentSyncData interface if dataName is None: print(SEVR+errmsg) status=False else: - self.currentSyncData.append(dataName) + self.currentRemoteData.append(dataName) status=True return(True) -- GitLab From 384fa456f3904477ec3c7926254819f35c4b0682 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Wed, 30 Aug 2017 21:43:37 +0200 Subject: [PATCH 07/11] In between commit Can now create a figure on the server side Now requires the figure client --- __init__.py | 6 ++++-- myAxes_client.py | 2 +- serverInterface.py | 23 ++++++++++++++++++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index a1bf2ff..33e5a89 100644 --- a/__init__.py +++ b/__init__.py @@ -96,10 +96,12 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): if D_HIERARCHY in ('CLIENT', 'client'): # MyAxes: Overlay on matplotlib.Axes class - from .myAxes import MyAxes_client as MyAxes + from .myAxes_client import MyAxes_client as MyAxes # MyFig: Overlay on matplotlib.Figure class - from .myFig import MyFig_client as MyFig + from .myFig_client import MyFig_client as MyFig + + from .serverInterface import ServerInterface elif(D_HIERARCHY in ('SERVER', 'server')): diff --git a/myAxes_client.py b/myAxes_client.py index f172114..5562e62 100644 --- a/myAxes_client.py +++ b/myAxes_client.py @@ -50,7 +50,7 @@ from . import rcParams # Class MyAxes Overwriting Matplotlib.figure.Axes -class MyAxes(Axes): +class MyAxes_client(Axes): # CONSTRUCTOR -------------------------------------------------------- def __init__(self, fig, ratio, frameRect, *args, **kwargs): diff --git a/serverInterface.py b/serverInterface.py index 66e151f..3b2403c 100644 --- a/serverInterface.py +++ b/serverInterface.py @@ -32,7 +32,15 @@ class ServerInterface(object): def newSyncFigure(self, figClass, symbolicRawdata, **kwargs): - syncFigure = figClass(symbolicRawdata, **kwargs) +# syncFigure = figClass(symbolicRawdata, **kwargs) + + content = "['"+str(figClass.__name__)+"', \""+str(symbolicRawdata)+"\", '"+str(kwargs)+"']" + signal = "("+"'newSyncFigure'"+", "+str(content)+")" + + sock = self.sendSignal(signal) + + answer = self.waitForAnswer(sock) + header, content, errmsg = eval(answer) # create a socket # connect to ip:port @@ -41,12 +49,21 @@ class ServerInterface(object): # response is [ID, errormsg] # ID = -1 no figure were created # maybe error msg? - ID = 1 + ID = content # if ok then create syncFigure. + if ID is not None: + syncFigure = figClass(symbolicRawdata, **kwargs) + syncFigure.syncID = ID + else: + print(WARN + 'The server could not create the figure') + print(errmsg) + syncFigure = None + # add the identifier of the server-side figure - syncFigure.syncID = ID + #syncFigure.syncID = ID # return the syncFigure +# return(True) return(syncFigure) def sendSignal(self, signal): -- GitLab From 91f99163b2411490e3197de816b2239d09d7fbf7 Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Thu, 31 Aug 2017 00:09:34 +0200 Subject: [PATCH 08/11] Now local figure will be create if server-side figure is also well created until now the Figure are different FigTestClient and FigTestServer they are both imported on both side as FigTest. This does the trick. But I would like to introduce decoration to have a single class. --- __init__.py | 5 +++-- test/__init__.py | 3 ++- test/figTestClient.py | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 test/figTestClient.py diff --git a/__init__.py b/__init__.py index 33e5a89..9f304fb 100644 --- a/__init__.py +++ b/__init__.py @@ -160,8 +160,9 @@ if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): if D_HIERARCHY in ('CLIENT', 'client', 'LOCAL', 'local'): - from .test import myTest - from .test import testList +# from .test import myTest +# from .test import testList + from .test import FigTest # for testing purpose from .test import readStupidData diff --git a/test/__init__.py b/test/__init__.py index 20e4671..374bd2a 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -4,9 +4,10 @@ from .myIOs import readStupidData, readStupidData2 if D_HIERARCHY in ('CLIENT', 'client'): from .axTestClient import AxTestClient -# from .figTestClient import FigTestClient + from .figTestClient import FigTestClient as FigTest # from .tests_client import myTest # from .tests_client import test100 +# from .tests import myTest # testList = [test100, # ] diff --git a/test/figTestClient.py b/test/figTestClient.py new file mode 100644 index 0000000..c5e5323 --- /dev/null +++ b/test/figTestClient.py @@ -0,0 +1,23 @@ +# IMPORT --------------------------------------------------------------- +from .. import D_HIERARCHY +from .. import SEVR, DBUG, INFO + +if D_HIERARCHY in ('Client', 'CLIENT', 'client'): + print(INFO+"Testing the client.") +else: + print(SEVR+"This is atest for the client, the imports are not correct.") + raise ImportError + +from .. import MyFig +from . import AxTestClient + +class FigTestClient(MyFig): + + FIGSIZE = (8., 6.) + + def addAxes(self): + + ratio = 6. / 8. # height/width of the axes (in inch) + frame1 = [0.1, 0.1, 0.8, 0.8] # part of the fig that is available + + self.add_axes(AxTestClient(self, ratio, frame1), "p1") -- GitLab From f4bdcba00ace9a702d10654764f76b950d5a7595 Mon Sep 17 00:00:00 2001 From: Yori Fournier Date: Thu, 31 Aug 2017 00:13:50 +0200 Subject: [PATCH 09/11] server-side of the tested version. Now figure is created on both side. newSyncFigure works --- __init__.py | 5 +++-- myAxes_server.py | 1 - server.py | 32 +++++++++++++++++++++++++++++++- test/__init__.py | 5 ++--- test/axTestServer.py | 3 +++ 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index a1bf2ff..0b2ee9c 100644 --- a/__init__.py +++ b/__init__.py @@ -103,13 +103,14 @@ if D_HIERARCHY in ('CLIENT', 'client'): elif(D_HIERARCHY in ('SERVER', 'server')): - - # MyAxes: Overlay on matplotlib.Axes class from .myAxes_server import MyAxes_server as MyAxes # MyFig: Overlay on matplotlib.Figure class from .myFig_server import MyFig_server as MyFig + +# from .test import FigTestServer + from .test import FigTest elif D_HIERARCHY in ('LOCAL', 'local'): diff --git a/myAxes_server.py b/myAxes_server.py index fd7a5dc..8b6643d 100644 --- a/myAxes_server.py +++ b/myAxes_server.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # # ================= FILE HEADER ======================================== diff --git a/server.py b/server.py index 446c05a..5b2e02e 100644 --- a/server.py +++ b/server.py @@ -4,9 +4,10 @@ from . import D_PORT from . import INFO, DBUG, WARN, SEVR, SPCE from . import readStupidData +from . import FigTest G_RAWDATA = {} - +G_FIGURES = {} class Server(): @@ -53,6 +54,7 @@ class Server(): except: print(WARN+"I could not execute the command") print(SPCE+'Received: '+str(signal)) + print(SPCE+'Received: '+eval(str(signal))) print(SPCE+"I expect '(header, content)'") answer = ('readData', None, 'answer not correct format') return(answer) @@ -90,6 +92,34 @@ class Server(): answer = ('readData', None, 'function could not be executed') return(answer) + elif header in ('newSyncFigure'): + + try: + (figClass, rawdata, kwargs) = eval(str(content)) + except: + print(WARN+"The content of readData could not be extracted") + print(SPCE+'Received: '+str(content)) + print(SPCE+"I expect '(figClass, rawdata, kwargs)'") + answer = ('newSyncFigure', None, 'could not extract content') + return(answer) + + if(True): +# try: +# print(figClass.__name__, rawdata, kwargs) + FigID = 'Test' + datas = "(" + for raw in eval(rawdata): + datas = datas + "G_RAWDATA['"+raw+"']," + datas = datas + ")" + print(datas) + print(figClass+'('+str(datas)+', '+kwargs+')') + G_FIGURES[FigID] = eval(figClass+'('+str(datas)+', '+kwargs+')') + answer = ('newSyncFigure', FigID, '') +# except: +# print(WARN+"The required fig could not be created") +# answer = ('newSyncFigure', None, 'could not create the Figure') +# return(answer) + else: print(WARN+"I don't know ths signal, sorry") answer = ('readData', None, 'signal unknown') diff --git a/test/__init__.py b/test/__init__.py index 20e4671..b925b9f 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -12,9 +12,8 @@ if D_HIERARCHY in ('CLIENT', 'client'): # ] elif D_HIERARCHY in ('SERVER', 'server'): - pass -# from .axTestServer import AxTestServer -# from .figTestServer import FigTestServer + from .axTestServer import AxTestServer + from .figTestServer import FigTestServer as FigTest # from .tests_server import myTest # from .tests_server import test100 diff --git a/test/axTestServer.py b/test/axTestServer.py index 0143bf7..50fbd6b 100644 --- a/test/axTestServer.py +++ b/test/axTestServer.py @@ -51,8 +51,11 @@ else: print(SEVR+"This is atest for the server, the imports are not correct.") raise ImportError +print(INFO+"Just before Imported my Axes") from .. import MyAxes +print(INFO+"Imported my Axes") + D_XRANGE = None D_YRANGE = None D_LOGY = False -- GitLab From 0037ffb21a24c7c2385fd04dc8d7e33d4bbb8d88 Mon Sep 17 00:00:00 2001 From: Yori Fournier Date: Thu, 31 Aug 2017 17:45:04 +0200 Subject: [PATCH 10/11] Server-side now accepting signal of type syncFigFormatRawData tested now win = MyWin(fig) works --- server.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/server.py b/server.py index 5b2e02e..c1487c1 100644 --- a/server.py +++ b/server.py @@ -120,6 +120,36 @@ class Server(): # answer = ('newSyncFigure', None, 'could not create the Figure') # return(answer) + elif header in ('syncFigFormatRawData'): + + try: + (figID,) = eval(str(content)) + except: + print(WARN+"The content of syncFigFormatRawData could not be extracted") + print(SPCE+'Received: '+str(content)) + print(SPCE+"I expect '(figID,)'") + answer = ('syncFigFormatRawData', None, 'could not extract content') + return(answer) + + try: + fig = G_FIGURES[figID] + except: + print(WARN+"Figure ID does not exists.") + print(SPCE+str(figID)) + answer = ('syncFigFormatRawData', None, 'Figure ID does not exists.') + + try: + fig.formatRawData() + except: + print(WARN+'Could not format the rawdata of the figure.') + answer = ('syncFigFormatRawData', None, 'Could not format the rawdata of the figure.') + + datas = [] + for ax in fig.get_axes(): + datas.append(ax.data) + + answer = ('syncFigFormatRawData', datas, '') + else: print(WARN+"I don't know ths signal, sorry") answer = ('readData', None, 'signal unknown') -- GitLab From 17bd3f3ea519fc0d7fd28a4c02f6a8e19f2f0d7f Mon Sep 17 00:00:00 2001 From: Yori 'AGy' Fournier Date: Thu, 31 Aug 2017 17:47:15 +0200 Subject: [PATCH 11/11] now client-side is able to send signal of type syncFigFormatRawData and treat the data such that win = MyWin(fig) will works. --- myFig_client.py | 21 +++++++++++++++++++-- serverInterface.py | 42 ++++++++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/myFig_client.py b/myFig_client.py index ee5ceea..bd1493f 100644 --- a/myFig_client.py +++ b/myFig_client.py @@ -95,6 +95,8 @@ class MyFig_client(Figure): self.debug = kwargs.pop('debug', D_DEBUG) self.formatted = kwargs.pop('formatted', D_FORMATTED) self.aliases = {} + self.syncID = -1 + self.client = None self.FIGSIZE = kwargs.pop('figsize', self.FIGSIZE) # parent constructor @@ -206,8 +208,23 @@ class MyFig_client(Figure): # HEre comes a function that send a signal to the server # and tells him to execute the format rawdata of the # synchronized figure (server-side) - pass - + if (self.client is not None) and (self.syncID != -1): + + try: + datas = self.client.syncFigFormatRawData(self.syncID) + except: + print(SEVR+'The server-side figure could not format the data...') + return(False) + + for ax, data in zip(self.get_axes(), datas): + ax.data = data + + else: + print(SEVR+'The figure is not properly synchronized.') + return(False) + + return(True) + # PLOT ------------------------------------------------------------- def plot(self): diff --git a/serverInterface.py b/serverInterface.py index 3b2403c..358b461 100644 --- a/serverInterface.py +++ b/serverInterface.py @@ -32,8 +32,8 @@ class ServerInterface(object): def newSyncFigure(self, figClass, symbolicRawdata, **kwargs): -# syncFigure = figClass(symbolicRawdata, **kwargs) - + syncFigure = figClass(symbolicRawdata, **kwargs) + content = "['"+str(figClass.__name__)+"', \""+str(symbolicRawdata)+"\", '"+str(kwargs)+"']" signal = "("+"'newSyncFigure'"+", "+str(content)+")" @@ -42,30 +42,40 @@ class ServerInterface(object): answer = self.waitForAnswer(sock) header, content, errmsg = eval(answer) - # create a socket - # connect to ip:port - # send signal with parameters - # wait for answer: - # response is [ID, errormsg] - # ID = -1 no figure were created - # maybe error msg? ID = content - # if ok then create syncFigure. + + # Figure was created then link sync version locally if ID is not None: - syncFigure = figClass(symbolicRawdata, **kwargs) syncFigure.syncID = ID + syncFigure.client = self + # else delete the sync Figure else: print(WARN + 'The server could not create the figure') print(errmsg) + del(syncFigure) syncFigure = None - # add the identifier of the server-side figure - #syncFigure.syncID = ID - # return the syncFigure - -# return(True) return(syncFigure) + def syncFigFormatRawData(self, syncID): + + content = "['"+str(syncID)+"']" + signal = "("+"'syncFigFormatRawData'"+", "+str(content)+")" + + sock = self.sendSignal(signal) + + answer = self.waitForAnswer(sock) + header, content, errmsg = eval(answer) + + if content is not None: + data = content + else: + print(WARN + 'The server could not format the rawdata.') + print(errmsg) + data = None + + return(data) + def sendSignal(self, signal): sock = socket(AF_INET, SOCK_STREAM) sock.connect((self.ip, self.port)) -- GitLab