--- a/controls/DebugVariablePanel/DebugVariableTablePanel.py Fri May 31 14:22:15 2013 +0200
+++ b/controls/DebugVariablePanel/DebugVariableTablePanel.py Fri May 31 18:32:51 2013 +0200
@@ -35,9 +35,22 @@
from DebugVariableItem import DebugVariableItem
def GetDebugVariablesTableColnames():
+ """
+ Function returning table column header labels
+ @return: List of labels [col_label,...]
+ """
_ = lambda x : x
return [_("Variable"), _("Value")]
+#-------------------------------------------------------------------------------
+# Debug Variable Table Panel
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements a custom table storing value to display in Debug Variable
+Table Panel grid
+"""
+
class DebugVariableTable(CustomTable):
def GetValue(self, row, col):
@@ -69,11 +82,6 @@
return self.data[row].IsForced()
return False
- def IsNumVariable(self, row):
- if row < self.GetNumberRows():
- return self.data[row].IsNumVariable()
- return False
-
def _updateColAttrs(self, grid):
"""
wx.grid.Grid -> update the column attributes to add the
@@ -92,15 +100,26 @@
grid.SetCellTextColour(row, col, wx.BLACK)
grid.SetReadOnly(row, col, True)
self.ResizeRow(grid, row)
-
- def AppendItem(self, data):
- self.data.append(data)
-
- def InsertItem(self, idx, data):
- self.data.insert(idx, data)
-
- def RemoveItem(self, idx):
- self.data.pop(idx)
+
+ def RefreshValues(self, grid):
+ for col in xrange(self.GetNumberCols()):
+ colname = self.GetColLabelValue(col, False)
+ if colname == "Value":
+ for row in xrange(self.GetNumberRows()):
+ grid.SetCellValue(row, col, str(self.data[row].GetValue()))
+ if self.IsForced(row):
+ grid.SetCellTextColour(row, col, wx.BLUE)
+ else:
+ grid.SetCellTextColour(row, col, wx.BLACK)
+
+ def AppendItem(self, item):
+ self.data.append(item)
+
+ def InsertItem(self, idx, item):
+ self.data.insert(idx, item)
+
+ def RemoveItem(self, item):
+ self.data.remove(item)
def MoveItem(self, idx, new_idx):
self.data.insert(new_idx, self.data.pop(idx))
@@ -108,52 +127,109 @@
def GetItem(self, idx):
return self.data[idx]
+
+#-------------------------------------------------------------------------------
+# Debug Variable Table Panel Drop Target
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements a custom drop target class for Debug Variable Table Panel
+"""
+
class DebugVariableTableDropTarget(wx.TextDropTarget):
def __init__(self, parent):
+ """
+ Constructor
+ @param window: Reference to the Debug Variable Panel
+ """
wx.TextDropTarget.__init__(self)
self.ParentWindow = parent
def __del__(self):
+ """
+ Destructor
+ """
+ # Remove reference to Debug Variable Panel
self.ParentWindow = None
def OnDropText(self, x, y, data):
+ """
+ Function called when mouse is dragged over Drop Target
+ @param x: X coordinate of mouse pointer
+ @param y: Y coordinate of mouse pointer
+ @param data: Text associated to drag'n drop
+ """
message = None
try:
values = eval(data)
+ if not isinstance(values, TupleType):
+ raise ValueError
except:
- message = _("Invalid value \"%s\" for debug variable")%data
- values = None
- if not isinstance(values, TupleType):
- message = _("Invalid value \"%s\" for debug variable")%data
+ message = _("Invalid value \"%s\" for debug variable") % data
values = None
if message is not None:
wx.CallAfter(self.ShowMessage, message)
- elif values is not None and values[1] == "debug":
+
+ elif values[1] == "debug":
grid = self.ParentWindow.VariablesGrid
+
+ # Get row where variable was dropped
x, y = grid.CalcUnscrolledPosition(x, y)
row = grid.YToRow(y - grid.GetColLabelSize())
+
+ # If no row found add variable at table end
if row == wx.NOT_FOUND:
row = self.ParentWindow.Table.GetNumberRows()
+
+ # Add variable to table
self.ParentWindow.InsertValue(values[0], row, force=True)
def ShowMessage(self, message):
- dialog = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR)
+ """
+ Show error message in Error Dialog
+ @param message: Error message to display
+ """
+ dialog = wx.MessageDialog(self.ParentWindow,
+ message,
+ _("Error"),
+ wx.OK|wx.ICON_ERROR)
dialog.ShowModal()
dialog.Destroy()
-
+
+
+#-------------------------------------------------------------------------------
+# Debug Variable Table Panel
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements a panel displaying debug variable values in a table
+"""
+
class DebugVariableTablePanel(wx.Panel, DebugViewer):
def __init__(self, parent, producer, window):
+ """
+ Constructor
+ @param parent: Reference to the parent wx.Window
+ @param producer: Object receiving debug value and dispatching them to
+ consumers
+ @param window: Reference to Beremiz frame
+ """
wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL)
+ # Save Reference to Beremiz frame
self.ParentWindow = window
+ # Variable storing flag indicating that variable displayed in table
+ # received new value and then table need to be refreshed
self.HasNewData = False
DebugViewer.__init__(self, producer, True)
+ # Construction of window layout by creating controls and sizers
+
main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0)
main_sizer.AddGrowableCol(0)
main_sizer.AddGrowableRow(1)
@@ -162,17 +238,24 @@
main_sizer.AddSizer(button_sizer, border=5,
flag=wx.ALIGN_RIGHT|wx.ALL)
+ # Creation of buttons for navigating in table
+
for name, bitmap, help in [
("DeleteButton", "remove_element", _("Remove debug variable")),
("UpButton", "up", _("Move debug variable up")),
("DownButton", "down", _("Move debug variable down"))]:
- button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
+ button = wx.lib.buttons.GenBitmapButton(self,
+ bitmap=GetBitmap(bitmap),
size=wx.Size(28, 28), style=wx.NO_BORDER)
button.SetToolTipString(help)
setattr(self, name, button)
button_sizer.AddWindow(button, border=5, flag=wx.LEFT)
- self.VariablesGrid = CustomGrid(self, size=wx.Size(-1, 150), style=wx.VSCROLL)
+ # Creation of grid and associated table
+
+ self.VariablesGrid = CustomGrid(self,
+ size=wx.Size(-1, 150), style=wx.VSCROLL)
+ # Define grid drop target
self.VariablesGrid.SetDropTarget(DebugVariableTableDropTarget(self))
self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK,
self.OnVariablesGridCellRightClick)
@@ -180,12 +263,15 @@
self.OnVariablesGridCellLeftClick)
main_sizer.AddWindow(self.VariablesGrid, flag=wx.GROW)
- self.Table = DebugVariableTable(self, [], GetDebugVariablesTableColnames())
+ self.Table = DebugVariableTable(self, [],
+ GetDebugVariablesTableColnames())
self.VariablesGrid.SetTable(self.Table)
self.VariablesGrid.SetButtons({"Delete": self.DeleteButton,
"Up": self.UpButton,
"Down": self.DownButton})
-
+
+ # Definition of function associated to navigation buttons
+
def _AddVariable(new_row):
return self.VariablesGrid.GetGridCursorRow()
setattr(self.VariablesGrid, "_AddRow", _AddVariable)
@@ -193,7 +279,7 @@
def _DeleteVariable(row):
item = self.Table.GetItem(row)
self.RemoveDataConsumer(item)
- self.Table.RemoveItem(row)
+ self.Table.RemoveItem(item)
self.RefreshView()
setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable)
@@ -205,6 +291,8 @@
return new_row
setattr(self.VariablesGrid, "_MoveRow", _MoveVariable)
+ # Initialization of grid layout
+
self.VariablesGrid.SetRowLabelSize(0)
self.GridColSizes = [200, 100]
@@ -221,35 +309,64 @@
self.SetSizer(main_sizer)
def RefreshNewData(self, *args, **kwargs):
+ """
+ Called to refresh Table according to values received by variables
+ Can receive any parameters (not used here)
+ """
+ # Refresh 'Value' column of table if new data have been received since
+ # last refresh
if self.HasNewData:
self.HasNewData = False
self.RefreshView(only_values=True)
DebugViewer.RefreshNewData(self, *args, **kwargs)
def RefreshView(self, only_values=False):
+ """
+ Function refreshing table layout and values
+ @param only_values: True if only 'Value' column need to be updated
+ """
+ # Block refresh until table layout and values are completely updated
self.Freeze()
+ # Update only 'value' column from table
if only_values:
- for col in xrange(self.Table.GetNumberCols()):
- if self.Table.GetColLabelValue(col, False) == "Value":
- for row in xrange(self.Table.GetNumberRows()):
- self.VariablesGrid.SetCellValue(row, col, str(self.Table.GetValueByName(row, "Value")))
- if self.Table.IsForced(row):
- self.VariablesGrid.SetCellTextColour(row, col, wx.BLUE)
- else:
- self.VariablesGrid.SetCellTextColour(row, col, wx.BLACK)
+ self.Table.RefreshValues(self.VariablesGrid)
+
+ # Update complete table layout refreshing table navigation buttons
+ # state according to
else:
self.Table.ResetView(self.VariablesGrid)
+ self.VariablesGrid.RefreshButtons()
+
+ self.Thaw()
+
+ def ResetView(self):
+ """
+ Function removing all variables denugged from table
+ @param only_values: True if only 'Value' column need to be updated
+ """
+ # Unsubscribe all variables debugged
+ self.UnsubscribeAllDataConsumers()
+
+ # Clear table content
+ self.Table.Empty()
+
+ # Update table layout
+ self.Freeze()
+ self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
-
self.Thaw()
-
- def UnsubscribeObsoleteData(self):
- self.SubscribeAllDataConsumers()
-
- items = [(idx, item) for idx, item in enumerate(self.Table.GetData())]
- items.reverse()
- for idx, item in items:
+
+ def SubscribeAllDataConsumers(self):
+ """
+ Function refreshing table layout and values
+ @param only_values: True if only 'Value' column need to be updated
+ """
+ DebugViewer.SubscribeAllDataConsumers(self)
+
+ # Navigate through variable displayed in table, removing those that
+ # doesn't exist anymore in PLC
+ for item in self.Table.GetData()[:]:
iec_path = item.GetVariable()
if self.GetDataType(iec_path) is None:
self.RemoveDataConsumer(item)
@@ -257,80 +374,130 @@
else:
self.AddDataConsumer(iec_path.upper(), item)
item.RefreshVariableType()
+
+ # Update table layout
self.Freeze()
self.Table.ResetView(self.VariablesGrid)
self.VariablesGrid.RefreshButtons()
self.Thaw()
- def ResetView(self):
- self.UnsubscribeAllDataConsumers()
-
- self.Table.Empty()
- self.Freeze()
- self.Table.ResetView(self.VariablesGrid)
- self.VariablesGrid.RefreshButtons()
- self.Thaw()
-
- def GetForceVariableMenuFunction(self, iec_path, item):
- iec_type = self.GetDataType(iec_path)
+ def GetForceVariableMenuFunction(self, item):
+ """
+ Function returning callback function for contextual menu 'Force' item
+ @param item: Debug Variable item where contextual menu was opened
+ @return: Callback function
+ """
def ForceVariableFunction(event):
- if iec_type is not None:
- dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
- if dialog.ShowModal() == wx.ID_OK:
- self.ForceDataValue(iec_path.upper(), dialog.GetValue())
+ # Get variable path and data type
+ iec_path = item.GetVariable()
+ iec_type = self.GetDataType(iec_path)
+
+ # Return immediately if not data type found
+ if iec_type is None:
+ return
+
+ # Open dialog for entering value to force variable
+ dialog = ForceVariableDialog(self, iec_type, str(item.GetValue()))
+
+ # If valid value entered, force variable
+ if dialog.ShowModal() == wx.ID_OK:
+ self.ForceDataValue(iec_path.upper(), dialog.GetValue())
+
return ForceVariableFunction
def GetReleaseVariableMenuFunction(self, iec_path):
+ """
+ Function returning callback function for contextual menu 'Release' item
+ @param iec_path: Debug Variable path where contextual menu was opened
+ @return: Callback function
+ """
def ReleaseVariableFunction(event):
+ # Release variable
self.ReleaseDataValue(iec_path)
return ReleaseVariableFunction
def OnVariablesGridCellLeftClick(self, event):
- if event.GetCol() == 0:
- row = event.GetRow()
- data = wx.TextDataObject(str((self.Table.GetValueByName(row, "Variable"), "debug")))
+ """
+ Called when left mouse button is pressed on a table cell
+ @param event: wx.grid.GridEvent
+ """
+ # Initiate a drag and drop if the cell clicked was in 'Variable' column
+ if self.Table.GetColLabelValue(event.GetCol(), False) == "Variable":
+ item = self.Table.GetItem(event.GetRow())
+ data = wx.TextDataObject(str((item.GetVariable(), "debug")))
dragSource = wx.DropSource(self.VariablesGrid)
dragSource.SetData(data)
dragSource.DoDragDrop()
+
event.Skip()
def OnVariablesGridCellRightClick(self, event):
- row, col = event.GetRow(), event.GetCol()
- if self.Table.GetColLabelValue(col, False) == "Value":
- iec_path = self.Table.GetValueByName(row, "Variable").upper()
-
+ """
+ Called when right mouse button is pressed on a table cell
+ @param event: wx.grid.GridEvent
+ """
+ # Open a contextual menu if the cell clicked was in 'Value' column
+ if self.Table.GetColLabelValue(event.GetCol(), False) == "Value":
+ row = event.GetRow()
+
+ # Get variable path
+ item = self.Table.GetItem(row)
+ iec_path = item.GetVariable().upper()
+
+ # Create contextual menu
menu = wx.Menu(title='')
- new_id = wx.NewId()
- menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Force value"))
- self.Bind(wx.EVT_MENU, self.GetForceVariableMenuFunction(iec_path.upper(), self.Table.GetItem(row)), id=new_id)
-
- new_id = wx.NewId()
- menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Release value"))
- self.Bind(wx.EVT_MENU, self.GetReleaseVariableMenuFunction(iec_path.upper()), id=new_id)
-
- if self.Table.IsForced(row):
- menu.Enable(new_id, True)
- else:
- menu.Enable(new_id, False)
-
+ # Add menu items
+ for text, enable, callback in [
+ (_("Force value"), True,
+ self.GetForceVariableMenuFunction(item)),
+ # Release menu item is enabled only if variable is forced
+ (_("Release value"), self.Table.IsForced(row),
+ self.GetReleaseVariableMenuFunction(iec_path))]:
+
+ new_id = wx.NewId()
+ menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=text)
+ menu.Enable(new_id, enable)
+ self.Bind(wx.EVT_MENU, callback, id=new_id)
+
+ # Popup contextual menu
self.PopupMenu(menu)
menu.Destroy()
event.Skip()
- def InsertValue(self, iec_path, idx = None, force=False):
+ def InsertValue(self, iec_path, index=None, force=False):
+ """
+ Insert a new variable to debug in table
+ @param iec_path: Variable path to debug
+ @param index: Row where insert the variable in table (default None,
+ insert at last position)
+ @param force: Force insertion of variable even if not defined in
+ producer side
+ """
+ # Return immediately if variable is already debugged
for item in self.Table.GetData():
if iec_path == item.GetVariable():
return
- if idx is None:
- idx = self.Table.GetNumberRows()
+
+ # Insert at last position if index not defined
+ if index is None:
+ index = self.Table.GetNumberRows()
+
+ # Subscribe variable to producer
item = DebugVariableItem(self, iec_path)
result = self.AddDataConsumer(iec_path.upper(), item)
+
+ # Insert variable in table if subscription done or insertion forced
if result is not None or force:
- self.Table.InsertItem(idx, item)
+ self.Table.InsertItem(index, item)
self.RefreshView()
def ResetGraphicsValues(self):
+ """
+ Called to reset graphics values when PLC is started
+ (Nothing to do because no graphic values here. Defined for
+ compatibility with Debug Variable Graphic Panel)
+ """
pass
\ No newline at end of file