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