controls/DebugVariablePanel.py
changeset 916 697d8b77d716
parent 912 bf855ccf3705
child 919 4a36e38e51d3
equal deleted inserted replaced
915:8dc28b21bdac 916:697d8b77d716
    20 #
    20 #
    21 #You should have received a copy of the GNU General Public
    21 #You should have received a copy of the GNU General Public
    22 #License along with this library; if not, write to the Free Software
    22 #License along with this library; if not, write to the Free Software
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24 
    24 
    25 from types import TupleType, FloatType
    25 from types import TupleType, ListType, FloatType
    26 from time import time as gettime
    26 from time import time as gettime
    27 import math
    27 import math
    28 import numpy
    28 import numpy
    29 
    29 
    30 import wx
    30 import wx
    33 try:
    33 try:
    34     import matplotlib
    34     import matplotlib
    35     matplotlib.use('WX')
    35     matplotlib.use('WX')
    36     import matplotlib.pyplot
    36     import matplotlib.pyplot
    37     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    37     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
    38     #from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
       
    39     from mpl_toolkits.mplot3d import Axes3D
    38     from mpl_toolkits.mplot3d import Axes3D
    40     USE_MPL = True
    39     USE_MPL = True
    41 except:
    40 except:
    42     USE_MPL = False
    41     USE_MPL = False
    43 
    42 
    44 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
    43 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
    45 from controls import CustomGrid, CustomTable
    44 from controls import CustomGrid, CustomTable
    46 from dialogs.ForceVariableDialog import ForceVariableDialog
    45 from dialogs.ForceVariableDialog import ForceVariableDialog
    47 from util.BitmapLibrary import GetBitmap
    46 from util.BitmapLibrary import GetBitmap
    48 
       
    49 SECOND = 1000000000
       
    50 MINUTE = 60 * SECOND
       
    51 HOUR = 60 * MINUTE
       
    52 
       
    53 ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
    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)] + \
       
    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)]
       
    58 
       
    59 GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
       
    60 
    47 
    61 def AppendMenu(parent, help, id, kind, text):
    48 def AppendMenu(parent, help, id, kind, text):
    62     parent.Append(help=help, id=id, kind=kind, text=text)
    49     parent.Append(help=help, id=id, kind=kind, text=text)
    63 
    50 
    64 def GetDebugVariablesTableColnames():
    51 def GetDebugVariablesTableColnames():
    79     
    66     
    80     def SetVariable(self, variable):
    67     def SetVariable(self, variable):
    81         if self.Parent and self.Variable != variable:
    68         if self.Parent and self.Variable != variable:
    82             self.Variable = variable
    69             self.Variable = variable
    83             self.RefreshVariableType()
    70             self.RefreshVariableType()
    84             self.Parent.RefreshGrid()
    71             self.Parent.RefreshView()
    85     
    72     
    86     def GetVariable(self):
    73     def GetVariable(self, max_size=None):
    87         return self.Variable
    74         variable = self.Variable
       
    75         if max_size is not None:
       
    76             max_size = max(max_size, 10)
       
    77             if len(variable) > max_size:
       
    78                 variable = "..." + variable[-(max_size - 3):]
       
    79         return variable
    88     
    80     
    89     def RefreshVariableType(self):
    81     def RefreshVariableType(self):
    90         self.VariableType = self.Parent.GetDataType(self.Variable)
    82         self.VariableType = self.Parent.GetDataType(self.Variable)
    91         self.ResetData()
    83         if USE_MPL:
       
    84             self.ResetData()
    92     
    85     
    93     def GetVariableType(self):
    86     def GetVariableType(self):
    94         return self.VariableType
    87         return self.VariableType
    95     
    88     
    96     def GetData(self, start_tick=None, end_tick=None):
    89     def GetData(self, start_tick=None, end_tick=None):
    97         if self.IsNumVariable():
    90         if USE_MPL and self.IsNumVariable():
    98             if len(self.Data) == 0:
    91             if len(self.Data) == 0:
    99                 return self.Data
    92                 return self.Data
   100             
    93             
   101             start_idx = end_idx = None
    94             start_idx = end_idx = None
   102             if start_tick is not None:
    95             if start_tick is not None:
   125     
   118     
   126     def IsNumVariable(self):
   119     def IsNumVariable(self):
   127         return self.Parent.IsNumType(self.VariableType)
   120         return self.Parent.IsNumType(self.VariableType)
   128     
   121     
   129     def NewValue(self, tick, value, forced=False):
   122     def NewValue(self, tick, value, forced=False):
   130         if self.IsNumVariable():
   123         if USE_MPL and self.IsNumVariable():
   131             num_value = {True:1., False:0.}.get(value, float(value))
   124             num_value = {True:1., False:0.}.get(value, float(value))
   132             if self.MinValue is None:
   125             if self.MinValue is None:
   133                 self.MinValue = num_value
   126                 self.MinValue = num_value
   134             else:
   127             else:
   135                 self.MinValue = min(self.MinValue, num_value)
   128                 self.MinValue = min(self.MinValue, num_value)
   162         elif isinstance(self.Value, FloatType):
   155         elif isinstance(self.Value, FloatType):
   163             return "%.6g" % self.Value
   156             return "%.6g" % self.Value
   164         return self.Value
   157         return self.Value
   165 
   158 
   166     def GetNearestData(self, tick, adjust):
   159     def GetNearestData(self, tick, adjust):
   167         if self.IsNumVariable():
   160         if USE_MPL and self.IsNumVariable():
   168             ticks = self.Data[:, 0]
   161             ticks = self.Data[:, 0]
   169             new_cursor = numpy.argmin(abs(ticks - tick))
   162             new_cursor = numpy.argmin(abs(ticks - tick))
   170             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   163             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
   171                 new_cursor -= 1
   164                 new_cursor -= 1
   172             elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(ticks):
   165             elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(ticks):
   244     def GetItem(self, idx):
   237     def GetItem(self, idx):
   245         return self.data[idx]
   238         return self.data[idx]
   246 
   239 
   247 class DebugVariableDropTarget(wx.TextDropTarget):
   240 class DebugVariableDropTarget(wx.TextDropTarget):
   248     
   241     
   249     def __init__(self, parent, control):
   242     def __init__(self, parent, control=None):
   250         wx.TextDropTarget.__init__(self)
   243         wx.TextDropTarget.__init__(self)
   251         self.ParentWindow = parent
   244         self.ParentWindow = parent
   252         self.ParentControl = control
   245         self.ParentControl = control
       
   246     
       
   247     def __del__(self):
       
   248         self.ParentWindow = None
       
   249         self.ParentControl = None
   253     
   250     
   254     def OnDropText(self, x, y, data):
   251     def OnDropText(self, x, y, data):
   255         message = None
   252         message = None
   256         try:
   253         try:
   257             values = eval(data)
   254             values = eval(data)
   263             values = None
   260             values = None
   264         
   261         
   265         if message is not None:
   262         if message is not None:
   266             wx.CallAfter(self.ShowMessage, message)
   263             wx.CallAfter(self.ShowMessage, message)
   267         elif values is not None and values[1] == "debug":
   264         elif values is not None and values[1] == "debug":
   268             if self.ParentControl == self.ParentWindow.VariablesGrid:
   265             if isinstance(self.ParentControl, CustomGrid):
   269                 x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
   266                 x, y = self.ParentControl.CalcUnscrolledPosition(x, y)
   270                 row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
   267                 row = self.ParentControl.YToRow(y - self.ParentControl.GetColLabelSize())
   271                 if row == wx.NOT_FOUND:
   268                 if row == wx.NOT_FOUND:
   272                     row = self.ParentWindow.Table.GetNumberRows()
   269                     row = self.ParentWindow.Table.GetNumberRows()
   273                 self.ParentWindow.InsertValue(values[0], row, force=True)
   270                 self.ParentWindow.InsertValue(values[0], row, force=True)
   274             else:
   271             elif self.ParentControl is not None:
   275                 width, height = self.ParentWindow.GraphicsCanvas.GetSize()
   272                 width, height = self.ParentControl.Canvas.GetSize()
   276                 target = None
   273                 target_idx = self.ParentControl.GetIndex()
   277                 merge_type = GRAPH_PARALLEL
   274                 merge_type = GRAPH_PARALLEL
   278                 for infos in self.ParentWindow.GraphicsAxes:
   275                 if self.ParentControl.Is3DCanvas():
   279                     if infos["axes"] != self.ParentWindow.Graphics3DAxes:
   276                     self.ParentWindow.InsertValue(values[0], target_idx, force=True)
   280                         ax, ay, aw, ah = infos["axes"].get_position().bounds
   277                 else:
   281                         rect = wx.Rect(ax * width, height - (ay + ah) * height,
   278                     ax, ay, aw, ah = self.ParentControl.Axes.get_position().bounds
   282                                        aw * width, ah * height)
   279                     rect = wx.Rect(ax * width, height - (ay + ah) * height,
   283                         if rect.InsideXY(x, y):
   280                                    aw * width, ah * height)
   284                             target = infos
   281                     if rect.InsideXY(x, y):
   285                             merge_rect = wx.Rect(ax * width, height - (ay + ah) * height,
   282                         merge_rect = wx.Rect(ax * width, height - (ay + ah) * height,
   286                                                  aw * width / 2., ah * height)
   283                                              aw * width / 2., ah * height)
   287                             if merge_rect.InsideXY(x, y):
   284                         if merge_rect.InsideXY(x, y):
   288                                 merge_type = GRAPH_ORTHOGONAL
   285                             merge_type = GRAPH_ORTHOGONAL
   289                             break
   286                         wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True)
   290                 self.ParentWindow.MergeGraphs(values[0], target, merge_type, force=True)
   287                     else:
       
   288                         self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   289             else:
       
   290                 self.ParentWindow.InsertValue(values[0], force=True)
   291             
   291             
   292     def ShowMessage(self, message):
   292     def ShowMessage(self, message):
   293         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)
   294         dialog.ShowModal()
   294         dialog.ShowModal()
   295         dialog.Destroy()
   295         dialog.Destroy()
   296 
   296 
   297 SCROLLBAR_UNIT = 10
   297 if USE_MPL:
   298 
   298     SECOND = 1000000000
   299 def NextTick(variables):
   299     MINUTE = 60 * SECOND
   300     next_tick = None
   300     HOUR = 60 * MINUTE
   301     for var_name, data in variables:
   301     
   302         if len(data) > 0:
   302     ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
   303             if next_tick is None:
   303     RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
   304                 next_tick = data[0][0]
   304     TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
   305             else:
   305                         [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
   306                 next_tick = min(next_tick, data[0][0])
   306                         [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
   307     return next_tick
   307     
   308 
   308     GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
   309 def OrthogonalData(item, start_tick, end_tick):
   309     
   310     data = item.GetData(start_tick, end_tick)
   310     SCROLLBAR_UNIT = 10
   311     min_value, max_value = item.GetRange()
   311     
   312     if min_value is not None and max_value is not None:
   312     def NextTick(variables):
   313         center = (min_value + max_value) / 2.
   313         next_tick = None
   314         range = max(1.0, max_value - min_value)
   314         for var_name, data in variables:
   315     else:
   315             if len(data) > 0:
   316         center = 0.5
   316                 if next_tick is None:
   317         range = 1.0
   317                     next_tick = data[0][0]
   318     return data, center - range * 0.55, center + range * 0.55 
   318                 else:
   319     
   319                     next_tick = min(next_tick, data[0][0])
   320 class DebugVariablePanel(wx.SplitterWindow, DebugViewer):
   320         return next_tick
       
   321     
       
   322     def OrthogonalData(item, start_tick, end_tick):
       
   323         data = item.GetData(start_tick, end_tick)
       
   324         min_value, max_value = item.GetRange()
       
   325         if min_value is not None and max_value is not None:
       
   326             center = (min_value + max_value) / 2.
       
   327             range = max(1.0, max_value - min_value)
       
   328         else:
       
   329             center = 0.5
       
   330             range = 1.0
       
   331         return data, center - range * 0.55, center + range * 0.55
       
   332     
       
   333     class DebugVariableViewer(wx.Panel):
       
   334         
       
   335         def AddViewer(self):
       
   336             pass
       
   337         
       
   338         def AddButtons(self):
       
   339             pass
       
   340         
       
   341         def __init__(self, parent, window, items=[]):
       
   342             wx.Panel.__init__(self, parent)
       
   343             self.SetBackgroundColour(wx.WHITE)
       
   344             self.SetDropTarget(DebugVariableDropTarget(window, self))
       
   345             
       
   346             self.ParentWindow = window
       
   347             self.Items = items
       
   348             
       
   349             self.MainSizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
       
   350             self.AddViewer()
       
   351             self.AddButtons()
       
   352             self.MainSizer.AddGrowableCol(0)
       
   353             
       
   354             self.SetSizer(self.MainSizer)
       
   355         
       
   356         def __del__(self):
       
   357             self.ParentWindow = None
       
   358             
       
   359         def GetIndex(self):
       
   360             return self.ParentWindow.GetViewerIndex(self)
       
   361         
       
   362         def GetItem(self, variable):
       
   363             for item in self.Items:
       
   364                 if item.GetVariable() == variable:
       
   365                     return item
       
   366             return None
       
   367         
       
   368         def GetItems(self):
       
   369             return self.Items
       
   370         
       
   371         def GetVariables(self):
       
   372             if len(self.Items) > 1:
       
   373                 variables = [item.GetVariable() for item in self.Items]
       
   374                 if self.GraphType == GRAPH_ORTHOGONAL:
       
   375                     return tuple(variables)
       
   376                 return variables
       
   377             return self.Items[0].GetVariable()
       
   378         
       
   379         def AddItem(self, item):
       
   380             self.Items.append(item)
       
   381     
       
   382         def RemoveItem(self, item):
       
   383             if item in self.Items:
       
   384                 self.Items.remove(item)
       
   385             
       
   386         def Clear(self):
       
   387             for item in self.Items:
       
   388                 self.ParentWindow.RemoveDataConsumer(item)
       
   389             self.Items = []
       
   390         
       
   391         def IsEmpty(self):
       
   392             return len(self.Items) == 0
       
   393         
       
   394         def UnregisterObsoleteData(self):
       
   395             for item in self.Items[:]:
       
   396                 iec_path = item.GetVariable().upper()
       
   397                 if self.ParentWindow.GetDataType(iec_path) is None:
       
   398                     self.ParentWindow.RemoveDataConsumer(item)
       
   399                     self.RemoveItem(item)
       
   400                 else:
       
   401                     self.ParentWindow.AddDataConsumer(iec_path, item)
       
   402                     item.RefreshVariableType()
       
   403         
       
   404         def ResetData(self):
       
   405             for item in self.Items:
       
   406                 item.ResetData()
       
   407         
       
   408         def Refresh(self):
       
   409             pass
       
   410     
       
   411         def OnForceButton(self, event):
       
   412             if len(self.Items) == 1:
       
   413                 wx.CallAfter(self.ForceValue, self.Items[0])
       
   414             else:
       
   415                 menu = wx.Menu(title='')
       
   416                 for item in self.Items:
       
   417                     new_id = wx.NewId()
       
   418                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
       
   419                         text=item.GetVariable(20))
       
   420                     self.Bind(wx.EVT_MENU, 
       
   421                         self.GetForceVariableMenuFunction(item),
       
   422                         id=new_id)
       
   423                 self.PopupMenu(menu)
       
   424             event.Skip()
       
   425         
       
   426         def OnReleaseButton(self, event):
       
   427             if len(self.Items) == 1:
       
   428                 wx.CallAfter(self.ReleaseValue, self.Items[0])
       
   429             else:
       
   430                 menu = wx.Menu(title='')
       
   431                 for item in self.Items:
       
   432                     new_id = wx.NewId()
       
   433                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
       
   434                         text=item.GetVariable(20))
       
   435                     self.Bind(wx.EVT_MENU, 
       
   436                         self.GetReleaseVariableMenuFunction(item),
       
   437                         id=new_id)
       
   438                 
       
   439                 self.PopupMenu(menu)
       
   440             event.Skip()
       
   441         
       
   442         def OnDeleteButton(self, event):
       
   443             if len(self.Items) == 1:
       
   444                 wx.CallAfter(self.ParentWindow.DeleteValue, self, self.Items[0])
       
   445             else:
       
   446                 menu = wx.Menu(title='')
       
   447                 for item in self.Items:
       
   448                     new_id = wx.NewId()
       
   449                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
       
   450                         text=item.GetVariable(20))
       
   451                     self.Bind(wx.EVT_MENU, 
       
   452                         self.GetDeleteValueMenuFunction(item),
       
   453                         id=new_id)
       
   454                 
       
   455                 new_id = wx.NewId()
       
   456                 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All"))
       
   457                 self.Bind(wx.EVT_MENU, self.OnDeleteAllValuesMenu, id=new_id)
       
   458                 
       
   459                 self.PopupMenu(menu)
       
   460             event.Skip()
       
   461     
       
   462         def GetForceVariableMenuFunction(self, item):
       
   463             def ForceVariableFunction(event):
       
   464                 self.ForceValue(item)
       
   465             return ForceVariableFunction
       
   466         
       
   467         def GetReleaseVariableMenuFunction(self, item):
       
   468             def ReleaseVariableFunction(event):
       
   469                 self.ReleaseValue(item)
       
   470             return ReleaseVariableFunction
       
   471         
       
   472         def GetDeleteValueMenuFunction(self, item):
       
   473             def DeleteValueFunction(event):
       
   474                 self.ParentWindow.DeleteValue(self, item)
       
   475             return DeleteValueFunction
       
   476         
       
   477         def ForceValue(self, item):
       
   478             iec_path = item.GetVariable().upper()
       
   479             iec_type = self.ParentWindow.GetDataType(iec_path)
       
   480             if iec_type is not None:
       
   481                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
   482                 if dialog.ShowModal() == wx.ID_OK:
       
   483                     self.ParentWindow.ForceDataValue(iec_path, dialog.GetValue())
       
   484         
       
   485         def ReleaseValue(self, item):
       
   486             iec_path = item.GetVariable().upper()
       
   487             self.ParentWindow.ReleaseDataValue(iec_path)
       
   488         
       
   489         def OnDeleteAllValuesMenu(self, event):
       
   490             wx.CallAfter(self.ParentWindow.DeleteValue, self)
       
   491         
       
   492     class DebugVariableText(DebugVariableViewer):
       
   493         
       
   494         def AddViewer(self):
       
   495             viewer_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0)
       
   496             viewer_sizer.AddGrowableCol(0)
       
   497             self.MainSizer.AddSizer(viewer_sizer, border=5, 
       
   498                 flag=wx.ALL|wx.GROW|wx.ALIGN_CENTER_VERTICAL)
       
   499             
       
   500             variable_name_label = wx.TextCtrl(self, size=wx.Size(0, -1),
       
   501                 value=self.Items[0].GetVariable(), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER)
       
   502             variable_name_label.SetSelection(variable_name_label.GetLastPosition(), -1)
       
   503             viewer_sizer.AddWindow(variable_name_label, flag=wx.GROW)
       
   504             
       
   505             self.ValueLabel = wx.TextCtrl(self,
       
   506                 size=wx.Size(100, -1), style=wx.TE_READONLY|wx.TE_RIGHT|wx.NO_BORDER)
       
   507             viewer_sizer.AddWindow(self.ValueLabel, 
       
   508                 border=5, flag=wx.LEFT)
       
   509         
       
   510         def AddButtons(self):
       
   511             button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   512             self.MainSizer.AddSizer(button_sizer, border=5, 
       
   513                 flag=wx.TOP|wx.BOTTOM|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
   514             
       
   515             buttons = [
       
   516                 ("ForceButton", "force", _("Force value")),
       
   517                 ("ReleaseButton", "release", _("Release value")),
       
   518                 ("DeleteButton", "remove_element", _("Remove debug variable"))]
       
   519             
       
   520             for name, bitmap, help in buttons:
       
   521                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
   522                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   523                 button.SetToolTipString(help)
       
   524                 setattr(self, name, button)
       
   525                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   526                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   527     
       
   528         def Refresh(self):
       
   529             self.ValueLabel.ChangeValue(self.Items[0].GetValue())
       
   530             if self.Items[0].IsForced():
       
   531                 self.ValueLabel.SetForegroundColour(wx.BLUE)
       
   532             else:
       
   533                 self.ValueLabel.SetForegroundColour(wx.BLACK)
       
   534             self.ValueLabel.SetSelection(self.ValueLabel.GetLastPosition(), -1)
       
   535     
       
   536     class DebugVariableGraphic(DebugVariableViewer):
       
   537         
       
   538         def __init__(self, parent, window, items, graph_type):
       
   539             DebugVariableViewer.__init__(self, parent, window, items)
       
   540         
       
   541             self.GraphType = graph_type
       
   542             
       
   543             self.ResetGraphics()
       
   544         
       
   545         def AddViewer(self):
       
   546             self.Figure = matplotlib.figure.Figure(facecolor='w')
       
   547             
       
   548             self.Canvas = FigureCanvas(self, -1, self.Figure)
       
   549             self.Canvas.SetMinSize(wx.Size(200, 200))
       
   550             self.Canvas.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
       
   551             self.Canvas.Bind(wx.EVT_LEFT_DOWN, self.OnCanvasClick)
       
   552             
       
   553             self.MainSizer.AddWindow(self.Canvas, flag=wx.GROW)
       
   554         
       
   555         def AddButtons(self):
       
   556             button_sizer = wx.BoxSizer(wx.VERTICAL)
       
   557             self.MainSizer.AddSizer(button_sizer, border=5, 
       
   558                 flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL)
       
   559             
       
   560             buttons = [
       
   561                 ("ForceButton", "force", _("Force value")),
       
   562                 ("ReleaseButton", "release", _("Release value")),
       
   563                 ("SplitButton", "split", _("Split graphs")),
       
   564                 ("DeleteButton", "remove_element", _("Remove debug variable"))]
       
   565             
       
   566             for name, bitmap, help in buttons:
       
   567                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
   568                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   569                 button.SetToolTipString(help)
       
   570                 setattr(self, name, button)
       
   571                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
   572                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   573     
       
   574         def OnCanvasClick(self, event):
       
   575             if len(self.Items) == 1:
       
   576                 width, height = self.Canvas.GetSize()
       
   577                 x, y = event.GetPosition()
       
   578                 ax, ay, aw, ah = self.Axes.get_position().bounds
       
   579                 rect = wx.Rect(ax * width, height - (ay + ah) * height,
       
   580                                aw * width, ah * height)
       
   581                 if rect.InsideXY(x, y):
       
   582                     data = wx.TextDataObject(str((self.Items[0].GetVariable(), "debug")))
       
   583                     dragSource = wx.DropSource(self.Canvas)
       
   584                     dragSource.SetData(data)
       
   585                     dragSource.DoDragDrop()
       
   586         
       
   587         def OnMotion(self, event):
       
   588             current_time = gettime()
       
   589             if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
   590                 self.LastMotionTime = current_time
       
   591                 Axes3D._on_move(self.Axes, event)
       
   592         
       
   593         def OnSplitButton(self, event):
       
   594             if len(self.Items) == 2 or self.GraphType == GRAPH_ORTHOGONAL:
       
   595                 wx.CallAfter(self.ParentWindow.SplitGraphs, self)
       
   596             else:
       
   597                 menu = wx.Menu(title='')
       
   598                 for item in self.Items:
       
   599                     new_id = wx.NewId()
       
   600                     AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, 
       
   601                         text=item.GetVariable(20))
       
   602                     self.Bind(wx.EVT_MENU, 
       
   603                         self.GetSplitGraphMenuFunction(item),
       
   604                         id=new_id)
       
   605                 
       
   606                 new_id = wx.NewId()
       
   607                 AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("All"))
       
   608                 self.Bind(wx.EVT_MENU, self.OnSplitAllGraphsMenu, id=new_id)
       
   609                 
       
   610                 self.PopupMenu(menu)
       
   611             event.Skip()
       
   612         
       
   613         def ResetGraphics(self):
       
   614             self.Figure.clear()
       
   615             if self.Is3DCanvas():
       
   616                 self.Axes = self.Figure.gca(projection='3d')
       
   617                 self.Axes.set_color_cycle(['b'])
       
   618                 self.LastMotionTime = gettime()
       
   619                 setattr(self.Axes, "_on_move", self.OnMotion)
       
   620                 self.Axes.mouse_init()
       
   621             else:
       
   622                 self.Axes = self.Figure.gca()
       
   623                 if self.GraphType == GRAPH_ORTHOGONAL:
       
   624                     self.Figure.subplotpars.update(bottom=0.15)
       
   625             self.Plots = []
       
   626             self.SplitButton.Enable(len(self.Items) > 1)
       
   627             
       
   628         def AddItem(self, item):
       
   629             DebugVariableViewer.AddItem(self, item)
       
   630             self.ResetGraphics()
       
   631             
       
   632         def RemoveItem(self, item):
       
   633             DebugVariableViewer.RemoveItem(self, item)
       
   634             if not self.IsEmpty():
       
   635                 self.ResetGraphics()
       
   636         
       
   637         def UnregisterObsoleteData(self):
       
   638             DebugVariableViewer.UnregisterObsoleteData(self)
       
   639             if not self.IsEmpty():
       
   640                 self.ResetGraphics()
       
   641         
       
   642         def Is3DCanvas(self):
       
   643             return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
       
   644         
       
   645         def Refresh(self, refresh_graphics=True):
       
   646             
       
   647             if refresh_graphics:
       
   648                 start_tick, end_tick = self.ParentWindow.GetRange()
       
   649                 
       
   650                 if self.GraphType == GRAPH_PARALLEL:    
       
   651                     min_value = max_value = None
       
   652                     
       
   653                     for idx, item in enumerate(self.Items):
       
   654                         data = item.GetData(start_tick, end_tick)
       
   655                         if data is not None:
       
   656                             item_min_value, item_max_value = item.GetRange()
       
   657                             if min_value is None:
       
   658                                 min_value = item_min_value
       
   659                             elif item_min_value is not None:
       
   660                                 min_value = min(min_value, item_min_value)
       
   661                             if max_value is None:
       
   662                                 max_value = item_max_value
       
   663                             elif item_max_value is not None:
       
   664                                 max_value = max(max_value, item_max_value)
       
   665                             
       
   666                             if len(self.Plots) <= idx:
       
   667                                 self.Plots.append(
       
   668                                     self.Axes.plot(data[:, 0], data[:, 1])[0])
       
   669                             else:
       
   670                                 self.Plots[idx].set_data(data[:, 0], data[:, 1])
       
   671                         
       
   672                     if min_value is not None and max_value is not None:
       
   673                         y_center = (min_value + max_value) / 2.
       
   674                         y_range = max(1.0, max_value - min_value)
       
   675                     else:
       
   676                         y_center = 0.5
       
   677                         y_range = 1.0
       
   678                     x_min, x_max = start_tick, end_tick
       
   679                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
       
   680                     
       
   681                 else:
       
   682                     min_start_tick = reduce(max, [item.GetData()[0, 0] 
       
   683                                                   for item in self.Items
       
   684                                                   if len(item.GetData()) > 0], 0)
       
   685                     start_tick = max(start_tick, min_start_tick)
       
   686                     end_tick = max(end_tick, min_start_tick)
       
   687                     x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick)
       
   688                     y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick)
       
   689                     length = 0
       
   690                     if x_data is not None and y_data is not None:  
       
   691                         length = min(len(x_data), len(y_data))
       
   692                     if len(self.Items) < 3:
       
   693                         if x_data is not None and y_data is not None:
       
   694                             if len(self.Plots) == 0:
       
   695                                 self.Plots.append(
       
   696                                     self.Axes.plot(x_data[:, 1][:length], 
       
   697                                                    y_data[:, 1][:length])[0])
       
   698                             else:
       
   699                                 self.Plots[0].set_data(
       
   700                                     x_data[:, 1][:length], 
       
   701                                     y_data[:, 1][:length])
       
   702                     else:
       
   703                         while len(self.Axes.lines) > 0:
       
   704                             self.Axes.lines.pop()
       
   705                         z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick)
       
   706                         if x_data is not None and y_data is not None and z_data is not None:
       
   707                             length = min(length, len(z_data))
       
   708                             self.Axes.plot(x_data[:, 1][:length],
       
   709                                            y_data[:, 1][:length],
       
   710                                            zs = z_data[:, 1][:length])
       
   711                         self.Axes.set_zlim(z_min, z_max)
       
   712                 
       
   713                 self.Axes.set_xlim(x_min, x_max)
       
   714                 self.Axes.set_ylim(y_min, y_max)
       
   715             
       
   716             labels = ["%s: %s" % (item.GetVariable(), item.GetValue())
       
   717                       for item in self.Items]
       
   718             colors = [{True: 'b', False: 'k'}[item.IsForced()] for item in self.Items]
       
   719             if self.GraphType == GRAPH_PARALLEL:
       
   720                 legend = self.Axes.legend(self.Plots, labels, 
       
   721                     loc="upper left", frameon=False,
       
   722                     prop={'size':'small'})
       
   723                 for t, color in zip(legend.get_texts(), colors):
       
   724                     t.set_color(color)
       
   725             else:
       
   726                 self.Axes.set_xlabel(labels[0], fontdict={'size':'small','color':colors[0]})
       
   727                 self.Axes.set_ylabel(labels[1], fontdict={'size':'small','color':colors[1]})
       
   728                 if len(labels) > 2:
       
   729                     self.Axes.set_zlabel(labels[2], fontdict={'size':'small','color':colors[2]})
       
   730             self.Canvas.draw()
       
   731         
       
   732         def GetSplitGraphMenuFunction(self, item):
       
   733             def SplitGraphFunction(event):
       
   734                 self.ParentWindow.SplitGraphs(self, item)
       
   735             return SplitGraphFunction
       
   736         
       
   737         def OnSplitAllGraphsMenu(self, event):
       
   738             self.ParentWindow.SplitGraphs(self)
       
   739     
       
   740 class DebugVariablePanel(wx.Panel, DebugViewer):
   321     
   741     
   322     def __init__(self, parent, producer, window):
   742     def __init__(self, parent, producer, window):
   323         wx.SplitterWindow.__init__(self, parent, style=wx.SP_3D)
   743         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
   324         
   744         
   325         self.ParentWindow = window
   745         self.ParentWindow = window
   326         
   746         
   327         DebugViewer.__init__(self, producer, True)
   747         DebugViewer.__init__(self, producer, True)
   328         
   748         
   329         self.SetSashGravity(0.5)
       
   330         self.SetNeedUpdating(True)
       
   331         self.SetMinimumPaneSize(1)
       
   332         
       
   333         self.MainPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
       
   334         
       
   335         main_panel_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
   336         main_panel_sizer.AddGrowableCol(0)
       
   337         main_panel_sizer.AddGrowableRow(1)
       
   338         
       
   339         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   340         main_panel_sizer.AddSizer(button_sizer, border=5, 
       
   341               flag=wx.ALIGN_RIGHT|wx.ALL)
       
   342         
       
   343         for name, bitmap, help in [
       
   344                 ("DeleteButton", "remove_element", _("Remove debug variable")),
       
   345                 ("UpButton", "up", _("Move debug variable up")),
       
   346                 ("DownButton", "down", _("Move debug variable down"))]:
       
   347             button = wx.lib.buttons.GenBitmapButton(self.MainPanel, bitmap=GetBitmap(bitmap), 
       
   348                   size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   349             button.SetToolTipString(help)
       
   350             setattr(self, name, button)
       
   351             button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   352         
       
   353         self.VariablesGrid = CustomGrid(self.MainPanel, size=wx.Size(-1, 150), style=wx.VSCROLL)
       
   354         self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
       
   355         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
       
   356               self.OnVariablesGridCellRightClick)
       
   357         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
       
   358               self.OnVariablesGridCellLeftClick)
       
   359         main_panel_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
   360         
       
   361         self.MainPanel.SetSizer(main_panel_sizer)
       
   362         
       
   363         self.HasNewData = False
   749         self.HasNewData = False
   364         self.Ticks = numpy.array([])
       
   365         self.RangeValues = None
       
   366         self.StartTick = 0
       
   367         self.Fixed = False
       
   368         self.Force = False
       
   369         
       
   370         self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
       
   371         self.VariablesGrid.SetTable(self.Table)
       
   372         self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
       
   373                                        "Up": self.UpButton,
       
   374                                        "Down": self.DownButton})
       
   375         
       
   376         def _AddVariable(new_row):
       
   377             return self.VariablesGrid.GetGridCursorRow()
       
   378         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
   379         
       
   380         def _DeleteVariable(row):
       
   381             item = self.Table.GetItem(row)
       
   382             self.RemoveDataConsumer(item)
       
   383             for infos in self.GraphicsAxes:
       
   384                 if item in infos["items"]:
       
   385                     if len(infos["items"]) == 1:
       
   386                         self.GraphicsAxes.remove(infos)
       
   387                     else:
       
   388                         infos["items"].remove(item)
       
   389                     break
       
   390             self.Table.RemoveItem(row)
       
   391             self.ResetGraphics()
       
   392             self.RefreshGrid()
       
   393         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
   394         
       
   395         def _MoveVariable(row, move):
       
   396             new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
       
   397             if new_row != row:
       
   398                 self.Table.MoveItem(row, new_row)
       
   399                 self.ResetGraphics()
       
   400                 self.RefreshGrid()
       
   401             return new_row
       
   402         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
   403         
       
   404         self.VariablesGrid.SetRowLabelSize(0)
       
   405         
       
   406         self.GridColSizes = [200, 100]
       
   407         
       
   408         for col in range(self.Table.GetNumberCols()):
       
   409             attr = wx.grid.GridCellAttr()
       
   410             attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
   411             self.VariablesGrid.SetColAttr(col, attr)
       
   412             self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
       
   413         
       
   414         self.Table.ResetView(self.VariablesGrid)
       
   415         self.VariablesGrid.RefreshButtons()
       
   416         
   750         
   417         if USE_MPL:
   751         if USE_MPL:
   418             self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL)
   752             main_sizer = wx.BoxSizer(wx.VERTICAL)
   419             
   753             
   420             self.GraphicsPanelSizer = wx.BoxSizer(wx.VERTICAL)
   754             self.Ticks = numpy.array([])
       
   755             self.RangeValues = None
       
   756             self.StartTick = 0
       
   757             self.Fixed = False
       
   758             self.Force = False
       
   759             self.GraphicPanels = []
   421             
   760             
   422             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   761             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   423             self.GraphicsPanelSizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   762             main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   424             
   763             
   425             range_label = wx.StaticText(self.GraphicsPanel, label=_('Range:'))
   764             range_label = wx.StaticText(self, label=_('Range:'))
   426             graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
   765             graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
   427             
   766             
   428             self.CanvasRange = wx.ComboBox(self.GraphicsPanel, style=wx.CB_READONLY)
   767             self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
   429             self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
   768             self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
   430             graphics_button_sizer.AddWindow(self.CanvasRange, 1, 
   769             graphics_button_sizer.AddWindow(self.CanvasRange, 1, 
   431                   border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
   770                   border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
   432             
   771             
   433             for name, bitmap, help in [
   772             for name, bitmap, help in [
   434                 ("ResetButton", "reset", _("Clear the graph values")),
   773                 ("ResetButton", "reset", _("Clear the graph values")),
   435                 ("CurrentButton", "current", _("Go to current value")),
   774                 ("CurrentButton", "current", _("Go to current value")),
   436                 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
   775                 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
   437                 button = wx.lib.buttons.GenBitmapButton(self.GraphicsPanel, 
   776                 button = wx.lib.buttons.GenBitmapButton(self, 
   438                       bitmap=GetBitmap(bitmap), 
   777                       bitmap=GetBitmap(bitmap), 
   439                       size=wx.Size(28, 28), style=wx.NO_BORDER)
   778                       size=wx.Size(28, 28), style=wx.NO_BORDER)
   440                 button.SetToolTipString(help)
   779                 button.SetToolTipString(help)
   441                 setattr(self, name, button)
   780                 setattr(self, name, button)
   442                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
   781                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
   443                 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   782                 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   444             
   783             
   445             self.CanvasPosition = wx.ScrollBar(self.GraphicsPanel, 
   784             self.CanvasPosition = wx.ScrollBar(self, 
   446                   size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
   785                   size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
   447             self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
   786             self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
   448                   self.OnPositionChanging, self.CanvasPosition)
   787                   self.OnPositionChanging, self.CanvasPosition)
   449             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
   788             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
   450                   self.OnPositionChanging, self.CanvasPosition)
   789                   self.OnPositionChanging, self.CanvasPosition)
   452                   self.OnPositionChanging, self.CanvasPosition)
   791                   self.OnPositionChanging, self.CanvasPosition)
   453             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
   792             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
   454                   self.OnPositionChanging, self.CanvasPosition)
   793                   self.OnPositionChanging, self.CanvasPosition)
   455             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
   794             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
   456                   self.OnPositionChanging, self.CanvasPosition)
   795                   self.OnPositionChanging, self.CanvasPosition)
   457             self.GraphicsPanelSizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
   796             main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
   458             
   797             
   459             self.GraphicsCanvasWindow = wx.ScrolledWindow(self.GraphicsPanel, style=wx.HSCROLL|wx.VSCROLL)
   798             self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
   460             self.GraphicsCanvasWindow.Bind(wx.EVT_SIZE, self.OnGraphicsCanvasWindowResize)
   799             self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
   461             self.GraphicsPanelSizer.AddWindow(self.GraphicsCanvasWindow, 1, flag=wx.GROW)
   800             self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
   462             
   801             main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
   463             graphics_canvas_window_sizer = wx.BoxSizer(wx.VERTICAL)
   802             
   464             
   803             self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
   465             self.GraphicsFigure = matplotlib.figure.Figure()
   804             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
   466             self.GraphicsAxes = []
   805             
   467             
   806             self.RefreshCanvasRange()
   468             self.GraphicsCanvas = FigureCanvas(self.GraphicsCanvasWindow, -1, self.GraphicsFigure)
   807             
   469             self.GraphicsCanvas.mpl_connect("button_press_event", self.OnGraphicsCanvasClick)
       
   470             self.GraphicsCanvas.SetDropTarget(DebugVariableDropTarget(self, self.GraphicsCanvas))
       
   471             graphics_canvas_window_sizer.AddWindow(self.GraphicsCanvas, 1, flag=wx.GROW)
       
   472             
       
   473             self.GraphicsCanvasWindow.SetSizer(graphics_canvas_window_sizer)
       
   474             
       
   475             self.Graphics3DFigure = matplotlib.figure.Figure()
       
   476             self.Graphics3DFigure.subplotpars.update(left=0.0, right=1.0, bottom=0.0, top=1.0)
       
   477             
       
   478             self.LastMotionTime = gettime()
       
   479             self.Graphics3DAxes = self.Graphics3DFigure.gca(projection='3d')
       
   480             self.Graphics3DAxes.set_color_cycle(['b'])
       
   481             setattr(self.Graphics3DAxes, "_on_move", self.OnGraphics3DMotion)
       
   482             
       
   483             self.Graphics3DCanvas = FigureCanvas(self.GraphicsPanel, -1, self.Graphics3DFigure)
       
   484             self.Graphics3DCanvas.SetMinSize(wx.Size(1, 1))
       
   485             self.GraphicsPanelSizer.AddWindow(self.Graphics3DCanvas, 1, flag=wx.GROW)
       
   486             
       
   487             self.Graphics3DAxes.mouse_init()
       
   488             
       
   489             self.GraphicsPanel.SetSizer(self.GraphicsPanelSizer)
       
   490             
       
   491             self.SplitHorizontally(self.MainPanel, self.GraphicsPanel, -200)
       
   492         
       
   493         else:
   808         else:
   494             self.Initialize(self.MainPanel)
   809             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
   495         
   810             main_sizer.AddGrowableCol(0)
   496         self.ResetGraphics()
   811             main_sizer.AddGrowableRow(1)
   497         self.RefreshCanvasRange()
   812             
   498         self.RefreshScrollBar()
   813             button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   814             main_sizer.AddSizer(button_sizer, border=5, 
       
   815                   flag=wx.ALIGN_RIGHT|wx.ALL)
       
   816             
       
   817             for name, bitmap, help in [
       
   818                     ("DeleteButton", "remove_element", _("Remove debug variable")),
       
   819                     ("UpButton", "up", _("Move debug variable up")),
       
   820                     ("DownButton", "down", _("Move debug variable down"))]:
       
   821                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
   822                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
   823                 button.SetToolTipString(help)
       
   824                 setattr(self, name, button)
       
   825                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
   826             
       
   827             self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL)
       
   828             self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
       
   829             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
       
   830                   self.OnVariablesGridCellRightClick)
       
   831             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
       
   832                   self.OnVariablesGridCellLeftClick)
       
   833             main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
   834         
       
   835             self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
       
   836             self.VariablesGrid.SetTable(self.Table)
       
   837             self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
       
   838                                            "Up": self.UpButton,
       
   839                                            "Down": self.DownButton})
       
   840         
       
   841             def _AddVariable(new_row):
       
   842                 return self.VariablesGrid.GetGridCursorRow()
       
   843             setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
   844         
       
   845             def _DeleteVariable(row):
       
   846                 item = self.Table.GetItem(row)
       
   847                 self.RemoveDataConsumer(item)
       
   848                 self.Table.RemoveItem(row)
       
   849                 self.RefreshView()
       
   850             setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
   851             
       
   852             def _MoveVariable(row, move):
       
   853                 new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
       
   854                 if new_row != row:
       
   855                     self.Table.MoveItem(row, new_row)
       
   856                     self.RefreshView()
       
   857                 return new_row
       
   858             setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
   859             
       
   860             self.VariablesGrid.SetRowLabelSize(0)
       
   861             
       
   862             self.GridColSizes = [200, 100]
       
   863             
       
   864             for col in range(self.Table.GetNumberCols()):
       
   865                 attr = wx.grid.GridCellAttr()
       
   866                 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
   867                 self.VariablesGrid.SetColAttr(col, attr)
       
   868                 self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
       
   869             
       
   870             self.Table.ResetView(self.VariablesGrid)
       
   871             self.VariablesGrid.RefreshButtons()
       
   872         
       
   873         self.SetSizer(main_sizer)
   499         
   874         
   500     def SetDataProducer(self, producer):
   875     def SetDataProducer(self, producer):
   501         DebugViewer.SetDataProducer(self, producer)
   876         DebugViewer.SetDataProducer(self, producer)
   502         
   877         
   503         if self.DataProducer is not None:
   878         if USE_MPL:
   504             self.Ticktime = self.DataProducer.GetTicktime()
   879             if self.DataProducer is not None:
   505             self.RefreshCanvasRange()
   880                 self.Ticktime = self.DataProducer.GetTicktime()
   506         else:
   881                 self.RefreshCanvasRange()
   507             self.Ticktime = 0
   882             else:
       
   883                 self.Ticktime = 0
   508     
   884     
   509     def RefreshNewData(self, *args, **kwargs):
   885     def RefreshNewData(self, *args, **kwargs):
   510         if self.HasNewData or self.Force:
   886         if self.HasNewData or self.Force:
   511             self.HasNewData = False
   887             self.HasNewData = False
   512             self.RefreshGrid(only_values=True)
   888             self.RefreshView(only_values=True)
   513         DebugViewer.RefreshNewData(self, *args, **kwargs)
   889         DebugViewer.RefreshNewData(self, *args, **kwargs)
   514     
   890     
   515     def NewDataAvailable(self, tick, *args, **kwargs):
   891     def NewDataAvailable(self, tick, *args, **kwargs):
   516         if tick is not None:
   892         if USE_MPL and tick is not None:
   517             self.Ticks = numpy.append(self.Ticks, [tick])
   893             self.Ticks = numpy.append(self.Ticks, [tick])
   518             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
   894             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
   519                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
   895                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
   520         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
   896         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
   521     
   897     
   522     def RefreshGrid(self, only_values=False):
   898     def RefreshGraphicsSizer(self):
   523         self.Freeze()
   899         self.GraphicsSizer.Clear()
   524         if only_values:
   900         
   525             for col in xrange(self.Table.GetNumberCols()):
   901         for panel in self.GraphicPanels:
   526                 if self.Table.GetColLabelValue(col, False) == "Value":
   902             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
   527                     for row in xrange(self.Table.GetNumberRows()):
   903             
   528                         self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
   904         self.GraphicsSizer.Layout()
   529         else:
   905         self.RefreshGraphicsWindowScrollbars()
   530             self.Table.ResetView(self.VariablesGrid)
   906     
   531         self.VariablesGrid.RefreshButtons()
   907     def RefreshView(self, only_values=False):
   532         
       
   533         self.RefreshScrollBar()
       
   534         
       
   535         self.Thaw()
       
   536         
       
   537         if USE_MPL:
   908         if USE_MPL:
   538         
   909             self.RefreshCanvasPosition()
       
   910             
   539             if not self.Fixed or self.Force:
   911             if not self.Fixed or self.Force:
   540                 self.Force = False
   912                 self.Force = False
   541                 
   913                 refresh_graphics = True
   542                 # Refresh graphics
   914             else:
   543                 start_tick, end_tick = self.StartTick, self.StartTick + self.CurrentRange
   915                 refresh_graphics = False
   544                 for infos in self.GraphicsAxes:
   916             
   545                     
   917             for panel in self.GraphicPanels:
   546                     if infos["type"] == GRAPH_PARALLEL:
   918                 if isinstance(panel, DebugVariableGraphic):
   547                         min_value = max_value = None
   919                     panel.Refresh(refresh_graphics)
   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
       
   613             for infos in self.GraphicsAxes:
       
   614                 labels = ["%s: %s" % (item.GetVariable(), item.GetValue())
       
   615                           for item in infos["items"]]
       
   616                 if infos["type"] == GRAPH_PARALLEL:
       
   617                     infos["axes"].legend(infos["plots"], labels, 
       
   618                         loc="upper left", frameon=False,
       
   619                         prop={'size':'small'})
       
   620                     plot2d += 1
       
   621                 else:
   920                 else:
   622                     infos["axes"].set_xlabel(labels[0], fontdict={'size':'small'})
   921                     panel.Refresh()
   623                     infos["axes"].set_ylabel(labels[1], fontdict={'size':'small'})
   922         
   624                     if len(labels) > 2:
   923         else:
   625                         infos["axes"].set_zlabel(labels[2], fontdict={'size':'small'})
   924             self.Freeze()
   626                         plot3d += 1
   925             
   627                     else:
   926             if only_values:
   628                         plot2d += 1
   927                 for col in xrange(self.Table.GetNumberCols()):
   629             
   928                     if self.Table.GetColLabelValue(col, False) == "Value":
   630             if plot2d > 0:
   929                         for row in xrange(self.Table.GetNumberRows()):
   631                 self.GraphicsCanvas.draw()
   930                             self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
   632             if plot3d > 0:
   931             else:
   633                 self.Graphics3DCanvas.draw()
   932                 self.Table.ResetView(self.VariablesGrid)
   634             
   933             self.VariablesGrid.RefreshButtons()
       
   934             
       
   935             self.Thaw()
       
   936         
   635     def UnregisterObsoleteData(self):
   937     def UnregisterObsoleteData(self):
   636         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   938         if USE_MPL:
   637         items.reverse()
   939             if self.DataProducer is not None:
   638         for idx, item in items:
   940                 self.Ticktime = self.DataProducer.GetTicktime()
   639             iec_path = item.GetVariable().upper()
   941                 self.RefreshCanvasRange()
   640             if self.GetDataType(iec_path) is None:
   942             
   641                 self.RemoveDataConsumer(item)
   943             for panel in self.GraphicPanels:
   642                 self.Table.RemoveItem(idx)
   944                 panel.UnregisterObsoleteData()
   643             else:
   945             
   644                 self.AddDataConsumer(iec_path, item)
   946         else:
   645                 item.RefreshVariableType()
   947             items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   646         self.Freeze()
   948             items.reverse()
   647         self.Table.ResetView(self.VariablesGrid)
   949             for idx, item in items:
   648         self.VariablesGrid.RefreshButtons()
   950                 iec_path = item.GetVariable().upper()
   649         self.Thaw()
   951                 if self.GetDataType(iec_path) is None:
   650         if self.DataProducer is not None:
   952                     self.RemoveDataConsumer(item)
   651             self.Ticktime = self.DataProducer.GetTicktime()
   953                     self.Table.RemoveItem(idx)
   652             self.RefreshCanvasRange()
   954                 else:
   653     
   955                     self.AddDataConsumer(iec_path, item)
   654     def ResetGrid(self):
   956                     item.RefreshVariableType()
       
   957             self.Freeze()
       
   958             self.Table.ResetView(self.VariablesGrid)
       
   959             self.VariablesGrid.RefreshButtons()
       
   960             self.Thaw()
       
   961     
       
   962     def ResetView(self):
   655         self.DeleteDataConsumers()
   963         self.DeleteDataConsumers()
   656         self.Table.Empty()
   964         if USE_MPL:
   657         self.Freeze()
   965             for panel in self.GraphicPanels:
   658         self.Table.ResetView(self.VariablesGrid)
   966                 panel.Destroy()
   659         self.VariablesGrid.RefreshButtons()
   967             self.GraphicPanels = []
   660         self.Thaw()
   968             self.RefreshGraphicsSizer()
   661         self.ResetGraphics()
   969         else:
       
   970             self.Table.Empty()
       
   971             self.Freeze()
       
   972             self.Table.ResetView(self.VariablesGrid)
       
   973             self.VariablesGrid.RefreshButtons()
       
   974             self.Thaw()
   662     
   975     
   663     def RefreshCanvasRange(self):
   976     def RefreshCanvasRange(self):
   664         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
   977         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
   665             self.RangeValues = RANGE_VALUES
   978             self.RangeValues = RANGE_VALUES
   666             self.CanvasRange.Clear()
   979             self.CanvasRange.Clear()
   667             for text, value in RANGE_VALUES:
   980             for text, value in RANGE_VALUES:
   668                 self.CanvasRange.Append(text)
   981                 self.CanvasRange.Append(text)
   669             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
   982             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
   670             self.CurrentRange = RANGE_VALUES[0][1]
   983             self.CurrentRange = RANGE_VALUES[0][1]
   671             self.RefreshGrid(True)
   984             self.RefreshView(True)
   672         elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES:
   985         elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES:
   673             self.RangeValues = TIME_RANGE_VALUES
   986             self.RangeValues = TIME_RANGE_VALUES
   674             self.CanvasRange.Clear()
   987             self.CanvasRange.Clear()
   675             for text, value in TIME_RANGE_VALUES:
   988             for text, value in TIME_RANGE_VALUES:
   676                 self.CanvasRange.Append(text)
   989                 self.CanvasRange.Append(text)
   677             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
   990             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
   678             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
   991             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
   679             self.RefreshGrid(True)
   992             self.RefreshView(True)
   680     
   993     
   681     def RefreshScrollBar(self):
   994     def RefreshCanvasPosition(self):
   682         if len(self.Ticks) > 0:
   995         if len(self.Ticks) > 0:
   683             pos = int(self.StartTick - self.Ticks[0])
   996             pos = int(self.StartTick - self.Ticks[0])
   684             range = int(self.Ticks[-1] - self.Ticks[0])
   997             range = int(self.Ticks[-1] - self.Ticks[0])
   685         else:
   998         else:
   686             pos = 0
   999             pos = 0
   742             if self.Fixed:
  1055             if self.Fixed:
   743                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
  1056                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
   744             else:
  1057             else:
   745                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1058                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   746         self.Force = True
  1059         self.Force = True
   747         self.RefreshGrid(True)
  1060         self.RefreshView(True)
   748     
  1061     
   749     def OnRangeChanged(self, event):
  1062     def OnRangeChanged(self, event):
   750         try:
  1063         try:
   751             if self.Ticktime == 0:
  1064             if self.Ticktime == 0:
   752                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
  1065                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
   758         event.Skip()
  1071         event.Skip()
   759     
  1072     
   760     def OnResetButton(self, event):
  1073     def OnResetButton(self, event):
   761         self.StartTick = 0
  1074         self.StartTick = 0
   762         self.Fixed = False
  1075         self.Fixed = False
   763         for item in self.Table.GetData():
  1076         for panel in self.GraphicPanels:
   764             item.ResetData()
  1077             panel.ResetData()
   765         self.RefreshGrid(True)
  1078         self.RefreshView(True)
   766         event.Skip()
  1079         event.Skip()
   767 
  1080 
   768     def OnCurrentButton(self, event):
  1081     def OnCurrentButton(self, event):
   769         if len(self.Ticks) > 0:
  1082         if len(self.Ticks) > 0:
   770             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
  1083             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   771             self.Fixed = False
  1084             self.Fixed = False
   772             self.Force = True
  1085             self.Force = True
   773             self.RefreshGrid(True)
  1086             self.RefreshView(True)
   774         event.Skip()
  1087         event.Skip()
   775     
  1088     
   776     def CopyDataToClipboard(self, variables):
  1089     def CopyDataToClipboard(self, variables):
   777         text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables])
  1090         text = "tick;%s;\n" % ";".join([var_name for var_name, data in variables])
   778         next_tick = NextTick(variables)
  1091         next_tick = NextTick(variables)
   804             self.Fixed = True
  1117             self.Fixed = True
   805             self.Force = True
  1118             self.Force = True
   806             wx.CallAfter(self.NewDataAvailable, None, True)
  1119             wx.CallAfter(self.NewDataAvailable, None, True)
   807         event.Skip()
  1120         event.Skip()
   808     
  1121     
       
  1122     def GetRange(self):
       
  1123         return self.StartTick, self.StartTick + self.CurrentRange
       
  1124     
       
  1125     def GetViewerIndex(self, viewer):
       
  1126         if viewer in self.GraphicPanels:
       
  1127             return self.GraphicPanels.index(viewer)
       
  1128         return None
       
  1129     
   809     def InsertValue(self, iec_path, idx = None, force=False):
  1130     def InsertValue(self, iec_path, idx = None, force=False):
   810         if idx is None:
  1131         if USE_MPL:
   811             idx = self.Table.GetNumberRows()
  1132             for panel in self.GraphicPanels:
   812         for item in self.Table.GetData():
  1133                 if panel.GetItem(iec_path) is not None:
   813             if iec_path == item.GetVariable():
  1134                     return
   814                 return
  1135             if idx is None:
       
  1136                 idx = len(self.GraphicPanels)
       
  1137         else:
       
  1138             for item in self.Table.GetData():
       
  1139                 if iec_path == item.GetVariable():
       
  1140                     return
       
  1141             if idx is None:
       
  1142                 idx = self.Table.GetNumberRows()
   815         item = VariableTableItem(self, iec_path)
  1143         item = VariableTableItem(self, iec_path)
   816         result = self.AddDataConsumer(iec_path.upper(), item)
  1144         result = self.AddDataConsumer(iec_path.upper(), item)
   817         if result is not None or force:
  1145         if result is not None or force:
   818             self.Table.InsertItem(idx, item)
  1146             
   819             if item.IsNumVariable():
  1147             if USE_MPL:
   820                 self.GraphicsAxes.append({
  1148                 if item.IsNumVariable():
   821                     "items": [item],
  1149                     panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   822                     "axes": None,
  1150                 else:
   823                     "type": GRAPH_PARALLEL,
  1151                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
   824                     "plots": []})
  1152                 if idx is not None:
   825             self.ResetGraphics()
  1153                     self.GraphicPanels.insert(idx, panel)
   826             self.RefreshGrid()
  1154                 else:
   827     
  1155                     self.GraphicPanels.append(panel)
   828     def MergeGraphs(self, source, target_infos, merge_type, force=False):
  1156                 self.RefreshGraphicsSizer()
       
  1157             else:
       
  1158                 self.Table.InsertItem(idx, item)
       
  1159             
       
  1160             self.RefreshView()
       
  1161     
       
  1162     def SplitGraphs(self, source_panel, item=None):
       
  1163         source_idx = self.GetViewerIndex(source_panel)
       
  1164         if source_idx is not None:
       
  1165             
       
  1166             if item is None:
       
  1167                 source_items = source_panel.GetItems()
       
  1168                 while len(source_items) > 1:
       
  1169                     item = source_items.pop(-1)
       
  1170                     if item.IsNumVariable():
       
  1171                         panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1172                     else:
       
  1173                         panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  1174                     self.GraphicPanels.insert(source_idx + 1, panel)
       
  1175                 if isinstance(source_panel, DebugVariableGraphic):
       
  1176                     source_panel.GraphType = GRAPH_PARALLEL
       
  1177                     source_panel.ResetGraphics()
       
  1178                     
       
  1179             else:
       
  1180                 source_panel.RemoveItem(item)
       
  1181                 if item.IsNumVariable():
       
  1182                     panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1183                 else:
       
  1184                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  1185                 self.GraphicPanels.insert(source_idx + 1, panel)
       
  1186             
       
  1187             self.RefreshGraphicsSizer()
       
  1188             self.RefreshView()
       
  1189     
       
  1190     def MergeGraphs(self, source, target_idx, merge_type, force=False):
   829         source_item = None
  1191         source_item = None
   830         for item in self.Table.GetData():
  1192         source_panel = None
   831             if item.GetVariable() == source:
  1193         for panel in self.GraphicPanels:
   832                 source_item = item
  1194             source_item = panel.GetItem(source)
       
  1195             if source_item is not None:
       
  1196                 source_panel = panel
       
  1197                 break
   833         if source_item is None:
  1198         if source_item is None:
   834             item = VariableTableItem(self, source)
  1199             item = VariableTableItem(self, source)
   835             if item.IsNumVariable():
  1200             if item.IsNumVariable():
   836                 result = self.AddDataConsumer(source.upper(), item)
  1201                 result = self.AddDataConsumer(source.upper(), item)
   837                 if result is not None or force:
  1202                 if result is not None or force:
   838                     self.Table.InsertItem(self.Table.GetNumberRows(), item)
       
   839                     source_item = item
  1203                     source_item = item
   840         if source_item is not None:
  1204         if source_item is not None:
   841             source_infos = None
  1205             target_panel = self.GraphicPanels[target_idx]
   842             for infos in self.GraphicsAxes:
  1206             graph_type = target_panel.GraphType
   843                 if source_item in infos["items"]:
  1207             if target_panel != source_panel:
   844                     source_infos = infos
  1208                 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
   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 and target_infos != source_infos:
       
   857                 if (merge_type == GRAPH_PARALLEL and target_infos["type"] != merge_type or
       
   858                     merge_type == GRAPH_ORTHOGONAL and 
  1209                     merge_type == GRAPH_ORTHOGONAL and 
   859                     (target_infos["type"] == GRAPH_PARALLEL and len(target_infos["items"]) > 1 or
  1210                     (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
   860                      target_infos["type"] == GRAPH_ORTHOGONAL and len(target_infos["items"]) >= 3)):
  1211                      graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
   861                     print "Graphs not compatible"
       
   862                     return
  1212                     return
   863                 
  1213                 
   864                 if source_infos is not None:
  1214                 if source_panel is not None:
   865                     source_infos["items"].remove(source_item)
  1215                     source_panel.RemoveItem(source_item)
   866                     if len(source_infos["items"]) == 0:
  1216                     if source_panel.IsEmpty():
   867                         self.GraphicsAxes.remove(source_infos)
  1217                         if source_panel.Canvas.HasCapture():
       
  1218                             source_panel.Canvas.ReleaseMouse()
       
  1219                         self.GraphicPanels.remove(source_panel)
       
  1220                         source_panel.Destroy()
       
  1221                         
       
  1222                 target_panel.AddItem(source_item)
       
  1223                 target_panel.GraphType = merge_type
       
  1224                 target_panel.ResetGraphics()
   868                 
  1225                 
   869                 target_infos["items"].append(source_item)
  1226                 self.RefreshGraphicsSizer()
   870                 target_infos["type"] = merge_type
  1227                 self.RefreshView()
   871                 
  1228     
   872                 self.ResetGraphics()
  1229     def DeleteValue(self, source_panel, item=None):
   873                 self.RefreshGrid()
  1230         source_idx = self.GetViewerIndex(source_panel)
   874             
  1231         if source_idx is not None:
   875             else:
  1232             
   876                 print "No modification to do"
  1233             if item is None:
   877             
  1234                 source_panel.Clear()
       
  1235                 source_panel.Destroy()
       
  1236                 self.GraphicPanels.remove(source_panel)
       
  1237                 self.RefreshGraphicsSizer()
       
  1238             else:
       
  1239                 source_panel.RemoveItem(item)
       
  1240                 if source_panel.IsEmpty():
       
  1241                     source_panel.Destroy()
       
  1242                     self.GraphicPanels.remove(source_panel)
       
  1243                     self.RefreshGraphicsSizer()
       
  1244             self.RefreshView()
       
  1245     
   878     def GetDebugVariables(self):
  1246     def GetDebugVariables(self):
   879         return [item.GetVariable() for item in self.Table.GetData()]
  1247         if USE_MPL:
   880     
  1248             return [panel.GetVariables() for panel in self.GraphicPanels]
   881     def OnGraphicsCanvasClick(self, event):
  1249         else:
   882         for infos in self.GraphicsAxes:
  1250             return [item.GetVariable() for item in self.Table.GetData()]
   883             if infos["axes"] == event.inaxes:
  1251     
   884                 if len(infos["items"]) == 1:
  1252     def SetDebugVariables(self, variables):
   885                     data = wx.TextDataObject(str((infos["items"][0].GetVariable(), "debug")))
  1253         if USE_MPL:
   886                     dragSource = wx.DropSource(self)
  1254             for variable in variables:
   887                     dragSource.SetData(data)
  1255                 if isinstance(variable, (TupleType, ListType)):
   888                     dragSource.DoDragDrop()
  1256                     items = []
   889                     if self.GraphicsCanvas.HasCapture():
  1257                     for iec_path in variable:
   890                         self.GraphicsCanvas.ReleaseMouse()
  1258                         item = VariableTableItem(self, iec_path)
   891                 break
  1259                         if not item.IsNumVariable():
       
  1260                             continue
       
  1261                         self.AddDataConsumer(iec_path.upper(), item)
       
  1262                         items.append(item)
       
  1263                     if isinstance(variable, ListType):
       
  1264                         panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_PARALLEL)
       
  1265                     elif isinstance(variable, TupleType) and len(items) <= 3:
       
  1266                         panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_ORTHOGONAL)
       
  1267                     else:
       
  1268                         continue
       
  1269                     self.GraphicPanels.append(panel)
       
  1270                     self.RefreshGraphicsSizer()
       
  1271                 else:
       
  1272                     self.InsertValue(variable, force=True)
       
  1273             self.RefreshView()
       
  1274         else:
       
  1275             for variable in variables:
       
  1276                 if isinstance(variable, (ListType, TupleType)):
       
  1277                     for iec_path in variable:
       
  1278                         self.InsertValue(iec_path, force=True)
       
  1279                 else:
       
  1280                     self.InsertValue(variable, force=True)
   892     
  1281     
   893     def ResetGraphicsValues(self):
  1282     def ResetGraphicsValues(self):
   894         self.Ticks = numpy.array([])
       
   895         self.StartTick = 0
       
   896         for item in self.Table.GetData():
       
   897             item.ResetData()
       
   898     
       
   899     def ResetGraphics(self):
       
   900         if USE_MPL:
  1283         if USE_MPL:
   901             self.GraphicsFigure.clear()
  1284             self.Ticks = numpy.array([])
   902             
  1285             self.StartTick = 0
   903             axes_num = 0
  1286             for panel in self.GraphicPanels:
   904             for infos in self.GraphicsAxes:
  1287                 panel.ResetData()
   905                 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
  1288 
   906                     axes_num += 1
  1289     def RefreshGraphicsWindowScrollbars(self):
   907             if axes_num == len(self.GraphicsAxes):
  1290         xstart, ystart = self.GraphicsWindow.GetViewStart()
   908                 self.Graphics3DCanvas.Hide()
  1291         window_size = self.GraphicsWindow.GetClientSize()
   909             else:
  1292         vwidth, vheight = self.GraphicsSizer.GetMinSize()
   910                 self.Graphics3DCanvas.Show()
       
   911             self.GraphicsPanelSizer.Layout()
       
   912             idx = 1
       
   913             for infos in self.GraphicsAxes:
       
   914                 if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
       
   915                     axes = self.GraphicsFigure.add_subplot(axes_num, 1, idx)
       
   916                     infos["axes"] = axes
       
   917                     idx += 1
       
   918                 else:
       
   919                     infos["axes"] = self.Graphics3DAxes
       
   920                 infos["plots"] = []
       
   921             self.RefreshGraphicsCanvasWindowScrollbars()
       
   922             self.GraphicsCanvas.draw()
       
   923     
       
   924     def OnGraphics3DMotion(self, event):
       
   925         current_time = gettime()
       
   926         if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
   927             self.LastMotionTime = current_time
       
   928             Axes3D._on_move(self.Graphics3DAxes, event)
       
   929     
       
   930     def RefreshGraphicsCanvasWindowScrollbars(self):
       
   931         xstart, ystart = self.GraphicsCanvasWindow.GetViewStart()
       
   932         window_size = self.GraphicsCanvasWindow.GetClientSize()
       
   933         axes_num = 0
       
   934         for infos in self.GraphicsAxes:
       
   935             if infos["type"] != GRAPH_ORTHOGONAL or len(infos["items"]) < 3:
       
   936                 axes_num += 1
       
   937         vwidth, vheight = (window_size[0], (axes_num + 1) * 80)
       
   938         self.GraphicsCanvas.SetMinSize(wx.Size(vwidth, vheight))
       
   939         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
  1293         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
   940         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
  1294         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
   941         self.GraphicsCanvasWindow.Scroll(posx, posy)
  1295         self.GraphicsWindow.Scroll(posx, posy)
   942         self.GraphicsCanvasWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
  1296         self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
   943                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
  1297                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
   944     
  1298     
   945     def OnGraphicsCanvasWindowResize(self, event):
  1299     def OnGraphicsWindowResize(self, event):
   946         self.RefreshGraphicsCanvasWindowScrollbars()
  1300         self.RefreshGraphicsWindowScrollbars()
   947         event.Skip()
  1301         event.Skip()