controls/DebugVariablePanel.py
branch1.1 Korean release
changeset 1280 72a826dfcfbb
parent 977 c8e008b8cefe
parent 1279 0eb9f8af479f
child 1384 02fe382c4511
equal deleted inserted replaced
977:c8e008b8cefe 1280:72a826dfcfbb
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #General Public License for more details.
       
    20 #
       
    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
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 from types import TupleType, ListType, FloatType
       
    26 from time import time as gettime
       
    27 import math
       
    28 import numpy
       
    29 import binascii
       
    30 
       
    31 import wx
       
    32 import wx.lib.buttons
       
    33 
       
    34 try:
       
    35     import matplotlib
       
    36     matplotlib.use('WX')
       
    37     import matplotlib.pyplot
       
    38     from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
       
    39     from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap
       
    40     from matplotlib.backends.backend_agg import FigureCanvasAgg
       
    41     from mpl_toolkits.mplot3d import Axes3D
       
    42     color_cycle = ['r', 'b', 'g', 'm', 'y', 'k']
       
    43     cursor_color = '#800080'
       
    44     USE_MPL = True
       
    45 except:
       
    46     USE_MPL = False
       
    47 
       
    48 from graphics import DebugDataConsumer, DebugViewer, REFRESH_PERIOD
       
    49 from controls import CustomGrid, CustomTable
       
    50 from dialogs.ForceVariableDialog import ForceVariableDialog
       
    51 from util.BitmapLibrary import GetBitmap
       
    52 
       
    53 def AppendMenu(parent, help, id, kind, text):
       
    54     parent.Append(help=help, id=id, kind=kind, text=text)
       
    55 
       
    56 def GetDebugVariablesTableColnames():
       
    57     _ = lambda x : x
       
    58     return [_("Variable"), _("Value")]
       
    59 
       
    60 CRC_SIZE = 8
       
    61 CRC_MASK = 2 ** CRC_SIZE - 1
       
    62 
       
    63 class VariableTableItem(DebugDataConsumer):
       
    64     
       
    65     def __init__(self, parent, variable):
       
    66         DebugDataConsumer.__init__(self)
       
    67         self.Parent = parent
       
    68         self.Variable = variable
       
    69         self.RefreshVariableType()
       
    70         self.Value = ""
       
    71         
       
    72     def __del__(self):
       
    73         self.Parent = None
       
    74     
       
    75     def SetVariable(self, variable):
       
    76         if self.Parent and self.Variable != variable:
       
    77             self.Variable = variable
       
    78             self.RefreshVariableType()
       
    79             self.Parent.RefreshView()
       
    80     
       
    81     def GetVariable(self, mask=None):
       
    82         variable = self.Variable
       
    83         if mask is not None:
       
    84             parts = variable.split('.')
       
    85             mask = mask + ['*'] * max(0, len(parts) - len(mask))
       
    86             last = None
       
    87             variable = ""
       
    88             for m, v in zip(mask, parts):
       
    89                 if m == '*':
       
    90                     if last == '*':
       
    91                         variable += '.'
       
    92                     variable += v
       
    93                 elif last is None or last == '*':
       
    94                     variable += '..'
       
    95                 last = m
       
    96         return variable
       
    97     
       
    98     def RefreshVariableType(self):
       
    99         self.VariableType = self.Parent.GetDataType(self.Variable)
       
   100         if USE_MPL:
       
   101             self.ResetData()
       
   102     
       
   103     def GetVariableType(self):
       
   104         return self.VariableType
       
   105     
       
   106     def GetData(self, start_tick=None, end_tick=None):
       
   107         if USE_MPL and self.IsNumVariable():
       
   108             if len(self.Data) == 0:
       
   109                 return self.Data
       
   110             
       
   111             start_idx = end_idx = None
       
   112             if start_tick is not None:
       
   113                 start_idx = self.GetNearestData(start_tick, -1)
       
   114             if end_tick is not None:
       
   115                 end_idx = self.GetNearestData(end_tick, 1)
       
   116             if start_idx is None:
       
   117                 start_idx = 0
       
   118             if end_idx is not None:
       
   119                 return self.Data[start_idx:end_idx + 1]
       
   120             else:
       
   121                 return self.Data[start_idx:]
       
   122             
       
   123         return None
       
   124     
       
   125     def GetRawValue(self, idx):
       
   126         if self.VariableType in ["STRING", "WSTRING"] and idx < len(self.RawData):
       
   127             return self.RawData[idx][0]
       
   128         return ""
       
   129     
       
   130     def GetRange(self):
       
   131         return self.MinValue, self.MaxValue
       
   132     
       
   133     def ResetData(self):
       
   134         if self.IsNumVariable():
       
   135             self.Data = numpy.array([]).reshape(0, 3)
       
   136             if self.VariableType in ["STRING", "WSTRING"]:
       
   137                 self.RawData = []
       
   138             self.MinValue = None
       
   139             self.MaxValue = None
       
   140         else:
       
   141             self.Data = None
       
   142     
       
   143     def IsNumVariable(self):
       
   144         return (self.Parent.IsNumType(self.VariableType) or 
       
   145                 self.VariableType in ["STRING", "WSTRING"])
       
   146     
       
   147     def NewValue(self, tick, value, forced=False):
       
   148         if USE_MPL and self.IsNumVariable():
       
   149             if self.VariableType in ["STRING", "WSTRING"]:
       
   150                 num_value = binascii.crc32(value) & CRC_MASK
       
   151             else:
       
   152                 num_value = float(value)
       
   153             if self.MinValue is None:
       
   154                 self.MinValue = num_value
       
   155             else:
       
   156                 self.MinValue = min(self.MinValue, num_value)
       
   157             if self.MaxValue is None:
       
   158                 self.MaxValue = num_value
       
   159             else:
       
   160                 self.MaxValue = max(self.MaxValue, num_value)
       
   161             forced_value = float(forced)
       
   162             if self.VariableType in ["STRING", "WSTRING"]:
       
   163                 raw_data = (value, forced_value)
       
   164                 if len(self.RawData) == 0 or self.RawData[-1] != raw_data:
       
   165                     extra_value = len(self.RawData)
       
   166                     self.RawData.append(raw_data)
       
   167                 else:
       
   168                     extra_value = len(self.RawData) - 1
       
   169             else:
       
   170                 extra_value = forced_value
       
   171             self.Data = numpy.append(self.Data, [[float(tick), num_value, extra_value]], axis=0)
       
   172             self.Parent.HasNewData = True
       
   173         DebugDataConsumer.NewValue(self, tick, value, forced)
       
   174     
       
   175     def SetForced(self, forced):
       
   176         if self.Forced != forced:
       
   177             self.Forced = forced
       
   178             self.Parent.HasNewData = True
       
   179     
       
   180     def SetValue(self, value):
       
   181         if (self.VariableType == "STRING" and value.startswith("'") and value.endswith("'") or
       
   182             self.VariableType == "WSTRING" and value.startswith('"') and value.endswith('"')):
       
   183             value = value[1:-1]
       
   184         if self.Value != value:
       
   185             self.Value = value
       
   186             self.Parent.HasNewData = True
       
   187             
       
   188     def GetValue(self, tick=None, raw=False):
       
   189         if tick is not None and self.IsNumVariable():
       
   190             if len(self.Data) > 0:
       
   191                 idx = numpy.argmin(abs(self.Data[:, 0] - tick))
       
   192                 if self.VariableType in ["STRING", "WSTRING"]:
       
   193                     value, forced = self.RawData[int(self.Data[idx, 2])]
       
   194                     if not raw:
       
   195                         if self.VariableType == "STRING":
       
   196                             value = "'%s'" % value
       
   197                         else:
       
   198                             value = '"%s"' % value
       
   199                     return value, forced
       
   200                 else:
       
   201                     value = self.Data[idx, 1]
       
   202                     if not raw and isinstance(value, FloatType):
       
   203                         value = "%.6g" % value
       
   204                     return value, self.Data[idx, 2]
       
   205             return self.Value, self.IsForced()
       
   206         elif not raw:
       
   207             if self.VariableType == "STRING":
       
   208                 return "'%s'" % self.Value
       
   209             elif self.VariableType == "WSTRING":
       
   210                 return '"%s"' % self.Value
       
   211             elif isinstance(self.Value, FloatType):
       
   212                 return "%.6g" % self.Value
       
   213         return self.Value
       
   214 
       
   215     def GetNearestData(self, tick, adjust):
       
   216         if USE_MPL and self.IsNumVariable():
       
   217             ticks = self.Data[:, 0]
       
   218             new_cursor = numpy.argmin(abs(ticks - tick))
       
   219             if adjust == -1 and ticks[new_cursor] > tick and new_cursor > 0:
       
   220                 new_cursor -= 1
       
   221             elif adjust == 1 and ticks[new_cursor] < tick and new_cursor < len(ticks):
       
   222                 new_cursor += 1
       
   223             return new_cursor
       
   224         return None
       
   225 
       
   226 class DebugVariableTable(CustomTable):
       
   227     
       
   228     def GetValue(self, row, col):
       
   229         if row < self.GetNumberRows():
       
   230             return self.GetValueByName(row, self.GetColLabelValue(col, False))
       
   231         return ""
       
   232     
       
   233     def SetValue(self, row, col, value):
       
   234         if col < len(self.colnames):
       
   235             self.SetValueByName(row, self.GetColLabelValue(col, False), value)
       
   236             
       
   237     def GetValueByName(self, row, colname):
       
   238         if row < self.GetNumberRows():
       
   239             if colname == "Variable":
       
   240                 return self.data[row].GetVariable()
       
   241             elif colname == "Value":
       
   242                 return self.data[row].GetValue()
       
   243         return ""
       
   244 
       
   245     def SetValueByName(self, row, colname, value):
       
   246         if row < self.GetNumberRows():
       
   247             if colname == "Variable":
       
   248                 self.data[row].SetVariable(value)
       
   249             elif colname == "Value":
       
   250                 self.data[row].SetValue(value)
       
   251     
       
   252     def IsForced(self, row):
       
   253         if row < self.GetNumberRows():
       
   254             return self.data[row].IsForced()
       
   255         return False
       
   256     
       
   257     def IsNumVariable(self, row):
       
   258         if row < self.GetNumberRows():
       
   259             return self.data[row].IsNumVariable()
       
   260         return False
       
   261     
       
   262     def _updateColAttrs(self, grid):
       
   263         """
       
   264         wx.grid.Grid -> update the column attributes to add the
       
   265         appropriate renderer given the column name.
       
   266 
       
   267         Otherwise default to the default renderer.
       
   268         """
       
   269         
       
   270         for row in range(self.GetNumberRows()):
       
   271             for col in range(self.GetNumberCols()):
       
   272                 colname = self.GetColLabelValue(col, False)
       
   273                 if colname == "Value":
       
   274                     if self.IsForced(row):
       
   275                         grid.SetCellTextColour(row, col, wx.BLUE)
       
   276                     else:
       
   277                         grid.SetCellTextColour(row, col, wx.BLACK)
       
   278                 grid.SetReadOnly(row, col, True)
       
   279             self.ResizeRow(grid, row)
       
   280                 
       
   281     def AppendItem(self, data):
       
   282         self.data.append(data)
       
   283     
       
   284     def InsertItem(self, idx, data):
       
   285         self.data.insert(idx, data)
       
   286     
       
   287     def RemoveItem(self, idx):
       
   288         self.data.pop(idx)
       
   289     
       
   290     def MoveItem(self, idx, new_idx):
       
   291         self.data.insert(new_idx, self.data.pop(idx))
       
   292         
       
   293     def GetItem(self, idx):
       
   294         return self.data[idx]
       
   295 
       
   296 class DebugVariableDropTarget(wx.TextDropTarget):
       
   297     
       
   298     def __init__(self, parent, control=None):
       
   299         wx.TextDropTarget.__init__(self)
       
   300         self.ParentWindow = parent
       
   301         self.ParentControl = control
       
   302     
       
   303     def __del__(self):
       
   304         self.ParentWindow = None
       
   305         self.ParentControl = None
       
   306     
       
   307     def OnDragOver(self, x, y, d):
       
   308         if self.ParentControl is not None:
       
   309             self.ParentControl.OnMouseDragging(x, y)
       
   310         else:
       
   311             self.ParentWindow.RefreshHighlight(x, y)
       
   312         return wx.TextDropTarget.OnDragOver(self, x, y, d)
       
   313         
       
   314     def OnDropText(self, x, y, data):
       
   315         message = None
       
   316         try:
       
   317             values = eval(data)
       
   318         except:
       
   319             message = _("Invalid value \"%s\" for debug variable")%data
       
   320             values = None
       
   321         if not isinstance(values, TupleType):
       
   322             message = _("Invalid value \"%s\" for debug variable")%data
       
   323             values = None
       
   324         
       
   325         if message is not None:
       
   326             wx.CallAfter(self.ShowMessage, message)
       
   327         elif values is not None and values[1] == "debug":
       
   328             if isinstance(self.ParentControl, CustomGrid):
       
   329                 x, y = self.ParentControl.CalcUnscrolledPosition(x, y)
       
   330                 row = self.ParentControl.YToRow(y - self.ParentControl.GetColLabelSize())
       
   331                 if row == wx.NOT_FOUND:
       
   332                     row = self.ParentWindow.Table.GetNumberRows()
       
   333                 self.ParentWindow.InsertValue(values[0], row, force=True)
       
   334             elif self.ParentControl is not None:
       
   335                 width, height = self.ParentControl.GetSize()
       
   336                 target_idx = self.ParentControl.GetIndex()
       
   337                 merge_type = GRAPH_PARALLEL
       
   338                 if isinstance(self.ParentControl, DebugVariableGraphic):
       
   339                     if self.ParentControl.Is3DCanvas():
       
   340                         if y > height / 2:
       
   341                             target_idx += 1
       
   342                         if len(values) > 1 and values[2] == "move":
       
   343                             self.ParentWindow.MoveValue(values[0], target_idx)
       
   344                         else:
       
   345                             self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   346                         
       
   347                     else:
       
   348                         rect = self.ParentControl.GetAxesBoundingBox()
       
   349                         if rect.InsideXY(x, y):
       
   350                             merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
       
   351                             if merge_rect.InsideXY(x, y):
       
   352                                 merge_type = GRAPH_ORTHOGONAL
       
   353                             wx.CallAfter(self.ParentWindow.MergeGraphs, values[0], target_idx, merge_type, force=True)
       
   354                         else:
       
   355                             if y > height / 2:
       
   356                                 target_idx += 1
       
   357                             if len(values) > 2 and values[2] == "move":
       
   358                                 self.ParentWindow.MoveValue(values[0], target_idx)
       
   359                             else:
       
   360                                 self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   361                 else:
       
   362                     if y > height / 2:
       
   363                         target_idx += 1
       
   364                     if len(values) > 2 and values[2] == "move":
       
   365                         self.ParentWindow.MoveValue(values[0], target_idx)
       
   366                     else:
       
   367                         self.ParentWindow.InsertValue(values[0], target_idx, force=True)
       
   368                     
       
   369             elif len(values) > 2 and values[2] == "move":
       
   370                 self.ParentWindow.MoveValue(values[0])
       
   371             else:
       
   372                 self.ParentWindow.InsertValue(values[0], force=True)
       
   373     
       
   374     def OnLeave(self):
       
   375         self.ParentWindow.ResetHighlight()
       
   376         return wx.TextDropTarget.OnLeave(self)
       
   377     
       
   378     def ShowMessage(self, message):
       
   379         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
       
   380         dialog.ShowModal()
       
   381         dialog.Destroy()
       
   382 
       
   383 if USE_MPL:
       
   384     MILLISECOND = 1000000
       
   385     SECOND = 1000 * MILLISECOND
       
   386     MINUTE = 60 * SECOND
       
   387     HOUR = 60 * MINUTE
       
   388     DAY = 24 * HOUR
       
   389     
       
   390     ZOOM_VALUES = map(lambda x:("x %.1f" % x, x), [math.sqrt(2) ** i for i in xrange(8)])
       
   391     RANGE_VALUES = map(lambda x: (str(x), x), [25 * 2 ** i for i in xrange(6)])
       
   392     TIME_RANGE_VALUES = [("%ds" % i, i * SECOND) for i in (1, 2, 5, 10, 20, 30)] + \
       
   393                         [("%dm" % i, i * MINUTE) for i in (1, 2, 5, 10, 20, 30)] + \
       
   394                         [("%dh" % i, i * HOUR) for i in (1, 2, 3, 6, 12, 24)]
       
   395     
       
   396     GRAPH_PARALLEL, GRAPH_ORTHOGONAL = range(2)
       
   397     
       
   398     SCROLLBAR_UNIT = 10
       
   399     
       
   400     #CANVAS_HIGHLIGHT_TYPES
       
   401     [HIGHLIGHT_NONE,
       
   402      HIGHLIGHT_BEFORE,
       
   403      HIGHLIGHT_AFTER,
       
   404      HIGHLIGHT_LEFT,
       
   405      HIGHLIGHT_RIGHT,
       
   406      HIGHLIGHT_RESIZE] = range(6)
       
   407     
       
   408     HIGHLIGHT_DROP_PEN = wx.Pen(wx.Colour(0, 128, 255))
       
   409     HIGHLIGHT_DROP_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128))
       
   410     HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200))
       
   411     HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200))
       
   412     
       
   413     #CANVAS_SIZE_TYPES
       
   414     [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200]
       
   415     
       
   416     DEFAULT_CANVAS_HEIGHT = 200.
       
   417     CANVAS_BORDER = (20., 10.)
       
   418     CANVAS_PADDING = 8.5
       
   419     VALUE_LABEL_HEIGHT = 17.
       
   420     AXES_LABEL_HEIGHT = 12.75
       
   421     
       
   422     def compute_mask(x, y):
       
   423         mask = []
       
   424         for xp, yp in zip(x, y):
       
   425             if xp == yp:
       
   426                 mask.append(xp)
       
   427             else:
       
   428                 mask.append("*")
       
   429         return mask
       
   430     
       
   431     def NextTick(variables):
       
   432         next_tick = None
       
   433         for item, data in variables:
       
   434             if len(data) > 0:
       
   435                 if next_tick is None:
       
   436                     next_tick = data[0][0]
       
   437                 else:
       
   438                     next_tick = min(next_tick, data[0][0])
       
   439         return next_tick
       
   440     
       
   441     def OrthogonalData(item, start_tick, end_tick):
       
   442         data = item.GetData(start_tick, end_tick)
       
   443         min_value, max_value = item.GetRange()
       
   444         if min_value is not None and max_value is not None:
       
   445             center = (min_value + max_value) / 2.
       
   446             range = max(1.0, max_value - min_value)
       
   447         else:
       
   448             center = 0.5
       
   449             range = 1.0
       
   450         return data, center - range * 0.55, center + range * 0.55
       
   451     
       
   452     class GraphButton():
       
   453         
       
   454         def __init__(self, x, y, bitmap, callback):
       
   455             self.Position = wx.Point(x, y)
       
   456             self.Bitmap = bitmap
       
   457             self.Shown = True
       
   458             self.Enabled = True
       
   459             self.Callback = callback
       
   460         
       
   461         def __del__(self):
       
   462             self.callback = None
       
   463         
       
   464         def GetSize(self):
       
   465             return self.Bitmap.GetSize()
       
   466         
       
   467         def SetPosition(self, x, y):
       
   468             self.Position = wx.Point(x, y)
       
   469         
       
   470         def Show(self):
       
   471             self.Shown = True
       
   472             
       
   473         def Hide(self):
       
   474             self.Shown = False
       
   475         
       
   476         def IsShown(self):
       
   477             return self.Shown
       
   478         
       
   479         def Enable(self):
       
   480             self.Enabled = True
       
   481         
       
   482         def Disable(self):
       
   483             self.Enabled = False
       
   484         
       
   485         def IsEnabled(self):
       
   486             return self.Enabled
       
   487         
       
   488         def HitTest(self, x, y):
       
   489             if self.Shown and self.Enabled:
       
   490                 w, h = self.Bitmap.GetSize()
       
   491                 rect = wx.Rect(self.Position.x, self.Position.y, w, h)
       
   492                 if rect.InsideXY(x, y):
       
   493                     return True
       
   494             return False
       
   495         
       
   496         def ProcessCallback(self):
       
   497             if self.Callback is not None:
       
   498                 wx.CallAfter(self.Callback)
       
   499                 
       
   500         def Draw(self, dc):
       
   501             if self.Shown and self.Enabled:
       
   502                 dc.DrawBitmap(self.Bitmap, self.Position.x, self.Position.y, True)
       
   503     
       
   504     class DebugVariableViewer:
       
   505         
       
   506         def __init__(self, window, items=[]):
       
   507             self.ParentWindow = window
       
   508             self.Items = items
       
   509             
       
   510             self.Highlight = HIGHLIGHT_NONE
       
   511             self.Buttons = []
       
   512         
       
   513         def __del__(self):
       
   514             self.ParentWindow = None
       
   515         
       
   516         def GetIndex(self):
       
   517             return self.ParentWindow.GetViewerIndex(self)
       
   518         
       
   519         def GetItem(self, variable):
       
   520             for item in self.Items:
       
   521                 if item.GetVariable() == variable:
       
   522                     return item
       
   523             return None
       
   524         
       
   525         def GetItems(self):
       
   526             return self.Items
       
   527         
       
   528         def GetVariables(self):
       
   529             if len(self.Items) > 1:
       
   530                 variables = [item.GetVariable() for item in self.Items]
       
   531                 if self.GraphType == GRAPH_ORTHOGONAL:
       
   532                     return tuple(variables)
       
   533                 return variables
       
   534             return self.Items[0].GetVariable()
       
   535         
       
   536         def AddItem(self, item):
       
   537             self.Items.append(item)
       
   538             
       
   539         def RemoveItem(self, item):
       
   540             if item in self.Items:
       
   541                 self.Items.remove(item)
       
   542             
       
   543         def Clear(self):
       
   544             for item in self.Items:
       
   545                 self.ParentWindow.RemoveDataConsumer(item)
       
   546             self.Items = []
       
   547             
       
   548         def IsEmpty(self):
       
   549             return len(self.Items) == 0
       
   550         
       
   551         def UnregisterObsoleteData(self):
       
   552             for item in self.Items[:]:
       
   553                 iec_path = item.GetVariable().upper()
       
   554                 if self.ParentWindow.GetDataType(iec_path) is None:
       
   555                     self.ParentWindow.RemoveDataConsumer(item)
       
   556                     self.RemoveItem(item)
       
   557                 else:
       
   558                     self.ParentWindow.AddDataConsumer(iec_path, item)
       
   559                     item.RefreshVariableType()
       
   560             
       
   561         def ResetData(self):
       
   562             for item in self.Items:
       
   563                 item.ResetData()
       
   564         
       
   565         def RefreshViewer(self):
       
   566             pass
       
   567         
       
   568         def SetHighlight(self, highlight):
       
   569             if self.Highlight != highlight:
       
   570                 self.Highlight = highlight
       
   571                 return True
       
   572             return False
       
   573         
       
   574         def GetButtons(self):
       
   575             return self.Buttons
       
   576         
       
   577         def HandleButtons(self, x, y):
       
   578             for button in self.GetButtons():
       
   579                 if button.HitTest(x, y):
       
   580                     button.ProcessCallback()
       
   581                     return True
       
   582             return False
       
   583         
       
   584         def IsOverButton(self, x, y):
       
   585             for button in self.GetButtons():
       
   586                 if button.HitTest(x, y):
       
   587                     return True
       
   588             return False
       
   589         
       
   590         def ShowButtons(self, show):
       
   591             for button in self.Buttons:
       
   592                 if show:
       
   593                     button.Show()
       
   594                 else:
       
   595                     button.Hide()
       
   596             self.RefreshButtonsState()
       
   597             self.ParentWindow.ForceRefresh()
       
   598         
       
   599         def RefreshButtonsState(self, refresh_positions=False):
       
   600             if self:
       
   601                 width, height = self.GetSize()
       
   602                 if refresh_positions:
       
   603                     offset = 0
       
   604                     buttons = self.Buttons[:]
       
   605                     buttons.reverse()
       
   606                     for button in buttons:
       
   607                         if button.IsShown() and button.IsEnabled():
       
   608                             w, h = button.GetSize()
       
   609                             button.SetPosition(width - 5 - w - offset, 5)
       
   610                             offset += w + 2
       
   611                     self.ParentWindow.ForceRefresh()
       
   612         
       
   613         def DrawCommonElements(self, dc, buttons=None):
       
   614             width, height = self.GetSize()
       
   615             
       
   616             dc.SetPen(HIGHLIGHT_DROP_PEN)
       
   617             dc.SetBrush(HIGHLIGHT_DROP_BRUSH)
       
   618             if self.Highlight in [HIGHLIGHT_BEFORE]:
       
   619                 dc.DrawLine(0, 1, width - 1, 1)
       
   620             elif self.Highlight in [HIGHLIGHT_AFTER]:
       
   621                 dc.DrawLine(0, height - 1, width - 1, height - 1)
       
   622             
       
   623             if buttons is None:
       
   624                 buttons = self.Buttons
       
   625             for button in buttons:
       
   626                 button.Draw(dc)
       
   627                 
       
   628             if self.ParentWindow.IsDragging():
       
   629                 destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self)
       
   630                 srcPos = self.ParentWindow.GetDraggingAxesPosition(self)
       
   631                 if destBBox.width > 0 and destBBox.height > 0:
       
   632                     srcPanel = self.ParentWindow.DraggingAxesPanel
       
   633                     srcBBox = srcPanel.GetAxesBoundingBox()
       
   634                     
       
   635                     if destBBox.x == 0:
       
   636                         srcX = srcBBox.x - srcPos.x
       
   637                     else:
       
   638                         srcX = srcBBox.x
       
   639                     if destBBox.y == 0:
       
   640                         srcY = srcBBox.y - srcPos.y
       
   641                     else:
       
   642                         srcY = srcBBox.y
       
   643                     
       
   644                     srcBmp = _convert_agg_to_wx_bitmap(srcPanel.get_renderer(), None)
       
   645                     srcDC = wx.MemoryDC()
       
   646                     srcDC.SelectObject(srcBmp)
       
   647                     
       
   648                     dc.Blit(destBBox.x, destBBox.y, 
       
   649                             int(destBBox.width), int(destBBox.height), 
       
   650                             srcDC, srcX, srcY)
       
   651         
       
   652         def OnEnter(self, event):
       
   653             self.ShowButtons(True)
       
   654             event.Skip()
       
   655             
       
   656         def OnLeave(self, event):
       
   657             if self.Highlight != HIGHLIGHT_RESIZE or self.CanvasStartSize is None:
       
   658                 x, y = event.GetPosition()
       
   659                 width, height = self.GetSize()
       
   660                 if (x <= 0 or x >= width - 1 or
       
   661                     y <= 0 or y >= height - 1):
       
   662                     self.ShowButtons(False)
       
   663             event.Skip()
       
   664         
       
   665         def OnCloseButton(self):
       
   666             wx.CallAfter(self.ParentWindow.DeleteValue, self)
       
   667         
       
   668         def OnForceButton(self):
       
   669             wx.CallAfter(self.ForceValue, self.Items[0])
       
   670             
       
   671         def OnReleaseButton(self):
       
   672             wx.CallAfter(self.ReleaseValue, self.Items[0])
       
   673         
       
   674         def OnResizeWindow(self, event):
       
   675             wx.CallAfter(self.RefreshButtonsState, True)
       
   676             event.Skip()
       
   677         
       
   678         def OnMouseDragging(self, x, y):
       
   679             xw, yw = self.GetPosition()
       
   680             self.ParentWindow.RefreshHighlight(x + xw, y + yw)
       
   681         
       
   682         def OnDragging(self, x, y):
       
   683             width, height = self.GetSize()
       
   684             if y < height / 2:
       
   685                 if self.ParentWindow.IsViewerFirst(self):
       
   686                     self.SetHighlight(HIGHLIGHT_BEFORE)
       
   687                 else:
       
   688                     self.SetHighlight(HIGHLIGHT_NONE)
       
   689                     self.ParentWindow.HighlightPreviousViewer(self)
       
   690             else:
       
   691                 self.SetHighlight(HIGHLIGHT_AFTER)
       
   692         
       
   693         def OnResize(self, event):
       
   694             wx.CallAfter(self.RefreshButtonsState, True)
       
   695             event.Skip()
       
   696         
       
   697         def ForceValue(self, item):
       
   698             iec_path = item.GetVariable().upper()
       
   699             iec_type = self.ParentWindow.GetDataType(iec_path)
       
   700             if iec_type is not None:
       
   701                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
   702                 if dialog.ShowModal() == wx.ID_OK:
       
   703                     self.ParentWindow.ForceDataValue(iec_path, dialog.GetValue())
       
   704         
       
   705         def ReleaseValue(self, item):
       
   706             iec_path = item.GetVariable().upper()
       
   707             self.ParentWindow.ReleaseDataValue(iec_path)
       
   708     
       
   709     
       
   710     class DebugVariableText(DebugVariableViewer, wx.Panel):
       
   711         
       
   712         def __init__(self, parent, window, items=[]):
       
   713             DebugVariableViewer.__init__(self, window, items)
       
   714             
       
   715             wx.Panel.__init__(self, parent)
       
   716             self.SetBackgroundColour(wx.WHITE)
       
   717             self.SetDropTarget(DebugVariableDropTarget(window, self))
       
   718             self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
       
   719             self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
       
   720             self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
       
   721             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
       
   722             self.Bind(wx.EVT_SIZE, self.OnResize)
       
   723             self.Bind(wx.EVT_PAINT, self.OnPaint)
       
   724             
       
   725             self.SetMinSize(wx.Size(0, 25))
       
   726             
       
   727             self.Buttons.append(
       
   728                 GraphButton(0, 0, GetBitmap("force"), self.OnForceButton))
       
   729             self.Buttons.append(
       
   730                 GraphButton(0, 0, GetBitmap("release"), self.OnReleaseButton))
       
   731             self.Buttons.append(
       
   732                 GraphButton(0, 0, GetBitmap("delete_graph"), self.OnCloseButton))
       
   733             
       
   734             self.ShowButtons(False)
       
   735             
       
   736         def RefreshViewer(self):
       
   737             width, height = self.GetSize()
       
   738             bitmap = wx.EmptyBitmap(width, height)
       
   739             
       
   740             dc = wx.BufferedDC(wx.ClientDC(self), bitmap)
       
   741             dc.Clear()
       
   742             dc.BeginDrawing()
       
   743             
       
   744             gc = wx.GCDC(dc)
       
   745             
       
   746             item_name = self.Items[0].GetVariable(self.ParentWindow.GetVariableNameMask())
       
   747             w, h = gc.GetTextExtent(item_name)
       
   748             gc.DrawText(item_name, 20, (height - h) / 2)
       
   749             
       
   750             if self.Items[0].IsForced():
       
   751                 gc.SetTextForeground(wx.BLUE)
       
   752                 self.Buttons[0].Disable()
       
   753                 self.Buttons[1].Enable()
       
   754             else:
       
   755                 self.Buttons[1].Disable()
       
   756                 self.Buttons[0].Enable()
       
   757             self.RefreshButtonsState(True)
       
   758             
       
   759             item_value = self.Items[0].GetValue()
       
   760             w, h = gc.GetTextExtent(item_value)
       
   761             gc.DrawText(item_value, width - 40 - w, (height - h) / 2)
       
   762             
       
   763             self.DrawCommonElements(gc)
       
   764             
       
   765             gc.EndDrawing()
       
   766         
       
   767         def OnLeftDown(self, event):
       
   768             width, height = self.GetSize()
       
   769             item_name = self.Items[0].GetVariable(self.ParentWindow.GetVariableNameMask())
       
   770             w, h = self.GetTextExtent(item_name)
       
   771             x, y = event.GetPosition()
       
   772             rect = wx.Rect(20, (height - h) / 2, w, h)
       
   773             if rect.InsideXY(x, y):
       
   774                 data = wx.TextDataObject(str((self.Items[0].GetVariable(), "debug", "move")))
       
   775                 dragSource = wx.DropSource(self)
       
   776                 dragSource.SetData(data)
       
   777                 dragSource.DoDragDrop()
       
   778             else:
       
   779                 event.Skip()
       
   780         
       
   781         def OnLeftUp(self, event):
       
   782             x, y = event.GetPosition()
       
   783             wx.CallAfter(self.HandleButtons, x, y)
       
   784             event.Skip()
       
   785         
       
   786         def OnPaint(self, event):
       
   787             self.RefreshViewer()
       
   788             event.Skip()
       
   789     
       
   790     class DebugVariableGraphic(DebugVariableViewer, FigureCanvas):
       
   791         
       
   792         def __init__(self, parent, window, items, graph_type):
       
   793             DebugVariableViewer.__init__(self, window, items)
       
   794             
       
   795             self.CanvasSize = SIZE_MAXI
       
   796             self.GraphType = graph_type
       
   797             self.CursorTick = None
       
   798             self.MouseStartPos = None
       
   799             self.StartCursorTick = None
       
   800             self.CanvasStartSize = None
       
   801             self.ContextualButtons = []
       
   802             self.ContextualButtonsItem = None
       
   803             
       
   804             self.Figure = matplotlib.figure.Figure(facecolor='w')
       
   805             self.Figure.subplotpars.update(top=0.95, left=0.1, bottom=0.1, right=0.95)
       
   806             
       
   807             FigureCanvas.__init__(self, parent, -1, self.Figure)
       
   808             self.SetBackgroundColour(wx.WHITE)
       
   809             self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter)
       
   810             self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
       
   811             self.Bind(wx.EVT_SIZE, self.OnResize)
       
   812             
       
   813             self.SetMinSize(wx.Size(200, 200))
       
   814             self.SetDropTarget(DebugVariableDropTarget(self.ParentWindow, self))
       
   815             self.mpl_connect('button_press_event', self.OnCanvasButtonPressed)
       
   816             self.mpl_connect('motion_notify_event', self.OnCanvasMotion)
       
   817             self.mpl_connect('button_release_event', self.OnCanvasButtonReleased)
       
   818             self.mpl_connect('scroll_event', self.OnCanvasScroll)
       
   819             
       
   820             for size, bitmap in zip([SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI],
       
   821                                     ["minimize_graph", "middle_graph", "maximize_graph"]):
       
   822                 self.Buttons.append(GraphButton(0, 0, GetBitmap(bitmap), self.GetOnChangeSizeButton(size)))
       
   823             self.Buttons.append(
       
   824                 GraphButton(0, 0, GetBitmap("export_graph_mini"), self.OnExportGraphButton))
       
   825             self.Buttons.append(
       
   826                 GraphButton(0, 0, GetBitmap("delete_graph"), self.OnCloseButton))
       
   827             
       
   828             self.ResetGraphics()
       
   829             self.RefreshLabelsPosition(200)
       
   830             self.ShowButtons(False)
       
   831         
       
   832         def draw(self, drawDC=None):
       
   833             FigureCanvasAgg.draw(self)
       
   834     
       
   835             self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
       
   836             self.bitmap.UseAlpha() 
       
   837             width, height = self.GetSize()
       
   838             bbox = self.GetAxesBoundingBox()
       
   839             
       
   840             destDC = wx.MemoryDC()
       
   841             destDC.SelectObject(self.bitmap)
       
   842             
       
   843             destGC = wx.GCDC(destDC)
       
   844             
       
   845             destGC.BeginDrawing()
       
   846             if self.Highlight == HIGHLIGHT_RESIZE:
       
   847                 destGC.SetPen(HIGHLIGHT_RESIZE_PEN)
       
   848                 destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH)
       
   849                 destGC.DrawRectangle(0, height - 5, width, 5)
       
   850             else:
       
   851                 destGC.SetPen(HIGHLIGHT_DROP_PEN)
       
   852                 destGC.SetBrush(HIGHLIGHT_DROP_BRUSH)
       
   853                 if self.Highlight == HIGHLIGHT_LEFT:
       
   854                     destGC.DrawRectangle(bbox.x, bbox.y, 
       
   855                                          bbox.width / 2, bbox.height)
       
   856                 elif self.Highlight == HIGHLIGHT_RIGHT:
       
   857                     destGC.DrawRectangle(bbox.x + bbox.width / 2, bbox.y, 
       
   858                                          bbox.width / 2, bbox.height)
       
   859             
       
   860             self.DrawCommonElements(destGC, self.Buttons + self.ContextualButtons)
       
   861             
       
   862             destGC.EndDrawing()
       
   863             
       
   864             self._isDrawn = True
       
   865             self.gui_repaint(drawDC=drawDC)
       
   866         
       
   867         def GetButtons(self):
       
   868             return self.Buttons + self.ContextualButtons
       
   869         
       
   870         def PopupContextualButtons(self, item, rect, style=wx.RIGHT):
       
   871             if self.ContextualButtonsItem is not None and item != self.ContextualButtonsItem:
       
   872                 self.DismissContextualButtons()
       
   873             
       
   874             if self.ContextualButtonsItem is None:
       
   875                 self.ContextualButtonsItem = item
       
   876                 
       
   877                 if self.ContextualButtonsItem.IsForced():
       
   878                     self.ContextualButtons.append(
       
   879                         GraphButton(0, 0, GetBitmap("release"), self.OnReleaseButton))
       
   880                 else:
       
   881                     self.ContextualButtons.append(
       
   882                         GraphButton(0, 0, GetBitmap("force"), self.OnForceButton))
       
   883                 self.ContextualButtons.append(
       
   884                     GraphButton(0, 0, GetBitmap("export_graph_mini"), self.OnExportItemGraphButton))
       
   885                 self.ContextualButtons.append(
       
   886                     GraphButton(0, 0, GetBitmap("delete_graph"), self.OnRemoveItemButton))
       
   887                 
       
   888                 offset = 0
       
   889                 buttons = self.ContextualButtons[:]
       
   890                 if style in [wx.TOP, wx.LEFT]:
       
   891                      buttons.reverse()
       
   892                 for button in buttons:
       
   893                     w, h = button.GetSize()
       
   894                     if style in [wx.LEFT, wx.RIGHT]:
       
   895                         if style == wx.LEFT:
       
   896                             x = rect.x - w - offset
       
   897                         else:
       
   898                             x = rect.x + rect.width + offset
       
   899                         y = rect.y + (rect.height - h) / 2
       
   900                         offset += w
       
   901                     else:
       
   902                         x = rect.x + (rect.width - w ) / 2
       
   903                         if style == wx.TOP:
       
   904                             y = rect.y - h - offset
       
   905                         else:
       
   906                             y = rect.y + rect.height + offset
       
   907                         offset += h
       
   908                     button.SetPosition(x, y)
       
   909                 self.ParentWindow.ForceRefresh()
       
   910         
       
   911         def DismissContextualButtons(self):
       
   912             if self.ContextualButtonsItem is not None:
       
   913                 self.ContextualButtonsItem = None
       
   914                 self.ContextualButtons = []
       
   915                 self.ParentWindow.ForceRefresh()
       
   916         
       
   917         def IsOverContextualButton(self, x, y):
       
   918             for button in self.ContextualButtons:
       
   919                 if button.HitTest(x, y):
       
   920                     return True
       
   921             return False
       
   922         
       
   923         def SetMinSize(self, size):
       
   924             wx.Window.SetMinSize(self, size)
       
   925             wx.CallAfter(self.RefreshButtonsState)
       
   926         
       
   927         def GetOnChangeSizeButton(self, size):
       
   928             def OnChangeSizeButton():
       
   929                 self.CanvasSize = size
       
   930                 self.SetCanvasSize(200, self.CanvasSize)
       
   931             return OnChangeSizeButton
       
   932         
       
   933         def OnExportGraphButton(self):
       
   934             self.ExportGraph()
       
   935         
       
   936         def OnForceButton(self):
       
   937             wx.CallAfter(self.ForceValue, 
       
   938                          self.ContextualButtonsItem)
       
   939             self.DismissContextualButtons()
       
   940             
       
   941         def OnReleaseButton(self):
       
   942             wx.CallAfter(self.ReleaseValue, 
       
   943                          self.ContextualButtonsItem)
       
   944             self.DismissContextualButtons()
       
   945         
       
   946         def OnExportItemGraphButton(self):
       
   947             wx.CallAfter(self.ExportGraph, 
       
   948                          self.ContextualButtonsItem)
       
   949             self.DismissContextualButtons()
       
   950             
       
   951         def OnRemoveItemButton(self):            
       
   952             wx.CallAfter(self.ParentWindow.DeleteValue, self, 
       
   953                          self.ContextualButtonsItem)
       
   954             self.DismissContextualButtons()
       
   955         
       
   956         def RefreshButtonsState(self, refresh_positions=False):
       
   957             if self:
       
   958                 width, height = self.GetSize()
       
   959                 min_width, min_height = self.GetCanvasMinSize()
       
   960                 for button, size in zip(self.Buttons, 
       
   961                                         [min_height, SIZE_MIDDLE, SIZE_MAXI]):
       
   962                     if size == height and button.IsEnabled():
       
   963                         button.Disable()
       
   964                         refresh_positions = True
       
   965                     elif not button.IsEnabled():
       
   966                         button.Enable()
       
   967                         refresh_positions = True
       
   968                 if refresh_positions:
       
   969                     offset = 0
       
   970                     buttons = self.Buttons[:]
       
   971                     buttons.reverse()
       
   972                     for button in buttons:
       
   973                         if button.IsShown() and button.IsEnabled():
       
   974                             w, h = button.GetSize()
       
   975                             button.SetPosition(width - 5 - w - offset, 5)
       
   976                             offset += w + 2
       
   977                     self.ParentWindow.ForceRefresh()
       
   978         
       
   979         def RefreshLabelsPosition(self, height):
       
   980             canvas_ratio = 1. / height
       
   981             graph_ratio = 1. / ((1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) * height)
       
   982             
       
   983             self.Figure.subplotpars.update(
       
   984                 top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, 
       
   985                 bottom= CANVAS_BORDER[0] * canvas_ratio)
       
   986             
       
   987             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
   988                 num_item = len(self.Items)
       
   989                 for idx in xrange(num_item):
       
   990                     if not self.Is3DCanvas():
       
   991                         self.AxesLabels[idx].set_position(
       
   992                             (0.05, 
       
   993                              1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio))
       
   994                     self.Labels[idx].set_position(
       
   995                         (0.95, 
       
   996                          CANVAS_PADDING * graph_ratio + 
       
   997                          (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio))
       
   998             else:
       
   999                 self.AxesLabels[0].set_position((0.1, CANVAS_PADDING * graph_ratio))
       
  1000                 self.Labels[0].set_position((0.95, CANVAS_PADDING * graph_ratio))
       
  1001                 self.AxesLabels[1].set_position((0.05, 2 * CANVAS_PADDING * graph_ratio))
       
  1002                 self.Labels[1].set_position((0.05, 1.0 - CANVAS_PADDING * graph_ratio))
       
  1003         
       
  1004             self.Figure.subplots_adjust()
       
  1005         
       
  1006         def GetCanvasMinSize(self):
       
  1007             return wx.Size(200, 
       
  1008                            CANVAS_BORDER[0] + CANVAS_BORDER[1] + 
       
  1009                            2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items))
       
  1010         
       
  1011         def SetCanvasSize(self, width, height):
       
  1012             height = max(height, self.GetCanvasMinSize()[1])
       
  1013             self.SetMinSize(wx.Size(width, height))
       
  1014             self.RefreshLabelsPosition(height)
       
  1015             self.ParentWindow.RefreshGraphicsSizer()
       
  1016             
       
  1017         def GetAxesBoundingBox(self, absolute=False):
       
  1018             width, height = self.GetSize()
       
  1019             ax, ay, aw, ah = self.figure.gca().get_position().bounds
       
  1020             bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1,
       
  1021                            aw * width + 2, ah * height + 1)
       
  1022             if absolute:
       
  1023                 xw, yw = self.GetPosition()
       
  1024                 bbox.x += xw
       
  1025                 bbox.y += yw
       
  1026             return bbox
       
  1027         
       
  1028         def OnCanvasButtonPressed(self, event):
       
  1029             width, height = self.GetSize()
       
  1030             x, y = event.x, height - event.y
       
  1031             if not self.IsOverButton(x, y):
       
  1032                 if event.inaxes == self.Axes:
       
  1033                     item_idx = None
       
  1034                     for i, t in ([pair for pair in enumerate(self.AxesLabels)] + 
       
  1035                                  [pair for pair in enumerate(self.Labels)]):
       
  1036                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
       
  1037                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
       
  1038                         if rect.InsideXY(x, y):
       
  1039                             item_idx = i
       
  1040                             break
       
  1041                     if item_idx is not None:
       
  1042                         self.ShowButtons(False)
       
  1043                         self.DismissContextualButtons()
       
  1044                         xw, yw = self.GetPosition()
       
  1045                         self.ParentWindow.StartDragNDrop(self, 
       
  1046                             self.Items[item_idx], x + xw, y + yw, x + xw, y + yw)
       
  1047                     elif not self.Is3DCanvas():
       
  1048                         self.MouseStartPos = wx.Point(x, y)
       
  1049                         if event.button == 1 and event.inaxes == self.Axes:
       
  1050                             self.StartCursorTick = self.CursorTick
       
  1051                             self.HandleCursorMove(event)
       
  1052                         elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
       
  1053                             width, height = self.GetSize()
       
  1054                             start_tick, end_tick = self.ParentWindow.GetRange()
       
  1055                             self.StartCursorTick = start_tick
       
  1056                 
       
  1057                 elif event.button == 1 and event.y <= 5:
       
  1058                     self.MouseStartPos = wx.Point(x, y)
       
  1059                     self.CanvasStartSize = self.GetSize()
       
  1060         
       
  1061         def OnCanvasButtonReleased(self, event):
       
  1062             if self.ParentWindow.IsDragging():
       
  1063                 width, height = self.GetSize()
       
  1064                 xw, yw = self.GetPosition()
       
  1065                 self.ParentWindow.StopDragNDrop(
       
  1066                     self.ParentWindow.DraggingAxesPanel.Items[0].GetVariable(),
       
  1067                     xw + event.x, 
       
  1068                     yw + height - event.y)
       
  1069             else:
       
  1070                 self.MouseStartPos = None
       
  1071                 self.StartCursorTick = None
       
  1072                 self.CanvasStartSize = None
       
  1073                 width, height = self.GetSize()
       
  1074                 self.HandleButtons(event.x, height - event.y)
       
  1075                 if event.y > 5 and self.SetHighlight(HIGHLIGHT_NONE):
       
  1076                     self.SetCursor(wx.NullCursor)
       
  1077                     self.ParentWindow.ForceRefresh()
       
  1078         
       
  1079         def OnCanvasMotion(self, event):
       
  1080             width, height = self.GetSize()
       
  1081             if self.ParentWindow.IsDragging():
       
  1082                 xw, yw = self.GetPosition()
       
  1083                 self.ParentWindow.MoveDragNDrop(
       
  1084                     xw + event.x, 
       
  1085                     yw + height - event.y)
       
  1086             else:
       
  1087                 if not self.Is3DCanvas():
       
  1088                     if event.button == 1 and self.CanvasStartSize is None:
       
  1089                         if event.inaxes == self.Axes:
       
  1090                             if self.MouseStartPos is not None:
       
  1091                                 self.HandleCursorMove(event)
       
  1092                         elif self.MouseStartPos is not None and len(self.Items) == 1:
       
  1093                             xw, yw = self.GetPosition()
       
  1094                             self.ParentWindow.SetCursorTick(self.StartCursorTick)
       
  1095                             self.ParentWindow.StartDragNDrop(self, 
       
  1096                                 self.Items[0],
       
  1097                                 event.x + xw, height - event.y + yw, 
       
  1098                                 self.MouseStartPos.x + xw, self.MouseStartPos.y + yw)
       
  1099                     elif event.button == 2 and self.GraphType == GRAPH_PARALLEL:
       
  1100                         start_tick, end_tick = self.ParentWindow.GetRange()
       
  1101                         rect = self.GetAxesBoundingBox()
       
  1102                         self.ParentWindow.SetCanvasPosition(
       
  1103                             self.StartCursorTick + (self.MouseStartPos.x - event.x) *
       
  1104                             (end_tick - start_tick) / rect.width)    
       
  1105                 
       
  1106                 if event.button == 1 and self.CanvasStartSize is not None:
       
  1107                     width, height = self.GetSize()
       
  1108                     self.SetCanvasSize(width, 
       
  1109                         self.CanvasStartSize.height + height - event.y - self.MouseStartPos.y)
       
  1110                     
       
  1111                 elif event.button in [None, "up", "down"]:
       
  1112                     if self.GraphType == GRAPH_PARALLEL:
       
  1113                         orientation = [wx.RIGHT] * len(self.AxesLabels) + [wx.LEFT] * len(self.Labels)
       
  1114                     elif len(self.AxesLabels) > 0:
       
  1115                         orientation = [wx.RIGHT, wx.TOP, wx.LEFT, wx.BOTTOM]
       
  1116                     else:
       
  1117                         orientation = [wx.LEFT] * len(self.Labels)
       
  1118                     item_idx = None
       
  1119                     item_style = None
       
  1120                     for (i, t), style in zip([pair for pair in enumerate(self.AxesLabels)] + 
       
  1121                                              [pair for pair in enumerate(self.Labels)], 
       
  1122                                              orientation):
       
  1123                         (x0, y0), (x1, y1) = t.get_window_extent().get_points()
       
  1124                         rect = wx.Rect(x0, height - y1, x1 - x0, y1 - y0)
       
  1125                         if rect.InsideXY(event.x, height - event.y):
       
  1126                             item_idx = i
       
  1127                             item_style = style
       
  1128                             break
       
  1129                     if item_idx is not None:
       
  1130                         self.PopupContextualButtons(self.Items[item_idx], rect, item_style)
       
  1131                         return 
       
  1132                     if not self.IsOverContextualButton(event.x, height - event.y):
       
  1133                         self.DismissContextualButtons()
       
  1134                     
       
  1135                     if event.y <= 5:
       
  1136                         if self.SetHighlight(HIGHLIGHT_RESIZE):
       
  1137                             self.SetCursor(wx.StockCursor(wx.CURSOR_SIZENS))
       
  1138                             self.ParentWindow.ForceRefresh()
       
  1139                     else:
       
  1140                         if self.SetHighlight(HIGHLIGHT_NONE):
       
  1141                             self.SetCursor(wx.NullCursor)
       
  1142                             self.ParentWindow.ForceRefresh()
       
  1143         
       
  1144         def OnCanvasScroll(self, event):
       
  1145             if event.inaxes is not None:
       
  1146                 if self.GraphType == GRAPH_ORTHOGONAL:
       
  1147                     start_tick, end_tick = self.ParentWindow.GetRange()
       
  1148                     tick = (start_tick + end_tick) / 2.
       
  1149                 else:
       
  1150                     tick = event.xdata
       
  1151                 self.ParentWindow.ChangeRange(int(-event.step) / 3, tick)
       
  1152         
       
  1153         def OnDragging(self, x, y):
       
  1154             width, height = self.GetSize()
       
  1155             bbox = self.GetAxesBoundingBox()
       
  1156             if bbox.InsideXY(x, y) and not self.Is3DCanvas():
       
  1157                 rect = wx.Rect(bbox.x, bbox.y, bbox.width / 2, bbox.height)
       
  1158                 if rect.InsideXY(x, y):
       
  1159                     self.SetHighlight(HIGHLIGHT_LEFT)
       
  1160                 else:
       
  1161                     self.SetHighlight(HIGHLIGHT_RIGHT)
       
  1162             elif y < height / 2:
       
  1163                 if self.ParentWindow.IsViewerFirst(self):
       
  1164                     self.SetHighlight(HIGHLIGHT_BEFORE)
       
  1165                 else:
       
  1166                     self.SetHighlight(HIGHLIGHT_NONE)
       
  1167                     self.ParentWindow.HighlightPreviousViewer(self)
       
  1168             else:
       
  1169                 self.SetHighlight(HIGHLIGHT_AFTER)
       
  1170         
       
  1171         def OnLeave(self, event):
       
  1172             if self.CanvasStartSize is None and self.SetHighlight(HIGHLIGHT_NONE):
       
  1173                 self.SetCursor(wx.NullCursor)
       
  1174                 self.ParentWindow.ForceRefresh()
       
  1175             DebugVariableViewer.OnLeave(self, event)
       
  1176         
       
  1177         def HandleCursorMove(self, event):
       
  1178             start_tick, end_tick = self.ParentWindow.GetRange()
       
  1179             cursor_tick = None
       
  1180             if self.GraphType == GRAPH_ORTHOGONAL:
       
  1181                 x_data = self.Items[0].GetData(start_tick, end_tick)
       
  1182                 y_data = self.Items[1].GetData(start_tick, end_tick)
       
  1183                 if len(x_data) > 0 and len(y_data) > 0:
       
  1184                     length = min(len(x_data), len(y_data))
       
  1185                     d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + (y_data[:length,1]-event.ydata) ** 2)
       
  1186                     cursor_tick = x_data[numpy.argmin(d), 0]
       
  1187             else:
       
  1188                 data = self.Items[0].GetData(start_tick, end_tick)
       
  1189                 if len(data) > 0:
       
  1190                     cursor_tick = data[numpy.argmin(numpy.abs(data[:,0] - event.xdata)), 0]
       
  1191             if cursor_tick is not None:
       
  1192                 self.ParentWindow.SetCursorTick(cursor_tick)
       
  1193         
       
  1194         def OnAxesMotion(self, event):
       
  1195             if self.Is3DCanvas():
       
  1196                 current_time = gettime()
       
  1197                 if current_time - self.LastMotionTime > REFRESH_PERIOD:
       
  1198                     self.LastMotionTime = current_time
       
  1199                     Axes3D._on_move(self.Axes, event)
       
  1200         
       
  1201         def ResetGraphics(self):
       
  1202             self.Figure.clear()
       
  1203             if self.Is3DCanvas():
       
  1204                 self.Axes = self.Figure.gca(projection='3d')
       
  1205                 self.Axes.set_color_cycle(['b'])
       
  1206                 self.LastMotionTime = gettime()
       
  1207                 setattr(self.Axes, "_on_move", self.OnAxesMotion)
       
  1208                 self.Axes.mouse_init()
       
  1209                 self.Axes.tick_params(axis='z', labelsize='small')
       
  1210             else:
       
  1211                 self.Axes = self.Figure.gca()
       
  1212                 self.Axes.set_color_cycle(color_cycle)
       
  1213             self.Axes.tick_params(axis='x', labelsize='small')
       
  1214             self.Axes.tick_params(axis='y', labelsize='small')
       
  1215             self.Plots = []
       
  1216             self.VLine = None
       
  1217             self.HLine = None
       
  1218             self.Labels = []
       
  1219             self.AxesLabels = []
       
  1220             if not self.Is3DCanvas():
       
  1221                 text_func = self.Axes.text
       
  1222             else:
       
  1223                 text_func = self.Axes.text2D
       
  1224             if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas():
       
  1225                 num_item = len(self.Items)
       
  1226                 for idx in xrange(num_item):
       
  1227                     if num_item == 1:
       
  1228                         color = 'k'
       
  1229                     else:
       
  1230                         color = color_cycle[idx % len(color_cycle)]
       
  1231                     if not self.Is3DCanvas():
       
  1232                         self.AxesLabels.append(
       
  1233                             text_func(0, 0, "", size='small',
       
  1234                                       verticalalignment='top', 
       
  1235                                       color=color,
       
  1236                                       transform=self.Axes.transAxes))
       
  1237                     self.Labels.append(
       
  1238                         text_func(0, 0, "", size='large', 
       
  1239                                   horizontalalignment='right',
       
  1240                                   color=color,
       
  1241                                   transform=self.Axes.transAxes))
       
  1242             else:
       
  1243                 self.AxesLabels.append(
       
  1244                     self.Axes.text(0, 0, "", size='small',
       
  1245                                    transform=self.Axes.transAxes))
       
  1246                 self.Labels.append(
       
  1247                     self.Axes.text(0, 0, "", size='large',
       
  1248                                    horizontalalignment='right',
       
  1249                                    transform=self.Axes.transAxes))
       
  1250                 self.AxesLabels.append(
       
  1251                     self.Axes.text(0, 0, "", size='small',
       
  1252                                    rotation='vertical',
       
  1253                                    verticalalignment='bottom',
       
  1254                                    transform=self.Axes.transAxes))
       
  1255                 self.Labels.append(
       
  1256                     self.Axes.text(0, 0, "", size='large',
       
  1257                                    rotation='vertical',
       
  1258                                    verticalalignment='top',
       
  1259                                    transform=self.Axes.transAxes))
       
  1260             width, height = self.GetSize()
       
  1261             self.RefreshLabelsPosition(height)
       
  1262             
       
  1263         def AddItem(self, item):
       
  1264             DebugVariableViewer.AddItem(self, item)
       
  1265             self.ResetGraphics()
       
  1266             
       
  1267         def RemoveItem(self, item):
       
  1268             DebugVariableViewer.RemoveItem(self, item)
       
  1269             if not self.IsEmpty():
       
  1270                 if len(self.Items) == 1:
       
  1271                     self.GraphType = GRAPH_PARALLEL
       
  1272                 self.ResetGraphics()
       
  1273         
       
  1274         def UnregisterObsoleteData(self):
       
  1275             DebugVariableViewer.UnregisterObsoleteData(self)
       
  1276             if not self.IsEmpty():
       
  1277                 self.ResetGraphics()
       
  1278         
       
  1279         def Is3DCanvas(self):
       
  1280             return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3
       
  1281         
       
  1282         def SetCursorTick(self, cursor_tick):
       
  1283             self.CursorTick = cursor_tick
       
  1284             
       
  1285         def RefreshViewer(self, refresh_graphics=True):
       
  1286             
       
  1287             if refresh_graphics:
       
  1288                 start_tick, end_tick = self.ParentWindow.GetRange()
       
  1289                 
       
  1290                 if self.GraphType == GRAPH_PARALLEL:    
       
  1291                     min_value = max_value = None
       
  1292                     
       
  1293                     for idx, item in enumerate(self.Items):
       
  1294                         data = item.GetData(start_tick, end_tick)
       
  1295                         if data is not None:
       
  1296                             item_min_value, item_max_value = item.GetRange()
       
  1297                             if min_value is None:
       
  1298                                 min_value = item_min_value
       
  1299                             elif item_min_value is not None:
       
  1300                                 min_value = min(min_value, item_min_value)
       
  1301                             if max_value is None:
       
  1302                                 max_value = item_max_value
       
  1303                             elif item_max_value is not None:
       
  1304                                 max_value = max(max_value, item_max_value)
       
  1305                             
       
  1306                             if len(self.Plots) <= idx:
       
  1307                                 self.Plots.append(
       
  1308                                     self.Axes.plot(data[:, 0], data[:, 1])[0])
       
  1309                             else:
       
  1310                                 self.Plots[idx].set_data(data[:, 0], data[:, 1])
       
  1311                         
       
  1312                     if min_value is not None and max_value is not None:
       
  1313                         y_center = (min_value + max_value) / 2.
       
  1314                         y_range = max(1.0, max_value - min_value)
       
  1315                     else:
       
  1316                         y_center = 0.5
       
  1317                         y_range = 1.0
       
  1318                     x_min, x_max = start_tick, end_tick
       
  1319                     y_min, y_max = y_center - y_range * 0.55, y_center + y_range * 0.55
       
  1320                     
       
  1321                     if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1322                         if self.VLine is None:
       
  1323                             self.VLine = self.Axes.axvline(self.CursorTick, color=cursor_color)
       
  1324                         else:
       
  1325                             self.VLine.set_xdata((self.CursorTick, self.CursorTick))
       
  1326                         self.VLine.set_visible(True)
       
  1327                     else:
       
  1328                         if self.VLine is not None:
       
  1329                             self.VLine.set_visible(False)
       
  1330                 else:
       
  1331                     min_start_tick = reduce(max, [item.GetData()[0, 0] 
       
  1332                                                   for item in self.Items
       
  1333                                                   if len(item.GetData()) > 0], 0)
       
  1334                     start_tick = max(start_tick, min_start_tick)
       
  1335                     end_tick = max(end_tick, min_start_tick)
       
  1336                     x_data, x_min, x_max = OrthogonalData(self.Items[0], start_tick, end_tick)
       
  1337                     y_data, y_min, y_max = OrthogonalData(self.Items[1], start_tick, end_tick)
       
  1338                     if self.CursorTick is not None:
       
  1339                         x_cursor, x_forced = self.Items[0].GetValue(self.CursorTick, raw=True)
       
  1340                         y_cursor, y_forced = self.Items[1].GetValue(self.CursorTick, raw=True)
       
  1341                     length = 0
       
  1342                     if x_data is not None and y_data is not None:  
       
  1343                         length = min(len(x_data), len(y_data))
       
  1344                     if len(self.Items) < 3:
       
  1345                         if x_data is not None and y_data is not None:
       
  1346                             if len(self.Plots) == 0:
       
  1347                                 self.Plots.append(
       
  1348                                     self.Axes.plot(x_data[:, 1][:length], 
       
  1349                                                    y_data[:, 1][:length])[0])
       
  1350                             else:
       
  1351                                 self.Plots[0].set_data(
       
  1352                                     x_data[:, 1][:length], 
       
  1353                                     y_data[:, 1][:length])
       
  1354                         
       
  1355                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1356                             if self.VLine is None:
       
  1357                                 self.VLine = self.Axes.axvline(x_cursor, color=cursor_color)
       
  1358                             else:
       
  1359                                 self.VLine.set_xdata((x_cursor, x_cursor))
       
  1360                             if self.HLine is None:
       
  1361                                 self.HLine = self.Axes.axhline(y_cursor, color=cursor_color)
       
  1362                             else:
       
  1363                                 self.HLine.set_ydata((y_cursor, y_cursor))
       
  1364                             self.VLine.set_visible(True)
       
  1365                             self.HLine.set_visible(True)
       
  1366                         else:
       
  1367                             if self.VLine is not None:
       
  1368                                 self.VLine.set_visible(False)
       
  1369                             if self.HLine is not None:
       
  1370                                 self.HLine.set_visible(False)
       
  1371                     else:
       
  1372                         while len(self.Axes.lines) > 0:
       
  1373                             self.Axes.lines.pop()
       
  1374                         z_data, z_min, z_max = OrthogonalData(self.Items[2], start_tick, end_tick)
       
  1375                         if self.CursorTick is not None:
       
  1376                             z_cursor, z_forced = self.Items[2].GetValue(self.CursorTick, raw=True)
       
  1377                         if x_data is not None and y_data is not None and z_data is not None:
       
  1378                             length = min(length, len(z_data))
       
  1379                             self.Axes.plot(x_data[:, 1][:length],
       
  1380                                            y_data[:, 1][:length],
       
  1381                                            zs = z_data[:, 1][:length])
       
  1382                         self.Axes.set_zlim(z_min, z_max)
       
  1383                         if self.CursorTick is not None and start_tick <= self.CursorTick <= end_tick:
       
  1384                             for kwargs in [{"xs": numpy.array([x_min, x_max])},
       
  1385                                            {"ys": numpy.array([y_min, y_max])},
       
  1386                                            {"zs": numpy.array([z_min, z_max])}]:
       
  1387                                 for param, value in [("xs", numpy.array([x_cursor, x_cursor])),
       
  1388                                                      ("ys", numpy.array([y_cursor, y_cursor])),
       
  1389                                                      ("zs", numpy.array([z_cursor, z_cursor]))]:
       
  1390                                     kwargs.setdefault(param, value)
       
  1391                                 kwargs["color"] = cursor_color
       
  1392                                 self.Axes.plot(**kwargs)
       
  1393                     
       
  1394                 self.Axes.set_xlim(x_min, x_max)
       
  1395                 self.Axes.set_ylim(y_min, y_max)
       
  1396             
       
  1397             variable_name_mask = self.ParentWindow.GetVariableNameMask()
       
  1398             if self.CursorTick is not None:
       
  1399                 values, forced = apply(zip, [item.GetValue(self.CursorTick) for item in self.Items])
       
  1400             else:
       
  1401                 values, forced = apply(zip, [(item.GetValue(), item.IsForced()) for item in self.Items])
       
  1402             labels = [item.GetVariable(variable_name_mask) for item in self.Items]
       
  1403             styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced)
       
  1404             if self.Is3DCanvas():
       
  1405                 for idx, label_func in enumerate([self.Axes.set_xlabel, 
       
  1406                                                   self.Axes.set_ylabel,
       
  1407                                                   self.Axes.set_zlabel]):
       
  1408                     label_func(labels[idx], fontdict={'size': 'small','color': color_cycle[idx]})
       
  1409             else:
       
  1410                 for label, text in zip(self.AxesLabels, labels):
       
  1411                     label.set_text(text)
       
  1412             for label, value, style in zip(self.Labels, values, styles):
       
  1413                 label.set_text(value)
       
  1414                 label.set_style(style)
       
  1415             
       
  1416             self.draw()
       
  1417     
       
  1418         def ExportGraph(self, item=None):
       
  1419             if item is not None:
       
  1420                 variables = [(item, [entry for entry in item.GetData()])]
       
  1421             else:
       
  1422                 variables = [(item, [entry for entry in item.GetData()])
       
  1423                              for item in self.Items]
       
  1424             self.ParentWindow.CopyDataToClipboard(variables)
       
  1425     
       
  1426 class DebugVariablePanel(wx.Panel, DebugViewer):
       
  1427     
       
  1428     def __init__(self, parent, producer, window):
       
  1429         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
       
  1430         self.SetBackgroundColour(wx.WHITE)
       
  1431         
       
  1432         self.ParentWindow = window
       
  1433         
       
  1434         DebugViewer.__init__(self, producer, True)
       
  1435         
       
  1436         self.HasNewData = False
       
  1437         
       
  1438         if USE_MPL:
       
  1439             main_sizer = wx.BoxSizer(wx.VERTICAL)
       
  1440             
       
  1441             self.Ticks = numpy.array([])
       
  1442             self.RangeValues = None
       
  1443             self.StartTick = 0
       
  1444             self.Fixed = False
       
  1445             self.Force = False
       
  1446             self.CursorTick = None
       
  1447             self.DraggingAxesPanel = None
       
  1448             self.DraggingAxesBoundingBox = None
       
  1449             self.DraggingAxesMousePos = None
       
  1450             self.VariableNameMask = []
       
  1451             
       
  1452             self.GraphicPanels = []
       
  1453             
       
  1454             graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1455             main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL)
       
  1456             
       
  1457             range_label = wx.StaticText(self, label=_('Range:'))
       
  1458             graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL)
       
  1459             
       
  1460             self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY)
       
  1461             self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange)
       
  1462             graphics_button_sizer.AddWindow(self.CanvasRange, 1, 
       
  1463                   border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
       
  1464             
       
  1465             for name, bitmap, help in [
       
  1466                 ("CurrentButton", "current", _("Go to current value")),
       
  1467                 ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]:
       
  1468                 button = wx.lib.buttons.GenBitmapButton(self, 
       
  1469                       bitmap=GetBitmap(bitmap), 
       
  1470                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
  1471                 button.SetToolTipString(help)
       
  1472                 setattr(self, name, button)
       
  1473                 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button)
       
  1474                 graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
  1475             
       
  1476             self.CanvasPosition = wx.ScrollBar(self, 
       
  1477                   size=wx.Size(0, 16), style=wx.SB_HORIZONTAL)
       
  1478             self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, 
       
  1479                   self.OnPositionChanging, self.CanvasPosition)
       
  1480             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, 
       
  1481                   self.OnPositionChanging, self.CanvasPosition)
       
  1482             self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, 
       
  1483                   self.OnPositionChanging, self.CanvasPosition)
       
  1484             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, 
       
  1485                   self.OnPositionChanging, self.CanvasPosition)
       
  1486             self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, 
       
  1487                   self.OnPositionChanging, self.CanvasPosition)
       
  1488             main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM)
       
  1489             
       
  1490             self.TickSizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1491             main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW)
       
  1492             
       
  1493             self.TickLabel = wx.StaticText(self)
       
  1494             self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT)
       
  1495             
       
  1496             self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER)
       
  1497             self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW)
       
  1498             
       
  1499             self.TickTimeLabel = wx.StaticText(self)
       
  1500             self.TickSizer.AddWindow(self.TickTimeLabel)
       
  1501             
       
  1502             self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL)
       
  1503             self.GraphicsWindow.SetBackgroundColour(wx.WHITE)
       
  1504             self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self))
       
  1505             self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize)
       
  1506             main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW)
       
  1507             
       
  1508             self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL)
       
  1509             self.GraphicsWindow.SetSizer(self.GraphicsSizer)
       
  1510             
       
  1511             self.RefreshCanvasRange()
       
  1512             
       
  1513         else:
       
  1514             main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
  1515             main_sizer.AddGrowableCol(0)
       
  1516             main_sizer.AddGrowableRow(1)
       
  1517             
       
  1518             button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
  1519             main_sizer.AddSizer(button_sizer, border=5, 
       
  1520                   flag=wx.ALIGN_RIGHT|wx.ALL)
       
  1521             
       
  1522             for name, bitmap, help in [
       
  1523                     ("DeleteButton", "remove_element", _("Remove debug variable")),
       
  1524                     ("UpButton", "up", _("Move debug variable up")),
       
  1525                     ("DownButton", "down", _("Move debug variable down"))]:
       
  1526                 button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
       
  1527                       size=wx.Size(28, 28), style=wx.NO_BORDER)
       
  1528                 button.SetToolTipString(help)
       
  1529                 setattr(self, name, button)
       
  1530                 button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
       
  1531             
       
  1532             self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL)
       
  1533             self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self, self.VariablesGrid))
       
  1534             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
       
  1535                   self.OnVariablesGridCellRightClick)
       
  1536             self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
       
  1537                   self.OnVariablesGridCellLeftClick)
       
  1538             main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
  1539         
       
  1540             self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
       
  1541             self.VariablesGrid.SetTable(self.Table)
       
  1542             self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
       
  1543                                            "Up": self.UpButton,
       
  1544                                            "Down": self.DownButton})
       
  1545         
       
  1546             def _AddVariable(new_row):
       
  1547                 return self.VariablesGrid.GetGridCursorRow()
       
  1548             setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
  1549         
       
  1550             def _DeleteVariable(row):
       
  1551                 item = self.Table.GetItem(row)
       
  1552                 self.RemoveDataConsumer(item)
       
  1553                 self.Table.RemoveItem(row)
       
  1554                 self.RefreshView()
       
  1555             setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
  1556             
       
  1557             def _MoveVariable(row, move):
       
  1558                 new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
       
  1559                 if new_row != row:
       
  1560                     self.Table.MoveItem(row, new_row)
       
  1561                     self.RefreshView()
       
  1562                 return new_row
       
  1563             setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
  1564             
       
  1565             self.VariablesGrid.SetRowLabelSize(0)
       
  1566             
       
  1567             self.GridColSizes = [200, 100]
       
  1568             
       
  1569             for col in range(self.Table.GetNumberCols()):
       
  1570                 attr = wx.grid.GridCellAttr()
       
  1571                 attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
  1572                 self.VariablesGrid.SetColAttr(col, attr)
       
  1573                 self.VariablesGrid.SetColSize(col, self.GridColSizes[col])
       
  1574             
       
  1575             self.Table.ResetView(self.VariablesGrid)
       
  1576             self.VariablesGrid.RefreshButtons()
       
  1577         
       
  1578         self.SetSizer(main_sizer)
       
  1579         
       
  1580     def SetDataProducer(self, producer):
       
  1581         DebugViewer.SetDataProducer(self, producer)
       
  1582         
       
  1583         if USE_MPL:
       
  1584             if self.DataProducer is not None:
       
  1585                 self.Ticktime = self.DataProducer.GetTicktime()
       
  1586                 self.RefreshCanvasRange()
       
  1587             else:
       
  1588                 self.Ticktime = 0
       
  1589     
       
  1590     def RefreshNewData(self, *args, **kwargs):
       
  1591         if self.HasNewData or self.Force:
       
  1592             self.HasNewData = False
       
  1593             self.RefreshView(only_values=True)
       
  1594         DebugViewer.RefreshNewData(self, *args, **kwargs)
       
  1595     
       
  1596     def NewDataAvailable(self, tick, *args, **kwargs):
       
  1597         if USE_MPL and tick is not None:
       
  1598             if len(self.Ticks) == 0:
       
  1599                 self.StartTick = tick 
       
  1600             self.Ticks = numpy.append(self.Ticks, [tick])
       
  1601             if not self.Fixed or tick < self.StartTick + self.CurrentRange:
       
  1602                 self.StartTick = max(self.StartTick, tick - self.CurrentRange)
       
  1603             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
       
  1604                 self.Force = True
       
  1605         DebugViewer.NewDataAvailable(self, tick, *args, **kwargs)
       
  1606     
       
  1607     def ForceRefresh(self):
       
  1608         self.Force = True
       
  1609         wx.CallAfter(self.NewDataAvailable, None, True)
       
  1610     
       
  1611     def RefreshGraphicsSizer(self):
       
  1612         self.GraphicsSizer.Clear()
       
  1613         
       
  1614         for panel in self.GraphicPanels:
       
  1615             self.GraphicsSizer.AddWindow(panel, flag=wx.GROW)
       
  1616             
       
  1617         self.GraphicsSizer.Layout()
       
  1618         self.RefreshGraphicsWindowScrollbars()
       
  1619     
       
  1620     def SetCanvasPosition(self, tick):
       
  1621         tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange))
       
  1622         self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))]
       
  1623         self.Fixed = True
       
  1624         self.RefreshCanvasPosition()
       
  1625         self.ForceRefresh()
       
  1626     
       
  1627     def SetCursorTick(self, cursor_tick):
       
  1628         self.CursorTick = cursor_tick
       
  1629         self.Fixed = True
       
  1630         self.ResetCursorTick() 
       
  1631     
       
  1632     def ResetCursorTick(self):
       
  1633         self.CursorTick = None
       
  1634         self.ResetCursorTick()
       
  1635     
       
  1636     def ResetCursorTick(self):
       
  1637         for panel in self.GraphicPanels:
       
  1638             if isinstance(panel, DebugVariableGraphic):
       
  1639                 panel.SetCursorTick(self.CursorTick)
       
  1640         self.ForceRefresh()
       
  1641     
       
  1642     def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start):
       
  1643         if len(panel.GetItems()) > 1:
       
  1644             self.DraggingAxesPanel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  1645             self.DraggingAxesPanel.SetCursorTick(self.CursorTick)
       
  1646             width, height = panel.GetSize()
       
  1647             self.DraggingAxesPanel.SetSize(wx.Size(width, height))
       
  1648             self.DraggingAxesPanel.SetPosition(wx.Point(0, -height))
       
  1649         else:
       
  1650             self.DraggingAxesPanel = panel
       
  1651         self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(absolute=True)
       
  1652         self.DraggingAxesMousePos = wx.Point(
       
  1653             x_mouse_start - self.DraggingAxesBoundingBox.x, 
       
  1654             y_mouse_start - self.DraggingAxesBoundingBox.y)
       
  1655         self.MoveDragNDrop(x_mouse, y_mouse)
       
  1656         
       
  1657     def MoveDragNDrop(self, x_mouse, y_mouse):
       
  1658         self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x
       
  1659         self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y
       
  1660         self.RefreshHighlight(x_mouse, y_mouse)
       
  1661     
       
  1662     def RefreshHighlight(self, x_mouse, y_mouse):
       
  1663         for idx, panel in enumerate(self.GraphicPanels):
       
  1664             x, y = panel.GetPosition()
       
  1665             width, height = panel.GetSize()
       
  1666             rect = wx.Rect(x, y, width, height)
       
  1667             if (rect.InsideXY(x_mouse, y_mouse) or 
       
  1668                 idx == 0 and y_mouse < 0 or
       
  1669                 idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]):
       
  1670                 panel.OnDragging(x_mouse - x, y_mouse - y)
       
  1671             else:
       
  1672                 panel.SetHighlight(HIGHLIGHT_NONE)
       
  1673         if wx.Platform == "__WXMSW__":
       
  1674             self.RefreshView()
       
  1675         else:
       
  1676             self.ForceRefresh()
       
  1677     
       
  1678     def ResetHighlight(self):
       
  1679         for panel in self.GraphicPanels:
       
  1680             panel.SetHighlight(HIGHLIGHT_NONE)
       
  1681         if wx.Platform == "__WXMSW__":
       
  1682             self.RefreshView()
       
  1683         else:
       
  1684             self.ForceRefresh()
       
  1685     
       
  1686     def IsDragging(self):
       
  1687         return self.DraggingAxesPanel is not None
       
  1688     
       
  1689     def GetDraggingAxesClippingRegion(self, panel):
       
  1690         x, y = panel.GetPosition()
       
  1691         width, height = panel.GetSize()
       
  1692         bbox = wx.Rect(x, y, width, height)
       
  1693         bbox = bbox.Intersect(self.DraggingAxesBoundingBox)
       
  1694         bbox.x -= x
       
  1695         bbox.y -= y
       
  1696         return bbox
       
  1697     
       
  1698     def GetDraggingAxesPosition(self, panel):
       
  1699         x, y = panel.GetPosition()
       
  1700         return wx.Point(self.DraggingAxesBoundingBox.x - x,
       
  1701                         self.DraggingAxesBoundingBox.y - y)
       
  1702     
       
  1703     def StopDragNDrop(self, variable, x_mouse, y_mouse):
       
  1704         if self.DraggingAxesPanel not in self.GraphicPanels:
       
  1705             self.DraggingAxesPanel.Destroy()
       
  1706         self.DraggingAxesPanel = None
       
  1707         self.DraggingAxesBoundingBox = None
       
  1708         self.DraggingAxesMousePos = None
       
  1709         for idx, panel in enumerate(self.GraphicPanels):
       
  1710             panel.SetHighlight(HIGHLIGHT_NONE)
       
  1711             xw, yw = panel.GetPosition()
       
  1712             width, height = panel.GetSize()
       
  1713             bbox = wx.Rect(xw, yw, width, height)
       
  1714             if bbox.InsideXY(x_mouse, y_mouse):
       
  1715                 panel.ShowButtons(True)
       
  1716                 merge_type = GRAPH_PARALLEL
       
  1717                 if isinstance(panel, DebugVariableText) or panel.Is3DCanvas():
       
  1718                     if y_mouse > yw + height / 2:
       
  1719                         idx += 1
       
  1720                     wx.CallAfter(self.MoveValue, variable, idx)
       
  1721                 else:
       
  1722                     rect = panel.GetAxesBoundingBox(True)
       
  1723                     if rect.InsideXY(x_mouse, y_mouse):
       
  1724                         merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height)
       
  1725                         if merge_rect.InsideXY(x_mouse, y_mouse):
       
  1726                             merge_type = GRAPH_ORTHOGONAL
       
  1727                         wx.CallAfter(self.MergeGraphs, variable, idx, merge_type, force=True)
       
  1728                     else:
       
  1729                         if y_mouse > yw + height / 2:
       
  1730                             idx += 1
       
  1731                         wx.CallAfter(self.MoveValue, variable, idx)
       
  1732                 self.ForceRefresh()
       
  1733                 return 
       
  1734         width, height = self.GraphicsWindow.GetVirtualSize()
       
  1735         rect = wx.Rect(0, 0, width, height)
       
  1736         if rect.InsideXY(x_mouse, y_mouse):
       
  1737             wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels))
       
  1738         self.ForceRefresh()
       
  1739     
       
  1740     def RefreshView(self, only_values=False):
       
  1741         if USE_MPL:
       
  1742             self.RefreshCanvasPosition()
       
  1743             
       
  1744             width, height = self.GraphicsWindow.GetVirtualSize()
       
  1745             bitmap = wx.EmptyBitmap(width, height)
       
  1746             dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap)
       
  1747             dc.Clear()
       
  1748             dc.BeginDrawing()
       
  1749             if self.DraggingAxesPanel is not None:
       
  1750                 destBBox = self.DraggingAxesBoundingBox
       
  1751                 srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox()
       
  1752                 
       
  1753                 srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None)
       
  1754                 srcDC = wx.MemoryDC()
       
  1755                 srcDC.SelectObject(srcBmp)
       
  1756                     
       
  1757                 dc.Blit(destBBox.x, destBBox.y, 
       
  1758                         int(destBBox.width), int(destBBox.height), 
       
  1759                         srcDC, srcBBox.x, srcBBox.y)
       
  1760             dc.EndDrawing()
       
  1761             
       
  1762             if not self.Fixed or self.Force:
       
  1763                 self.Force = False
       
  1764                 refresh_graphics = True
       
  1765             else:
       
  1766                 refresh_graphics = False
       
  1767             
       
  1768             if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels:
       
  1769                 self.DraggingAxesPanel.RefreshViewer(refresh_graphics)
       
  1770             for panel in self.GraphicPanels:
       
  1771                 if isinstance(panel, DebugVariableGraphic):
       
  1772                     panel.RefreshViewer(refresh_graphics)
       
  1773                 else:
       
  1774                     panel.RefreshViewer()
       
  1775             
       
  1776             if self.CursorTick is not None:
       
  1777                 tick = self.CursorTick
       
  1778             elif len(self.Ticks) > 0:
       
  1779                 tick = self.Ticks[-1]
       
  1780             else:
       
  1781                 tick = None
       
  1782             if tick is not None:
       
  1783                 self.TickLabel.SetLabel("Tick: %d" % tick)
       
  1784                 if self.Ticktime > 0:
       
  1785                     tick_duration = int(tick * self.Ticktime)
       
  1786                     not_null = False
       
  1787                     duration = ""
       
  1788                     for value, format in [(tick_duration / DAY, "%dd"),
       
  1789                                           ((tick_duration % DAY) / HOUR, "%dh"),
       
  1790                                           ((tick_duration % HOUR) / MINUTE, "%dm"),
       
  1791                                           ((tick_duration % MINUTE) / SECOND, "%ds")]:
       
  1792                         
       
  1793                         if value > 0 or not_null:
       
  1794                             duration += format % value
       
  1795                             not_null = True
       
  1796                     
       
  1797                     duration += "%gms" % (float(tick_duration % SECOND) / MILLISECOND) 
       
  1798                     self.TickTimeLabel.SetLabel("t: %s" % duration)
       
  1799                 else:
       
  1800                     self.TickTimeLabel.SetLabel("")
       
  1801             else:
       
  1802                 self.TickLabel.SetLabel("")
       
  1803                 self.TickTimeLabel.SetLabel("")
       
  1804             self.TickSizer.Layout()
       
  1805         else:
       
  1806             self.Freeze()
       
  1807             
       
  1808             if only_values:
       
  1809                 for col in xrange(self.Table.GetNumberCols()):
       
  1810                     if self.Table.GetColLabelValue(col, False) == "Value":
       
  1811                         for row in xrange(self.Table.GetNumberRows()):
       
  1812                             self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
       
  1813             else:
       
  1814                 self.Table.ResetView(self.VariablesGrid)
       
  1815             self.VariablesGrid.RefreshButtons()
       
  1816             
       
  1817             self.Thaw()
       
  1818         
       
  1819     def UnregisterObsoleteData(self):
       
  1820         if USE_MPL:
       
  1821             if self.DataProducer is not None:
       
  1822                 self.Ticktime = self.DataProducer.GetTicktime()
       
  1823                 self.RefreshCanvasRange()
       
  1824             
       
  1825             for panel in self.GraphicPanels:
       
  1826                 panel.UnregisterObsoleteData()
       
  1827                 if panel.IsEmpty():
       
  1828                     if panel.HasCapture():
       
  1829                         panel.ReleaseMouse()
       
  1830                     self.GraphicPanels.remove(panel)
       
  1831                     panel.Destroy()
       
  1832             
       
  1833             self.ResetVariableNameMask()
       
  1834             self.RefreshGraphicsSizer()
       
  1835             self.ForceRefresh()
       
  1836             
       
  1837         else:
       
  1838             items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
       
  1839             items.reverse()
       
  1840             for idx, item in items:
       
  1841                 iec_path = item.GetVariable().upper()
       
  1842                 if self.GetDataType(iec_path) is None:
       
  1843                     self.RemoveDataConsumer(item)
       
  1844                     self.Table.RemoveItem(idx)
       
  1845                 else:
       
  1846                     self.AddDataConsumer(iec_path, item)
       
  1847                     item.RefreshVariableType()
       
  1848             self.Freeze()
       
  1849             self.Table.ResetView(self.VariablesGrid)
       
  1850             self.VariablesGrid.RefreshButtons()
       
  1851             self.Thaw()
       
  1852     
       
  1853     def ResetView(self):
       
  1854         self.DeleteDataConsumers()
       
  1855         if USE_MPL:
       
  1856             self.Fixed = False
       
  1857             for panel in self.GraphicPanels:
       
  1858                 panel.Destroy()
       
  1859             self.GraphicPanels = []
       
  1860             self.ResetVariableNameMask()
       
  1861             self.RefreshGraphicsSizer()
       
  1862         else:
       
  1863             self.Table.Empty()
       
  1864             self.Freeze()
       
  1865             self.Table.ResetView(self.VariablesGrid)
       
  1866             self.VariablesGrid.RefreshButtons()
       
  1867             self.Thaw()
       
  1868     
       
  1869     def RefreshCanvasRange(self):
       
  1870         if self.Ticktime == 0 and self.RangeValues != RANGE_VALUES:
       
  1871             self.RangeValues = RANGE_VALUES
       
  1872             self.CanvasRange.Clear()
       
  1873             for text, value in RANGE_VALUES:
       
  1874                 self.CanvasRange.Append(text)
       
  1875             self.CanvasRange.SetStringSelection(RANGE_VALUES[0][0])
       
  1876             self.CurrentRange = RANGE_VALUES[0][1]
       
  1877             self.RefreshView(True)
       
  1878         elif self.Ticktime != 0 and self.RangeValues != TIME_RANGE_VALUES:
       
  1879             self.RangeValues = TIME_RANGE_VALUES
       
  1880             self.CanvasRange.Clear()
       
  1881             for text, value in TIME_RANGE_VALUES:
       
  1882                 self.CanvasRange.Append(text)
       
  1883             self.CanvasRange.SetStringSelection(TIME_RANGE_VALUES[0][0])
       
  1884             self.CurrentRange = TIME_RANGE_VALUES[0][1] / self.Ticktime
       
  1885             self.RefreshView(True)
       
  1886     
       
  1887     def RefreshCanvasPosition(self):
       
  1888         if len(self.Ticks) > 0:
       
  1889             pos = int(self.StartTick - self.Ticks[0])
       
  1890             range = int(self.Ticks[-1] - self.Ticks[0])
       
  1891         else:
       
  1892             pos = 0
       
  1893             range = 0
       
  1894         self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange)
       
  1895     
       
  1896     def GetForceVariableMenuFunction(self, iec_path, item):
       
  1897         iec_type = self.GetDataType(iec_path)
       
  1898         def ForceVariableFunction(event):
       
  1899             if iec_type is not None:
       
  1900                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
  1901                 if dialog.ShowModal() == wx.ID_OK:
       
  1902                     self.ForceDataValue(iec_path, dialog.GetValue())
       
  1903         return ForceVariableFunction
       
  1904 
       
  1905     def GetReleaseVariableMenuFunction(self, iec_path):
       
  1906         def ReleaseVariableFunction(event):
       
  1907             self.ReleaseDataValue(iec_path)
       
  1908         return ReleaseVariableFunction
       
  1909     
       
  1910     def OnVariablesGridCellLeftClick(self, event):
       
  1911         if event.GetCol() == 0:
       
  1912             row = event.GetRow()
       
  1913             data = wx.TextDataObject(str((self.Table.GetValueByName(row, "Variable"), "debug")))
       
  1914             dragSource = wx.DropSource(self.VariablesGrid)
       
  1915             dragSource.SetData(data)
       
  1916             dragSource.DoDragDrop()
       
  1917         event.Skip()
       
  1918     
       
  1919     def OnVariablesGridCellRightClick(self, event):
       
  1920         row, col = event.GetRow(), event.GetCol()
       
  1921         if self.Table.GetColLabelValue(col, False) == "Value":
       
  1922             iec_path = self.Table.GetValueByName(row, "Variable").upper()
       
  1923 
       
  1924             menu = wx.Menu(title='')
       
  1925             
       
  1926             new_id = wx.NewId()
       
  1927             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
       
  1928             self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
       
  1929             
       
  1930             new_id = wx.NewId()
       
  1931             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
       
  1932             self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
       
  1933             
       
  1934             if self.Table.IsForced(row):
       
  1935                 menu.Enable(new_id, True)
       
  1936             else:
       
  1937                 menu.Enable(new_id, False)
       
  1938             
       
  1939             self.PopupMenu(menu)
       
  1940             
       
  1941             menu.Destroy()
       
  1942         event.Skip()
       
  1943     
       
  1944     def ChangeRange(self, dir, tick=None):
       
  1945         current_range = self.CurrentRange
       
  1946         current_range_idx = self.CanvasRange.GetSelection()
       
  1947         new_range_idx = max(0, min(current_range_idx + dir, len(self.RangeValues) - 1))
       
  1948         if new_range_idx != current_range_idx:
       
  1949             self.CanvasRange.SetSelection(new_range_idx)
       
  1950             if self.Ticktime == 0:
       
  1951                 self.CurrentRange = self.RangeValues[new_range_idx][1]
       
  1952             else:
       
  1953                 self.CurrentRange = self.RangeValues[new_range_idx][1] / self.Ticktime
       
  1954             if len(self.Ticks) > 0:
       
  1955                 if tick is None:
       
  1956                     tick = self.StartTick + self.CurrentRange / 2.
       
  1957                 new_start_tick = tick - (tick - self.StartTick) * self.CurrentRange / current_range 
       
  1958                 self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))]
       
  1959                 self.Fixed = self.StartTick < self.Ticks[-1] - self.CurrentRange
       
  1960             self.ForceRefresh()
       
  1961     
       
  1962     def RefreshRange(self):
       
  1963         if len(self.Ticks) > 0:
       
  1964             if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange:
       
  1965                 self.Fixed = False
       
  1966             if self.Fixed:
       
  1967                 self.StartTick = min(self.StartTick, self.Ticks[-1] - self.CurrentRange)
       
  1968             else:
       
  1969                 self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
  1970         self.ForceRefresh()
       
  1971     
       
  1972     def OnRangeChanged(self, event):
       
  1973         try:
       
  1974             if self.Ticktime == 0:
       
  1975                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1]
       
  1976             else:
       
  1977                 self.CurrentRange = self.RangeValues[self.CanvasRange.GetSelection()][1] / self.Ticktime
       
  1978         except ValueError, e:
       
  1979             self.CanvasRange.SetValue(str(self.CurrentRange))
       
  1980         wx.CallAfter(self.RefreshRange)
       
  1981         event.Skip()
       
  1982     
       
  1983     def OnCurrentButton(self, event):
       
  1984         if len(self.Ticks) > 0:
       
  1985             self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange)
       
  1986             self.Fixed = False
       
  1987             self.CursorTick = None
       
  1988             self.ResetCursorTick()
       
  1989         event.Skip()
       
  1990     
       
  1991     def CopyDataToClipboard(self, variables):
       
  1992         text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables])
       
  1993         next_tick = NextTick(variables)
       
  1994         while next_tick is not None:
       
  1995             values = []
       
  1996             for item, data in variables:
       
  1997                 if len(data) > 0:
       
  1998                     if next_tick == data[0][0]:
       
  1999                         var_type = item.GetVariableType()
       
  2000                         if var_type in ["STRING", "WSTRING"]:
       
  2001                             value = item.GetRawValue(int(data.pop(0)[2]))
       
  2002                             if var_type == "STRING":
       
  2003                                 values.append("'%s'" % value)
       
  2004                             else:
       
  2005                                 values.append('"%s"' % value)
       
  2006                         else:
       
  2007                             values.append("%.3f" % data.pop(0)[1])
       
  2008                     else:
       
  2009                         values.append("")
       
  2010                 else:
       
  2011                     values.append("")
       
  2012             text += "%d;%s;\n" % (next_tick, ";".join(values))
       
  2013             next_tick = NextTick(variables)
       
  2014         self.ParentWindow.SetCopyBuffer(text)
       
  2015     
       
  2016     def OnExportGraphButton(self, event):
       
  2017         variables = []
       
  2018         if USE_MPL:
       
  2019             items = []
       
  2020             for panel in self.GraphicPanels:
       
  2021                 items.extend(panel.GetItems())
       
  2022         else:
       
  2023             items = self.Table.GetData()
       
  2024         for item in items:
       
  2025             if item.IsNumVariable():
       
  2026                 variables.append((item, [entry for entry in item.GetData()]))
       
  2027         wx.CallAfter(self.CopyDataToClipboard, variables)
       
  2028         event.Skip()
       
  2029     
       
  2030     def OnPositionChanging(self, event):
       
  2031         if len(self.Ticks) > 0:
       
  2032             self.StartTick = self.Ticks[0] + event.GetPosition()
       
  2033             self.Fixed = True
       
  2034             self.ForceRefresh()
       
  2035         event.Skip()
       
  2036     
       
  2037     def GetRange(self):
       
  2038         return self.StartTick, self.StartTick + self.CurrentRange
       
  2039     
       
  2040     def GetViewerIndex(self, viewer):
       
  2041         if viewer in self.GraphicPanels:
       
  2042             return self.GraphicPanels.index(viewer)
       
  2043         return None
       
  2044     
       
  2045     def IsViewerFirst(self, viewer):
       
  2046         return viewer == self.GraphicPanels[0]
       
  2047     
       
  2048     def IsViewerLast(self, viewer):
       
  2049         return viewer == self.GraphicPanels[-1]
       
  2050     
       
  2051     def HighlightPreviousViewer(self, viewer):
       
  2052         if self.IsViewerFirst(viewer):
       
  2053             return
       
  2054         idx = self.GetViewerIndex(viewer)
       
  2055         if idx is None:
       
  2056             return
       
  2057         self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER)
       
  2058     
       
  2059     def ResetVariableNameMask(self):
       
  2060         items = []
       
  2061         for panel in self.GraphicPanels:
       
  2062             items.extend(panel.GetItems())
       
  2063         if len(items) > 1:
       
  2064             self.VariableNameMask = reduce(compute_mask,
       
  2065                 [item.GetVariable().split('.') for item in items])
       
  2066         elif len(items) > 0:
       
  2067             self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*']
       
  2068         else:
       
  2069             self.VariableNameMask = []
       
  2070         self.MaskLabel.ChangeValue(".".join(self.VariableNameMask))
       
  2071         self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition())
       
  2072             
       
  2073     def GetVariableNameMask(self):
       
  2074         return self.VariableNameMask
       
  2075     
       
  2076     def InsertValue(self, iec_path, idx = None, force=False):
       
  2077         if USE_MPL:
       
  2078             for panel in self.GraphicPanels:
       
  2079                 if panel.GetItem(iec_path) is not None:
       
  2080                     return
       
  2081             if idx is None:
       
  2082                 idx = len(self.GraphicPanels)
       
  2083         else:
       
  2084             for item in self.Table.GetData():
       
  2085                 if iec_path == item.GetVariable():
       
  2086                     return
       
  2087             if idx is None:
       
  2088                 idx = self.Table.GetNumberRows()
       
  2089         item = VariableTableItem(self, iec_path)
       
  2090         result = self.AddDataConsumer(iec_path.upper(), item)
       
  2091         if result is not None or force:
       
  2092             
       
  2093             if USE_MPL:
       
  2094                 if item.IsNumVariable():
       
  2095                     panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  2096                     if self.CursorTick is not None:
       
  2097                         panel.SetCursorTick(self.CursorTick)
       
  2098                 else:
       
  2099                     panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  2100                 if idx is not None:
       
  2101                     self.GraphicPanels.insert(idx, panel)
       
  2102                 else:
       
  2103                     self.GraphicPanels.append(panel)
       
  2104                 self.ResetVariableNameMask()
       
  2105                 self.RefreshGraphicsSizer()
       
  2106             else:
       
  2107                 self.Table.InsertItem(idx, item)
       
  2108             
       
  2109             self.ForceRefresh()
       
  2110     
       
  2111     def MoveValue(self, iec_path, idx = None):
       
  2112         if idx is None:
       
  2113             idx = len(self.GraphicPanels)
       
  2114         source_panel = None
       
  2115         item = None
       
  2116         for panel in self.GraphicPanels:
       
  2117             item = panel.GetItem(iec_path)
       
  2118             if item is not None:
       
  2119                 source_panel = panel
       
  2120                 break
       
  2121         if source_panel is not None:
       
  2122             source_panel.RemoveItem(item)
       
  2123             if source_panel.IsEmpty():
       
  2124                 if source_panel.HasCapture():
       
  2125                     source_panel.ReleaseMouse()
       
  2126                 self.GraphicPanels.remove(source_panel)
       
  2127                 source_panel.Destroy()
       
  2128             
       
  2129             if item.IsNumVariable():
       
  2130                 panel = DebugVariableGraphic(self.GraphicsWindow, self, [item], GRAPH_PARALLEL)
       
  2131                 if self.CursorTick is not None:
       
  2132                     panel.SetCursorTick(self.CursorTick)
       
  2133             else:
       
  2134                 panel = DebugVariableText(self.GraphicsWindow, self, [item])
       
  2135             self.GraphicPanels.insert(idx, panel)
       
  2136             self.ResetVariableNameMask()
       
  2137             self.RefreshGraphicsSizer()
       
  2138             self.ForceRefresh()
       
  2139     
       
  2140     def MergeGraphs(self, source, target_idx, merge_type, force=False):
       
  2141         source_item = None
       
  2142         source_panel = None
       
  2143         for panel in self.GraphicPanels:
       
  2144             source_item = panel.GetItem(source)
       
  2145             if source_item is not None:
       
  2146                 source_panel = panel
       
  2147                 break
       
  2148         if source_item is None:
       
  2149             item = VariableTableItem(self, source)
       
  2150             if item.IsNumVariable():
       
  2151                 result = self.AddDataConsumer(source.upper(), item)
       
  2152                 if result is not None or force:
       
  2153                     source_item = item
       
  2154         if source_item is not None and source_item.IsNumVariable():
       
  2155             target_panel = self.GraphicPanels[target_idx]
       
  2156             graph_type = target_panel.GraphType
       
  2157             if target_panel != source_panel:
       
  2158                 if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or
       
  2159                     merge_type == GRAPH_ORTHOGONAL and 
       
  2160                     (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or
       
  2161                      graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)):
       
  2162                     return
       
  2163                 
       
  2164                 if source_panel is not None:
       
  2165                     source_panel.RemoveItem(source_item)
       
  2166                     if source_panel.IsEmpty():
       
  2167                         if source_panel.HasCapture():
       
  2168                             source_panel.ReleaseMouse()
       
  2169                         self.GraphicPanels.remove(source_panel)
       
  2170                         source_panel.Destroy()
       
  2171             elif (merge_type != graph_type and len(target_panel.Items) == 2):
       
  2172                 target_panel.RemoveItem(source_item)
       
  2173             else:
       
  2174                 target_panel = None
       
  2175                 
       
  2176             if target_panel is not None:
       
  2177                 target_panel.AddItem(source_item)
       
  2178                 target_panel.GraphType = merge_type
       
  2179                 target_panel.ResetGraphics()
       
  2180                 
       
  2181                 self.ResetVariableNameMask()
       
  2182                 self.RefreshGraphicsSizer()
       
  2183                 self.ForceRefresh()
       
  2184     
       
  2185     def DeleteValue(self, source_panel, item=None):
       
  2186         source_idx = self.GetViewerIndex(source_panel)
       
  2187         if source_idx is not None:
       
  2188             
       
  2189             if item is None:
       
  2190                 source_panel.Clear()
       
  2191                 source_panel.Destroy()
       
  2192                 self.GraphicPanels.remove(source_panel)
       
  2193                 self.ResetVariableNameMask()
       
  2194                 self.RefreshGraphicsSizer()
       
  2195             else:
       
  2196                 source_panel.RemoveItem(item)
       
  2197                 if source_panel.IsEmpty():
       
  2198                     source_panel.Destroy()
       
  2199                     self.GraphicPanels.remove(source_panel)
       
  2200                     self.ResetVariableNameMask()
       
  2201                     self.RefreshGraphicsSizer()
       
  2202             self.ForceRefresh()
       
  2203     
       
  2204     def GetDebugVariables(self):
       
  2205         if USE_MPL:
       
  2206             return [panel.GetVariables() for panel in self.GraphicPanels]
       
  2207         else:
       
  2208             return [item.GetVariable() for item in self.Table.GetData()]
       
  2209     
       
  2210     def SetDebugVariables(self, variables):
       
  2211         if USE_MPL:
       
  2212             for variable in variables:
       
  2213                 if isinstance(variable, (TupleType, ListType)):
       
  2214                     items = []
       
  2215                     for iec_path in variable:
       
  2216                         item = VariableTableItem(self, iec_path)
       
  2217                         if not item.IsNumVariable():
       
  2218                             continue
       
  2219                         self.AddDataConsumer(iec_path.upper(), item)
       
  2220                         items.append(item)
       
  2221                     if isinstance(variable, ListType):
       
  2222                         panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_PARALLEL)
       
  2223                     elif isinstance(variable, TupleType) and len(items) <= 3:
       
  2224                         panel = DebugVariableGraphic(self.GraphicsWindow, self, items, GRAPH_ORTHOGONAL)
       
  2225                     else:
       
  2226                         continue
       
  2227                     self.GraphicPanels.append(panel)
       
  2228                     self.ResetVariableNameMask()
       
  2229                     self.RefreshGraphicsSizer()
       
  2230                 else:
       
  2231                     self.InsertValue(variable, force=True)
       
  2232             self.ForceRefresh()
       
  2233         else:
       
  2234             for variable in variables:
       
  2235                 if isinstance(variable, (ListType, TupleType)):
       
  2236                     for iec_path in variable:
       
  2237                         self.InsertValue(iec_path, force=True)
       
  2238                 else:
       
  2239                     self.InsertValue(variable, force=True)
       
  2240     
       
  2241     def ResetGraphicsValues(self):
       
  2242         if USE_MPL:
       
  2243             self.Ticks = numpy.array([])
       
  2244             self.StartTick = 0
       
  2245             self.Fixed = False
       
  2246             for panel in self.GraphicPanels:
       
  2247                 panel.ResetData()
       
  2248 
       
  2249     def RefreshGraphicsWindowScrollbars(self):
       
  2250         xstart, ystart = self.GraphicsWindow.GetViewStart()
       
  2251         window_size = self.GraphicsWindow.GetClientSize()
       
  2252         vwidth, vheight = self.GraphicsSizer.GetMinSize()
       
  2253         posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT))
       
  2254         posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT))
       
  2255         self.GraphicsWindow.Scroll(posx, posy)
       
  2256         self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, 
       
  2257                 vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy)
       
  2258     
       
  2259     def OnGraphicsWindowResize(self, event):
       
  2260         self.RefreshGraphicsWindowScrollbars()
       
  2261         event.Skip()