controls/DebugVariablePanel/DebugVariablePanel.py
changeset 1730 64d8f52bc8c8
parent 1578 f8e2a04c4445
child 1734 750eeb7230a1
equal deleted inserted replaced
1726:d51af006fa6b 1730:64d8f52bc8c8
    65 def NextTick(variables):
    65 def NextTick(variables):
    66     next_tick = None
    66     next_tick = None
    67     for item, data in variables:
    67     for item, data in variables:
    68         if len(data) == 0:
    68         if len(data) == 0:
    69             continue
    69             continue
    70         
    70 
    71         next_tick = (data[0][0]
    71         next_tick = (data[0][0]
    72                      if next_tick is None
    72                      if next_tick is None
    73                      else min(next_tick, data[0][0]))
    73                      else min(next_tick, data[0][0]))
    74     
    74 
    75     return next_tick
    75     return next_tick
    76 
    76 
    77 #-------------------------------------------------------------------------------
    77 #-------------------------------------------------------------------------------
    78 #                    Debug Variable Graphic Panel Drop Target
    78 #                    Debug Variable Graphic Panel Drop Target
    79 #-------------------------------------------------------------------------------
    79 #-------------------------------------------------------------------------------
    82 Class that implements a custom drop target class for Debug Variable Graphic
    82 Class that implements a custom drop target class for Debug Variable Graphic
    83 Panel
    83 Panel
    84 """
    84 """
    85 
    85 
    86 class DebugVariableDropTarget(wx.TextDropTarget):
    86 class DebugVariableDropTarget(wx.TextDropTarget):
    87     
    87 
    88     def __init__(self, window):
    88     def __init__(self, window):
    89         """
    89         """
    90         Constructor
    90         Constructor
    91         @param window: Reference to the Debug Variable Panel
    91         @param window: Reference to the Debug Variable Panel
    92         """
    92         """
    93         wx.TextDropTarget.__init__(self)
    93         wx.TextDropTarget.__init__(self)
    94         self.ParentWindow = window
    94         self.ParentWindow = window
    95     
    95 
    96     def __del__(self):
    96     def __del__(self):
    97         """
    97         """
    98         Destructor
    98         Destructor
    99         """
    99         """
   100         # Remove reference to Debug Variable Panel
   100         # Remove reference to Debug Variable Panel
   101         self.ParentWindow = None
   101         self.ParentWindow = None
   102     
   102 
   103     def OnDragOver(self, x, y, d):
   103     def OnDragOver(self, x, y, d):
   104         """
   104         """
   105         Function called when mouse is dragged over Drop Target
   105         Function called when mouse is dragged over Drop Target
   106         @param x: X coordinate of mouse pointer
   106         @param x: X coordinate of mouse pointer
   107         @param y: Y coordinate of mouse pointer
   107         @param y: Y coordinate of mouse pointer
   108         @param d: Suggested default for return value
   108         @param d: Suggested default for return value
   109         """
   109         """
   110        # Signal Debug Variable Panel to refresh highlight giving mouse position
   110        # Signal Debug Variable Panel to refresh highlight giving mouse position
   111         self.ParentWindow.RefreshHighlight(x, y)
   111         self.ParentWindow.RefreshHighlight(x, y)
   112         return wx.TextDropTarget.OnDragOver(self, x, y, d)
   112         return wx.TextDropTarget.OnDragOver(self, x, y, d)
   113         
   113 
   114     def OnDropText(self, x, y, data):
   114     def OnDropText(self, x, y, data):
   115         """
   115         """
   116         Function called when mouse is released in Drop Target
   116         Function called when mouse is released in Drop Target
   117         @param x: X coordinate of mouse pointer
   117         @param x: X coordinate of mouse pointer
   118         @param y: Y coordinate of mouse pointer
   118         @param y: Y coordinate of mouse pointer
   119         @param data: Text associated to drag'n drop
   119         @param data: Text associated to drag'n drop
   120         """
   120         """
   121         # Signal Debug Variable Panel to reset highlight
   121         # Signal Debug Variable Panel to reset highlight
   122         self.ParentWindow.ResetHighlight()
   122         self.ParentWindow.ResetHighlight()
   123         
   123 
   124         message = None
   124         message = None
   125         
   125 
   126         # Check that data is valid regarding DebugVariablePanel
   126         # Check that data is valid regarding DebugVariablePanel
   127         try:
   127         try:
   128             values = eval(data)
   128             values = eval(data)
   129             if not isinstance(values, TupleType):
   129             if not isinstance(values, TupleType):
   130                 raise ValueError
   130                 raise ValueError
   131         except:
   131         except:
   132             message = _("Invalid value \"%s\" for debug variable")%data
   132             message = _("Invalid value \"%s\" for debug variable")%data
   133             values = None
   133             values = None
   134             
   134 
   135         # Display message if data is invalid
   135         # Display message if data is invalid
   136         if message is not None:
   136         if message is not None:
   137             wx.CallAfter(self.ShowMessage, message)
   137             wx.CallAfter(self.ShowMessage, message)
   138         
   138 
   139         # Data contain a reference to a variable to debug
   139         # Data contain a reference to a variable to debug
   140         elif values[1] == "debug":
   140         elif values[1] == "debug":
   141             
   141 
   142             # Drag'n Drop is an internal is an internal move inside Debug
   142             # Drag'n Drop is an internal is an internal move inside Debug
   143             # Variable Panel 
   143             # Variable Panel
   144             if len(values) > 2 and values[2] == "move":
   144             if len(values) > 2 and values[2] == "move":
   145                 self.ParentWindow.MoveValue(values[0])
   145                 self.ParentWindow.MoveValue(values[0])
   146             
   146 
   147             # Drag'n Drop was initiated by another control of Beremiz
   147             # Drag'n Drop was initiated by another control of Beremiz
   148             else:
   148             else:
   149                 self.ParentWindow.InsertValue(values[0], force=True)
   149                 self.ParentWindow.InsertValue(values[0], force=True)
   150     
   150 
   151     def OnLeave(self):
   151     def OnLeave(self):
   152         """
   152         """
   153         Function called when mouse is leave Drop Target
   153         Function called when mouse is leave Drop Target
   154         """
   154         """
   155         # Signal Debug Variable Panel to reset highlight
   155         # Signal Debug Variable Panel to reset highlight
   156         self.ParentWindow.ResetHighlight()
   156         self.ParentWindow.ResetHighlight()
   157         return wx.TextDropTarget.OnLeave(self)
   157         return wx.TextDropTarget.OnLeave(self)
   158     
   158 
   159     def ShowMessage(self, message):
   159     def ShowMessage(self, message):
   160         """
   160         """
   161         Show error message in Error Dialog
   161         Show error message in Error Dialog
   162         @param message: Error message to display
   162         @param message: Error message to display
   163         """
   163         """
   164         dialog = wx.MessageDialog(self.ParentWindow, 
   164         dialog = wx.MessageDialog(self.ParentWindow,
   165                                   message, 
   165                                   message,
   166                                   _("Error"), 
   166                                   _("Error"),
   167                                   wx.OK|wx.ICON_ERROR)
   167                                   wx.OK|wx.ICON_ERROR)
   168         dialog.ShowModal()
   168         dialog.ShowModal()
   169         dialog.Destroy()
   169         dialog.Destroy()
   170 
   170 
   171 
   171 
   176 """
   176 """
   177 Class that implements a Viewer that display variable values as a graphs
   177 Class that implements a Viewer that display variable values as a graphs
   178 """
   178 """
   179 
   179 
   180 class DebugVariablePanel(wx.Panel, DebugViewer):
   180 class DebugVariablePanel(wx.Panel, DebugViewer):
   181     
   181 
   182     def __init__(self, parent, producer, window):
   182     def __init__(self, parent, producer, window):
   183         """
   183         """
   184         Constructor
   184         Constructor
   185         @param parent: Reference to the parent wx.Window
   185         @param parent: Reference to the parent wx.Window
   186         @param producer: Object receiving debug value and dispatching them to
   186         @param producer: Object receiving debug value and dispatching them to
   187         consumers
   187         consumers
   188         @param window: Reference to Beremiz frame
   188         @param window: Reference to Beremiz frame
   189         """
   189         """
   190         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
   190         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
   191         
   191 
   192         # Save Reference to Beremiz frame
   192         # Save Reference to Beremiz frame
   193         self.ParentWindow = window
   193         self.ParentWindow = window
   194         
   194 
   195         # Variable storing flag indicating that variable displayed in table
   195         # Variable storing flag indicating that variable displayed in table
   196         # received new value and then table need to be refreshed
   196         # received new value and then table need to be refreshed
   197         self.HasNewData = False
   197         self.HasNewData = False
   198         
   198 
   199         # Variable storing flag indicating that refresh has been forced, and
   199         # Variable storing flag indicating that refresh has been forced, and
   200         # that next time refresh is possible, it will be done even if no new
   200         # that next time refresh is possible, it will be done even if no new
   201         # data is available
   201         # data is available
   202         self.Force = False
   202         self.Force = False
   203         
   203 
   204         self.SetBackgroundColour(wx.WHITE)
   204         self.SetBackgroundColour(wx.WHITE)
   205         
   205 
   206         main_sizer = wx.BoxSizer(wx.VERTICAL)
   206         main_sizer = wx.BoxSizer(wx.VERTICAL)
   207         
   207 
   208         self.Ticks = numpy.array([]) # List of tick received
   208         self.Ticks = numpy.array([]) # List of tick received
   209         self.StartTick = 0           # Tick starting range of data displayed
   209         self.StartTick = 0           # Tick starting range of data displayed
   210         self.Fixed = False           # Flag that range of data is fixed
   210         self.Fixed = False           # Flag that range of data is fixed
   211         self.CursorTick = None       # Tick of cursor for displaying values
   211         self.CursorTick = None       # Tick of cursor for displaying values
   212         
   212 
   213         self.DraggingAxesPanel = None
   213         self.DraggingAxesPanel = None
   214         self.DraggingAxesBoundingBox = None
   214         self.DraggingAxesBoundingBox = None
   215         self.DraggingAxesMousePos = None
   215         self.DraggingAxesMousePos = None
   216         self.VetoScrollEvent = False
   216         self.VetoScrollEvent = False
   217         
   217 
   218         self.VariableNameMask = []
   218         self.VariableNameMask = []
   219         
   219 
   220         self.GraphicPanels = []
   220         self.GraphicPanels = []
   221         
   221 
   222         graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   222         graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   223         main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   223         main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
   224         
   224 
   225         range_label = wx.StaticText(self, label=_('Range:'))
   225         range_label = wx.StaticText(self, label=_('Range:'))
   226         graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
   226         graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
   227         
   227 
   228         self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
   228         self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
   229         self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
   229         self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
   230         graphics_button_sizer.AddWindow(self.CanvasRange, 1, 
   230         graphics_button_sizer.AddWindow(self.CanvasRange, 1,
   231               border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
   231               border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
   232         
   232 
   233         self.CanvasRange.Clear()
   233         self.CanvasRange.Clear()
   234         default_range_idx = 0
   234         default_range_idx = 0
   235         for idx, (text, value) in enumerate(RANGE_VALUES):
   235         for idx, (text, value) in enumerate(RANGE_VALUES):
   236             self.CanvasRange.Append(text)
   236             self.CanvasRange.Append(text)
   237             if text == "1s":
   237             if text == "1s":
   238                 default_range_idx = idx
   238                 default_range_idx = idx
   239         self.CanvasRange.SetSelection(default_range_idx)
   239         self.CanvasRange.SetSelection(default_range_idx)
   240         
   240 
   241         for name, bitmap, help in [
   241         for name, bitmap, help in [
   242             ("CurrentButton", "current", _("Go to current value")),
   242             ("CurrentButton", "current", _("Go to current value")),
   243             ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
   243             ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
   244             button = wx.lib.buttons.GenBitmapButton(self, 
   244             button = wx.lib.buttons.GenBitmapButton(self,
   245                   bitmap=GetBitmap(bitmap), 
   245                   bitmap=GetBitmap(bitmap),
   246                   size=wx.Size(28, 28), style=wx.NO_BORDER)
   246                   size=wx.Size(28, 28), style=wx.NO_BORDER)
   247             button.SetToolTipString(help)
   247             button.SetToolTipString(help)
   248             setattr(self, name, button)
   248             setattr(self, name, button)
   249             self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
   249             self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
   250             graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   250             graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   251         
   251 
   252         self.CanvasPosition = wx.ScrollBar(self, 
   252         self.CanvasPosition = wx.ScrollBar(self,
   253               size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
   253               size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
   254         self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
   254         self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK,
   255               self.OnPositionChanging, self.CanvasPosition)
   255               self.OnPositionChanging, self.CanvasPosition)
   256         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
   256         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP,
   257               self.OnPositionChanging, self.CanvasPosition)
   257               self.OnPositionChanging, self.CanvasPosition)
   258         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, 
   258         self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN,
   259               self.OnPositionChanging, self.CanvasPosition)
   259               self.OnPositionChanging, self.CanvasPosition)
   260         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
   260         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP,
   261               self.OnPositionChanging, self.CanvasPosition)
   261               self.OnPositionChanging, self.CanvasPosition)
   262         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
   262         self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN,
   263               self.OnPositionChanging, self.CanvasPosition)
   263               self.OnPositionChanging, self.CanvasPosition)
   264         main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
   264         main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
   265         
   265 
   266         self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
   266         self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
   267         main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
   267         main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
   268         
   268 
   269         self.TickLabel = wx.StaticText(self)
   269         self.TickLabel = wx.StaticText(self)
   270         self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
   270         self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
   271         
   271 
   272         self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
   272         self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
   273         self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
   273         self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
   274         
   274 
   275         self.TickTimeLabel = wx.StaticText(self)
   275         self.TickTimeLabel = wx.StaticText(self)
   276         self.TickSizer.AddWindow(self.TickTimeLabel)
   276         self.TickSizer.AddWindow(self.TickTimeLabel)
   277         
   277 
   278         self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
   278         self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
   279         self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
   279         self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
   280         self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
   280         self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
   281         self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground)
   281         self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground)
   282         self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint)
   282         self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint)
   283         self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
   283         self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
   284         self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel)
   284         self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel)
   285         
   285 
   286         main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
   286         main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
   287         
   287 
   288         self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
   288         self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
   289         self.GraphicsWindow.SetSizer(self.GraphicsSizer)
   289         self.GraphicsWindow.SetSizer(self.GraphicsSizer)
   290     
   290 
   291         DebugViewer.__init__(self, producer, True)
   291         DebugViewer.__init__(self, producer, True)
   292         
   292 
   293         self.SetSizer(main_sizer)
   293         self.SetSizer(main_sizer)
   294         self.SetTickTime()
   294         self.SetTickTime()
   295     
   295 
   296     def SetTickTime(self, ticktime=0):
   296     def SetTickTime(self, ticktime=0):
   297         """
   297         """
   298         Set Ticktime for calculate data range according to time range selected
   298         Set Ticktime for calculate data range according to time range selected
   299         @param ticktime: Ticktime to apply to range (default: 0)
   299         @param ticktime: Ticktime to apply to range (default: 0)
   300         """
   300         """
   301         # Save ticktime
   301         # Save ticktime
   302         self.Ticktime = ticktime
   302         self.Ticktime = ticktime
   303         
   303 
   304         # Set ticktime to millisecond if undefined
   304         # Set ticktime to millisecond if undefined
   305         if self.Ticktime == 0:
   305         if self.Ticktime == 0:
   306             self.Ticktime = MILLISECOND
   306             self.Ticktime = MILLISECOND
   307         
   307 
   308         # Calculate range to apply to data
   308         # Calculate range to apply to data
   309         self.CurrentRange = RANGE_VALUES[
   309         self.CurrentRange = RANGE_VALUES[
   310             self.CanvasRange.GetSelection()][1] / self.Ticktime
   310             self.CanvasRange.GetSelection()][1] / self.Ticktime
   311     
   311 
   312     def SetDataProducer(self, producer):
   312     def SetDataProducer(self, producer):
   313         """
   313         """
   314         Set Data Producer
   314         Set Data Producer
   315         @param producer: Data Producer
   315         @param producer: Data Producer
   316         """
   316         """
   317         DebugViewer.SetDataProducer(self, producer)
   317         DebugViewer.SetDataProducer(self, producer)
   318         
   318 
   319         # Set ticktime if data producer is available
   319         # Set ticktime if data producer is available
   320         if self.DataProducer is not None:
   320         if self.DataProducer is not None:
   321             self.SetTickTime(self.DataProducer.GetTicktime())
   321             self.SetTickTime(self.DataProducer.GetTicktime())
   322     
   322 
   323     def RefreshNewData(self):
   323     def RefreshNewData(self):
   324         """
   324         """
   325         Called to refresh Panel according to values received by variables
   325         Called to refresh Panel according to values received by variables
   326         Can receive any parameters (not used here)
   326         Can receive any parameters (not used here)
   327         """
   327         """
   328         # Refresh graphs if new data is available or refresh is forced
   328         # Refresh graphs if new data is available or refresh is forced
   329         if self.HasNewData or self.Force:
   329         if self.HasNewData or self.Force:
   330             self.HasNewData = False
   330             self.HasNewData = False
   331             self.RefreshView()
   331             self.RefreshView()
   332         
   332 
   333         DebugViewer.RefreshNewData(self)
   333         DebugViewer.RefreshNewData(self)
   334     
   334 
   335     def NewDataAvailable(self, ticks):
   335     def NewDataAvailable(self, ticks):
   336         """
   336         """
   337         Called by DataProducer for each tick captured or by panel to refresh
   337         Called by DataProducer for each tick captured or by panel to refresh
   338         graphs
   338         graphs
   339         @param tick: PLC tick captured
   339         @param tick: PLC tick captured
   340         All other parameters are passed to refresh function 
   340         All other parameters are passed to refresh function
   341         """
   341         """
   342         # If tick given
   342         # If tick given
   343         if ticks is not None:
   343         if ticks is not None:
   344             tick = ticks[-1]
   344             tick = ticks[-1]
   345             
   345 
   346             # Save tick as start tick for range if data is still empty
   346             # Save tick as start tick for range if data is still empty
   347             if len(self.Ticks) == 0:
   347             if len(self.Ticks) == 0:
   348                 self.StartTick = ticks[0]
   348                 self.StartTick = ticks[0]
   349             
   349 
   350             # Add tick to list of ticks received
   350             # Add tick to list of ticks received
   351             self.Ticks = numpy.append(self.Ticks, ticks)
   351             self.Ticks = numpy.append(self.Ticks, ticks)
   352             
   352 
   353             # Update start tick for range if range follow ticks received
   353             # Update start tick for range if range follow ticks received
   354             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
   354             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
   355                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
   355                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
   356             
   356 
   357             # Force refresh if graph is fixed because range of data received
   357             # Force refresh if graph is fixed because range of data received
   358             # is too small to fill data range selected
   358             # is too small to fill data range selected
   359             if self.Fixed and \
   359             if self.Fixed and \
   360                self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   360                self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   361                 self.Force = True
   361                 self.Force = True
   362             
   362 
   363             self.HasNewData = False
   363             self.HasNewData = False
   364             self.RefreshView()
   364             self.RefreshView()
   365             
   365 
   366         else:
   366         else:
   367             DebugViewer.NewDataAvailable(self, ticks)
   367             DebugViewer.NewDataAvailable(self, ticks)
   368     
   368 
   369     def ForceRefresh(self):
   369     def ForceRefresh(self):
   370         """
   370         """
   371         Called to force refresh of graphs
   371         Called to force refresh of graphs
   372         """
   372         """
   373         self.Force = True
   373         self.Force = True
   374         wx.CallAfter(self.NewDataAvailable, None)
   374         wx.CallAfter(self.NewDataAvailable, None)
   375     
   375 
   376     def SetCursorTick(self, cursor_tick):
   376     def SetCursorTick(self, cursor_tick):
   377         """
   377         """
   378         Set Cursor for displaying values of items at a tick given
   378         Set Cursor for displaying values of items at a tick given
   379         @param cursor_tick: Tick of cursor
   379         @param cursor_tick: Tick of cursor
   380         """
   380         """
   381         # Save cursor tick
   381         # Save cursor tick
   382         self.CursorTick = cursor_tick
   382         self.CursorTick = cursor_tick
   383         self.Fixed = cursor_tick is not None
   383         self.Fixed = cursor_tick is not None
   384         self.UpdateCursorTick() 
   384         self.UpdateCursorTick()
   385     
   385 
   386     def MoveCursorTick(self, move):
   386     def MoveCursorTick(self, move):
   387         if self.CursorTick is not None:
   387         if self.CursorTick is not None:
   388             cursor_tick = max(self.Ticks[0], 
   388             cursor_tick = max(self.Ticks[0],
   389                           min(self.CursorTick + move, 
   389                           min(self.CursorTick + move,
   390                               self.Ticks[-1]))
   390                               self.Ticks[-1]))
   391             cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
   391             cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick))
   392             if self.Ticks[cursor_tick_idx] == self.CursorTick:
   392             if self.Ticks[cursor_tick_idx] == self.CursorTick:
   393                 cursor_tick_idx = max(0, 
   393                 cursor_tick_idx = max(0,
   394                                   min(cursor_tick_idx + abs(move) / move, 
   394                                   min(cursor_tick_idx + abs(move) / move,
   395                                       len(self.Ticks) - 1))
   395                                       len(self.Ticks) - 1))
   396             self.CursorTick = self.Ticks[cursor_tick_idx]
   396             self.CursorTick = self.Ticks[cursor_tick_idx]
   397             self.StartTick = max(self.Ticks[
   397             self.StartTick = max(self.Ticks[
   398                                 numpy.argmin(numpy.abs(self.Ticks - 
   398                                 numpy.argmin(numpy.abs(self.Ticks -
   399                                         self.CursorTick + self.CurrentRange))],
   399                                         self.CursorTick + self.CurrentRange))],
   400                              min(self.StartTick, self.CursorTick))
   400                              min(self.StartTick, self.CursorTick))
   401             self.RefreshCanvasPosition()
   401             self.RefreshCanvasPosition()
   402             self.UpdateCursorTick() 
   402             self.UpdateCursorTick()
   403             
   403 
   404     def ResetCursorTick(self):
   404     def ResetCursorTick(self):
   405         self.CursorTick = None
   405         self.CursorTick = None
   406         self.Fixed = False
   406         self.Fixed = False
   407         self.UpdateCursorTick()
   407         self.UpdateCursorTick()
   408     
   408 
   409     def UpdateCursorTick(self):
   409     def UpdateCursorTick(self):
   410         for panel in self.GraphicPanels:
   410         for panel in self.GraphicPanels:
   411             if isinstance(panel, DebugVariableGraphicViewer):
   411             if isinstance(panel, DebugVariableGraphicViewer):
   412                 panel.SetCursorTick(self.CursorTick)
   412                 panel.SetCursorTick(self.CursorTick)
   413         self.ForceRefresh()
   413         self.ForceRefresh()
   414     
   414 
   415     def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
   415     def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
   416         if len(panel.GetItems()) > 1:
   416         if len(panel.GetItems()) > 1:
   417             self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   417             self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   418             self.DraggingAxesPanel.SetCursorTick(self.CursorTick)
   418             self.DraggingAxesPanel.SetCursorTick(self.CursorTick)
   419             width, height = panel.GetSize()
   419             width, height = panel.GetSize()
   422             self.DraggingAxesPanel.SetPosition(wx.Point(0, -height))
   422             self.DraggingAxesPanel.SetPosition(wx.Point(0, -height))
   423         else:
   423         else:
   424             self.DraggingAxesPanel = panel
   424             self.DraggingAxesPanel = panel
   425         self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True)
   425         self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True)
   426         self.DraggingAxesMousePos = wx.Point(
   426         self.DraggingAxesMousePos = wx.Point(
   427             x_mouse_start - self.DraggingAxesBoundingBox.x, 
   427             x_mouse_start - self.DraggingAxesBoundingBox.x,
   428             y_mouse_start - self.DraggingAxesBoundingBox.y)
   428             y_mouse_start - self.DraggingAxesBoundingBox.y)
   429         self.MoveDragNDrop(x_mouse, y_mouse)
   429         self.MoveDragNDrop(x_mouse, y_mouse)
   430         
   430 
   431     def MoveDragNDrop(self, x_mouse, y_mouse):
   431     def MoveDragNDrop(self, x_mouse, y_mouse):
   432         self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
   432         self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
   433         self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
   433         self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
   434         self.RefreshHighlight(x_mouse, y_mouse)
   434         self.RefreshHighlight(x_mouse, y_mouse)
   435     
   435 
   436     def RefreshHighlight(self, x_mouse, y_mouse):
   436     def RefreshHighlight(self, x_mouse, y_mouse):
   437         for idx, panel in enumerate(self.GraphicPanels):
   437         for idx, panel in enumerate(self.GraphicPanels):
   438             x, y = panel.GetPosition()
   438             x, y = panel.GetPosition()
   439             width, height = panel.GetSize()
   439             width, height = panel.GetSize()
   440             rect = wx.Rect(x, y, width, height)
   440             rect = wx.Rect(x, y, width, height)
   441             if (rect.InsideXY(x_mouse, y_mouse) or 
   441             if (rect.InsideXY(x_mouse, y_mouse) or
   442                 idx == 0 and y_mouse < 0 or
   442                 idx == 0 and y_mouse < 0 or
   443                 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
   443                 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
   444                 panel.RefreshHighlight(x_mouse - x, y_mouse - y)
   444                 panel.RefreshHighlight(x_mouse - x, y_mouse - y)
   445             else:
   445             else:
   446                 panel.SetHighlight(HIGHLIGHT_NONE)
   446                 panel.SetHighlight(HIGHLIGHT_NONE)
   447         if wx.Platform == "__WXMSW__":
   447         if wx.Platform == "__WXMSW__":
   448             self.RefreshView()
   448             self.RefreshView()
   449         else:
   449         else:
   450             self.ForceRefresh()
   450             self.ForceRefresh()
   451     
   451 
   452     def ResetHighlight(self):
   452     def ResetHighlight(self):
   453         for panel in self.GraphicPanels:
   453         for panel in self.GraphicPanels:
   454             panel.SetHighlight(HIGHLIGHT_NONE)
   454             panel.SetHighlight(HIGHLIGHT_NONE)
   455         if wx.Platform == "__WXMSW__":
   455         if wx.Platform == "__WXMSW__":
   456             self.RefreshView()
   456             self.RefreshView()
   457         else:
   457         else:
   458             self.ForceRefresh()
   458             self.ForceRefresh()
   459     
   459 
   460     def IsDragging(self):
   460     def IsDragging(self):
   461         return self.DraggingAxesPanel is not None
   461         return self.DraggingAxesPanel is not None
   462     
   462 
   463     def GetDraggingAxesClippingRegion(self, panel):
   463     def GetDraggingAxesClippingRegion(self, panel):
   464         x, y = panel.GetPosition()
   464         x, y = panel.GetPosition()
   465         width, height = panel.GetSize()
   465         width, height = panel.GetSize()
   466         bbox = wx.Rect(x, y, width, height)
   466         bbox = wx.Rect(x, y, width, height)
   467         bbox = bbox.Intersect(self.DraggingAxesBoundingBox)
   467         bbox = bbox.Intersect(self.DraggingAxesBoundingBox)
   468         bbox.x -= x
   468         bbox.x -= x
   469         bbox.y -= y
   469         bbox.y -= y
   470         return bbox
   470         return bbox
   471     
   471 
   472     def GetDraggingAxesPosition(self, panel):
   472     def GetDraggingAxesPosition(self, panel):
   473         x, y = panel.GetPosition()
   473         x, y = panel.GetPosition()
   474         return wx.Point(self.DraggingAxesBoundingBox.x - x,
   474         return wx.Point(self.DraggingAxesBoundingBox.x - x,
   475                         self.DraggingAxesBoundingBox.y - y)
   475                         self.DraggingAxesBoundingBox.y - y)
   476     
   476 
   477     def StopDragNDrop(self, variable, x_mouse, y_mouse):
   477     def StopDragNDrop(self, variable, x_mouse, y_mouse):
   478         if self.DraggingAxesPanel not in self.GraphicPanels:
   478         if self.DraggingAxesPanel not in self.GraphicPanels:
   479             self.DraggingAxesPanel.Destroy()
   479             self.DraggingAxesPanel.Destroy()
   480         self.DraggingAxesPanel = None
   480         self.DraggingAxesPanel = None
   481         self.DraggingAxesBoundingBox = None
   481         self.DraggingAxesBoundingBox = None
   502                     else:
   502                     else:
   503                         if y_mouse > yw + height / 2:
   503                         if y_mouse > yw + height / 2:
   504                             idx += 1
   504                             idx += 1
   505                         wx.CallAfter(self.MoveValue, variable, idx, True)
   505                         wx.CallAfter(self.MoveValue, variable, idx, True)
   506                 self.ForceRefresh()
   506                 self.ForceRefresh()
   507                 return 
   507                 return
   508         width, height = self.GraphicsWindow.GetVirtualSize()
   508         width, height = self.GraphicsWindow.GetVirtualSize()
   509         rect = wx.Rect(0, 0, width, height)
   509         rect = wx.Rect(0, 0, width, height)
   510         if rect.InsideXY(x_mouse, y_mouse):
   510         if rect.InsideXY(x_mouse, y_mouse):
   511             wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True)
   511             wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True)
   512         self.ForceRefresh()
   512         self.ForceRefresh()
   513     
   513 
   514     def RefreshGraphicsSizer(self):
   514     def RefreshGraphicsSizer(self):
   515         self.GraphicsSizer.Clear()
   515         self.GraphicsSizer.Clear()
   516         
   516 
   517         for panel in self.GraphicPanels:
   517         for panel in self.GraphicPanels:
   518             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
   518             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
   519             
   519 
   520         self.GraphicsSizer.Layout()
   520         self.GraphicsSizer.Layout()
   521         self.RefreshGraphicsWindowScrollbars()
   521         self.RefreshGraphicsWindowScrollbars()
   522     
   522 
   523     def RefreshView(self):
   523     def RefreshView(self):
   524         self.RefreshCanvasPosition()
   524         self.RefreshCanvasPosition()
   525         
   525 
   526         width, height = self.GraphicsWindow.GetVirtualSize()
   526         width, height = self.GraphicsWindow.GetVirtualSize()
   527         bitmap = wx.EmptyBitmap(width, height)
   527         bitmap = wx.EmptyBitmap(width, height)
   528         dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
   528         dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
   529         dc.Clear()
   529         dc.Clear()
   530         dc.BeginDrawing()
   530         dc.BeginDrawing()
   531         if self.DraggingAxesPanel is not None:
   531         if self.DraggingAxesPanel is not None:
   532             destBBox = self.DraggingAxesBoundingBox
   532             destBBox = self.DraggingAxesBoundingBox
   533             srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
   533             srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
   534             
   534 
   535             srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
   535             srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
   536             srcDC = wx.MemoryDC()
   536             srcDC = wx.MemoryDC()
   537             srcDC.SelectObject(srcBmp)
   537             srcDC.SelectObject(srcBmp)
   538                 
   538 
   539             dc.Blit(destBBox.x, destBBox.y, 
   539             dc.Blit(destBBox.x, destBBox.y,
   540                     int(destBBox.width), int(destBBox.height), 
   540                     int(destBBox.width), int(destBBox.height),
   541                     srcDC, srcBBox.x, srcBBox.y)
   541                     srcDC, srcBBox.x, srcBBox.y)
   542         dc.EndDrawing()
   542         dc.EndDrawing()
   543         
   543 
   544         if not self.Fixed or self.Force:
   544         if not self.Fixed or self.Force:
   545             self.Force = False
   545             self.Force = False
   546             refresh_graphics = True
   546             refresh_graphics = True
   547         else:
   547         else:
   548             refresh_graphics = False
   548             refresh_graphics = False
   549         
   549 
   550         if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
   550         if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
   551             self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
   551             self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
   552         for panel in self.GraphicPanels:
   552         for panel in self.GraphicPanels:
   553             if isinstance(panel, DebugVariableGraphicViewer):
   553             if isinstance(panel, DebugVariableGraphicViewer):
   554                 panel.RefreshViewer(refresh_graphics)
   554                 panel.RefreshViewer(refresh_graphics)
   555             else:
   555             else:
   556                 panel.RefreshViewer()
   556                 panel.RefreshViewer()
   557         
   557 
   558         if self.CursorTick is not None:
   558         if self.CursorTick is not None:
   559             tick = self.CursorTick
   559             tick = self.CursorTick
   560         elif len(self.Ticks) > 0:
   560         elif len(self.Ticks) > 0:
   561             tick = self.Ticks[-1]
   561             tick = self.Ticks[-1]
   562         else:
   562         else:
   568             duration = ""
   568             duration = ""
   569             for value, format in [(tick_duration / DAY, _("%dd")),
   569             for value, format in [(tick_duration / DAY, _("%dd")),
   570                                   ((tick_duration % DAY) / HOUR, _("%dh")),
   570                                   ((tick_duration % DAY) / HOUR, _("%dh")),
   571                                   ((tick_duration % HOUR) / MINUTE, _("%dm")),
   571                                   ((tick_duration % HOUR) / MINUTE, _("%dm")),
   572                                   ((tick_duration % MINUTE) / SECOND, _("%ds"))]:
   572                                   ((tick_duration % MINUTE) / SECOND, _("%ds"))]:
   573                 
   573 
   574                 if value > 0 or not_null:
   574                 if value > 0 or not_null:
   575                     duration += format % value
   575                     duration += format % value
   576                     not_null = True
   576                     not_null = True
   577             
   577 
   578             duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) 
   578             duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND)
   579             self.TickTimeLabel.SetLabel("t: %s" % duration)
   579             self.TickTimeLabel.SetLabel("t: %s" % duration)
   580         else:
   580         else:
   581             self.TickLabel.SetLabel("")
   581             self.TickLabel.SetLabel("")
   582             self.TickTimeLabel.SetLabel("")
   582             self.TickTimeLabel.SetLabel("")
   583         self.TickSizer.Layout()
   583         self.TickSizer.Layout()
   584     
   584 
   585     def SubscribeAllDataConsumers(self):
   585     def SubscribeAllDataConsumers(self):
   586         DebugViewer.SubscribeAllDataConsumers(self)
   586         DebugViewer.SubscribeAllDataConsumers(self)
   587         
   587 
   588         if self.DataProducer is not None:
   588         if self.DataProducer is not None:
   589             if self.DataProducer is not None:
   589             if self.DataProducer is not None:
   590                 self.SetTickTime(self.DataProducer.GetTicktime())
   590                 self.SetTickTime(self.DataProducer.GetTicktime())
   591         
   591 
   592         self.ResetCursorTick()
   592         self.ResetCursorTick()
   593         
   593 
   594         for panel in self.GraphicPanels[:]:
   594         for panel in self.GraphicPanels[:]:
   595             panel.SubscribeAllDataConsumers()
   595             panel.SubscribeAllDataConsumers()
   596             if panel.ItemsIsEmpty():
   596             if panel.ItemsIsEmpty():
   597                 if panel.HasCapture():
   597                 if panel.HasCapture():
   598                     panel.ReleaseMouse()
   598                     panel.ReleaseMouse()
   599                 self.GraphicPanels.remove(panel)
   599                 self.GraphicPanels.remove(panel)
   600                 panel.Destroy()
   600                 panel.Destroy()
   601         
   601 
   602         self.ResetVariableNameMask()
   602         self.ResetVariableNameMask()
   603         self.RefreshGraphicsSizer()
   603         self.RefreshGraphicsSizer()
   604         self.ForceRefresh()
   604         self.ForceRefresh()
   605     
   605 
   606     def ResetView(self):
   606     def ResetView(self):
   607         self.UnsubscribeAllDataConsumers()
   607         self.UnsubscribeAllDataConsumers()
   608         
   608 
   609         self.Fixed = False
   609         self.Fixed = False
   610         for panel in self.GraphicPanels:
   610         for panel in self.GraphicPanels:
   611             panel.Destroy()
   611             panel.Destroy()
   612         self.GraphicPanels = []
   612         self.GraphicPanels = []
   613         self.ResetVariableNameMask()
   613         self.ResetVariableNameMask()
   614         self.RefreshGraphicsSizer()
   614         self.RefreshGraphicsSizer()
   615     
   615 
   616     def SetCanvasPosition(self, tick):
   616     def SetCanvasPosition(self, tick):
   617         tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
   617         tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
   618         self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
   618         self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
   619         self.Fixed = True
   619         self.Fixed = True
   620         self.RefreshCanvasPosition()
   620         self.RefreshCanvasPosition()
   621         self.ForceRefresh()
   621         self.ForceRefresh()
   622     
   622 
   623     def RefreshCanvasPosition(self):
   623     def RefreshCanvasPosition(self):
   624         if len(self.Ticks) > 0:
   624         if len(self.Ticks) > 0:
   625             pos = int(self.StartTick - self.Ticks[0])
   625             pos = int(self.StartTick - self.Ticks[0])
   626             range = int(self.Ticks[-1] - self.Ticks[0])
   626             range = int(self.Ticks[-1] - self.Ticks[0])
   627         else:
   627         else:
   628             pos = 0
   628             pos = 0
   629             range = 0
   629             range = 0
   630         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
   630         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
   631     
   631 
   632     def ChangeRange(self, dir, tick=None):
   632     def ChangeRange(self, dir, tick=None):
   633         current_range = self.CurrentRange
   633         current_range = self.CurrentRange
   634         current_range_idx = self.CanvasRange.GetSelection()
   634         current_range_idx = self.CanvasRange.GetSelection()
   635         new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1))
   635         new_range_idx = max(0, min(current_range_idx + dir, len(RANGE_VALUES) - 1))
   636         if new_range_idx != current_range_idx:
   636         if new_range_idx != current_range_idx:
   642                 new_start_tick = min(tick - (tick - self.StartTick) * self.CurrentRange / current_range,
   642                 new_start_tick = min(tick - (tick - self.StartTick) * self.CurrentRange / current_range,
   643                                      self.Ticks[-1] - self.CurrentRange)
   643                                      self.Ticks[-1] - self.CurrentRange)
   644                 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
   644                 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
   645                 self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange
   645                 self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange
   646             self.ForceRefresh()
   646             self.ForceRefresh()
   647     
   647 
   648     def RefreshRange(self):
   648     def RefreshRange(self):
   649         if len(self.Ticks) > 0:
   649         if len(self.Ticks) > 0:
   650             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   650             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
   651                 self.Fixed = False
   651                 self.Fixed = False
   652             if self.Fixed:
   652             if self.Fixed:
   653                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
   653                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
   654             else:
   654             else:
   655                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   655                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   656         self.ForceRefresh()
   656         self.ForceRefresh()
   657     
   657 
   658     def OnRangeChanged(self, event):
   658     def OnRangeChanged(self, event):
   659         try:
   659         try:
   660             self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime
   660             self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime
   661         except ValueError, e:
   661         except ValueError, e:
   662             self.CanvasRange.SetValue(str(self.CurrentRange))
   662             self.CanvasRange.SetValue(str(self.CurrentRange))
   663         wx.CallAfter(self.RefreshRange)
   663         wx.CallAfter(self.RefreshRange)
   664         event.Skip()
   664         event.Skip()
   665     
   665 
   666     def OnCurrentButton(self, event):
   666     def OnCurrentButton(self, event):
   667         if len(self.Ticks) > 0:
   667         if len(self.Ticks) > 0:
   668             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   668             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
   669             self.ResetCursorTick()
   669             self.ResetCursorTick()
   670         event.Skip()
   670         event.Skip()
   671     
   671 
   672     def CopyDataToClipboard(self, variables):
   672     def CopyDataToClipboard(self, variables):
   673         text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
   673         text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
   674         next_tick = NextTick(variables)
   674         next_tick = NextTick(variables)
   675         while next_tick is not None:
   675         while next_tick is not None:
   676             values = []
   676             values = []
   691                 else:
   691                 else:
   692                     values.append("")
   692                     values.append("")
   693             text += "%d;%s;\n" % (next_tick, ";".join(values))
   693             text += "%d;%s;\n" % (next_tick, ";".join(values))
   694             next_tick = NextTick(variables)
   694             next_tick = NextTick(variables)
   695         self.ParentWindow.SetCopyBuffer(text)
   695         self.ParentWindow.SetCopyBuffer(text)
   696     
   696 
   697     def OnExportGraphButton(self, event):
   697     def OnExportGraphButton(self, event):
   698         items = reduce(lambda x, y: x + y,
   698         items = reduce(lambda x, y: x + y,
   699                        [panel.GetItems() for panel in self.GraphicPanels],
   699                        [panel.GetItems() for panel in self.GraphicPanels],
   700                        [])
   700                        [])
   701         variables = [(item, [entry for entry in item.GetData()])
   701         variables = [(item, [entry for entry in item.GetData()])
   702                      for item in items
   702                      for item in items
   703                      if item.IsNumVariable()]
   703                      if item.IsNumVariable()]
   704         wx.CallAfter(self.CopyDataToClipboard, variables)
   704         wx.CallAfter(self.CopyDataToClipboard, variables)
   705         event.Skip()
   705         event.Skip()
   706     
   706 
   707     def OnPositionChanging(self, event):
   707     def OnPositionChanging(self, event):
   708         if len(self.Ticks) > 0:
   708         if len(self.Ticks) > 0:
   709             self.StartTick = self.Ticks[0] + event.GetPosition()
   709             self.StartTick = self.Ticks[0] + event.GetPosition()
   710             self.Fixed = True
   710             self.Fixed = True
   711             self.ForceRefresh()
   711             self.ForceRefresh()
   712         event.Skip()
   712         event.Skip()
   713     
   713 
   714     def GetRange(self):
   714     def GetRange(self):
   715         return self.StartTick, self.StartTick + self.CurrentRange
   715         return self.StartTick, self.StartTick + self.CurrentRange
   716     
   716 
   717     def GetViewerIndex(self, viewer):
   717     def GetViewerIndex(self, viewer):
   718         if viewer in self.GraphicPanels:
   718         if viewer in self.GraphicPanels:
   719             return self.GraphicPanels.index(viewer)
   719             return self.GraphicPanels.index(viewer)
   720         return None
   720         return None
   721     
   721 
   722     def IsViewerFirst(self, viewer):
   722     def IsViewerFirst(self, viewer):
   723         return viewer == self.GraphicPanels[0]
   723         return viewer == self.GraphicPanels[0]
   724     
   724 
   725     def HighlightPreviousViewer(self, viewer):
   725     def HighlightPreviousViewer(self, viewer):
   726         if self.IsViewerFirst(viewer):
   726         if self.IsViewerFirst(viewer):
   727             return
   727             return
   728         idx = self.GetViewerIndex(viewer)
   728         idx = self.GetViewerIndex(viewer)
   729         if idx is None:
   729         if idx is None:
   730             return
   730             return
   731         self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
   731         self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
   732     
   732 
   733     def ResetVariableNameMask(self):
   733     def ResetVariableNameMask(self):
   734         items = []
   734         items = []
   735         for panel in self.GraphicPanels:
   735         for panel in self.GraphicPanels:
   736             items.extend(panel.GetItems())
   736             items.extend(panel.GetItems())
   737         if len(items) > 1:
   737         if len(items) > 1:
   741             self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*']
   741             self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*']
   742         else:
   742         else:
   743             self.VariableNameMask = []
   743             self.VariableNameMask = []
   744         self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
   744         self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
   745         self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
   745         self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
   746             
   746 
   747     def GetVariableNameMask(self):
   747     def GetVariableNameMask(self):
   748         return self.VariableNameMask
   748         return self.VariableNameMask
   749     
   749 
   750     def InsertValue(self, iec_path, idx = None, force=False, graph=False):
   750     def InsertValue(self, iec_path, idx = None, force=False, graph=False):
   751         for panel in self.GraphicPanels:
   751         for panel in self.GraphicPanels:
   752             if panel.GetItem(iec_path) is not None:
   752             if panel.GetItem(iec_path) is not None:
   753                 if graph and isinstance(panel, DebugVariableTextViewer):
   753                 if graph and isinstance(panel, DebugVariableTextViewer):
   754                     self.ToggleViewerType(panel)
   754                     self.ToggleViewerType(panel)
   756         if idx is None:
   756         if idx is None:
   757             idx = len(self.GraphicPanels)
   757             idx = len(self.GraphicPanels)
   758         item = DebugVariableItem(self, iec_path, True)
   758         item = DebugVariableItem(self, iec_path, True)
   759         result = self.AddDataConsumer(iec_path.upper(), item, True)
   759         result = self.AddDataConsumer(iec_path.upper(), item, True)
   760         if result is not None or force:
   760         if result is not None or force:
   761             
   761 
   762             self.Freeze()
   762             self.Freeze()
   763             if item.IsNumVariable() and graph:
   763             if item.IsNumVariable() and graph:
   764                 panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   764                 panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   765                 if self.CursorTick is not None:
   765                 if self.CursorTick is not None:
   766                     panel.SetCursorTick(self.CursorTick)
   766                     panel.SetCursorTick(self.CursorTick)
   772                 self.GraphicPanels.append(panel)
   772                 self.GraphicPanels.append(panel)
   773             self.ResetVariableNameMask()
   773             self.ResetVariableNameMask()
   774             self.RefreshGraphicsSizer()
   774             self.RefreshGraphicsSizer()
   775             self.Thaw()
   775             self.Thaw()
   776             self.ForceRefresh()
   776             self.ForceRefresh()
   777     
   777 
   778     def MoveValue(self, iec_path, idx = None, graph=False):
   778     def MoveValue(self, iec_path, idx = None, graph=False):
   779         if idx is None:
   779         if idx is None:
   780             idx = len(self.GraphicPanels)
   780             idx = len(self.GraphicPanels)
   781         source_panel = None
   781         source_panel = None
   782         item = None
   782         item = None
   785             if item is not None:
   785             if item is not None:
   786                 source_panel = panel
   786                 source_panel = panel
   787                 break
   787                 break
   788         if source_panel is not None:
   788         if source_panel is not None:
   789             source_panel_idx = self.GraphicPanels.index(source_panel)
   789             source_panel_idx = self.GraphicPanels.index(source_panel)
   790             
   790 
   791             if (len(source_panel.GetItems()) == 1):
   791             if (len(source_panel.GetItems()) == 1):
   792                 
   792 
   793                 if source_panel_idx < idx:
   793                 if source_panel_idx < idx:
   794                     self.GraphicPanels.insert(idx, source_panel)
   794                     self.GraphicPanels.insert(idx, source_panel)
   795                     self.GraphicPanels.pop(source_panel_idx)
   795                     self.GraphicPanels.pop(source_panel_idx)
   796                 elif source_panel_idx > idx:
   796                 elif source_panel_idx > idx:
   797                     self.GraphicPanels.pop(source_panel_idx)
   797                     self.GraphicPanels.pop(source_panel_idx)
   798                     self.GraphicPanels.insert(idx, source_panel)
   798                     self.GraphicPanels.insert(idx, source_panel)
   799                 else:
   799                 else:
   800                     return
   800                     return
   801                 
   801 
   802             else:
   802             else:
   803                 source_panel.RemoveItem(item)
   803                 source_panel.RemoveItem(item)
   804                 source_size = source_panel.GetSize()
   804                 source_size = source_panel.GetSize()
   805                 if item.IsNumVariable() and graph:
   805                 if item.IsNumVariable() and graph:
   806                     panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   806                     panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
   807                     panel.SetCanvasHeight(source_size.height)
   807                     panel.SetCanvasHeight(source_size.height)
   808                     if self.CursorTick is not None:
   808                     if self.CursorTick is not None:
   809                         panel.SetCursorTick(self.CursorTick)
   809                         panel.SetCursorTick(self.CursorTick)
   810                 
   810 
   811                 else:
   811                 else:
   812                     panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item])
   812                     panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item])
   813                 
   813 
   814                 self.GraphicPanels.insert(idx, panel)
   814                 self.GraphicPanels.insert(idx, panel)
   815                 
   815 
   816                 if source_panel.ItemsIsEmpty():
   816                 if source_panel.ItemsIsEmpty():
   817                     if source_panel.HasCapture():
   817                     if source_panel.HasCapture():
   818                         source_panel.ReleaseMouse()
   818                         source_panel.ReleaseMouse()
   819                     source_panel.Destroy()
   819                     source_panel.Destroy()
   820                     self.GraphicPanels.remove(source_panel)
   820                     self.GraphicPanels.remove(source_panel)
   821                 
   821 
   822             self.ResetVariableNameMask()
   822             self.ResetVariableNameMask()
   823             self.RefreshGraphicsSizer()
   823             self.RefreshGraphicsSizer()
   824             self.ForceRefresh()
   824             self.ForceRefresh()
   825     
   825 
   826     def MergeGraphs(self, source, target_idx, merge_type, force=False):
   826     def MergeGraphs(self, source, target_idx, merge_type, force=False):
   827         source_item = None
   827         source_item = None
   828         source_panel = None
   828         source_panel = None
   829         for panel in self.GraphicPanels:
   829         for panel in self.GraphicPanels:
   830             source_item = panel.GetItem(source)
   830             source_item = panel.GetItem(source)
   844                 source_size = None
   844                 source_size = None
   845             target_panel = self.GraphicPanels[target_idx]
   845             target_panel = self.GraphicPanels[target_idx]
   846             graph_type = target_panel.GraphType
   846             graph_type = target_panel.GraphType
   847             if target_panel != source_panel:
   847             if target_panel != source_panel:
   848                 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
   848                 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
   849                     merge_type == GRAPH_ORTHOGONAL and 
   849                     merge_type == GRAPH_ORTHOGONAL and
   850                     (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
   850                     (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
   851                      graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
   851                      graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
   852                     return
   852                     return
   853                 
   853 
   854                 if source_panel is not None:
   854                 if source_panel is not None:
   855                     source_panel.RemoveItem(source_item)
   855                     source_panel.RemoveItem(source_item)
   856                     if source_panel.ItemsIsEmpty():
   856                     if source_panel.ItemsIsEmpty():
   857                         if source_panel.HasCapture():
   857                         if source_panel.HasCapture():
   858                             source_panel.ReleaseMouse()
   858                             source_panel.ReleaseMouse()
   860                         self.GraphicPanels.remove(source_panel)
   860                         self.GraphicPanels.remove(source_panel)
   861             elif (merge_type != graph_type and len(target_panel.Items) == 2):
   861             elif (merge_type != graph_type and len(target_panel.Items) == 2):
   862                 target_panel.RemoveItem(source_item)
   862                 target_panel.RemoveItem(source_item)
   863             else:
   863             else:
   864                 target_panel = None
   864                 target_panel = None
   865                 
   865 
   866             if target_panel is not None:
   866             if target_panel is not None:
   867                 target_panel.AddItem(source_item)
   867                 target_panel.AddItem(source_item)
   868                 target_panel.GraphType = merge_type
   868                 target_panel.GraphType = merge_type
   869                 size = target_panel.GetSize()
   869                 size = target_panel.GetSize()
   870                 if merge_type == GRAPH_ORTHOGONAL:
   870                 if merge_type == GRAPH_ORTHOGONAL:
   872                 elif source_size is not None and source_panel != target_panel:
   872                 elif source_size is not None and source_panel != target_panel:
   873                     target_panel.SetCanvasHeight(size.height + source_size.height)
   873                     target_panel.SetCanvasHeight(size.height + source_size.height)
   874                 else:
   874                 else:
   875                     target_panel.SetCanvasHeight(size.height)
   875                     target_panel.SetCanvasHeight(size.height)
   876                 target_panel.ResetGraphics()
   876                 target_panel.ResetGraphics()
   877                 
   877 
   878                 self.ResetVariableNameMask()
   878                 self.ResetVariableNameMask()
   879                 self.RefreshGraphicsSizer()
   879                 self.RefreshGraphicsSizer()
   880                 self.ForceRefresh()
   880                 self.ForceRefresh()
   881     
   881 
   882     def DeleteValue(self, source_panel, item=None):
   882     def DeleteValue(self, source_panel, item=None):
   883         source_idx = self.GetViewerIndex(source_panel)
   883         source_idx = self.GetViewerIndex(source_panel)
   884         if source_idx is not None:
   884         if source_idx is not None:
   885             
   885 
   886             if item is None:
   886             if item is None:
   887                 source_panel.ClearItems()
   887                 source_panel.ClearItems()
   888                 source_panel.Destroy()
   888                 source_panel.Destroy()
   889                 self.GraphicPanels.remove(source_panel)
   889                 self.GraphicPanels.remove(source_panel)
   890                 self.ResetVariableNameMask()
   890                 self.ResetVariableNameMask()
   898                     self.RefreshGraphicsSizer()
   898                     self.RefreshGraphicsSizer()
   899             if len(self.GraphicPanels) == 0:
   899             if len(self.GraphicPanels) == 0:
   900                 self.Fixed = False
   900                 self.Fixed = False
   901                 self.ResetCursorTick()
   901                 self.ResetCursorTick()
   902             self.ForceRefresh()
   902             self.ForceRefresh()
   903     
   903 
   904     def ToggleViewerType(self, panel):
   904     def ToggleViewerType(self, panel):
   905         panel_idx = self.GetViewerIndex(panel)
   905         panel_idx = self.GetViewerIndex(panel)
   906         if panel_idx is not None:
   906         if panel_idx is not None:
   907             self.GraphicPanels.remove(panel)
   907             self.GraphicPanels.remove(panel)
   908             items = panel.GetItems()
   908             items = panel.GetItems()
   914                 new_panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, items, GRAPH_PARALLEL)
   914                 new_panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, items, GRAPH_PARALLEL)
   915                 self.GraphicPanels.insert(panel_idx, new_panel)
   915                 self.GraphicPanels.insert(panel_idx, new_panel)
   916             panel.Destroy()
   916             panel.Destroy()
   917         self.RefreshGraphicsSizer()
   917         self.RefreshGraphicsSizer()
   918         self.ForceRefresh()
   918         self.ForceRefresh()
   919     
   919 
   920     def ResetGraphicsValues(self):
   920     def ResetGraphicsValues(self):
   921         self.Ticks = numpy.array([])
   921         self.Ticks = numpy.array([])
   922         self.StartTick = 0
   922         self.StartTick = 0
   923         for panel in self.GraphicPanels:
   923         for panel in self.GraphicPanels:
   924             panel.ResetItemsData()
   924             panel.ResetItemsData()
   929         window_size = self.GraphicsWindow.GetClientSize()
   929         window_size = self.GraphicsWindow.GetClientSize()
   930         vwidth, vheight = self.GraphicsSizer.GetMinSize()
   930         vwidth, vheight = self.GraphicsSizer.GetMinSize()
   931         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
   931         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
   932         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
   932         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
   933         self.GraphicsWindow.Scroll(posx, posy)
   933         self.GraphicsWindow.Scroll(posx, posy)
   934         self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
   934         self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
   935                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
   935                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
   936     
   936 
   937     def OnGraphicsWindowEraseBackground(self, event):
   937     def OnGraphicsWindowEraseBackground(self, event):
   938         pass
   938         pass
   939     
   939 
   940     def OnGraphicsWindowPaint(self, event):
   940     def OnGraphicsWindowPaint(self, event):
   941         self.RefreshView()
   941         self.RefreshView()
   942         event.Skip()
   942         event.Skip()
   943     
   943 
   944     def OnGraphicsWindowResize(self, event):
   944     def OnGraphicsWindowResize(self, event):
   945         size = self.GetSize()
   945         size = self.GetSize()
   946         for panel in self.GraphicPanels:
   946         for panel in self.GraphicPanels:
   947             panel_size = panel.GetSize()
   947             panel_size = panel.GetSize()
   948             if (isinstance(panel, DebugVariableGraphicViewer) and 
   948             if (isinstance(panel, DebugVariableGraphicViewer) and
   949                 panel.GraphType == GRAPH_ORTHOGONAL and 
   949                 panel.GraphType == GRAPH_ORTHOGONAL and
   950                 panel_size.width == panel_size.height):
   950                 panel_size.width == panel_size.height):
   951                 panel.SetCanvasHeight(size.width)
   951                 panel.SetCanvasHeight(size.width)
   952         self.RefreshGraphicsWindowScrollbars()
   952         self.RefreshGraphicsWindowScrollbars()
   953         self.GraphicsSizer.Layout()
   953         self.GraphicsSizer.Layout()
   954         event.Skip()
   954         event.Skip()