controls/DebugVariablePanel/DebugVariableTablePanel.py
changeset 1207 fb9799a0c0f7
parent 1202 3d8c87ab2b5d
child 1209 953a8f14040a
equal deleted inserted replaced
1206:35046bc74554 1207:fb9799a0c0f7
    33 from util.BitmapLibrary import GetBitmap
    33 from util.BitmapLibrary import GetBitmap
    34 
    34 
    35 from DebugVariableItem import DebugVariableItem
    35 from DebugVariableItem import DebugVariableItem
    36 
    36 
    37 def GetDebugVariablesTableColnames():
    37 def GetDebugVariablesTableColnames():
       
    38     """
       
    39     Function returning table column header labels
       
    40     @return: List of labels [col_label,...]
       
    41     """
    38     _ = lambda x : x
    42     _ = lambda x : x
    39     return [_("Variable"), _("Value")]
    43     return [_("Variable"), _("Value")]
       
    44 
       
    45 #-------------------------------------------------------------------------------
       
    46 #                        Debug Variable Table Panel
       
    47 #-------------------------------------------------------------------------------
       
    48 
       
    49 """
       
    50 Class that implements a custom table storing value to display in Debug Variable
       
    51 Table Panel grid
       
    52 """
    40 
    53 
    41 class DebugVariableTable(CustomTable):
    54 class DebugVariableTable(CustomTable):
    42     
    55     
    43     def GetValue(self, row, col):
    56     def GetValue(self, row, col):
    44         if row < self.GetNumberRows():
    57         if row < self.GetNumberRows():
    65                 self.data[row].SetValue(value)
    78                 self.data[row].SetValue(value)
    66     
    79     
    67     def IsForced(self, row):
    80     def IsForced(self, row):
    68         if row < self.GetNumberRows():
    81         if row < self.GetNumberRows():
    69             return self.data[row].IsForced()
    82             return self.data[row].IsForced()
    70         return False
       
    71     
       
    72     def IsNumVariable(self, row):
       
    73         if row < self.GetNumberRows():
       
    74             return self.data[row].IsNumVariable()
       
    75         return False
    83         return False
    76     
    84     
    77     def _updateColAttrs(self, grid):
    85     def _updateColAttrs(self, grid):
    78         """
    86         """
    79         wx.grid.Grid -> update the column attributes to add the
    87         wx.grid.Grid -> update the column attributes to add the
    90                         grid.SetCellTextColour(row, col, wx.BLUE)
    98                         grid.SetCellTextColour(row, col, wx.BLUE)
    91                     else:
    99                     else:
    92                         grid.SetCellTextColour(row, col, wx.BLACK)
   100                         grid.SetCellTextColour(row, col, wx.BLACK)
    93                 grid.SetReadOnly(row, col, True)
   101                 grid.SetReadOnly(row, col, True)
    94             self.ResizeRow(grid, row)
   102             self.ResizeRow(grid, row)
    95                 
   103     
    96     def AppendItem(self, data):
   104     def RefreshValues(self, grid):
    97         self.data.append(data)
   105         for col in xrange(self.GetNumberCols()):
    98     
   106             colname = self.GetColLabelValue(col, False)
    99     def InsertItem(self, idx, data):
   107             if colname == "Value":
   100         self.data.insert(idx, data)
   108                 for row in xrange(self.GetNumberRows()):
   101     
   109                     grid.SetCellValue(row, col, str(self.data[row].GetValue()))
   102     def RemoveItem(self, idx):
   110                     if self.IsForced(row):
   103         self.data.pop(idx)
   111                         grid.SetCellTextColour(row, col, wx.BLUE)
       
   112                     else:
       
   113                         grid.SetCellTextColour(row, col, wx.BLACK)
       
   114       
       
   115     def AppendItem(self, item):
       
   116         self.data.append(item)
       
   117     
       
   118     def InsertItem(self, idx, item):
       
   119         self.data.insert(idx, item)
       
   120     
       
   121     def RemoveItem(self, item):
       
   122         self.data.remove(item)
   104     
   123     
   105     def MoveItem(self, idx, new_idx):
   124     def MoveItem(self, idx, new_idx):
   106         self.data.insert(new_idx, self.data.pop(idx))
   125         self.data.insert(new_idx, self.data.pop(idx))
   107         
   126         
   108     def GetItem(self, idx):
   127     def GetItem(self, idx):
   109         return self.data[idx]
   128         return self.data[idx]
   110 
   129 
       
   130 
       
   131 #-------------------------------------------------------------------------------
       
   132 #                  Debug Variable Table Panel Drop Target
       
   133 #-------------------------------------------------------------------------------
       
   134 
       
   135 """
       
   136 Class that implements a custom drop target class for Debug Variable Table Panel
       
   137 """
       
   138 
   111 class DebugVariableTableDropTarget(wx.TextDropTarget):
   139 class DebugVariableTableDropTarget(wx.TextDropTarget):
   112     
   140     
   113     def __init__(self, parent):
   141     def __init__(self, parent):
       
   142         """
       
   143         Constructor
       
   144         @param window: Reference to the Debug Variable Panel
       
   145         """
   114         wx.TextDropTarget.__init__(self)
   146         wx.TextDropTarget.__init__(self)
   115         self.ParentWindow = parent
   147         self.ParentWindow = parent
   116         
   148         
   117     def __del__(self):
   149     def __del__(self):
       
   150         """
       
   151         Destructor
       
   152         """
       
   153         # Remove reference to Debug Variable Panel
   118         self.ParentWindow = None
   154         self.ParentWindow = None
   119         
   155         
   120     def OnDropText(self, x, y, data):
   156     def OnDropText(self, x, y, data):
       
   157         """
       
   158         Function called when mouse is dragged over Drop Target
       
   159         @param x: X coordinate of mouse pointer
       
   160         @param y: Y coordinate of mouse pointer
       
   161         @param data: Text associated to drag'n drop
       
   162         """
   121         message = None
   163         message = None
   122         try:
   164         try:
   123             values = eval(data)
   165             values = eval(data)
       
   166             if not isinstance(values, TupleType):
       
   167                 raise ValueError
   124         except:
   168         except:
   125             message = _("Invalid value \"%s\" for debug variable")%data
   169             message = _("Invalid value \"%s\" for debug variable") % data
   126             values = None
       
   127         if not isinstance(values, TupleType):
       
   128             message = _("Invalid value \"%s\" for debug variable")%data
       
   129             values = None
   170             values = None
   130         
   171         
   131         if message is not None:
   172         if message is not None:
   132             wx.CallAfter(self.ShowMessage, message)
   173             wx.CallAfter(self.ShowMessage, message)
   133         elif values is not None and values[1] == "debug":
   174         
       
   175         elif values[1] == "debug":
   134             grid = self.ParentWindow.VariablesGrid
   176             grid = self.ParentWindow.VariablesGrid
       
   177             
       
   178             # Get row where variable was dropped
   135             x, y = grid.CalcUnscrolledPosition(x, y)
   179             x, y = grid.CalcUnscrolledPosition(x, y)
   136             row = grid.YToRow(y - grid.GetColLabelSize())
   180             row = grid.YToRow(y - grid.GetColLabelSize())
       
   181             
       
   182             # If no row found add variable at table end
   137             if row == wx.NOT_FOUND:
   183             if row == wx.NOT_FOUND:
   138                 row = self.ParentWindow.Table.GetNumberRows()
   184                 row = self.ParentWindow.Table.GetNumberRows()
       
   185             
       
   186             # Add variable to table
   139             self.ParentWindow.InsertValue(values[0], row, force=True)
   187             self.ParentWindow.InsertValue(values[0], row, force=True)
   140             
   188             
   141     def ShowMessage(self, message):
   189     def ShowMessage(self, message):
   142         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
   190         """
       
   191         Show error message in Error Dialog
       
   192         @param message: Error message to display
       
   193         """
       
   194         dialog = wx.MessageDialog(self.ParentWindow, 
       
   195                                   message, 
       
   196                                   _("Error"), 
       
   197                                   wx.OK|wx.ICON_ERROR)
   143         dialog.ShowModal()
   198         dialog.ShowModal()
   144         dialog.Destroy()
   199         dialog.Destroy()
   145     
   200 
       
   201 
       
   202 #-------------------------------------------------------------------------------
       
   203 #                       Debug Variable Table Panel
       
   204 #-------------------------------------------------------------------------------
       
   205 
       
   206 """
       
   207 Class that implements a panel displaying debug variable values in a table
       
   208 """
       
   209 
   146 class DebugVariableTablePanel(wx.Panel, DebugViewer):
   210 class DebugVariableTablePanel(wx.Panel, DebugViewer):
   147     
   211     
   148     def __init__(self, parent, producer, window):
   212     def __init__(self, parent, producer, window):
       
   213         """
       
   214         Constructor
       
   215         @param parent: Reference to the parent wx.Window
       
   216         @param producer: Object receiving debug value and dispatching them to
       
   217         consumers
       
   218         @param window: Reference to Beremiz frame
       
   219         """
   149         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
   220         wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
   150         
   221         
       
   222         # Save Reference to Beremiz frame
   151         self.ParentWindow = window
   223         self.ParentWindow = window
   152         
   224         
       
   225         # Variable storing flag indicating that variable displayed in table
       
   226         # received new value and then table need to be refreshed
   153         self.HasNewData = False
   227         self.HasNewData = False
   154         
   228         
   155         DebugViewer.__init__(self, producer, True)
   229         DebugViewer.__init__(self, producer, True)
       
   230         
       
   231         # Construction of window layout by creating controls and sizers
   156         
   232         
   157         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
   233         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
   158         main_sizer.AddGrowableCol(0)
   234         main_sizer.AddGrowableCol(0)
   159         main_sizer.AddGrowableRow(1)
   235         main_sizer.AddGrowableRow(1)
   160         
   236         
   161         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   237         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
   162         main_sizer.AddSizer(button_sizer, border=5, 
   238         main_sizer.AddSizer(button_sizer, border=5, 
   163               flag=wx.ALIGN_RIGHT|wx.ALL)
   239               flag=wx.ALIGN_RIGHT|wx.ALL)
       
   240         
       
   241         # Creation of buttons for navigating in table
   164         
   242         
   165         for name, bitmap, help in [
   243         for name, bitmap, help in [
   166                 ("DeleteButton", "remove_element", _("Remove debug variable")),
   244                 ("DeleteButton", "remove_element", _("Remove debug variable")),
   167                 ("UpButton", "up", _("Move debug variable up")),
   245                 ("UpButton", "up", _("Move debug variable up")),
   168                 ("DownButton", "down", _("Move debug variable down"))]:
   246                 ("DownButton", "down", _("Move debug variable down"))]:
   169             button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), 
   247             button = wx.lib.buttons.GenBitmapButton(self, 
       
   248                   bitmap=GetBitmap(bitmap), 
   170                   size=wx.Size(28, 28), style=wx.NO_BORDER)
   249                   size=wx.Size(28, 28), style=wx.NO_BORDER)
   171             button.SetToolTipString(help)
   250             button.SetToolTipString(help)
   172             setattr(self, name, button)
   251             setattr(self, name, button)
   173             button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   252             button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
   174         
   253         
   175         self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL)
   254         # Creation of grid and associated table
       
   255         
       
   256         self.VariablesGrid = CustomGrid(self, 
       
   257                 size=wx.Size(-1, 150), style=wx.VSCROLL)
       
   258         # Define grid drop target
   176         self.VariablesGrid.SetDropTarget(DebugVariableTableDropTarget(self))
   259         self.VariablesGrid.SetDropTarget(DebugVariableTableDropTarget(self))
   177         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
   260         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
   178               self.OnVariablesGridCellRightClick)
   261               self.OnVariablesGridCellRightClick)
   179         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
   262         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, 
   180               self.OnVariablesGridCellLeftClick)
   263               self.OnVariablesGridCellLeftClick)
   181         main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   264         main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
   182     
   265     
   183         self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
   266         self.Table = DebugVariableTable(self, [], 
       
   267                 GetDebugVariablesTableColnames())
   184         self.VariablesGrid.SetTable(self.Table)
   268         self.VariablesGrid.SetTable(self.Table)
   185         self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
   269         self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
   186                                        "Up": self.UpButton,
   270                                        "Up": self.UpButton,
   187                                        "Down": self.DownButton})
   271                                        "Down": self.DownButton})
   188     
   272         
       
   273         # Definition of function associated to navigation buttons
       
   274         
   189         def _AddVariable(new_row):
   275         def _AddVariable(new_row):
   190             return self.VariablesGrid.GetGridCursorRow()
   276             return self.VariablesGrid.GetGridCursorRow()
   191         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
   277         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
   192     
   278     
   193         def _DeleteVariable(row):
   279         def _DeleteVariable(row):
   194             item = self.Table.GetItem(row)
   280             item = self.Table.GetItem(row)
   195             self.RemoveDataConsumer(item)
   281             self.RemoveDataConsumer(item)
   196             self.Table.RemoveItem(row)
   282             self.Table.RemoveItem(item)
   197             self.RefreshView()
   283             self.RefreshView()
   198         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
   284         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
   199         
   285         
   200         def _MoveVariable(row, move):
   286         def _MoveVariable(row, move):
   201             new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
   287             new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
   203                 self.Table.MoveItem(row, new_row)
   289                 self.Table.MoveItem(row, new_row)
   204                 self.RefreshView()
   290                 self.RefreshView()
   205             return new_row
   291             return new_row
   206         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
   292         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
   207         
   293         
       
   294         # Initialization of grid layout
       
   295         
   208         self.VariablesGrid.SetRowLabelSize(0)
   296         self.VariablesGrid.SetRowLabelSize(0)
   209         
   297         
   210         self.GridColSizes = [200, 100]
   298         self.GridColSizes = [200, 100]
   211         
   299         
   212         for col in range(self.Table.GetNumberCols()):
   300         for col in range(self.Table.GetNumberCols()):
   219         self.VariablesGrid.RefreshButtons()
   307         self.VariablesGrid.RefreshButtons()
   220         
   308         
   221         self.SetSizer(main_sizer)
   309         self.SetSizer(main_sizer)
   222     
   310     
   223     def RefreshNewData(self, *args, **kwargs):
   311     def RefreshNewData(self, *args, **kwargs):
       
   312         """
       
   313         Called to refresh Table according to values received by variables
       
   314         Can receive any parameters (not used here)
       
   315         """
       
   316         # Refresh 'Value' column of table if new data have been received since
       
   317         # last refresh
   224         if self.HasNewData:
   318         if self.HasNewData:
   225             self.HasNewData = False
   319             self.HasNewData = False
   226             self.RefreshView(only_values=True)
   320             self.RefreshView(only_values=True)
   227         DebugViewer.RefreshNewData(self, *args, **kwargs)
   321         DebugViewer.RefreshNewData(self, *args, **kwargs)
   228     
   322     
   229     def RefreshView(self, only_values=False):
   323     def RefreshView(self, only_values=False):
       
   324         """
       
   325         Function refreshing table layout and values
       
   326         @param only_values: True if only 'Value' column need to be updated
       
   327         """
       
   328         # Block refresh until table layout and values are completely updated
   230         self.Freeze()
   329         self.Freeze()
   231         
   330         
       
   331         # Update only 'value' column from table 
   232         if only_values:
   332         if only_values:
   233             for col in xrange(self.Table.GetNumberCols()):
   333             self.Table.RefreshValues(self.VariablesGrid)
   234                 if self.Table.GetColLabelValue(col, False) == "Value":
   334         
   235                     for row in xrange(self.Table.GetNumberRows()):
   335         # Update complete table layout refreshing table navigation buttons
   236                         self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
   336         # state according to 
   237                         if self.Table.IsForced(row):
       
   238                             self.VariablesGrid.SetCellTextColour(row, col, wx.BLUE)
       
   239                         else:
       
   240                             self.VariablesGrid.SetCellTextColour(row, col, wx.BLACK)
       
   241         else:
   337         else:
   242             self.Table.ResetView(self.VariablesGrid)
   338             self.Table.ResetView(self.VariablesGrid)
       
   339             self.VariablesGrid.RefreshButtons()
       
   340         
       
   341         self.Thaw()
       
   342         
       
   343     def ResetView(self):
       
   344         """
       
   345         Function removing all variables denugged from table
       
   346         @param only_values: True if only 'Value' column need to be updated
       
   347         """
       
   348         # Unsubscribe all variables debugged
       
   349         self.UnsubscribeAllDataConsumers()
       
   350         
       
   351         # Clear table content
       
   352         self.Table.Empty()
       
   353         
       
   354         # Update table layout
       
   355         self.Freeze()
       
   356         self.Table.ResetView(self.VariablesGrid)
   243         self.VariablesGrid.RefreshButtons()
   357         self.VariablesGrid.RefreshButtons()
   244         
       
   245         self.Thaw()
   358         self.Thaw()
   246         
   359     
   247     def UnsubscribeObsoleteData(self):
   360     def SubscribeAllDataConsumers(self):
   248         self.SubscribeAllDataConsumers()
   361         """
   249         
   362         Function refreshing table layout and values
   250         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
   363         @param only_values: True if only 'Value' column need to be updated
   251         items.reverse()
   364         """
   252         for idx, item in items:
   365         DebugViewer.SubscribeAllDataConsumers(self)
       
   366         
       
   367         # Navigate through variable displayed in table, removing those that
       
   368         # doesn't exist anymore in PLC
       
   369         for item in self.Table.GetData()[:]:
   253             iec_path = item.GetVariable()
   370             iec_path = item.GetVariable()
   254             if self.GetDataType(iec_path) is None:
   371             if self.GetDataType(iec_path) is None:
   255                 self.RemoveDataConsumer(item)
   372                 self.RemoveDataConsumer(item)
   256                 self.Table.RemoveItem(idx)
   373                 self.Table.RemoveItem(idx)
   257             else:
   374             else:
   258                 self.AddDataConsumer(iec_path.upper(), item)
   375                 self.AddDataConsumer(iec_path.upper(), item)
   259                 item.RefreshVariableType()
   376                 item.RefreshVariableType()
       
   377         
       
   378         # Update table layout
   260         self.Freeze()
   379         self.Freeze()
   261         self.Table.ResetView(self.VariablesGrid)
   380         self.Table.ResetView(self.VariablesGrid)
   262         self.VariablesGrid.RefreshButtons()
   381         self.VariablesGrid.RefreshButtons()
   263         self.Thaw()
   382         self.Thaw()
   264     
   383     
   265     def ResetView(self):
   384     def GetForceVariableMenuFunction(self, item):
   266         self.UnsubscribeAllDataConsumers()
   385         """
   267         
   386         Function returning callback function for contextual menu 'Force' item
   268         self.Table.Empty()
   387         @param item: Debug Variable item where contextual menu was opened 
   269         self.Freeze()
   388         @return: Callback function
   270         self.Table.ResetView(self.VariablesGrid)
   389         """
   271         self.VariablesGrid.RefreshButtons()
       
   272         self.Thaw()
       
   273     
       
   274     def GetForceVariableMenuFunction(self, iec_path, item):
       
   275         iec_type = self.GetDataType(iec_path)
       
   276         def ForceVariableFunction(event):
   390         def ForceVariableFunction(event):
   277             if iec_type is not None:
   391             # Get variable path and data type
   278                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
   392             iec_path = item.GetVariable()
   279                 if dialog.ShowModal() == wx.ID_OK:
   393             iec_type = self.GetDataType(iec_path)
   280                     self.ForceDataValue(iec_path.upper(), dialog.GetValue())
   394             
       
   395             # Return immediately if not data type found
       
   396             if iec_type is None:
       
   397                 return
       
   398             
       
   399             # Open dialog for entering value to force variable
       
   400             dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
   401             
       
   402             # If valid value entered, force variable
       
   403             if dialog.ShowModal() == wx.ID_OK:
       
   404                 self.ForceDataValue(iec_path.upper(), dialog.GetValue())
       
   405         
   281         return ForceVariableFunction
   406         return ForceVariableFunction
   282 
   407 
   283     def GetReleaseVariableMenuFunction(self, iec_path):
   408     def GetReleaseVariableMenuFunction(self, iec_path):
       
   409         """
       
   410         Function returning callback function for contextual menu 'Release' item
       
   411         @param iec_path: Debug Variable path where contextual menu was opened
       
   412         @return: Callback function
       
   413         """
   284         def ReleaseVariableFunction(event):
   414         def ReleaseVariableFunction(event):
       
   415             # Release variable
   285             self.ReleaseDataValue(iec_path)
   416             self.ReleaseDataValue(iec_path)
   286         return ReleaseVariableFunction
   417         return ReleaseVariableFunction
   287     
   418     
   288     def OnVariablesGridCellLeftClick(self, event):
   419     def OnVariablesGridCellLeftClick(self, event):
   289         if event.GetCol() == 0:
   420         """
   290             row = event.GetRow()
   421         Called when left mouse button is pressed on a table cell
   291             data = wx.TextDataObject(str((self.Table.GetValueByName(row, "Variable"), "debug")))
   422         @param event: wx.grid.GridEvent
       
   423         """
       
   424         # Initiate a drag and drop if the cell clicked was in 'Variable' column
       
   425         if self.Table.GetColLabelValue(event.GetCol(), False) == "Variable":
       
   426             item = self.Table.GetItem(event.GetRow())
       
   427             data = wx.TextDataObject(str((item.GetVariable(), "debug")))
   292             dragSource = wx.DropSource(self.VariablesGrid)
   428             dragSource = wx.DropSource(self.VariablesGrid)
   293             dragSource.SetData(data)
   429             dragSource.SetData(data)
   294             dragSource.DoDragDrop()
   430             dragSource.DoDragDrop()
       
   431         
   295         event.Skip()
   432         event.Skip()
   296     
   433     
   297     def OnVariablesGridCellRightClick(self, event):
   434     def OnVariablesGridCellRightClick(self, event):
   298         row, col = event.GetRow(), event.GetCol()
   435         """
   299         if self.Table.GetColLabelValue(col, False) == "Value":
   436         Called when right mouse button is pressed on a table cell
   300             iec_path = self.Table.GetValueByName(row, "Variable").upper()
   437         @param event: wx.grid.GridEvent
   301 
   438         """
       
   439         # Open a contextual menu if the cell clicked was in 'Value' column
       
   440         if self.Table.GetColLabelValue(event.GetCol(), False) == "Value":
       
   441             row = event.GetRow()
       
   442             
       
   443             # Get variable path
       
   444             item = self.Table.GetItem(row)
       
   445             iec_path = item.GetVariable().upper()
       
   446             
       
   447             # Create contextual menu
   302             menu = wx.Menu(title='')
   448             menu = wx.Menu(title='')
   303             
   449             
   304             new_id = wx.NewId()
   450             # Add menu items
   305             menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
   451             for text, enable, callback in [
   306             self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
   452                 (_("Force value"), True,
   307             
   453                  self.GetForceVariableMenuFunction(item)),
   308             new_id = wx.NewId()
   454                 # Release menu item is enabled only if variable is forced 
   309             menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
   455                 (_("Release value"), self.Table.IsForced(row),
   310             self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
   456                  self.GetReleaseVariableMenuFunction(iec_path))]:
   311             
   457                 
   312             if self.Table.IsForced(row):
   458                 new_id = wx.NewId()
   313                 menu.Enable(new_id, True)
   459                 menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=text)
   314             else:
   460                 menu.Enable(new_id, enable)
   315                 menu.Enable(new_id, False)
   461                 self.Bind(wx.EVT_MENU, callback, id=new_id)
   316             
   462             
       
   463             # Popup contextual menu
   317             self.PopupMenu(menu)
   464             self.PopupMenu(menu)
   318             
   465             
   319             menu.Destroy()
   466             menu.Destroy()
   320         event.Skip()
   467         event.Skip()
   321     
   468     
   322     def InsertValue(self, iec_path, idx = None, force=False):
   469     def InsertValue(self, iec_path, index=None, force=False):
       
   470         """
       
   471         Insert a new variable to debug in table
       
   472         @param iec_path: Variable path to debug
       
   473         @param index: Row where insert the variable in table (default None,
       
   474         insert at last position)
       
   475         @param force: Force insertion of variable even if not defined in
       
   476         producer side
       
   477         """
       
   478         # Return immediately if variable is already debugged
   323         for item in self.Table.GetData():
   479         for item in self.Table.GetData():
   324             if iec_path == item.GetVariable():
   480             if iec_path == item.GetVariable():
   325                 return
   481                 return
   326         if idx is None:
   482             
   327             idx = self.Table.GetNumberRows()
   483         # Insert at last position if index not defined
       
   484         if index is None:
       
   485             index = self.Table.GetNumberRows()
       
   486         
       
   487         # Subscribe variable to producer
   328         item = DebugVariableItem(self, iec_path)
   488         item = DebugVariableItem(self, iec_path)
   329         result = self.AddDataConsumer(iec_path.upper(), item)
   489         result = self.AddDataConsumer(iec_path.upper(), item)
       
   490         
       
   491         # Insert variable in table if subscription done or insertion forced
   330         if result is not None or force:
   492         if result is not None or force:
   331             self.Table.InsertItem(idx, item)
   493             self.Table.InsertItem(index, item)
   332             self.RefreshView()
   494             self.RefreshView()
   333     
   495     
   334     def ResetGraphicsValues(self):
   496     def ResetGraphicsValues(self):
       
   497         """
       
   498         Called to reset graphics values when PLC is started
       
   499         (Nothing to do because no graphic values here. Defined for
       
   500         compatibility with Debug Variable Graphic Panel)
       
   501         """
   335         pass
   502         pass
   336     
   503