Added support for displaying ToolTip, starting drag'n drop and Double click on Block connectors when debugging
authorLaurent Bessard
Thu, 23 May 2013 20:39:10 +0200
changeset 1170 074e46cdedbc
parent 1169 53e4a2b775a7
child 1171 a506e4de8f84
Added support for displaying ToolTip, starting drag'n drop and Double click on Block connectors when debugging
controls/CustomToolTip.py
controls/LogViewer.py
editors/Viewer.py
graphics/GraphicCommons.py
graphics/ToolTipProducer.py
--- a/controls/CustomToolTip.py	Thu May 23 18:47:44 2013 +0200
+++ b/controls/CustomToolTip.py	Thu May 23 20:39:10 2013 +0200
@@ -116,9 +116,9 @@
         # Prevent to call wx method in non-wx threads
         wx.CallAfter(self.RefreshTip)
     
-    def MoveToolTip(self, pos):
-        """
-        Move tool tip
+    def SetToolTipPosition(self, pos):
+        """
+        Set tool tip position
         @param pos: New tool tip position
         """
         # Get screen size to prevent tool tip to go out of the screen
--- a/controls/LogViewer.py	Thu May 23 18:47:44 2013 +0200
+++ b/controls/LogViewer.py	Thu May 23 20:39:10 2013 +0200
@@ -702,7 +702,7 @@
                 tooltip_pos.y += 10
                 self.MessageToolTip = CustomToolTip(self.MessagePanel, message.GetFullText(), False)
                 self.MessageToolTip.SetFont(self.Font)
-                self.MessageToolTip.MoveToolTip(tooltip_pos)
+                self.MessageToolTip.SetToolTipPosition(tooltip_pos)
                 self.MessageToolTip.Show()
         event.Skip()
     
--- a/editors/Viewer.py	Thu May 23 18:47:44 2013 +0200
+++ b/editors/Viewer.py	Thu May 23 20:39:10 2013 +0200
@@ -1060,7 +1060,7 @@
             self.AddDataConsumer("%s.Q" % self.InstancePath.upper(), self)
         
         if self.ToolTipElement is not None:
-            self.ToolTipElement.ClearToolTip()
+            self.ToolTipElement.DestroyToolTip()
             self.ToolTipElement = None
         
         self.Inhibit(True)
@@ -1644,15 +1644,19 @@
             self.ResetBuffer()
             element = None
             if not event.Leaving() and not event.LeftUp() and not event.LeftDClick():
-                element = self.FindElement(event, True, False)
+                dc = self.GetLogicalDC()
+                pos = event.GetLogicalPosition(dc)
+                element = self.FindBlockConnector(pos)
+                if element is None:
+                    element = self.FindElement(event, True, False)
             if self.ToolTipElement is not None:
-                self.ToolTipElement.ClearToolTip()
+                self.ToolTipElement.DestroyToolTip()
             self.ToolTipElement = element
             if self.ToolTipElement is not None:
                 tooltip_pos = self.Editor.ClientToScreen(event.GetPosition())
                 tooltip_pos.x += 10
                 tooltip_pos.y += 10
-                self.ToolTipElement.CreateToolTip(tooltip_pos)
+                self.ToolTipElement.DisplayToolTip(tooltip_pos)
         event.Skip()
     
     def OnViewerLeftDown(self, event):
@@ -1889,7 +1893,7 @@
         event.Skip()
     
     def OnViewerLeftDClick(self, event):
-        element = self.FindElement(event, connectors=False)
+        element = self.FindElement(event)
         if self.Mode == MODE_SELECTION and element is not None:
             if self.SelectedElement is not None and self.SelectedElement != element:
                 self.SelectedElement.SetSelected(False)
@@ -1902,15 +1906,24 @@
             
             if self.Debug:
                 if isinstance(self.SelectedElement, FBD_Block):
-                    instance_type = self.SelectedElement.GetType()
-                    pou_type = {
-                        "program": ITEM_PROGRAM,
-                        "functionBlock": ITEM_FUNCTIONBLOCK,
-                    }.get(self.Controler.GetPouType(instance_type))
-                    if pou_type is not None and instance_type in self.Controler.GetProjectPouNames(self.Debug):
-                        self.ParentWindow.OpenDebugViewer(pou_type, 
-                            "%s.%s"%(self.GetInstancePath(True), self.SelectedElement.GetName()),
-                            self.Controler.ComputePouName(instance_type))
+                    dc = self.GetLogicalDC()
+                    pos = event.GetLogicalPosition(dc)
+                    connector = self.SelectedElement.TestConnector(pos, EAST)
+                    if connector is not None and len(connector.GetWires()) == 0:
+                        iec_path = self.GetElementIECPath(connector)
+                        if iec_path is not None:
+                            self.ParentWindow.OpenDebugViewer(
+                                ITEM_VAR_LOCAL, iec_path, connector.GetType())
+                    else:
+                        instance_type = self.SelectedElement.GetType()
+                        pou_type = {
+                            "program": ITEM_PROGRAM,
+                            "functionBlock": ITEM_FUNCTIONBLOCK,
+                        }.get(self.Controler.GetPouType(instance_type))
+                        if pou_type is not None and instance_type in self.Controler.GetProjectPouNames(self.Debug):
+                            self.ParentWindow.OpenDebugViewer(pou_type, 
+                                "%s.%s"%(self.GetInstancePath(True), self.SelectedElement.GetName()),
+                                self.Controler.ComputePouName(instance_type))
                 else:
                     iec_path = self.GetElementIECPath(self.SelectedElement)
                     if iec_path is not None:
@@ -1993,7 +2006,15 @@
             elif self.Debug and self.StartMousePos is not None and event.Dragging():
                 pos = event.GetPosition()
                 if abs(self.StartMousePos.x - pos.x) > 5 or abs(self.StartMousePos.y - pos.y) > 5:
-                    iec_path = self.GetElementIECPath(self.SelectedElement)
+                    element = self.SelectedElement
+                    if isinstance(self.SelectedElement, FBD_Block):
+                        dc = self.GetLogicalDC()
+                        connector = self.SelectedElement.TestConnector(
+                            wx.Point(dc.DeviceToLogicalX(self.StartMousePos.x), 
+                                     dc.DeviceToLogicalY(self.StartMousePos.y)))
+                        if connector is not None:
+                            element = connector
+                    iec_path = self.GetElementIECPath(element)
                     if iec_path is not None:
                         self.StartMousePos = None
                         if self.HighlightedElement is not None:
--- a/graphics/GraphicCommons.py	Thu May 23 18:47:44 2013 +0200
+++ b/graphics/GraphicCommons.py	Thu May 23 20:39:10 2013 +0200
@@ -29,7 +29,7 @@
 import datetime
 from threading import Lock,Timer
 
-from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
+from graphics.ToolTipProducer import ToolTipProducer
 
 #-------------------------------------------------------------------------------
 #                               Common constants
@@ -594,10 +594,11 @@
 Class that implements a generic graphic element
 """
 
-class Graphic_Element:
+class Graphic_Element(ToolTipProducer):
     
     # Create a new graphic element
     def __init__(self, parent, id = None):
+        ToolTipProducer.__init__(self, parent)
         self.Parent = parent
         self.Id = id
         self.oldPos = None
@@ -611,13 +612,6 @@
         self.Size = wx.Size(0, 0)
         self.BoundingBox = wx.Rect(0, 0, 0, 0)
         self.Visible = False
-        self.ToolTip = None
-        self.ToolTipPos = None
-        self.ToolTipTimer = wx.Timer(self.Parent, -1)
-        self.Parent.Bind(wx.EVT_TIMER, self.OnToolTipTimer, self.ToolTipTimer)
-    
-    def __del__(self):
-        self.ToolTipTimer.Stop()
     
     def GetDefinition(self):
         return [self.Id], []
@@ -990,36 +984,6 @@
             return movex, movey
         return 0, 0
     
-    def OnToolTipTimer(self, event):
-        value = self.GetToolTipValue()
-        if value is not None and self.ToolTipPos is not None:
-            self.ToolTip = CustomToolTip(self.Parent, value)
-            self.ToolTip.MoveToolTip(self.ToolTipPos)
-            self.ToolTip.Show()
-        
-    def GetToolTipValue(self):
-        return None
-    
-    def CreateToolTip(self, pos):
-        value = self.GetToolTipValue()
-        if value is not None:
-            self.ToolTipPos = pos
-            self.ToolTipTimer.Start(int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True)
-        
-    def MoveToolTip(self, pos):
-        if self.ToolTip is not None:
-            self.ToolTip.MoveToolTip(pos)
-        elif self.ToolTipPos is not None:
-            self.ToolTipPos = pos
-            self.ToolTipTimer.Start(int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True)
-    
-    def ClearToolTip(self):
-        self.ToolTipTimer.Stop()
-        self.ToolTipPos = None
-        if self.ToolTip is not None:
-            self.ToolTip.Destroy()
-            self.ToolTip = None
-    
     # Override this method for defining the method to call for adding an highlight to this element
     def AddHighlight(self, infos, start, end, highlight_type):
         pass
@@ -1400,11 +1364,12 @@
 Class that implements a connector for any type of block
 """
 
-class Connector(DebugDataConsumer):
+class Connector(DebugDataConsumer, ToolTipProducer):
     
     # Create a new connector
     def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False):
         DebugDataConsumer.__init__(self)
+        ToolTipProducer.__init__(self, parent.Parent)
         self.ParentBlock = parent
         self.Name = name
         self.Type = type
@@ -1526,20 +1491,28 @@
             self.Forced = forced
             if self.Visible:
                 self.Parent.ElementNeedRefresh(self)
-
+    
+    def GetComputedValue(self):
+        if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
+            wire_type = self.GetType()
+            if wire_type == "STRING":
+                return "'%s'"%self.Value
+            elif wire_type == "WSTRING":
+                return "\"%s\""%self.Value
+            else:
+                return str(self.Value)
+        return None
+    
+    def GetToolTipValue(self):
+        return self.GetComputedValue()
+    
     def SetValue(self, value):
         if self.Value != value:
             self.Value = value
-            if value is not None and not isinstance(value, BooleanType):
-                connector_type = self.GetType()
-                if connector_type == "STRING":
-                    self.ComputedValue = "'%s'"%value
-                elif connector_type == "WSTRING":
-                    self.ComputedValue = "\"%s\""%value
-                else:
-                    self.ComputedValue = str(value)
-                #if self.ToolTip is not None:
-                #    self.ToolTip.SetTip(self.ComputedValue)
+            computed_value = self.GetComputedValue()
+            if computed_value is not None:
+                self.ComputedValue = computed_value
+                self.SetToolTipText(self.ComputedValue)
                 if len(self.ComputedValue) > 4:
                     self.ComputedValue = self.ComputedValue[:4] + "..."
             self.ValueSize = None
@@ -1945,17 +1918,6 @@
         self.StartConnected = None
         self.EndConnected = None
     
-    def GetToolTipValue(self):
-        if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
-            wire_type = self.GetEndConnectedType()
-            if wire_type == "STRING":
-                return "'%s'"%self.Value
-            elif wire_type == "WSTRING":
-                return "\"%s\""%self.Value
-            else:
-                return str(self.Value)
-        return None
-    
     # Returns the RedrawRect
     def GetRedrawRect(self, movex = 0, movey = 0):
         rect = Graphic_Element.GetRedrawRect(self, movex, movey)
@@ -2113,19 +2075,27 @@
             if self.Visible:
                 self.Parent.ElementNeedRefresh(self)
 
+    def GetComputedValue(self):
+        if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType):
+            wire_type = self.GetEndConnectedType()
+            if wire_type == "STRING":
+                return "'%s'"%self.Value
+            elif wire_type == "WSTRING":
+                return "\"%s\""%self.Value
+            else:
+                return str(self.Value)
+        return None
+    
+    def GetToolTipValue(self):
+        return self.GetComputedValue()
+
     def SetValue(self, value):
         if self.Value != value:
             self.Value = value
-            if value is not None and not isinstance(value, BooleanType):
-                wire_type = self.GetEndConnectedType()
-                if wire_type == "STRING":
-                    self.ComputedValue = "'%s'"%value
-                elif wire_type == "WSTRING":
-                    self.ComputedValue = "\"%s\""%value
-                else:
-                    self.ComputedValue = str(value)
-                if self.ToolTip is not None:
-                    self.ToolTip.SetTip(self.ComputedValue)
+            computed_value = self.GetComputedValue()
+            if computed_value is not None:
+                self.ComputedValue = computed_value
+                self.SetToolTipText(self.ComputedValue)
                 if len(self.ComputedValue) > 4:
                     self.ComputedValue = self.ComputedValue[:4] + "..."
             self.ValueSize = None
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graphics/ToolTipProducer.py	Thu May 23 20:39:10 2013 +0200
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
+#based on the plcopen standard. 
+#
+#Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD
+#
+#See COPYING file for copyrights details.
+#
+#This library is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public
+#License as published by the Free Software Foundation; either
+#version 2.1 of the License, or (at your option) any later version.
+#
+#This library is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#General Public License for more details.
+#
+#You should have received a copy of the GNU General Public
+#License along with this library; if not, write to the Free Software
+#Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import wx
+
+from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD
+    
+#-------------------------------------------------------------------------------
+#                           Tool Tip Producer class
+#-------------------------------------------------------------------------------
+
+"""
+Class that implements an element that generate Tool Tip
+"""
+
+class ToolTipProducer:
+    
+    def __init__(self, parent):
+        """
+        Constructor
+        @param parent: Parent Viewer
+        """
+        self.Parent = parent
+        
+        self.ToolTip = None
+        self.ToolTipPos = None
+        
+        # Timer for firing Tool tip display
+        self.ToolTipTimer = wx.Timer(self.Parent, -1)
+        self.Parent.Bind(wx.EVT_TIMER, 
+            self.OnToolTipTimer, 
+            self.ToolTipTimer)
+    
+    def __del__(self):
+        """
+        Destructor
+        """
+        self.DestroyToolTip()
+    
+    def OnToolTipTimer(self, event):
+        """
+        Callback for Tool Tip firing timer Event
+        @param event: Tool tip text
+        """
+        # Get Tool Tip text
+        value = self.GetToolTipValue()
+        
+        if value is not None and self.ToolTipPos is not None:
+            # Create Tool Tip
+            self.ToolTip = CustomToolTip(self.Parent, value)
+            self.ToolTip.SetToolTipPosition(self.ToolTipPos)
+            self.ToolTip.Show()
+    
+    def GetToolTipValue(self):
+        """
+        Generic method that have to be overridden by derived classes 
+        @return: Tool tip text (None if not overridden) 
+        """
+        return None
+    
+    def DisplayToolTip(self, pos):
+        """
+        Display Tool tip
+        @param pos: Tool tip position
+        """
+        # Destroy current displayed Tool tip
+        self.DestroyToolTip()
+        
+        # Save Tool Tip position
+        self.ToolTipPos = pos
+        # Start Tool tip firing timer
+        self.ToolTipTimer.Start(
+            int(TOOLTIP_WAIT_PERIOD * 1000), 
+            oneShot=True)
+    
+    def SetToolTipText(self, text):
+        """
+        Set current Tool tip text
+        @param text: Tool tip Text
+        """
+        if self.ToolTip is not None:
+            self.ToolTip.SetTip(text)
+    
+    def DestroyToolTip(self):
+        """
+        Destroy current displayed Tool Tip
+        """
+        # Stop Tool tip firing timer
+        self.ToolTipTimer.Stop()
+        self.ToolTipPos = None
+        
+        # Destroy Tool Tip
+        if self.ToolTip is not None:
+            self.ToolTip.Destroy()
+            self.ToolTip = None