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