diff --git a/Components.py b/Components.py index 343c60da..6a606e14 100644 --- a/Components.py +++ b/Components.py @@ -403,7 +403,7 @@ def BlockModelAdapter(cls, label="", specific_behavior=""): args = GetArgs(cls) ### find if there is filename param on the constructor and if there is no extention - L = [os.path.isabs(str(a)) for a in list(args.values())] + L = [os.path.isabs(str(a)) or str(a)=='result' for a in list(args.values())] filename_without_ext_flag = L.index(True) if True in L else -1 ### if there is a filename and if there is no extention -> its a to disk like object disk_model = filename_without_ext_flag >= 0 and not os.path.splitext(list(args.values())[filename_without_ext_flag])[-1] != '' diff --git a/Container.py b/Container.py index 6e423b55..3b156e20 100644 --- a/Container.py +++ b/Container.py @@ -3633,7 +3633,7 @@ def __setstate__(self, state): finally: clsmembers = inspect.getmembers(module, inspect.isclass) names = [t[0] for t in clsmembers] - + ### if model inherite of ScopeGUI, it requires to redefine the class with the ScopeGUI class if 'To_Disk' in names or 'MessagesCollector' in names: new_class = DiskGUI diff --git a/Domain/Collector/MessagesCollector.py b/Domain/Collector/MessagesCollector.py index 1d83223c..745bfad9 100644 --- a/Domain/Collector/MessagesCollector.py +++ b/Domain/Collector/MessagesCollector.py @@ -24,17 +24,17 @@ class MessagesCollector(DomainBehavior): """ ### - def __init__(self, fileName = None, ext = '.dat', comma = ""): + def __init__(self, fileName = "result", ext = '.dat', comma = ""): """ Constructor. - @param fileName : name of output fileName - @param ext : output file extension - @param comma : comma separated + @param fileName: name of output fileName + @param ext: output file extension + @param comma: comma separated """ DomainBehavior.__init__(self) ### a way to overcome the random initialization of the fileNam attr directly in the param list of the constructor! - fileName = fileName if fileName is not None else os.path.join(os.getcwd(),"out","result%d"%random.randint(1,100)) + fileName = fileName if fileName!= 'result' else os.path.join(os.getcwd(),"out","result%d"%random.randint(1,100)) # local copy self.fileName = fileName diff --git a/Domain/Collector/To_Disk.py b/Domain/Collector/To_Disk.py index 3f3aa1a6..d24cc731 100644 --- a/Domain/Collector/To_Disk.py +++ b/Domain/Collector/To_Disk.py @@ -37,17 +37,20 @@ class To_Disk(QuickScope): """ ### - def __init__(self, fileName = os.path.join(os.getcwd(),"result%d"%random.randint(1,100)), eventAxis = False, comma = " ", ext = '.dat', col = 0): + def __init__(self, fileName = "result", eventAxis = False, comma = " ", ext = '.dat', col = 0): """ Constructor. - @param fileName : Name of output fileName - @param eventAxis : Flag to plot depending events axis - @param comma : Comma symbol - @param ext : Output file extension - @param col : Considered column + @param fileName: Name of output fileName + @param eventAxis: Flag to plot depending events axis + @param comma: Comma symbol + @param ext: Output file extension + @param col: Considered column """ QuickScope.__init__(self) + ### a way to overcome the random initialization of the fileNam attr directly in the param list of the constructor! + fileName = fileName if fileName!= 'result' else os.path.join(os.getcwd(),"out","result%d"%random.randint(1,100)) + # local copy self.fileName = fileName self.comma = comma diff --git a/Domain/Generator/RandomGenerator.py b/Domain/Generator/RandomGenerator.py index 1bd08674..ebb49d2e 100644 --- a/Domain/Generator/RandomGenerator.py +++ b/Domain/Generator/RandomGenerator.py @@ -14,14 +14,15 @@ class RandomGenerator(DomainBehavior): @version: 1.0 """ - def __init__(self, minValue=0, maxValue=10, minStep=1, maxStep=1, start=0): + def __init__(self, minValue=0, maxValue=10, minStep=1, maxStep=1, start=0, choice=[]): """ Constructor. - @param minValue : minimum value - @param maxValue : maximum value - @param minStep : minimum step - @param maxStep : maximum step - @param start : time start + @param minValue: minimum value + @param maxValue: maximum value + @param minStep: minimum step + @param maxStep: maximum step + @param start: time start + @param choice: list of items """ DomainBehavior.__init__(self) @@ -32,6 +33,8 @@ def __init__(self, minValue=0, maxValue=10, minStep=1, maxStep=1, start=0): self.maxValue = maxValue self.minStep = minStep self.maxStep = maxStep + self.choice = choice + self.msg = Message(None, None) def outputFnc(self): @@ -41,7 +44,7 @@ def outputFnc(self): portsToSend = random.sample(self.OPorts, numberMessage) # The port with number message for port in portsToSend: - value = random.randint(self.minValue, self.maxValue) + value = random.randint(self.minValue, self.maxValue) if self.choice else random.choice(self.choice) self.msg.value = [value, 0.0, 0.0] self.msg.time = self.timeNext ### adapted with PyPDEVS diff --git a/plugins/state_trajectory.py b/plugins/state_trajectory.py index 7f0d5f6b..729ce02b 100644 --- a/plugins/state_trajectory.py +++ b/plugins/state_trajectory.py @@ -2,7 +2,7 @@ """ Authors: L. Capocchi (capocchi@univ-corse.fr) - Date: 08/11/2014 + Date: 30/10/2020 Description: Plot the state trajectory of model. Based on transition function decorator. @@ -14,16 +14,70 @@ ### ---------------------------------------------------------- ### at the beginning to prevent with statement for python version <=2.5 - +from __future__ import with_statement import sys import wx import os import inspect +import subprocess +import importlib + +required_libs = ['matplotlib'] + +for lib_name in required_libs: + try: + importlib.import_module(lib_name) + except: + subprocess.run(f'pip install {lib_name}'.split()) + +#import matplotlib.pyplot as plt + +import wx.lib.agw.aui as aui +#import wx.lib.mixins.inspection as wit + +import matplotlib as mpl +from matplotlib.backends.backend_wxagg import ( + FigureCanvasWxAgg as FigureCanvas, + NavigationToolbar2WxAgg as NavigationToolbar) + +class PlotPanel(wx.Panel): + def __init__(self, parent, id=-1, dpi=None, **kwargs): + wx.Panel.__init__(self, parent, id=id, **kwargs) + self.figure = mpl.figure.Figure(dpi=dpi, figsize=(2, 2)) + self.canvas = FigureCanvas(self, -1, self.figure) + self.toolbar = NavigationToolbar(self.canvas) + self.toolbar.Realize() + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(self.canvas, 1, wx.EXPAND) + sizer.Add(self.toolbar, 0, wx.LEFT | wx.EXPAND) + self.SetSizer(sizer) + self.SetAutoLayout(True) + +class PlotNotebook(wx.Panel): + def __init__(self, parent, id=-1): + wx.Panel.__init__(self, parent, id=id) + self.nb = aui.AuiNotebook(self) + sizer = wx.BoxSizer() + sizer.Add(self.nb, 1, wx.EXPAND) + self.SetSizer(sizer) + self.SetAutoLayout(True) + + def add(self, name="plot"): + page = PlotPanel(self.nb) + self.nb.AddPage(page, name) + return page.figure + +# to send event +try: + from pubsub import pub +except Exception: + sys.stdout.write('Last version for Python2 is PyPubSub 3.3.0 \n pip install PyPubSub==3.3.0') + sys.exit() from PluginManager import PluginManager from Container import Block, CodeBlock, ContainerBlock -from Utilities import install_and_import ID_SHAPE = wx.NewIdRef() @@ -31,13 +85,14 @@ def log(func): def wrapped(*args, **kwargs): try: + #print "Entering: [%s] with parameters %s" % (func.__name__, args) try: ### DEVS instance devs = func.__self__ ### condition - cond = hasattr(devs, 'state') and 'status' in list(devs.state.keys()) + cond = hasattr(devs, 'state') and 'status' in devs.state.keys() ### is DEVS model has no state_trajectory attribute, we create it only for init! @@ -74,7 +129,7 @@ def state_trajectory_decorator(inst): ''' for name, m in inspect.getmembers(inst, inspect.isfunction)+inspect.getmembers(inst, inspect.ismethod): - if name in list(inst.getBlockModel().state_trajectory.values()): + if name in inst.getBlockModel().state_trajectory.values(): setattr(inst, name, log(m)) return inst @@ -105,7 +160,7 @@ def GetFlatShapesList(diagram,L): return L def PlotStateTrajectory(m): - """ Plot the state trajectory of m model + """ Plot the state trajectory of m model. """ if m: @@ -121,45 +176,31 @@ def PlotStateTrajectory(m): y = [] ### adapted to PyPDEVS - times_lst = [a[0] if isinstance(a, tuple) else a for a in list(st.keys())] + times_lst = list(map(lambda a: a[0] if isinstance(a, tuple) else a, st.keys())) states_lst = [states.index(st[k]) for k in st] - items = list(zip(times_lst, states_lst)) - + items = zip(times_lst, states_lst) + sorted_items = sorted(items, key=lambda x: (x[0], x[1])) - - x, y = list(zip(*sorted_items)) + + x, y = zip(*sorted_items) assert len(x)==len(y) - #for plotting - if install_and_import('matplotlib'): - import matplotlib.pyplot as plt - - fig = plt.figure() - - ### change the title of the plot - #fig.suptitle('%s State Trajectory'%label, fontsize=17) - - ### change the title of the plot window - fig.canvas.set_window_title('%s State Trajectory'%label) - - ### changes the color of the space around the plot - fig.patch.set_facecolor('white') - - ### change the axis label - plt.xlabel('time', fontsize=16) - plt.ylabel('state', fontsize=16) - - ax = fig.add_subplot(111) - ### changes the color of the space inside the plot - ax.patch.set_facecolor('white') - - ax.step(x, y) - plt.yticks(list(range(len(states))), states, size='small') - - plt.show() + frame = wx.Frame(None, -1, 'Plotter') + plotter = PlotNotebook(frame) + axes1 = plotter.add('%s State Trajectory'%label).gca() + axes1.set_yticks(range(len(states))) + axes1.set_yticklabels(states) + axes1.set_xlabel('time',fontsize=16) + axes1.set_ylabel('state',fontsize=16) + axes1.plot(x, y) + + #axes2 = plotter.add('figure 2').gca() + #axes2.plot([1, 2, 3, 4, 5], [2, 1, 4, 2, 3]) + + frame.Show() else: dial = wx.MessageDialog(None, @@ -205,7 +246,7 @@ def start_state_trajectory(*args, **kwargs): master = kwargs['master'] parent = kwargs['parent'] - if not pluginmanager.is_enable('start_activity_tracking'): + if not PluginManager.is_enable('start_activity_tracking'): for devs in GetFlatDEVSList(master, []): block = devs.getBlockModel() if hasattr(block, 'state_trajectory'): @@ -249,10 +290,10 @@ def Config(parent): master = None frame = wx.Frame(parent, + wx.ID_ANY, title = _('State Trajectory Plotting'), - style = wx.DEFAULT_FRAME_STYLE | wx.CLIP_CHILDREN) - - panel = wx.Panel(frame) + style = wx.DEFAULT_FRAME_STYLE | wx.CLIP_CHILDREN | wx.STAY_ON_TOP) + panel = wx.Panel(frame, wx.ID_ANY) lst_1 = GetFlatShapesList(diagram,[]) lst_2 = ('confTransition', 'extTransition', 'intTransition') @@ -261,15 +302,15 @@ def Config(parent): hbox = wx.BoxSizer(wx.HORIZONTAL) hbox2 = wx.BoxSizer(wx.HORIZONTAL) - st = wx.StaticText(panel, wx.NewIdRef(), _("Select models and functions:"), (10,10)) + st = wx.StaticText(panel, wx.ID_ANY, _("Select models and functions:"), (10,10)) - cb1 = wx.CheckListBox(panel, wx.NewIdRef(), (10, 30), wx.DefaultSize, lst_1, style=wx.LB_SORT) - cb2 = wx.CheckListBox(panel, wx.NewIdRef(), (10, 30), wx.DefaultSize, lst_2) + cb1 = wx.CheckListBox(panel, wx.ID_ANY, (10, 30), wx.DefaultSize, lst_1, style=wx.LB_SORT) + cb2 = wx.CheckListBox(panel, wx.ID_ANY, (10, 30), wx.DefaultSize, lst_2) selBtn = wx.Button(panel, wx.ID_SELECTALL) - desBtn = wx.Button(panel, wx.NewIdRef(), _('Deselect All')) + desBtn = wx.Button(panel, wx.ID_ANY, _('Deselect All')) okBtn = wx.Button(panel, wx.ID_OK) - #reportBtn = wx.Button(panel, wx.NewIdRef(), _('Report')) + #reportBtn = wx.Button(panel, wx.ID_ANY, _('Report')) hbox2.Add(cb1, 1, wx.EXPAND, 5) hbox2.Add(cb2, 1, wx.EXPAND, 5) @@ -292,14 +333,14 @@ def Config(parent): block=diagram.GetShapeByLabel(cb1.GetString(index)) if hasattr(block,'state_trajectory'): L1.append(index) - L2[block.label] = list(block.state_trajectory.keys()) + L2[block.label] = block.state_trajectory.keys() if L1: cb1.SetCheckedItems(L1) - ### tout les block on la meme liste de function active pour le trace, donc on prend la première + ### tout les blocks on la meme liste de function active pour le trace, donc on prend la premi�re cb2.SetCheckedItems(list(L2.values())[0]) - ### ckeck delta_ext and delta_int + ### ckeck par defaut delta_ext et delta_int if L2 == {}: cb2.SetCheckedItems([1,2]) @@ -316,7 +357,7 @@ def OnPlot(event): def OnSelectAll(evt): """ Select All button has been pressed and all plug-ins are enabled. """ - cb1.SetCheckedItems(list(range(cb1.GetCount()))) + cb1.SetCheckedItems(range(cb1.GetCount())) def OnDeselectAll(evt): """ Deselect All button has been pressed and all plugins are disabled.