Laurent@814: #!/usr/bin/env python Laurent@814: # -*- coding: utf-8 -*- Laurent@814: andrej@1571: # This file is part of Beremiz, a Integrated Development Environment for andrej@1571: # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. andrej@1571: # andrej@1571: # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD andrej@1571: # andrej@1571: # See COPYING file for copyrights details. andrej@1571: # andrej@1571: # This program is free software; you can redistribute it and/or andrej@1571: # modify it under the terms of the GNU General Public License andrej@1571: # as published by the Free Software Foundation; either version 2 andrej@1571: # of the License, or (at your option) any later version. andrej@1571: # andrej@1571: # This program is distributed in the hope that it will be useful, andrej@1571: # but WITHOUT ANY WARRANTY; without even the implied warranty of andrej@1571: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the andrej@1571: # GNU General Public License for more details. andrej@1571: # andrej@1571: # You should have received a copy of the GNU General Public License andrej@1571: # along with this program; if not, write to the Free Software andrej@1571: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Laurent@814: Laurent@1198: from types import TupleType Laurent@814: Laurent@814: import wx Laurent@814: Laurent@1199: from DebugVariableItem import DebugVariableItem Laurent@1200: from DebugVariableViewer import DebugVariableViewer Laurent@1199: from GraphButton import GraphButton Laurent@1199: Laurent@1200: #------------------------------------------------------------------------------- Laurent@1200: # Debug Variable Text Viewer Drop Target Laurent@1200: #------------------------------------------------------------------------------- Laurent@1200: Laurent@1200: Laurent@1200: class DebugVariableTextDropTarget(wx.TextDropTarget): andrej@1736: """ andrej@1736: Class that implements a custom drop target class for Debug Variable Text Viewer andrej@1736: """ andrej@1730: Laurent@1200: def __init__(self, parent, window): Laurent@1200: """ Laurent@1200: Constructor Laurent@1200: @param parent: Reference to Debug Variable Text Viewer Laurent@1200: @param window: Reference to the Debug Variable Panel Laurent@1200: """ Laurent@814: wx.TextDropTarget.__init__(self) Laurent@1200: self.ParentControl = parent Laurent@1200: self.ParentWindow = window andrej@1730: Laurent@916: def __del__(self): Laurent@1200: """ Laurent@1200: Destructor Laurent@1200: """ Laurent@1200: # Remove reference to Debug Variable Text Viewer and Debug Variable Laurent@1200: # Panel Laurent@1200: self.ParentControl = None Laurent@916: self.ParentWindow = None andrej@1730: Laurent@928: def OnDragOver(self, x, y, d): Laurent@1200: """ Laurent@1200: Function called when mouse is dragged over Drop Target Laurent@1200: @param x: X coordinate of mouse pointer Laurent@1200: @param y: Y coordinate of mouse pointer Laurent@1200: @param d: Suggested default for return value Laurent@1200: """ Laurent@1200: # Signal parent that mouse is dragged over Laurent@1200: self.ParentControl.OnMouseDragging(x, y) andrej@1730: Laurent@928: return wx.TextDropTarget.OnDragOver(self, x, y, d) andrej@1730: Laurent@814: def OnDropText(self, x, y, data): Laurent@1200: """ Laurent@1215: Function called when mouse is released in Drop Target Laurent@1200: @param x: X coordinate of mouse pointer Laurent@1200: @param y: Y coordinate of mouse pointer Laurent@1200: @param data: Text associated to drag'n drop Laurent@1200: """ Laurent@1218: # Signal Debug Variable Panel to reset highlight Laurent@1218: self.ParentWindow.ResetHighlight() andrej@1730: Laurent@814: message = None andrej@1730: Laurent@1200: # Check that data is valid regarding DebugVariablePanel Laurent@814: try: Laurent@814: values = eval(data) Laurent@1200: if not isinstance(values, TupleType): Laurent@1207: raise ValueError Laurent@814: except: Laurent@1200: message = _("Invalid value \"%s\" for debug variable") % data Laurent@814: values = None andrej@1730: Laurent@1200: # Display message if data is invalid Laurent@814: if message is not None: Laurent@814: wx.CallAfter(self.ShowMessage, message) andrej@1730: Laurent@1200: # Data contain a reference to a variable to debug Laurent@1200: elif values[1] == "debug": andrej@1730: Laurent@1200: # Get Before which Viewer the variable has to be moved or added Laurent@1200: # according to the position of mouse in Viewer. Laurent@1200: width, height = self.ParentControl.GetSize() Laurent@1200: target_idx = self.ParentControl.GetIndex() Laurent@1200: if y > height / 2: Laurent@1200: target_idx += 1 andrej@1730: Laurent@1200: # Drag'n Drop is an internal is an internal move inside Debug andrej@1730: # Variable Panel Laurent@1200: if len(values) > 2 and values[2] == "move": andrej@1730: self.ParentWindow.MoveValue(values[0], Laurent@1200: target_idx) andrej@1730: Laurent@1200: # Drag'n Drop was initiated by another control of Beremiz Laurent@945: else: andrej@1730: self.ParentWindow.InsertValue(values[0], andrej@1730: target_idx, Laurent@1200: force=True) andrej@1730: Laurent@928: def OnLeave(self): Laurent@1200: """ Laurent@1200: Function called when mouse is leave Drop Target Laurent@1200: """ Laurent@1200: # Signal Debug Variable Panel to reset highlight Laurent@1198: self.ParentWindow.ResetHighlight() andrej@1730: Laurent@928: return wx.TextDropTarget.OnLeave(self) andrej@1730: Laurent@814: def ShowMessage(self, message): Laurent@1200: """ Laurent@1200: Show error message in Error Dialog Laurent@1200: @param message: Error message to display Laurent@1200: """ andrej@1730: dialog = wx.MessageDialog(self.ParentWindow, andrej@1730: message, andrej@1730: _("Error"), Laurent@1200: wx.OK|wx.ICON_ERROR) Laurent@814: dialog.ShowModal() Laurent@814: dialog.Destroy() Laurent@814: Laurent@1200: Laurent@1200: #------------------------------------------------------------------------------- Laurent@1200: # Debug Variable Text Viewer Class Laurent@1200: #------------------------------------------------------------------------------- Laurent@1200: Laurent@1200: class DebugVariableTextViewer(DebugVariableViewer, wx.Panel): andrej@1736: """ andrej@1736: Class that implements a Viewer that display variable values as a text andrej@1736: """ andrej@1730: Laurent@1198: def __init__(self, parent, window, items=[]): Laurent@1200: """ Laurent@1200: Constructor Laurent@1200: @param parent: Parent wx.Window of DebugVariableText Laurent@1200: @param window: Reference to the Debug Variable Panel Laurent@1200: @param items: List of DebugVariableItem displayed by Viewer Laurent@1200: """ Laurent@1198: DebugVariableViewer.__init__(self, window, items) andrej@1730: Laurent@1198: wx.Panel.__init__(self, parent) Laurent@1200: # Set panel background colour Laurent@1198: self.SetBackgroundColour(wx.WHITE) Laurent@1200: # Define panel drop target Laurent@1200: self.SetDropTarget(DebugVariableTextDropTarget(self, window)) andrej@1730: Laurent@1200: # Bind events Laurent@1198: self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) Laurent@1198: self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) Laurent@1214: self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) Laurent@1198: self.Bind(wx.EVT_ENTER_WINDOW, self.OnEnter) Laurent@1198: self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) Laurent@1198: self.Bind(wx.EVT_SIZE, self.OnResize) Laurent@1198: self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) Laurent@1198: self.Bind(wx.EVT_PAINT, self.OnPaint) andrej@1730: Laurent@1200: # Define panel min size for parent sizer layout Laurent@1198: self.SetMinSize(wx.Size(0, 25)) andrej@1730: Laurent@1200: # Add buttons to Viewer Laurent@1199: for bitmap, callback in [("force", self.OnForceButton), Laurent@1199: ("release", self.OnReleaseButton), Laurent@1199: ("delete_graph", self.OnCloseButton)]: Laurent@1199: self.Buttons.append(GraphButton(0, 0, bitmap, callback)) andrej@1730: Laurent@1198: def RefreshViewer(self): Laurent@1200: """ Laurent@1200: Method that refresh the content displayed by Viewer Laurent@1200: """ Laurent@1200: # Create buffered DC for drawing in panel Laurent@1198: width, height = self.GetSize() Laurent@1198: bitmap = wx.EmptyBitmap(width, height) Laurent@1198: dc = wx.BufferedDC(wx.ClientDC(self), bitmap) Laurent@1198: dc.Clear() andrej@1730: Laurent@1200: # Get Graphics Context for DC, for anti-aliased and transparent Laurent@1200: # rendering Laurent@1198: gc = wx.GCDC(dc) andrej@1730: Laurent@1209: gc.BeginDrawing() andrej@1730: Laurent@1200: # Get first item Laurent@1200: item = self.ItemsDict.values()[0] andrej@1730: Laurent@1200: # Get item variable path masked according Debug Variable Panel mask Laurent@1200: item_path = item.GetVariable( Laurent@1200: self.ParentWindow.GetVariableNameMask()) andrej@1730: Laurent@1200: # Draw item variable path at Viewer left side Laurent@1200: w, h = gc.GetTextExtent(item_path) Laurent@1200: gc.DrawText(item_path, 20, (height - h) / 2) andrej@1730: Laurent@1200: # Update 'Release' button state and text color according to item forced Laurent@1200: # flag value Laurent@1200: item_forced = item.IsForced() Laurent@1200: self.Buttons[1].Enable(item_forced) Laurent@1200: self.RefreshButtonsPosition() Laurent@1200: if item_forced: Laurent@1198: gc.SetTextForeground(wx.BLUE) andrej@1730: Laurent@1200: # Draw item current value at right side of Viewer Laurent@1200: item_value = item.GetValue() Laurent@1198: w, h = gc.GetTextExtent(item_value) Laurent@1198: gc.DrawText(item_value, width - 40 - w, (height - h) / 2) andrej@1730: Laurent@1200: # Draw other Viewer common elements Laurent@1198: self.DrawCommonElements(gc) andrej@1730: Laurent@1198: gc.EndDrawing() andrej@1730: Laurent@1198: def OnLeftDown(self, event): Laurent@1200: """ Laurent@1200: Function called when mouse left button is pressed Laurent@1200: @param event: wx.MouseEvent Laurent@1200: """ Laurent@1200: # Get first item Laurent@1200: item = self.ItemsDict.values()[0] andrej@1730: Laurent@1200: # Calculate item path bounding box Laurent@1198: width, height = self.GetSize() Laurent@1200: item_path = item.GetVariable( Laurent@1200: self.ParentWindow.GetVariableNameMask()) Laurent@1200: w, h = self.GetTextExtent(item_path) andrej@1730: Laurent@1200: # Test if mouse has been pressed in this bounding box. In that case andrej@1730: # start a move drag'n drop of item variable Laurent@1198: x, y = event.GetPosition() Laurent@1200: item_path_bbox = wx.Rect(20, (height - h) / 2, w, h) Laurent@1200: if item_path_bbox.InsideXY(x, y): Laurent@1214: self.ShowButtons(False) Laurent@1200: data = wx.TextDataObject(str((item.GetVariable(), "debug", "move"))) Laurent@1198: dragSource = wx.DropSource(self) Laurent@1198: dragSource.SetData(data) Laurent@1198: dragSource.DoDragDrop() andrej@1730: Laurent@1200: # In other case handle event normally Laurent@1198: else: Laurent@1198: event.Skip() andrej@1730: Laurent@1198: def OnLeftUp(self, event): Laurent@1200: """ Laurent@1200: Function called when mouse left button is released Laurent@1200: @param event: wx.MouseEvent Laurent@1200: """ Laurent@1200: # Execute callback on button under mouse pointer if it exists Laurent@1198: x, y = event.GetPosition() Laurent@1200: wx.CallAfter(self.HandleButton, x, y) Laurent@1198: event.Skip() andrej@1730: Laurent@1214: def OnLeftDClick(self, event): Laurent@1214: """ Laurent@1214: Function called when mouse left button is double clicked Laurent@1214: @param event: wx.MouseEvent Laurent@1214: """ Laurent@1214: # Only numeric variables can be toggled to graph canvas Laurent@1214: if self.ItemsDict.values()[0].IsNumVariable(): Laurent@1214: self.ParentWindow.ToggleViewerType(self) andrej@1730: Laurent@1198: def OnPaint(self, event): Laurent@1200: """ Laurent@1200: Function called when redrawing Viewer content is needed Laurent@1200: @param event: wx.PaintEvent Laurent@1200: """ Laurent@1198: self.RefreshViewer() Laurent@1198: event.Skip()