Fixing ToolTip behavior and bug with INOUT interface variables in Function Blocks and adding support for display connection point between wire and connector when mouse passing over.
authorlaurent
Mon, 30 Jan 2012 16:12:19 +0100
changeset 633 3536f4469cde
parent 632 3ea55a5db68e
child 634 cc3335911c01
Fixing ToolTip behavior and bug with INOUT interface variables in Function Blocks and adding support for display connection point between wire and connector when mouse passing over.
Viewer.py
graphics/FBD_Objects.py
graphics/GraphicCommons.py
graphics/LD_Objects.py
graphics/SFC_Objects.py
plcopen/plcopen.py
--- a/Viewer.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/Viewer.py	Mon Jan 30 16:12:19 2012 +0100
@@ -539,6 +539,7 @@
         self.Editor.Bind(wx.EVT_SCROLLWIN_THUMBRELEASE, self.OnScrollStop)
         self.Editor.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheelWindow)
         self.Editor.Bind(wx.EVT_SIZE, self.OnMoveWindow)
+        self.Editor.Bind(wx.EVT_MOUSE_EVENTS, self.OnViewerMouseEvent)
     
     def __del__(self):
         DebugViewer.__del__(self)
@@ -621,6 +622,9 @@
     def GetScale(self):
         return self.CurrentScale
 
+    def GetViewScale(self):
+        return self.ViewScale
+
     def GetLogicalDC(self, buffered=False):
         if buffered:
             bitmap = wx.EmptyBitmap(*self.Editor.GetClientSize())
@@ -781,6 +785,7 @@
         self.Subscribed = {}
         self.SelectedElement = None
         self.HighlightedElement = None
+        self.ToolTipElement = None
     
     def Flush(self):
         self.DeleteDataConsumers()
@@ -1130,14 +1135,14 @@
                 return wire
         return None
     
-    def FindElement(self, event, exclude_group = False):
+    def FindElement(self, event, exclude_group = False, connectors = True):
         dc = self.GetLogicalDC()
         pos = event.GetLogicalPosition(dc)
         if self.SelectedElement and not (exclude_group and isinstance(self.SelectedElement, Graphic_Group)):
-            if self.SelectedElement.HitTest(pos) or self.SelectedElement.TestHandle(event) != (0, 0):
+            if self.SelectedElement.HitTest(pos, connectors) or self.SelectedElement.TestHandle(event) != (0, 0):
                 return self.SelectedElement
         for element in self.GetElements():
-            if element.HitTest(pos) or element.TestHandle(event) != (0, 0):
+            if element.HitTest(pos, connectors) or element.TestHandle(event) != (0, 0):
                 return element
         return None
     
@@ -1400,6 +1405,21 @@
 #                          Mouse event functions
 #-------------------------------------------------------------------------------
 
+    def OnViewerMouseEvent(self, event):
+        if not event.Entering():
+            element = None
+            if not event.Leaving():
+                element = self.FindElement(event, True, False)
+            if self.ToolTipElement is not None:
+                self.ToolTipElement.ClearToolTip()
+            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)
+        event.Skip()
+
     def OnViewerLeftDown(self, event):
         if self.Mode == MODE_SELECTION:
             dc = self.GetLogicalDC()
@@ -1466,8 +1486,11 @@
                     if self.SelectedElement is not None:
                         self.SelectedElement.SetSelected(False)
                     self.SelectedElement = wire
+                    if self.HighlightedElement is not None:
+                        self.HighlightedElement.SetHighlighted(False)
+                    self.HighlightedElement = wire
                     self.RefreshVisibleElements()
-                    self.SelectedElement.Refresh()
+                    self.SelectedElement.SetHighlighted(True)
                 else:
                     if self.SelectedElement is not None and self.SelectedElement != element:
                         self.SelectedElement.SetSelected(False)
@@ -1556,12 +1579,8 @@
                     self.SelectedElement.OnMotion(event, dc, self.Scaling)
                     self.SelectedElement.GeneratePoints()
                     self.SelectedElement.RefreshModel()
-                    if self.HighlightedElement is not None:
-                        self.HighlightedElement.SetHighlighted(False)
-                        self.HighlightedElement = None
-                    self.SelectedElement.SetHighlighted(True)
-                    self.HighlightedElement = self.SelectedElement
                     self.SelectedElement.SetSelected(True)
+                    self.SelectedElement.HighlightPoint(pos)
                     self.RefreshBuffer()
                 elif connector is None or self.SelectedElement.GetDragging():
                     self.DrawingWire = False
@@ -1666,20 +1685,15 @@
                 self.RefreshVisibleElements()
         else:
             if not event.Dragging():
-                highlighted = self.FindElement(event) 
+                highlighted = self.FindElement(event, connectors=False) 
                 if self.HighlightedElement is not None and self.HighlightedElement != highlighted:
-                    self.HighlightedElement.ClearToolTip()
                     self.HighlightedElement.SetHighlighted(False)
                     self.HighlightedElement = None
                 if highlighted is not None:
-                    tooltip_pos = self.Editor.ClientToScreen(event.GetPosition())
-                    tooltip_pos.x += 10
-                    tooltip_pos.y += 10
+                    if isinstance(highlighted, (Wire, Graphic_Group)):
+                        highlighted.HighlightPoint(pos)
                     if self.HighlightedElement != highlighted:
-                        highlighted.CreateToolTip(tooltip_pos)
                         highlighted.SetHighlighted(True)
-                    else:
-                        highlighted.MoveToolTip(tooltip_pos)
                 self.HighlightedElement = highlighted
             if self.rubberBand.IsShown():
                 self.rubberBand.OnMotion(event, dc, self.Scaling)
@@ -1692,6 +1706,8 @@
                         self.SelectedElement.GeneratePoints()
                         if movex != 0 or movey != 0:
                             self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(movex, movey)), False)
+                    else:
+                        self.SelectedElement.HighlightPoint(pos)
                 else:
                     movex, movey = self.SelectedElement.OnMotion(event, dc, self.Scaling)
                     if movex != 0 or movey != 0:
@@ -1703,8 +1719,6 @@
                     if iec_path is not None:
                         self.StartMousePos = None
                         if self.HighlightedElement is not None:
-                            if isinstance(self.HighlightedElement, Wire):
-                                self.HighlightedElement.ClearToolTip()
                             self.HighlightedElement.SetHighlighted(False)
                             self.HighlightedElement = None
                         data = wx.TextDataObject(str((iec_path, "debug")))
@@ -1720,7 +1734,6 @@
         if self.SelectedElement is not None and self.SelectedElement.GetDragging():
             event.Skip()
         elif self.HighlightedElement is not None:
-            self.HighlightedElement.ClearToolTip()
             self.HighlightedElement.SetHighlighted(False)
             self.HighlightedElement = None
         event.Skip()
--- a/graphics/FBD_Objects.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/graphics/FBD_Objects.py	Mon Jan 30 16:12:19 2012 +0100
@@ -160,11 +160,11 @@
     # Returns the block connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test each input and output connector
-            for input in self.Inputs:
-                if name == input.GetName():
-                    return input
+            #for input in self.Inputs:
+            #    if name == input.GetName():
+            #        return input
             for output in self.Outputs:
                 if name == output.GetName():
                     return output
@@ -583,10 +583,10 @@
     # Returns the block connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test input and output connector if they exists
-            if self.Input and name == self.Input.GetName():
-                return self.Input
+            #if self.Input and name == self.Input.GetName():
+            #    return self.Input
             if self.Output and name == self.Output.GetName():
                 return self.Output
         connectors = []
--- a/graphics/GraphicCommons.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/graphics/GraphicCommons.py	Mon Jan 30 16:12:19 2012 +0100
@@ -454,7 +454,7 @@
 
     # Method that erase the last box and draw the new box
     def Redraw(self, dc = None):
-        if not dc:
+        if dc is None:
             dc = self.Viewer.GetLogicalDC()
         scalex, scaley = dc.GetUserScale()
         dc.SetUserScale(1, 1)
@@ -473,7 +473,7 @@
     
     # Erase last box
     def Erase(self, dc = None):
-        if not dc:
+        if dc is None:
             dc = self.Viewer.GetLogicalDC()
         scalex, scaley = dc.GetUserScale()
         dc.SetUserScale(1, 1)
@@ -487,7 +487,7 @@
 
     # Draw current box
     def Draw(self, dc = None):
-        if not dc:
+        if dc is None:
             dc = self.Viewer.GetLogicalDC()
         scalex, scaley = dc.GetUserScale()
         dc.SetUserScale(1, 1)
@@ -523,6 +523,9 @@
               'size' : 12,
              }
 
+TOOLTIP_MAX_CHARACTERS = 30
+TOOLTIP_MAX_LINE = 5
+
 class ToolTip(wx.PopupWindow):
     
     def __init__(self, parent, tip):
@@ -536,7 +539,28 @@
         self.Bind(wx.EVT_PAINT, self.OnPaint)
         
     def SetTip(self, tip):
-        self.Tip = tip
+        lines = []
+        for line in tip.splitlines():
+            if line != "":
+                words = line.split()
+                new_line = words[0]
+                for word in words[1:]:
+                    if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS:
+                        new_line += " " + word
+                    else:
+                        lines.append(new_line)
+                        new_line = word
+                lines.append(new_line)
+            else:
+                lines.append(line)
+        if len(lines) > TOOLTIP_MAX_LINE:
+            self.Tip = lines[:TOOLTIP_MAX_LINE]
+            if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3:
+                self.Tip[-1] += "..."
+            else:
+                self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + "..."
+        else:
+            self.Tip = lines
         wx.CallAfter(self.RefreshTip)
     
     def MoveToolTip(self, pos):
@@ -546,7 +570,7 @@
     def GetTipExtent(self):
         max_width = 0
         max_height = 0
-        for line in self.Tip.splitlines():
+        for line in self.Tip:
             w, h = self.GetTextExtent(line)
             max_width = max(max_width, w)
             max_height += h
@@ -569,7 +593,7 @@
         w, h = self.GetTipExtent()
         dc.DrawRectangle(0, 0, w + 4, h + 4)
         offset = 0
-        for line in self.Tip.splitlines():
+        for line in self.Tip:
             dc.DrawText(line, 2, offset + 2)
             w, h = dc.GetTextExtent(line)
             offset += h
@@ -640,7 +664,7 @@
         self.Parent.Bind(wx.EVT_TIMER, self.OnToolTipTimer, self.ToolTipTimer)
     
     def __del__(self):
-        self
+        self.ToolTipTimer.Stop()
     
     def GetDefinition(self):
         return [self.Id], []
@@ -733,8 +757,11 @@
         return self.Id
     
     # Returns if the point given is in the bounding box
-    def HitTest(self, pt):
-        rect = self.BoundingBox
+    def HitTest(self, pt, connectors=True):
+        if connectors:
+            rect = self.BoundingBox
+        else:
+            rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
         return rect.InsideXY(pt.x, pt.y)
     
     # Returns if the point given is in the bounding box
@@ -751,8 +778,7 @@
     
     # Returns the RedrawRect
     def GetRedrawRect(self, movex = 0, movey = 0):
-        dc = self.Parent.GetLogicalDC()
-        scalex, scaley = dc.GetUserScale()
+        scalex, scaley = self.Parent.GetViewScale()
         rect = wx.Rect()
         rect.x = self.BoundingBox.x - int(HANDLE_SIZE / scalex) - 3 - abs(movex)
         rect.y = self.BoundingBox.y - int(HANDLE_SIZE / scaley) - 3 - abs(movey)
@@ -1212,10 +1238,10 @@
         self.WireExcluded = []
     
     # Returns if the point given is in the bounding box of one of the elements of this group
-    def HitTest(self, pt):
+    def HitTest(self, pt, connectors=True):
         result = False
         for element in self.Elements:
-            result |= element.HitTest(pt)
+            result |= element.HitTest(pt, connectors)
         return result
     
     # Returns if the element given is in this group
@@ -1359,7 +1385,12 @@
     def SetHighlighted(self, highlighted):
         for element in self.Elements:
             element.SetHighlighted(highlighted)
-
+    
+    def HighlightPoint(self, pos):
+        for element in self.Elements:
+            if isinstance(element, Wire):
+                element.HighlightPoint(pos)
+    
     # Method called when a LeftDown event have been generated
     def OnLeftDown(self, event, dc, scaling):
         Graphic_Element.OnLeftDown(self, event, dc, scaling)
@@ -2181,12 +2212,20 @@
         return width + 1, height + 1
     
     # Returns if the point given is on one of the wire segments
-    def HitTest(self, pt):
+    def HitTest(self, pt, connectors=True):
         test = False
         for i in xrange(len(self.Points) - 1):
             rect = wx.Rect(0, 0, 0, 0)
-            x1, y1 = self.Points[i].x, self.Points[i].y
-            x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
+            if i == 0 and self.StartConnected is not None:
+                x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE
+                y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE
+            else:
+                x1, y1 = self.Points[i].x, self.Points[i].y    
+            if i == len(self.Points) - 2 and self.EndConnected is not None:
+                x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE
+                y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE
+            else:
+                x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y
             # Calculate a rectangle around the segment
             rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE,
                 abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE)
@@ -2784,16 +2823,6 @@
                     wx.CallAfter(self.Parent.SetCurrentCursor, 5)
                 return 0, 0
             else:
-                # Test if a point has been handled
-                #result = self.TestPoint(pos)
-                #if result != None:
-                #    if result == 0 and self.StartConnected:
-                #        self.OverStart = True
-                #    elif result != 0 and self.EndConnected:
-                #        self.OverEnd = True
-                #else:
-                #    self.OverStart = False
-                #    self.OverEnd = False
                 # Execute the default method for a graphic element
                 return Graphic_Element.OnMotion(self, event, dc, scaling)
         else:
@@ -2868,6 +2897,29 @@
         if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]:
             self.EndConnected.RefreshParentBlock()
     
+    # Change the variable that indicates if this element is highlighted
+    def SetHighlighted(self, highlighted):
+        self.Highlighted = highlighted
+        if not highlighted:
+            self.OverStart = False
+            self.OverEnd = False
+        self.Refresh()
+    
+    def HighlightPoint(self, pos):
+        refresh = False
+        start, end = self.OverStart, self.OverEnd
+        self.OverStart = False
+        self.OverEnd = False
+        # Test if a point has been handled
+        result = self.TestPoint(pos)
+        if result != None:
+            if result == 0 and self.StartConnected is not None:
+                self.OverStart = True
+            elif result != 0 and self.EndConnected is not None:
+                self.OverEnd = True
+        if start != self.OverStart or end != self.OverEnd:
+            self.Refresh()
+    
     # Draws the highlightment of this element if it is highlighted
     def DrawHighlightment(self, dc):
         scalex, scaley = dc.GetUserScale()
--- a/graphics/LD_Objects.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/graphics/LD_Objects.py	Mon Jan 30 16:12:19 2012 +0100
@@ -89,9 +89,9 @@
         self.RefreshConnectors()
     
     # Forbids to select a power rail
-    def HitTest(self, pt):
+    def HitTest(self, pt, connectors=True):
         if self.Parent.GetDrawingMode() == FREEDRAWING_MODE:
-            return Graphic_Element.HitTest(self, pt) or self.TestConnector(pt, exclude=False) != None
+            return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) != None
         return False
     
     # Forbids to select a power rail
@@ -199,7 +199,7 @@
     # Returns the power rail connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test each connector if it exists
             for connector in self.Connectors:
                 if name == connector.GetName():
@@ -509,10 +509,10 @@
     # Returns the contact connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test input and output connector
-            if name == self.Input.GetName():
-                return self.Input
+            #if name == self.Input.GetName():
+            #    return self.Input
             if name == self.Output.GetName():
                 return self.Output
         return self.FindNearestConnector(position, [self.Input, self.Output])
@@ -828,10 +828,10 @@
     # Returns the coil connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test input and output connector
-            if self.Input and name == self.Input.GetName():
-                return self.Input
+            #if self.Input and name == self.Input.GetName():
+            #    return self.Input
             if self.Output and name == self.Output.GetName():
                 return self.Output
         return self.FindNearestConnector(position, [self.Input, self.Output])
--- a/graphics/SFC_Objects.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/graphics/SFC_Objects.py	Mon Jan 30 16:12:19 2012 +0100
@@ -253,10 +253,10 @@
     # Returns the step connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test input, output and action connector if they exists
-            if self.Input and name == self.Input.GetName():
-                return self.Input
+            #if self.Input and name == self.Input.GetName():
+            #    return self.Input
             if self.Output and name == self.Output.GetName():
                 return self.Output
             if self.Action and name == self.Action.GetName():
@@ -757,10 +757,10 @@
     # Returns the transition connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test input and output connector
-            if name == self.Input.GetName():
-                return self.Input
+            #if name == self.Input.GetName():
+            #    return self.Input
             if name == self.Output.GetName():
                 return self.Output
             if self.Type == "connection" and name == self.Condition.GetName():
@@ -1178,8 +1178,11 @@
             return len(self.Inputs)
     
     # Returns if the point given is in the bounding box
-    def HitTest(self, pt):
-        rect = self.BoundingBox
+    def HitTest(self, pt, connectors=True):
+        if connectors:
+            rect = self.BoundingBox
+        else:
+            rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1])
         return rect.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None
     
     # Refresh the divergence bounding box
@@ -1229,11 +1232,11 @@
     # Returns the divergence connector that starts with the point given if it exists 
     def GetConnector(self, position, name = None):
         # if a name is given
-        if name:
+        if name is not None:
             # Test each input and output connector
-            for input in self.Inputs:
-                if name == input.GetName():
-                    return input
+            #for input in self.Inputs:
+            #    if name == input.GetName():
+            #        return input
             for output in self.Outputs:
                 if name == output.GetName():
                     return output
--- a/plcopen/plcopen.py	Wed Jan 25 01:26:29 2012 +0100
+++ b/plcopen/plcopen.py	Mon Jan 30 16:12:19 2012 +0100
@@ -433,13 +433,8 @@
     def AddCustomBlockType(self, pou): 
         pou_name = pou.getname()
         pou_type = pou.getpouType()
-        pou_description = pou.getdescription()
-        if pou_description != "":
-            pou_comment = "%s\n%s" % (pou_name, pou_description)
-        else:
-            pou_comment = pou_name
         block_infos = {"name" : pou_name, "type" : pou_type, "extensible" : False,
-                       "inputs" : [], "outputs" : [], "comment" : pou_comment,
+                       "inputs" : [], "outputs" : [], "comment" : pou.getdescription(),
                        "generate" : generate_block, "initialise" : initialise_block}
         if pou.getinterface():
             return_type = pou.interface.getreturnType()