controls/DebugVariablePanel.py
changeset 909 852af7c6f0ef
parent 907 591cb3d96980
child 912 bf855ccf3705
equal deleted inserted replaced
908:50a8192fbb23 909:852af7c6f0ef
    54 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
    54 RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
    55 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
    55 TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
    56                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
    56                     [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
    57                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
    57                     [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
    58 
    58 
       
    59 GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
       
    60 
    59 def AppendMenu(parent, help, id, kind, text):
    61 def AppendMenu(parent, help, id, kind, text):
    60     parent.Append(help=help, id=id, kind=kind, text=text)
    62     parent.Append(help=help, id=id, kind=kind, text=text)
    61 
    63 
    62 def GetDebugVariablesTableColnames():
    64 def GetDebugVariablesTableColnames():
    63     _ = lambda x : x
    65     _ = lambda x : x
    64     cols = [_("Variable"), _("Value")]
    66     return [_("Variable"), _("Value")]
    65     if USE_MPL:
    67     
    66         cols.append(_("3DAxis"))
       
    67     return cols
       
    68 
       
    69 class VariableTableItem(DebugDataConsumer):
    68 class VariableTableItem(DebugDataConsumer):
    70     
    69     
    71     def __init__(self, parent, variable):
    70     def __init__(self, parent, variable):
    72         DebugDataConsumer.__init__(self)
    71         DebugDataConsumer.__init__(self)
    73         self.Parent = parent
    72         self.Parent = parent
    74         self.Variable = variable
    73         self.Variable = variable
    75         self.RefreshVariableType()
    74         self.RefreshVariableType()
    76         self.Value = ""
    75         self.Value = ""
    77         self.Axis3D = False
    76         
    78     
       
    79     def __del__(self):
    77     def __del__(self):
    80         self.Parent = None
    78         self.Parent = None
    81     
    79     
    82     def SetVariable(self, variable):
    80     def SetVariable(self, variable):
    83         if self.Parent and self.Variable != variable:
    81         if self.Parent and self.Variable != variable:
   163             return "\"%s\"" % self.Value
   161             return "\"%s\"" % self.Value
   164         elif isinstance(self.Value, FloatType):
   162         elif isinstance(self.Value, FloatType):
   165             return "%.6g" % self.Value
   163             return "%.6g" % self.Value
   166         return self.Value
   164         return self.Value
   167 
   165 
   168     def SetAxis3D(self, axis_3d):
       
   169         if self.IsNumVariable():
       
   170             self.Axis3D = axis_3d
       
   171         
       
   172     def GetAxis3D(self):
       
   173         if self.IsNumVariable():
       
   174             return self.Axis3D
       
   175         return ""
       
   176 
       
   177     def GetNearestData(self, tick, adjust):
   166     def GetNearestData(self, tick, adjust):
   178         if self.IsNumVariable():
   167         if self.IsNumVariable():
   179             ticks = self.Data[:, 0]
   168             ticks = self.Data[:, 0]
   180             new_cursor = numpy.argmin(abs(ticks - tick))
   169             new_cursor = numpy.argmin(abs(ticks - tick))
   181             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   170             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   200         if row < self.GetNumberRows():
   189         if row < self.GetNumberRows():
   201             if colname == "Variable":
   190             if colname == "Variable":
   202                 return self.data[row].GetVariable()
   191                 return self.data[row].GetVariable()
   203             elif colname == "Value":
   192             elif colname == "Value":
   204                 return self.data[row].GetValue()
   193                 return self.data[row].GetValue()
   205             elif colname == "3DAxis":
       
   206                 return self.data[row].GetAxis3D()
       
   207         return ""
   194         return ""
   208 
   195 
   209     def SetValueByName(self, row, colname, value):
   196     def SetValueByName(self, row, colname, value):
   210         if row < self.GetNumberRows():
   197         if row < self.GetNumberRows():
   211             if colname == "Variable":
   198             if colname == "Variable":
   212                 self.data[row].SetVariable(value)
   199                 self.data[row].SetVariable(value)
   213             elif colname == "Value":
   200             elif colname == "Value":
   214                 self.data[row].SetValue(value)
   201                 self.data[row].SetValue(value)
   215             elif colname == "3DAxis":
       
   216                 self.data[row].SetAxis3D(value)
       
   217     
   202     
   218     def IsForced(self, row):
   203     def IsForced(self, row):
   219         if row < self.GetNumberRows():
   204         if row < self.GetNumberRows():
   220             return self.data[row].IsForced()
   205             return self.data[row].IsForced()
   221         return False
   206         return False
   234         """
   219         """
   235         
   220         
   236         for row in range(self.GetNumberRows()):
   221         for row in range(self.GetNumberRows()):
   237             for col in range(self.GetNumberCols()):
   222             for col in range(self.GetNumberCols()):
   238                 colname = self.GetColLabelValue(col, False)
   223                 colname = self.GetColLabelValue(col, False)
   239                 if colname == "3DAxis":
   224                 if colname == "Value":
   240                     if self.IsNumVariable(row):
   225                     if self.IsForced(row):
   241                         grid.SetCellRenderer(row, col, wx.grid.GridCellBoolRenderer())
   226                         grid.SetCellTextColour(row, col, wx.BLUE)
   242                         grid.SetCellEditor(row, col, wx.grid.GridCellBoolEditor())
       
   243                         grid.SetReadOnly(row, col, False)
       
   244                     else:
   227                     else:
   245                         grid.SetReadOnly(row, col, True)
   228                         grid.SetCellTextColour(row, col, wx.BLACK)
   246                 else:
   229                 grid.SetReadOnly(row, col, True)
   247                     if colname == "Value":
       
   248                         if self.IsForced(row):
       
   249                             grid.SetCellTextColour(row, col, wx.BLUE)
       
   250                         else:
       
   251                             grid.SetCellTextColour(row, col, wx.BLACK)
       
   252                     grid.SetReadOnly(row, col, True)
       
   253             self.ResizeRow(grid, row)
   230             self.ResizeRow(grid, row)
   254                 
   231                 
   255     def AppendItem(self, data):
   232     def AppendItem(self, data):
   256         self.data.append(data)
   233         self.data.append(data)
   257     
   234     
   267     def GetItem(self, idx):
   244     def GetItem(self, idx):
   268         return self.data[idx]
   245         return self.data[idx]
   269 
   246 
   270 class DebugVariableDropTarget(wx.TextDropTarget):
   247 class DebugVariableDropTarget(wx.TextDropTarget):
   271     
   248     
   272     def __init__(self, parent):
   249     def __init__(self, parent, control):
   273         wx.TextDropTarget.__init__(self)
   250         wx.TextDropTarget.__init__(self)
   274         self.ParentWindow = parent
   251         self.ParentWindow = parent
       
   252         self.ParentControl = control
   275     
   253     
   276     def OnDropText(self, x, y, data):
   254     def OnDropText(self, x, y, data):
   277         x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
       
   278         row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
       
   279         if row == wx.NOT_FOUND:
       
   280             row = self.ParentWindow.Table.GetNumberRows()
       
   281         message = None
   255         message = None
   282         try:
   256         try:
   283             values = eval(data)
   257             values = eval(data)
   284         except:
   258         except:
   285             message = _("Invalid value \"%s\" for debug variable")%data
   259             message = _("Invalid value \"%s\" for debug variable")%data
   286             values = None
   260             values = None
   287         if not isinstance(values, TupleType):
   261         if not isinstance(values, TupleType):
   288             message = _("Invalid value \"%s\" for debug variable")%data
   262             message = _("Invalid value \"%s\" for debug variable")%data
   289             values = None
   263             values = None
   290         if values is not None and values[1] == "debug":
   264         
   291             self.ParentWindow.InsertValue(values[0], row)
       
   292         if message is not None:
   265         if message is not None:
   293             wx.CallAfter(self.ShowMessage, message)
   266             wx.CallAfter(self.ShowMessage, message)
       
   267         elif values is not None and values[1] == "debug":
       
   268             if self.ParentControl == self.ParentWindow.VariablesGrid:
       
   269                 x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
       
   270                 row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
       
   271                 if row == wx.NOT_FOUND:
       
   272                     row = self.ParentWindow.Table.GetNumberRows()
       
   273                 self.ParentWindow.InsertValue(values[0], row, force=True)
       
   274             else:
       
   275                 x, y = self.ParentWindow.GraphicsCanvasWindow.CalcUnscrolledPosition(x, y)
       
   276                 width, height = self.ParentWindow.GraphicsCanvas.GetSize()
       
   277                 target = None
       
   278                 merge_type = GRAPH_PARALLEL
       
   279                 for infos in self.ParentWindow.GraphicsAxes:
       
   280                     ax, ay, aw, ah = infos["axes"].get_position().bounds
       
   281                     rect = wx.Rect(ax * width, height - (ay + ah) * height,
       
   282                                    aw * width, ah * height)
       
   283                     if rect.InsideXY(x, y):
       
   284                         target = infos
       
   285                         merge_rect = wx.Rect(ax * width, height - (ay + ah) * height,
       
   286                                              aw * width / 2., ah * height)
       
   287                         if merge_rect.InsideXY(x, y):
       
   288                             merge_type = GRAPH_ORTHOGONAL
       
   289                         break
       
   290                 self.ParentWindow.MergeGraphs(values[0], target, merge_type, force=True)
   294             
   291             
   295     def ShowMessage(self, message):
   292     def ShowMessage(self, message):
   296         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
   293         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
   297         dialog.ShowModal()
   294         dialog.ShowModal()
   298         dialog.Destroy()
   295         dialog.Destroy()
   307                 next_tick = data[0][0]
   304                 next_tick = data[0][0]
   308             else:
   305             else:
   309                 next_tick = min(next_tick, data[0][0])
   306                 next_tick = min(next_tick, data[0][0])
   310     return next_tick
   307     return next_tick
   311 
   308 
       
   309 def OrthogonalData(item, start_tick, end_tick):
       
   310     data = item.GetData(start_tick, end_tick)
       
   311     min_value, max_value = item.GetRange()
       
   312     if min_value is not None and max_value is not None:
       
   313         center = (min_value + max_value) / 2.
       
   314         range = max(1.0, max_value - min_value)
       
   315     else:
       
   316         center = 0.5
       
   317         range = 1.0
       
   318     return data, center - range * 0.55, center + range * 0.55 
       
   319     
   312 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
   320 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
   313     
   321     
   314     def __init__(self, parent, producer, window):
   322     def __init__(self, parent, producer, window):
   315         wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
   323         wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
   316         
   324         
   341             button.SetToolTipString(help)
   349             button.SetToolTipString(help)
   342             setattr(self, name, button)
   350             setattr(self, name, button)
   343             button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   351             button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   344         
   352         
   345         self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL)
   353         self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL)
   346         self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self))
   354         self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
   347         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
   355         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
   348               self.OnVariablesGridCellRightClick)
   356               self.OnVariablesGridCellRightClick)
   349         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
   357         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
   350               self.OnVariablesGridCellLeftClick)
   358               self.OnVariablesGridCellLeftClick)
   351         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, 
       
   352               self.OnVariablesGridCellChange)
       
   353         main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   359         main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   354         
   360         
   355         self.MainPanel.SetSizer(main_panel_sizer)
   361         self.MainPanel.SetSizer(main_panel_sizer)
   356         
   362         
   357         self.HasNewData = False
   363         self.HasNewData = False
   395             return new_row
   401             return new_row
   396         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
   402         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
   397         
   403         
   398         self.VariablesGrid.SetRowLabelSize(0)
   404         self.VariablesGrid.SetRowLabelSize(0)
   399         
   405         
       
   406         self.GridColSizes = [200, 100]
       
   407         
   400         for col in range(self.Table.GetNumberCols()):
   408         for col in range(self.Table.GetNumberCols()):
   401             attr = wx.grid.GridCellAttr()
   409             attr = wx.grid.GridCellAttr()
   402             if self.Table.GetColLabelValue(col, False) == "3DAxis":
   410             attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
   403                 attr.SetAlignment(wx.ALIGN_CENTER, wx.ALIGN_CENTER)
       
   404             else:
       
   405                 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
   406             self.VariablesGrid.SetColAttr(col, attr)
   411             self.VariablesGrid.SetColAttr(col, attr)
   407             self.VariablesGrid.SetColSize(col, 100)
   412             self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
   408         
   413         
   409         self.Table.ResetView(self.VariablesGrid)
   414         self.Table.ResetView(self.VariablesGrid)
   410         self.VariablesGrid.RefreshButtons()
   415         self.VariablesGrid.RefreshButtons()
   411         
   416         
   412         if USE_MPL:
   417         if USE_MPL:
   456             self.GraphicsPanelSizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
   461             self.GraphicsPanelSizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
   457             
   462             
   458             graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
   463             graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
   459             
   464             
   460             self.GraphicsFigure = matplotlib.figure.Figure()
   465             self.GraphicsFigure = matplotlib.figure.Figure()
   461             self.GraphicsFigure.subplots_adjust(hspace=0)
       
   462             self.GraphicsAxes = []
   466             self.GraphicsAxes = []
   463             
   467             
   464             self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure)
   468             self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure)
       
   469             self.GraphicsCanvas.mpl_connect("button_press_event", self.OnGraphicsCanvasClick)
       
   470             self.GraphicsCanvas.SetDropTarget(DebugVariableDropTarget(self, self.GraphicsCanvas))
   465             graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW)
   471             graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW)
   466             
   472             
   467             self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer)
   473             self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer)
   468             
   474             
   469             self.Graphics3DFigure = matplotlib.figure.Figure()
   475             self.Graphics3DFigure = matplotlib.figure.Figure()
   526         
   532         
   527         self.RefreshScrollBar()
   533         self.RefreshScrollBar()
   528         
   534         
   529         self.Thaw()
   535         self.Thaw()
   530         
   536         
   531         if USE_MPL and (not self.Fixed or self.Force):
   537         if USE_MPL:
   532             self.Force = False
   538         
   533             
   539             if not self.Fixed or self.Force:
   534             # Refresh graphics
   540                 self.Force = False
   535             start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
   541                 
       
   542                 # Refresh graphics
       
   543                 start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
       
   544                 for infos in self.GraphicsAxes:
       
   545                     
       
   546                     if infos["type"] == GRAPH_PARALLEL:
       
   547                         min_value = max_value = None
       
   548                         
       
   549                         for idx, item in enumerate(infos["items"]):
       
   550                             data = item.GetData(start_tick, end_tick)
       
   551                             if data is not None:
       
   552                                 item_min_value, item_max_value = item.GetRange()
       
   553                                 if min_value is None:
       
   554                                     min_value = item_min_value
       
   555                                 elif item_min_value is not None:
       
   556                                     min_value = min(min_value, item_min_value)
       
   557                                 if max_value is None:
       
   558                                     max_value = item_max_value
       
   559                                 elif item_max_value is not None:
       
   560                                     max_value = max(max_value, item_max_value)
       
   561                                 
       
   562                                 if len(infos["plots"]) <= idx:
       
   563                                     infos["plots"].append(
       
   564                                         infos["axes"].plot(data[:, 0], data[:, 1])[0])
       
   565                                 else:
       
   566                                     infos["plots"][idx].set_data(data[:, 0], data[:, 1])
       
   567                         
       
   568                         if min_value is not None and max_value is not None:
       
   569                             y_center = (min_value + max_value) / 2.
       
   570                             y_range = max(1.0, max_value - min_value)
       
   571                         else:
       
   572                             y_center = 0.5
       
   573                             y_range = 1.0
       
   574                         x_min, x_max = start_tick, end_tick
       
   575                         y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
       
   576                     
       
   577                     else:
       
   578                         min_start_tick = reduce(max, [item.GetData()[0, 0] 
       
   579                                                       for item in infos["items"]
       
   580                                                       if len(item.GetData()) > 0], 0)
       
   581                         start_tick = max(self.StartTick, min_start_tick)
       
   582                         end_tick = max(self.StartTick + self.CurrentRange, min_start_tick)
       
   583                         x_data, x_min, x_max = OrthogonalData(infos["items"][0], start_tick, end_tick)
       
   584                         y_data, y_min, y_max = OrthogonalData(infos["items"][1], start_tick, end_tick)
       
   585                         length = 0
       
   586                         if x_data is not None and y_data is not None:  
       
   587                             length = min(len(x_data), len(y_data))
       
   588                         if len(infos["items"]) < 3:
       
   589                             if x_data is not None and y_data is not None:
       
   590                                 if len(infos["plots"]) == 0:
       
   591                                     infos["plots"].append(
       
   592                                         infos["axes"].plot(x_data[:, 1][:length], 
       
   593                                                            y_data[:, 1][:length])[0])
       
   594                                 else:
       
   595                                     infos["plots"][0].set_data(
       
   596                                         x_data[:, 1][:length], 
       
   597                                         y_data[:, 1][:length])
       
   598                         else:
       
   599                             while len(infos["axes"].lines) > 0:
       
   600                                 infos["axes"].lines.pop()
       
   601                             z_data, z_min, z_max = OrthogonalData(infos["items"][2], start_tick, end_tick)
       
   602                             if x_data is not None and y_data is not None and z_data is not None:
       
   603                                 length = min(length, len(z_data))
       
   604                                 infos["axes"].plot(x_data[:, 1][:length],
       
   605                                                    y_data[:, 1][:length],
       
   606                                                    zs = z_data[:, 1][:length])
       
   607                             infos["axes"].set_zlim(z_min, z_max)
       
   608                     
       
   609                     infos["axes"].set_xlim(x_min, x_max)
       
   610                     infos["axes"].set_ylim(y_min, y_max)
       
   611                 
       
   612             plot2d = plot3d = 0
   536             for infos in self.GraphicsAxes:
   613             for infos in self.GraphicsAxes:
   537                 min_value = max_value = None
   614                 labels = ["%s: %s" % (item.GetVariable(), item.GetValue())
   538                 
   615                           for item in infos["items"]]
   539                 for idx, item in enumerate(infos["items"]):
   616                 if infos["type"] == GRAPH_PARALLEL:
   540                     data = item.GetData(start_tick, end_tick)
   617                     infos["axes"].legend(infos["plots"], labels, 
   541                     if data is not None:
   618                         loc="upper left", frameon=False,
   542                         item_min_value, item_max_value = item.GetRange()
   619                         prop={'size':'small'})
   543                         if min_value is None:
   620                     plot2d += 1
   544                             min_value = item_min_value
       
   545                         elif item_min_value is not None:
       
   546                             min_value = min(min_value, item_min_value)
       
   547                         if max_value is None:
       
   548                             max_value = item_max_value
       
   549                         elif item_max_value is not None:
       
   550                             max_value = max(max_value, item_max_value)
       
   551                         
       
   552                         if len(infos["plots"]) <= idx:
       
   553                             infos["plots"].append(infos["axes"].plot(data[:, 0], data[:, 1])[0])
       
   554                         else:
       
   555                             infos["plots"][idx].set_data(data[:, 0], data[:, 1])
       
   556                 
       
   557                 if min_value is not None and max_value is not None:
       
   558                     y_center = (min_value + max_value) / 2.
       
   559                     y_range = max(1.0, max_value - min_value)
       
   560                 else:
   621                 else:
   561                     y_center = 0.5
   622                     infos["axes"].set_xlabel(labels[0], fontdict={'size':'small'})
   562                     y_range = 1.0
   623                     infos["axes"].set_ylabel(labels[1], fontdict={'size':'small'})
   563                 infos["axes"].set_xlim(start_tick, end_tick)
   624                     if len(labels) > 2:
   564                 infos["axes"].set_ylim(y_center - y_range * 0.55, y_center + y_range * 0.55)
   625                         infos["axes"].set_zlabel(labels[2], fontdict={'size':'small'})
   565             
   626                         plot3d += 1
   566             if len(self.GraphicsAxes) > 0:
   627                     else:
       
   628                         plot2d += 1
       
   629             
       
   630             if plot2d > 0:
   567                 self.GraphicsCanvas.draw()
   631                 self.GraphicsCanvas.draw()
   568                     
   632             if plot3d > 0:
   569             # Refresh 3D graphics
       
   570             while len(self.Graphics3DAxes.lines) > 0:
       
   571                 self.Graphics3DAxes.lines.pop()
       
   572             if self.Axis3DValues is not None:
       
   573                 axis = self.Axis3DValues[0]
       
   574                 start_tick = max(self.StartTick, self.Axis3DValues[1])
       
   575                 end_tick = max(self.StartTick + self.CurrentRange, self.Axis3DValues[1])
       
   576                 xyz_data = [axe.GetData(start_tick, end_tick)[:, 1] for axe in axis]
       
   577                 length = reduce(min, [len(data) for data in xyz_data])
       
   578                 self.Graphics3DAxes.plot(xyz_data[0][:length],
       
   579                     xyz_data[1][:length],
       
   580                     zs = xyz_data[2][:length])
       
   581                 self.Graphics3DCanvas.draw()
   633                 self.Graphics3DCanvas.draw()
   582             
   634             
   583     def UnregisterObsoleteData(self):
   635     def UnregisterObsoleteData(self):
   584         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   636         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   585         items.reverse()
   637         items.reverse()
   681             self.PopupMenu(menu)
   733             self.PopupMenu(menu)
   682             
   734             
   683             menu.Destroy()
   735             menu.Destroy()
   684         event.Skip()
   736         event.Skip()
   685     
   737     
   686     def OnVariablesGridCellChange(self, event):
       
   687         row, col = event.GetRow(), event.GetCol()
       
   688         if self.Table.GetColLabelValue(col, False) == "3DAxis":
       
   689             wx.CallAfter(self.Reset3DGraphics)
       
   690         event.Skip()
       
   691     
       
   692     def RefreshRange(self):
   738     def RefreshRange(self):
   693         if len(self.Ticks) > 0:
   739         if len(self.Ticks) > 0:
   694             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   740             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   695                 self.Fixed = False
   741                 self.Fixed = False
   696             if self.Fixed:
   742             if self.Fixed:
   758             self.Fixed = True
   804             self.Fixed = True
   759             self.Force = True
   805             self.Force = True
   760             wx.CallAfter(self.NewDataAvailable, None, True)
   806             wx.CallAfter(self.NewDataAvailable, None, True)
   761         event.Skip()
   807         event.Skip()
   762     
   808     
   763     def InsertValue(self, iec_path, idx = None, force=False, axis3D=False):
   809     def InsertValue(self, iec_path, idx = None, force=False):
   764         if idx is None:
   810         if idx is None:
   765             idx = self.Table.GetNumberRows()
   811             idx = self.Table.GetNumberRows()
   766         for item in self.Table.GetData():
   812         for item in self.Table.GetData():
   767             if iec_path == item.GetVariable():
   813             if iec_path == item.GetVariable():
   768                 return
   814                 return
   772             self.Table.InsertItem(idx, item)
   818             self.Table.InsertItem(idx, item)
   773             if item.IsNumVariable():
   819             if item.IsNumVariable():
   774                 self.GraphicsAxes.append({
   820                 self.GraphicsAxes.append({
   775                     "items": [item],
   821                     "items": [item],
   776                     "axes": None,
   822                     "axes": None,
   777                     "type": "y",
   823                     "type": GRAPH_PARALLEL,
   778                     "plots": []})
   824                     "plots": []})
   779             item.SetAxis3D(int(axis3D))
       
   780             self.ResetGraphics()
   825             self.ResetGraphics()
   781             self.RefreshGrid()
   826             self.RefreshGrid()
       
   827     
       
   828     def MergeGraphs(self, source, target_infos, merge_type, force=False):
       
   829         source_item = None
       
   830         for item in self.Table.GetData():
       
   831             if item.GetVariable() == source:
       
   832                 source_item = item
       
   833         if source_item is None:
       
   834             item = VariableTableItem(self, source)
       
   835             if item.IsNumVariable():
       
   836                 result = self.AddDataConsumer(source.upper(), item)
       
   837                 if result is not None or force:
       
   838                     self.Table.InsertItem(self.Table.GetNumberRows(), item)
       
   839                     source_item = item
       
   840         if source_item is not None:
       
   841             source_infos = None
       
   842             for infos in self.GraphicsAxes:
       
   843                 if source_item in infos["items"]:
       
   844                     source_infos = infos
       
   845                     break
       
   846             if target_infos is None and source_infos is None:
       
   847                 self.GraphicsAxes.append({
       
   848                     "items": [source_item],
       
   849                     "axes": None,
       
   850                     "type": GRAPH_PARALLEL,
       
   851                     "plots": []})
       
   852                 
       
   853                 self.ResetGraphics()
       
   854                 self.RefreshGrid()
       
   855             
       
   856             elif target_infos is not None:
       
   857                 if (merge_type == GRAPH_PARALLEL and target_infos["type"] != merge_type or
       
   858                     merge_type == GRAPH_ORTHOGONAL and 
       
   859                     (target_infos["type"] == GRAPH_PARALLEL and len(target_infos["items"]) > 1 or
       
   860                      target_infos["type"] == GRAPH_ORTHOGONAL and len(target_infos["items"]) >= 3)):
       
   861                     return
       
   862                 
       
   863                 if source_infos is not None:
       
   864                     source_infos["items"].remove(source_item)
       
   865                     if len(source_infos["items"]) == 0:
       
   866                         self.GraphicsAxes.remove(source_infos)
       
   867                 
       
   868                 target_infos["items"].append(source_item)
       
   869                 target_infos["type"] = merge_type
       
   870                 
       
   871                 self.ResetGraphics()
       
   872                 self.RefreshGrid()
   782             
   873             
   783     def GetDebugVariables(self):
   874     def GetDebugVariables(self):
   784         return [item.GetVariable() for item in self.Table.GetData()]
   875         return [item.GetVariable() for item in self.Table.GetData()]
   785     
   876     
   786     def GetAxis3D(self):
   877     def OnGraphicsCanvasClick(self, event):
   787         return [item.GetVariable() for item in self.Table.GetData() if item.GetAxis3D()]
   878         for infos in self.GraphicsAxes:
       
   879             if infos["axes"] == event.inaxes:
       
   880                 if len(infos["items"]) == 1:
       
   881                     data = wx.TextDataObject(str((infos["items"][0].GetVariable(), "debug")))
       
   882                     dragSource = wx.DropSource(self.GraphicsCanvas)
       
   883                     dragSource.SetData(data)
       
   884                     dragSource.DoDragDrop()
       
   885                     if self.GraphicsCanvas.HasCapture():
       
   886                         self.GraphicsCanvas.ReleaseMouse()
       
   887                 break
   788     
   888     
   789     def ResetGraphicsValues(self):
   889     def ResetGraphicsValues(self):
   790         self.Ticks = numpy.array([])
   890         self.Ticks = numpy.array([])
   791         self.StartTick = 0
   891         self.StartTick = 0
   792         for item in self.Table.GetData():
   892         for item in self.Table.GetData():
   794     
   894     
   795     def ResetGraphics(self):
   895     def ResetGraphics(self):
   796         if USE_MPL:
   896         if USE_MPL:
   797             self.GraphicsFigure.clear()
   897             self.GraphicsFigure.clear()
   798             
   898             
   799             axes_num = len(self.GraphicsAxes)
   899             axes_num = 0
   800             for idx in xrange(axes_num):
   900             for infos in self.GraphicsAxes:
   801                 if idx == 0:
   901                 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
   802                     axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx + 1)
   902                     axes_num += 1
       
   903             if axes_num == len(self.GraphicsAxes):
       
   904                 self.Graphics3DCanvas.Hide()
       
   905             else:
       
   906                 self.Graphics3DCanvas.Show()
       
   907             self.GraphicsPanelSizer.Layout()
       
   908             idx = 1
       
   909             for infos in self.GraphicsAxes:
       
   910                 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
       
   911                     axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx)
       
   912                     infos["axes"] = axes
   803                 else:
   913                 else:
   804                     axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx + 1, sharex=self.GraphicsAxes[0]["axes"])
   914                     infos["axes"] = self.Graphics3DAxes
   805                 self.GraphicsAxes[idx]["axes"] = axes
   915                 infos["plots"] = []
   806                 self.GraphicsAxes[idx]["plots"] = []
   916                 idx += 1
   807             
       
   808             self.RefreshGraphicsCanvasWindowScrollbars()
   917             self.RefreshGraphicsCanvasWindowScrollbars()
   809             self.GraphicsCanvas.draw()
   918             self.GraphicsCanvas.draw()
   810             
       
   811             self.Reset3DGraphics()
       
   812     
       
   813     def Reset3DGraphics(self):
       
   814         self.Axis3DValues = None
       
   815         axis = [item for item in self.Table.GetData() if item.GetAxis3D()]
       
   816         if len(axis) == 3:
       
   817             max_tick = None
       
   818             xaxis, yaxis, zaxis = [item.GetData() for item in axis]
       
   819             if len(xaxis) > 0 and len(yaxis) > 0 and len(zaxis) > 0:
       
   820                 max_tick = max(xaxis[0, 0], yaxis[0, 0], zaxis[0, 0])
       
   821             if max_tick is not None:
       
   822                 self.Axis3DValues = (axis, max_tick)
       
   823             else:
       
   824                 self.Axis3DValues = (axis, 0)
       
   825             self.Graphics3DCanvas.Show()
       
   826         else:
       
   827             self.Graphics3DCanvas.Hide()
       
   828         self.GraphicsPanelSizer.Layout()
       
   829     
   919     
   830     def OnGraphics3DMotion(self, event):
   920     def OnGraphics3DMotion(self, event):
   831         current_time = gettime()
   921         current_time = gettime()
   832         if current_time - self.LastMotionTime > REFRESH_PERIOD:
   922         if current_time - self.LastMotionTime > REFRESH_PERIOD:
   833             self.LastMotionTime = current_time
   923             self.LastMotionTime = current_time
   834             Axes3D._on_move(self.Graphics3DAxes, event)
   924             Axes3D._on_move(self.Graphics3DAxes, event)
   835     
   925     
   836     def RefreshGraphicsCanvasWindowScrollbars(self):
   926     def RefreshGraphicsCanvasWindowScrollbars(self):
   837         xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
   927         xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
   838         window_size = self.GraphicsCanvasWindow.GetClientSize()
   928         window_size = self.GraphicsCanvasWindow.GetClientSize()
   839         vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 50)
   929         vwidth, vheight = (window_size[0], (len(self.GraphicsAxes) + 1) * 100)
   840         self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
   930         self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
   841         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
   931         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
   842         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
   932         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
   843         self.GraphicsCanvasWindow.Scroll(posx, posy)
   933         self.GraphicsCanvasWindow.Scroll(posx, posy)
   844         self.GraphicsCanvasWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
   934         self.GraphicsCanvasWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,