controls/DebugVariablePanel.py
changeset 711 5f6a743dcde5
child 714 131ea7f237b9
equal deleted inserted replaced
710:365bb7496697 711:5f6a743dcde5
       
     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 import wx
       
    26 
       
    27 from graphics import DebugDataConsumer, DebugViewer
       
    28 from controls import CustomGrid, CustomTable
       
    29 
       
    30 def GetDebugVariablesTableColnames():
       
    31     _ = lambda x : x
       
    32     return [_("Variable"), _("Value")]
       
    33 
       
    34 class VariableTableItem(DebugDataConsumer):
       
    35     
       
    36     def __init__(self, parent, variable, value):
       
    37         DebugDataConsumer.__init__(self)
       
    38         self.Parent = parent
       
    39         self.Variable = variable
       
    40         self.Value = value
       
    41     
       
    42     def __del__(self):
       
    43         self.Parent = None
       
    44     
       
    45     def SetVariable(self, variable):
       
    46         if self.Parent and self.Variable != variable:
       
    47             self.Variable = variable
       
    48             self.Parent.RefreshGrid()
       
    49     
       
    50     def GetVariable(self):
       
    51         return self.Variable
       
    52     
       
    53     def SetForced(self, forced):
       
    54         if self.Forced != forced:
       
    55             self.Forced = forced
       
    56             self.Parent.HasNewData = True
       
    57     
       
    58     def SetValue(self, value):
       
    59         if self.Value != value:
       
    60             self.Value = value
       
    61             self.Parent.HasNewData = True
       
    62             
       
    63     def GetValue(self):
       
    64         return self.Value
       
    65 
       
    66 class DebugVariableTable(CustomTable):
       
    67     
       
    68     def GetValue(self, row, col):
       
    69         if row < self.GetNumberRows():
       
    70             return self.GetValueByName(row, self.GetColLabelValue(col, False))
       
    71         return ""
       
    72     
       
    73     def SetValue(self, row, col, value):
       
    74         if col < len(self.colnames):
       
    75             self.SetValueByName(row, self.GetColLabelValue(col, False), value)
       
    76             
       
    77     def GetValueByName(self, row, colname):
       
    78         if row < self.GetNumberRows():
       
    79             if colname == "Variable":
       
    80                 return self.data[row].GetVariable()
       
    81             elif colname == "Value":
       
    82                 return self.data[row].GetValue()
       
    83         return ""
       
    84 
       
    85     def SetValueByName(self, row, colname, value):
       
    86         if row < self.GetNumberRows():
       
    87             if colname == "Variable":
       
    88                 self.data[row].SetVariable(value)
       
    89             elif colname == "Value":
       
    90                 self.data[row].SetValue(value)
       
    91     
       
    92     def IsForced(self, row):
       
    93         if row < self.GetNumberRows():
       
    94             return self.data[row].IsForced()
       
    95         return False
       
    96     
       
    97     def _updateColAttrs(self, grid):
       
    98         """
       
    99         wx.grid.Grid -> update the column attributes to add the
       
   100         appropriate renderer given the column name.
       
   101 
       
   102         Otherwise default to the default renderer.
       
   103         """
       
   104         
       
   105         for row in range(self.GetNumberRows()):
       
   106             for col in range(self.GetNumberCols()):
       
   107                 if self.GetColLabelValue(col, False) == "Value":
       
   108                     if self.IsForced(row):
       
   109                         grid.SetCellTextColour(row, col, wx.BLUE)
       
   110                     else:
       
   111                         grid.SetCellTextColour(row, col, wx.BLACK)
       
   112                 grid.SetReadOnly(row, col, True)
       
   113             self.ResizeRow(grid, row)
       
   114                 
       
   115     def AppendItem(self, data):
       
   116         self.data.append(data)
       
   117     
       
   118     def InsertItem(self, idx, data):
       
   119         self.data.insert(idx, data)
       
   120     
       
   121     def RemoveItem(self, idx):
       
   122         self.data.pop(idx)
       
   123     
       
   124     def MoveItem(self, idx, new_idx):
       
   125         self.data.insert(new_idx, self.data.pop(idx))
       
   126         
       
   127     def GetItem(self, idx):
       
   128         return self.data[idx]
       
   129 
       
   130 class DebugVariableDropTarget(wx.TextDropTarget):
       
   131     
       
   132     def __init__(self, parent):
       
   133         wx.TextDropTarget.__init__(self)
       
   134         self.ParentWindow = parent
       
   135     
       
   136     def OnDropText(self, x, y, data):
       
   137         x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y)
       
   138         row = self.ParentWindow.VariablesGrid.YToRow(y - self.ParentWindow.VariablesGrid.GetColLabelSize())
       
   139         if row == wx.NOT_FOUND:
       
   140             row = self.ParentWindow.Table.GetNumberRows()
       
   141         message = None
       
   142         try:
       
   143             values = eval(data)
       
   144         except:
       
   145             message = _("Invalid value \"%s\" for debug variable")%data
       
   146             values = None
       
   147         if not isinstance(values, TupleType):
       
   148             message = _("Invalid value \"%s\" for debug variable")%data
       
   149             values = None
       
   150         if values is not None and values[1] == "debug":
       
   151             self.ParentWindow.InsertValue(values[0], row)
       
   152         if message is not None:
       
   153             wx.CallAfter(self.ShowMessage, message)
       
   154             
       
   155     def ShowMessage(self, message):
       
   156         dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
       
   157         dialog.ShowModal()
       
   158         dialog.Destroy()
       
   159 
       
   160 class DebugVariablePanel(wx.Panel, DebugViewer):
       
   161     
       
   162     def __init__(self, parent, window, producer):
       
   163         wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL)
       
   164         DebugViewer.__init__(self, producer, True)
       
   165         
       
   166         main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
       
   167         main_sizer.AddGrowableCol(0)
       
   168         main_sizer.AddGrowableRow(1)
       
   169         
       
   170         button_sizer = wx.BoxSizer(wx.HORIZONTAL)
       
   171         main_sizer.AddSizer(button_sizer, border=5, 
       
   172               flag=wx.ALIGN_RIGHT|wx.ALL)
       
   173         
       
   174         up_button = wx.Button(self, label='^', size=wx.Size(28, 28))
       
   175         button_sizer.AddWindow(up_button, border=5, flag=wx.RIGHT)
       
   176         
       
   177         down_button = wx.Button(self, label='v', size=wx.Size(28, 28))
       
   178         button_sizer.AddWindow(down_button, border=5, flag=wx.RIGHT)
       
   179         
       
   180         delete_button = wx.Button(self, label=_('Delete'), size=wx.DefaultSize)
       
   181         button_sizer.AddWindow(delete_button)
       
   182         
       
   183         self.VariablesGrid = CustomGrid(self, size=wx.Size(0, 150), style=wx.VSCROLL)
       
   184         self.VariablesGrid.SetDropTarget(DebugVariableDropTarget(self))
       
   185         self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, 
       
   186               self.OnVariablesGridCellRightClick)
       
   187         main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
       
   188         
       
   189         self.SetSizer(main_sizer)
       
   190         
       
   191         self.HasNewData = False
       
   192         
       
   193         self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
       
   194         self.VariablesGrid.SetTable(self.Table)
       
   195         self.VariablesGrid.SetButtons({"Delete": delete_button,
       
   196                                        "Up": up_button,
       
   197                                        "Down": down_button})
       
   198         
       
   199         def _AddVariable(new_row):
       
   200             return self.VariablesGrid.GetGridCursorRow()
       
   201         setattr(self.VariablesGrid, "_AddRow", _AddVariable)
       
   202         
       
   203         def _DeleteVariable(row):
       
   204             item = self.Table.GetItem(row)
       
   205             self.RemoveDataConsumer(item)
       
   206             self.Table.RemoveItem(row)
       
   207             self.RefreshGrid()
       
   208         setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
       
   209         
       
   210         def _MoveVariable(row, move):
       
   211             new_row = max(0, min(row + move, self.Table.GetNumberRows() - 1))
       
   212             if new_row != row:
       
   213                 self.Table.MoveItem(row, new_row)
       
   214                 self.RefreshGrid()
       
   215             return new_row
       
   216         setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
       
   217         
       
   218         self.VariablesGrid.SetRowLabelSize(0)
       
   219         
       
   220         for col in range(self.Table.GetNumberCols()):
       
   221             attr = wx.grid.GridCellAttr()
       
   222             attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTER)
       
   223             self.VariablesGrid.SetColAttr(col, attr)
       
   224             self.VariablesGrid.SetColSize(col, 100)
       
   225         
       
   226         self.Table.ResetView(self.VariablesGrid)
       
   227         self.VariablesGrid.RefreshButtons()
       
   228     
       
   229     def RefreshNewData(self):
       
   230         if self.HasNewData:
       
   231             self.HasNewData = False
       
   232             self.RefreshGrid()
       
   233         DebugViewer.RefreshNewData(self)
       
   234     
       
   235     def RefreshGrid(self):
       
   236         self.Freeze()
       
   237         self.Table.ResetView(self.VariablesGrid)
       
   238         self.VariablesGrid.RefreshButtons()
       
   239         self.Thaw()
       
   240     
       
   241     def UnregisterObsoleteData(self):
       
   242         items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
       
   243         items.reverse()
       
   244         for idx, item in items:
       
   245             iec_path = item.GetVariable().upper()
       
   246             if self.GetDataType(iec_path) is None:
       
   247                 self.RemoveDataConsumer(item)
       
   248                 self.Table.RemoveItem(idx)
       
   249             else:
       
   250                 self.AddDataConsumer(iec_path, item)
       
   251         self.Freeze()
       
   252         self.Table.ResetView(self.VariablesGrid)
       
   253         self.VariablesGrid.RefreshButtons()
       
   254         self.Thaw()
       
   255     
       
   256     def ResetGrid(self):
       
   257         self.DeleteDataConsumers()
       
   258         self.Table.Empty()
       
   259         self.Freeze()
       
   260         self.Table.ResetView(self.VariablesGrid)
       
   261         self.VariablesGrid.RefreshButtons()
       
   262         self.Thaw()
       
   263     
       
   264     def GetForceVariableMenuFunction(self, iec_path, item):
       
   265         iec_type = self.GetDataType(iec_path)
       
   266         def ForceVariableFunction(event):
       
   267             if iec_type is not None:
       
   268                 dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
       
   269                 if dialog.ShowModal() == wx.ID_OK:
       
   270                     self.ForceDataValue(iec_path, dialog.GetValue())
       
   271         return ForceVariableFunction
       
   272 
       
   273     def GetReleaseVariableMenuFunction(self, iec_path):
       
   274         def ReleaseVariableFunction(event):
       
   275             self.ReleaseDataValue(iec_path)
       
   276         return ReleaseVariableFunction
       
   277     
       
   278     def OnVariablesGridCellRightClick(self, event):
       
   279         row, col = event.GetRow(), event.GetCol()
       
   280         if self.Table.GetColLabelValue(col, False) == "Value":
       
   281             iec_path = self.Table.GetValueByName(row, "Variable").upper()
       
   282 
       
   283             menu = wx.Menu(title='')
       
   284             
       
   285             new_id = wx.NewId()
       
   286             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
       
   287             self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
       
   288             
       
   289             new_id = wx.NewId()
       
   290             AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
       
   291             self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
       
   292             
       
   293             if self.Table.IsForced(row):
       
   294                 menu.Enable(new_id, True)
       
   295             else:
       
   296                 menu.Enable(new_id, False)
       
   297             
       
   298             self.PopupMenu(menu)
       
   299             
       
   300             menu.Destroy()
       
   301         event.Skip()
       
   302     
       
   303     def InsertValue(self, iec_path, idx = None, force=False):
       
   304         if idx is None:
       
   305             idx = self.Table.GetNumberRows()
       
   306         for item in self.Table.GetData():
       
   307             if iec_path == item.GetVariable():
       
   308                 return
       
   309         item = VariableTableItem(self, iec_path, "")
       
   310         result = self.AddDataConsumer(iec_path.upper(), item)
       
   311         if result is not None or force:
       
   312             self.Table.InsertItem(idx, item)
       
   313             self.RefreshGrid()
       
   314         
       
   315     def GetDebugVariables(self):
       
   316         return [item.GetVariable() for item in self.Table.GetData()]