# HG changeset patch # User laurent # Date 1327936339 -3600 # Node ID 3536f4469cdea4ec668e8c010af9860c5d08acca # Parent 3ea55a5db68e518a25dd855fa1b12085b3c97da1 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. diff -r 3ea55a5db68e -r 3536f4469cde Viewer.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() diff -r 3ea55a5db68e -r 3536f4469cde graphics/FBD_Objects.py --- 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 = [] diff -r 3ea55a5db68e -r 3536f4469cde graphics/GraphicCommons.py --- 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() diff -r 3ea55a5db68e -r 3536f4469cde graphics/LD_Objects.py --- 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]) diff -r 3ea55a5db68e -r 3536f4469cde graphics/SFC_Objects.py --- 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 diff -r 3ea55a5db68e -r 3536f4469cde plcopen/plcopen.py --- 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()