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. Laurent@814: # andrej@1571: # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD Laurent@814: # andrej@1571: # See COPYING file for copyrights details. Laurent@814: # 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. Laurent@814: # 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. Laurent@814: # 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@814: import wx Laurent@814: Laurent@1176: from graphics.GraphicCommons import * Laurent@1176: from graphics.DebugDataConsumer import DebugDataConsumer Laurent@814: from plcopen.structures import * Laurent@814: andrej@1736: Laurent@814: def GetWireSize(block): Laurent@814: if isinstance(block, SFC_Step): Laurent@814: return SFC_WIRE_MIN_SIZE + block.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1] Laurent@814: else: Laurent@814: return SFC_WIRE_MIN_SIZE Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Sequencial Function Chart Step andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: Laurent@814: class SFC_Step(Graphic_Element, DebugDataConsumer): andrej@1736: """ andrej@1736: Class that implements the graphic representation of a step andrej@1736: """ andrej@1730: Laurent@814: # Create a new step andrej@1744: def __init__(self, parent, name, initial=False, id=None): Laurent@814: Graphic_Element.__init__(self, parent) Laurent@814: DebugDataConsumer.__init__(self) Laurent@814: self.SetName(name) Laurent@814: self.Initial = initial Laurent@814: self.Id = id Laurent@814: self.Highlights = [] Laurent@814: self.Size = wx.Size(SFC_STEP_DEFAULT_SIZE[0], SFC_STEP_DEFAULT_SIZE[1]) Laurent@814: # Create an input and output connector Laurent@814: if not self.Initial: Laurent@814: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH) Laurent@814: else: Laurent@814: self.Input = None Laurent@814: self.Output = None Laurent@814: self.Action = None Laurent@814: self.PreviousValue = None Laurent@814: self.PreviousSpreading = False andrej@1730: Laurent@814: def Flush(self): Laurent@814: if self.Input is not None: Laurent@814: self.Input.Flush() Laurent@814: self.Input = None Laurent@814: if self.Output is not None: Laurent@814: self.Output.Flush() Laurent@814: self.Output = None surkovsv93@1653: if self.Action is not None: Laurent@814: self.Action.Flush() Laurent@814: self.Action = None andrej@1730: Laurent@814: def SetForced(self, forced): Laurent@814: if self.Forced != forced: Laurent@814: self.Forced = forced Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) andrej@1730: Laurent@814: def SetValue(self, value): Laurent@814: self.PreviousValue = self.Value Laurent@814: self.Value = value Laurent@814: if self.Value != self.PreviousValue: Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) Laurent@814: self.SpreadCurrent() andrej@1730: Laurent@814: def SpreadCurrent(self): Laurent@814: if self.Parent.Debug: Laurent@814: spreading = self.Value Laurent@814: if spreading and not self.PreviousSpreading: Laurent@814: if self.Output is not None: Laurent@814: self.Output.SpreadCurrent(True) Laurent@814: if self.Action is not None: Laurent@814: self.Action.SpreadCurrent(True) Laurent@814: elif not spreading and self.PreviousSpreading: Laurent@814: if self.Output is not None: Laurent@814: self.Output.SpreadCurrent(False) Laurent@814: if self.Action is not None: Laurent@814: self.Action.SpreadCurrent(False) Laurent@814: self.PreviousSpreading = spreading andrej@1730: Laurent@814: # Make a clone of this SFC_Step andrej@1744: def Clone(self, parent, id=None, name="Step", pos=None): Laurent@814: step = SFC_Step(parent, name, self.Initial, id) Laurent@814: step.SetSize(self.Size[0], self.Size[1]) Laurent@814: if pos is not None: Laurent@814: step.SetPosition(pos.x, pos.y) Laurent@814: else: Laurent@814: step.SetPosition(self.Pos.x, self.Pos.y) Laurent@814: if self.Input: Laurent@814: step.Input = self.Input.Clone(step) Laurent@814: if self.Output: Laurent@814: step.Output = self.Output.Clone(step) Laurent@814: if self.Action: Laurent@814: step.Action = self.Action.Clone(step) Laurent@814: return step andrej@1730: Laurent@814: def GetConnectorTranslation(self, element): Laurent@814: connectors = {} Laurent@814: if self.Input is not None: Laurent@814: connectors[self.Input] = element.Input Laurent@814: if self.Output is not None: Laurent@814: connectors[self.Output] = element.Output Laurent@814: if self.Action is not None: Laurent@814: connectors[self.Action] = element.Action Laurent@814: return connectors andrej@1730: Laurent@814: # Returns the RedrawRect andrej@1744: def GetRedrawRect(self, movex=0, movey=0): Laurent@814: rect = Graphic_Element.GetRedrawRect(self, movex, movey) Laurent@814: if self.Input: Laurent@814: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) Laurent@814: if self.Output: Laurent@814: rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) Laurent@814: if self.Action: Laurent@814: rect = rect.Union(self.Action.GetRedrawRect(movex, movey)) Laurent@814: if movex != 0 or movey != 0: Laurent@814: if self.Input and self.Input.IsConnected(): Laurent@814: rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) Laurent@814: if self.Output and self.Output.IsConnected(): Laurent@814: rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) Laurent@814: if self.Action and self.Action.IsConnected(): Laurent@814: rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey)) Laurent@814: return rect andrej@1730: Laurent@814: # Delete this step by calling the appropriate method Laurent@814: def Delete(self): Laurent@814: self.Parent.DeleteStep(self) andrej@1730: Laurent@814: # Unconnect input and output Laurent@814: def Clean(self): Laurent@814: if self.Input: andrej@1744: self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: if self.Output: andrej@1744: self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: if self.Action: andrej@1744: self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1730: Laurent@814: # Refresh the size of text for name Laurent@814: def RefreshNameSize(self): Laurent@814: self.NameSize = self.Parent.GetTextExtent(self.Name) andrej@1730: Laurent@814: # Add output connector to step Laurent@814: def AddInput(self): Laurent@814: if not self.Input: Laurent@814: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH) Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Remove output connector from step Laurent@814: def RemoveInput(self): Laurent@814: if self.Input: andrej@1744: self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: self.Input = None Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Add output connector to step Laurent@814: def AddOutput(self): Laurent@814: if not self.Output: andrej@1744: self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Remove output connector from step Laurent@814: def RemoveOutput(self): Laurent@814: if self.Output: andrej@1744: self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: self.Output = None Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Add action connector to step Laurent@814: def AddAction(self): Laurent@814: if not self.Action: andrej@1744: self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone=True) Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Remove action connector from step Laurent@814: def RemoveAction(self): Laurent@814: if self.Action: andrej@1744: self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: self.Action = None Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Refresh the step bounding box Laurent@814: def RefreshBoundingBox(self): Laurent@814: # Calculate the bounding box size Laurent@814: if self.Action: Laurent@814: bbx_width = self.Size[0] + CONNECTOR_SIZE Laurent@814: else: Laurent@814: bbx_width = self.Size[0] Laurent@814: if self.Initial: Laurent@814: bbx_y = self.Pos.y Laurent@814: bbx_height = self.Size[1] Laurent@814: if self.Output: Laurent@814: bbx_height += CONNECTOR_SIZE Laurent@814: else: Laurent@814: bbx_y = self.Pos.y - CONNECTOR_SIZE Laurent@814: bbx_height = self.Size[1] + CONNECTOR_SIZE Laurent@814: if self.Output: Laurent@814: bbx_height += CONNECTOR_SIZE andrej@1782: # self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1) Laurent@814: self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) andrej@1730: Laurent@814: # Refresh the positions of the step connectors Laurent@814: def RefreshConnectors(self): Laurent@814: scaling = self.Parent.GetScaling() Laurent@814: horizontal_pos = self.Size[0] / 2 Laurent@814: vertical_pos = self.Size[1] / 2 Laurent@814: if scaling is not None: Laurent@814: horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y Laurent@814: # Update input position if it exists Laurent@814: if self.Input: Laurent@814: self.Input.SetPosition(wx.Point(horizontal_pos, 0)) Laurent@814: # Update output position Laurent@814: if self.Output: Laurent@814: self.Output.SetPosition(wx.Point(horizontal_pos, self.Size[1])) Laurent@814: # Update action position if it exists Laurent@814: if self.Action: Laurent@814: self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos)) Laurent@814: self.RefreshConnected() andrej@1730: Laurent@814: # Refresh the position of wires connected to step andrej@1744: def RefreshConnected(self, exclude=[]): Laurent@814: if self.Input: Laurent@814: self.Input.MoveConnected(exclude) Laurent@814: if self.Output: Laurent@814: self.Output.MoveConnected(exclude) Laurent@814: if self.Action: Laurent@814: self.Action.MoveConnected(exclude) andrej@1730: andrej@1730: # Returns the step connector that starts with the point given if it exists andrej@1744: def GetConnector(self, position, name=None): Laurent@814: # if a name is given Laurent@814: if name is not None: Laurent@814: # Test input, output and action connector if they exists andrej@1782: # if self.Input and name == self.Input.GetName(): Laurent@814: # return self.Input Laurent@814: if self.Output and name == self.Output.GetName(): Laurent@814: return self.Output Laurent@814: if self.Action and name == self.Action.GetName(): Laurent@814: return self.Action Laurent@814: connectors = [] Laurent@814: # Test input connector if it exists Laurent@814: if self.Input: Laurent@814: connectors.append(self.Input) Laurent@814: # Test output connector if it exists Laurent@814: if self.Output: Laurent@814: connectors.append(self.Output) Laurent@814: # Test action connector if it exists Laurent@814: if self.Action: Laurent@814: connectors.append(self.Action) Laurent@814: return self.FindNearestConnector(position, connectors) andrej@1730: andrej@1730: # Returns action step connector Laurent@814: def GetActionConnector(self): Laurent@814: return self.Action andrej@1730: andrej@1730: # Returns input and output step connectors Laurent@814: def GetConnectors(self): Laurent@814: connectors = {"inputs": [], "outputs": []} Laurent@814: if self.Input: Laurent@814: connectors["inputs"].append(self.Input) Laurent@814: if self.Output: Laurent@814: connectors["outputs"].append(self.Output) Laurent@814: return connectors andrej@1730: Laurent@814: # Test if point given is on step input or output connector andrej@1744: def TestConnector(self, pt, direction=None, exclude=True): Laurent@814: # Test input connector if it exists Laurent@814: if self.Input and self.Input.TestPoint(pt, direction, exclude): Laurent@814: return self.Input Laurent@814: # Test output connector Laurent@814: if self.Output and self.Output.TestPoint(pt, direction, exclude): Laurent@814: return self.Output Laurent@814: # Test action connector Laurent@814: if self.Action and self.Action.TestPoint(pt, direction, exclude): Laurent@814: return self.Action Laurent@814: return None Laurent@814: Laurent@814: # Changes the step name Laurent@814: def SetName(self, name): Laurent@814: self.Name = name Laurent@814: self.RefreshNameSize() Laurent@814: Laurent@814: # Returns the step name Laurent@814: def GetName(self): Laurent@814: return self.Name Laurent@814: Laurent@814: # Returns the step initial property Laurent@814: def GetInitial(self): Laurent@814: return self.Initial andrej@1730: Laurent@814: # Returns the connector connected to input Laurent@814: def GetPreviousConnector(self): Laurent@814: if self.Input: Laurent@814: wires = self.Input.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Input) Laurent@814: return None andrej@1730: Laurent@814: # Returns the connector connected to output Laurent@814: def GetNextConnector(self): Laurent@814: if self.Output: Laurent@814: wires = self.Output.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Output) Laurent@814: return None andrej@1730: Laurent@814: # Returns the connector connected to action Laurent@814: def GetActionConnected(self): Laurent@814: if self.Action: Laurent@814: wires = self.Action.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Action) Laurent@814: return None andrej@1730: Laurent@814: # Returns the number of action line Laurent@814: def GetActionExtraLineNumber(self): Laurent@814: if self.Action: Laurent@814: wires = self.Action.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return 0 Laurent@814: action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() Laurent@814: return max(0, action_block.GetLineNumber() - 1) Laurent@814: return 0 andrej@1730: Laurent@814: # Returns the step minimum size Laurent@814: def GetMinSize(self): Laurent@814: text_width, text_height = self.Parent.GetTextExtent(self.Name) Laurent@814: if self.Initial: Laurent@814: return text_width + 14, text_height + 14 Laurent@814: else: Laurent@814: return text_width + 10, text_height + 10 andrej@1730: Laurent@814: # Updates the step size Laurent@814: def UpdateSize(self, width, height): Laurent@814: diffx = self.Size.GetWidth() / 2 - width / 2 Laurent@814: diffy = height - self.Size.GetHeight() Laurent@814: self.Move(diffx, 0) Laurent@814: Graphic_Element.SetSize(self, width, height) Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: self.RefreshConnected() Laurent@814: else: Laurent@814: self.RefreshOutputPosition((0, diffy)) andrej@1730: Laurent@814: # Align input element with this step Laurent@814: def RefreshInputPosition(self): Laurent@814: if self.Input: Laurent@814: current_pos = self.Input.GetPosition(False) Laurent@814: input = self.GetPreviousConnector() Laurent@814: if input: Laurent@814: input_pos = input.GetPosition(False) Laurent@814: diffx = current_pos.x - input_pos.x Laurent@814: input_block = input.GetParentBlock() Laurent@814: if isinstance(input_block, SFC_Divergence): Laurent@814: input_block.MoveConnector(input, diffx) Laurent@814: else: Laurent@814: if isinstance(input_block, SFC_Step): Laurent@814: input_block.MoveActionBlock((diffx, 0)) Laurent@814: input_block.Move(diffx, 0) Laurent@814: input_block.RefreshInputPosition() andrej@1730: Laurent@814: # Align output element with this step andrej@1744: def RefreshOutputPosition(self, move=None): Laurent@814: if self.Output: Laurent@814: wires = self.Output.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: current_pos = self.Output.GetPosition(False) Laurent@814: output = wires[0][0].GetOtherConnected(self.Output) Laurent@814: output_pos = output.GetPosition(False) Laurent@814: diffx = current_pos.x - output_pos.x Laurent@814: output_block = output.GetParentBlock() Laurent@814: wire_size = SFC_WIRE_MIN_SIZE + self.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1] Laurent@814: diffy = wire_size - output_pos.y + current_pos.y Laurent@814: if diffy != 0: Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock((diffx, diffy)) Laurent@814: wires[0][0].SetPoints([wx.Point(current_pos.x, current_pos.y + wire_size), andrej@1768: wx.Point(current_pos.x, current_pos.y)]) Laurent@814: if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: Laurent@814: output_block.Move(diffx, diffy, self.Parent.Wires) Laurent@814: output_block.RefreshOutputPosition((diffx, diffy)) Laurent@814: else: Laurent@814: output_block.RefreshPosition() Laurent@814: elif move: Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock(move) Laurent@814: wires[0][0].Move(move[0], move[1], True) Laurent@814: if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: Laurent@814: output_block.Move(move[0], move[1], self.Parent.Wires) Laurent@814: output_block.RefreshOutputPosition(move) Laurent@814: else: Laurent@814: output_block.RefreshPosition() Laurent@814: elif isinstance(output_block, SFC_Divergence): Laurent@814: output_block.MoveConnector(output, diffx) Laurent@814: else: Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock((diffx, 0)) Laurent@814: output_block.Move(diffx, 0) Laurent@814: output_block.RefreshOutputPosition() andrej@1730: Laurent@814: # Refresh action element with this step Laurent@814: def MoveActionBlock(self, move): Laurent@814: if self.Action: Laurent@814: wires = self.Action.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() Laurent@814: action_block.Move(move[0], move[1], self.Parent.Wires) Laurent@814: wires[0][0].Move(move[0], move[1], True) andrej@1730: Laurent@814: # Resize the divergence from position and size given Laurent@814: def Resize(self, x, y, width, height): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.UpdateSize(width, height) Laurent@814: else: Laurent@814: Graphic_Element.Resize(self, x, y, width, height) andrej@1730: Laurent@814: # Method called when a LeftDClick event have been generated Laurent@814: def OnLeftDClick(self, event, dc, scaling): Laurent@814: # Edit the step properties Laurent@814: self.Parent.EditStepContent(self) andrej@1730: Laurent@814: # Method called when a RightUp event have been generated Laurent@814: def OnRightUp(self, event, dc, scaling): Laurent@814: # Popup the menu with special items for a step Laurent@814: self.Parent.PopupDefaultMenu() andrej@1730: Laurent@814: # Refreshes the step state according to move defined and handle selected Laurent@814: def ProcessDragging(self, movex, movey, event, scaling): Laurent@814: handle_type, handle = self.Handle Laurent@814: if handle_type == HANDLE_MOVE: Laurent@814: movex = max(-self.BoundingBox.x, movex) Laurent@814: movey = max(-self.BoundingBox.y, movey) Laurent@814: if scaling is not None: Laurent@814: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: movey = round(float(self.Pos.y + movey) / float(scaling[1])) * scaling[1] - self.Pos.y Laurent@814: action_block = None Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: self.Move(movex, movey) Laurent@814: self.RefreshConnected() Laurent@814: return movex, movey Laurent@814: elif self.Initial: Laurent@814: self.MoveActionBlock((movex, movey)) Laurent@814: self.Move(movex, movey, self.Parent.Wires) Laurent@814: self.RefreshOutputPosition((movex, movey)) Laurent@814: return movex, movey Laurent@814: else: Laurent@814: self.MoveActionBlock((movex, 0)) Laurent@814: self.Move(movex, 0) Laurent@814: self.RefreshInputPosition() Laurent@814: self.RefreshOutputPosition() Laurent@814: return movex, 0 Laurent@814: else: Laurent@814: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) andrej@1730: Laurent@814: # Refresh input element model Laurent@814: def RefreshInputModel(self): Laurent@814: if self.Input: Laurent@814: input = self.GetPreviousConnector() andrej@1730: if input: Laurent@814: input_block = input.GetParentBlock() Laurent@814: input_block.RefreshModel(False) Laurent@814: if not isinstance(input_block, SFC_Divergence): Laurent@814: input_block.RefreshInputModel() andrej@1730: Laurent@814: # Refresh output element model Laurent@814: def RefreshOutputModel(self, move=False): Laurent@814: if self.Output: Laurent@814: output = self.GetNextConnector() Laurent@814: if output: Laurent@814: output_block = output.GetParentBlock() Laurent@814: output_block.RefreshModel(False) Laurent@814: if not isinstance(output_block, SFC_Divergence) or move: Laurent@814: output_block.RefreshOutputModel(move) andrej@1730: Laurent@814: # Refreshes the step model Laurent@814: def RefreshModel(self, move=True): Laurent@814: self.Parent.RefreshStepModel(self) Laurent@814: if self.Action: Laurent@814: action = self.GetActionConnected() Laurent@814: if action: Laurent@814: action_block = action.GetParentBlock() Laurent@814: action_block.RefreshModel(False) Laurent@814: # If step has moved, refresh the model of wires connected to output Laurent@814: if move: Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.RefreshInputModel() Laurent@814: self.RefreshOutputModel(self.Initial) Laurent@814: elif self.Output: Laurent@814: self.Output.RefreshWires() andrej@1730: Laurent@814: # Adds an highlight to the connection Laurent@814: def AddHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "name" and start[0] == 0 and end[0] == 0: Laurent@814: AddHighlight(self.Highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes an highlight from the connection Laurent@814: def RemoveHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "name": Laurent@814: RemoveHighlight(self.Highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes all the highlights of one particular type from the connection Laurent@814: def ClearHighlight(self, highlight_type=None): Laurent@814: ClearHighlights(self.Highlights, highlight_type) andrej@1730: Laurent@814: # Draws step Laurent@814: def Draw(self, dc): Laurent@814: Graphic_Element.Draw(self, dc) Laurent@814: if self.Value: Laurent@814: if self.Forced: Laurent@814: dc.SetPen(MiterPen(wx.CYAN)) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.GREEN)) Laurent@814: elif self.Forced: Laurent@814: dc.SetPen(MiterPen(wx.BLUE)) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.WHITE_BRUSH) andrej@1730: Laurent@814: if getattr(dc, "printing", False): Laurent@814: name_size = dc.GetTextExtent(self.Name) Laurent@814: else: Laurent@814: name_size = self.NameSize andrej@1730: Laurent@814: # Draw two rectangles for representing the step Laurent@814: dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) Laurent@814: if self.Initial: Laurent@814: dc.DrawRectangle(self.Pos.x + 2, self.Pos.y + 2, self.Size[0] - 3, self.Size[1] - 3) Laurent@814: # Draw step name Laurent@814: name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, Laurent@814: self.Pos.y + (self.Size[1] - name_size[1]) / 2) Laurent@814: dc.DrawText(self.Name, name_pos[0], name_pos[1]) Laurent@814: # Draw input and output connectors Laurent@814: if self.Input: Laurent@814: self.Input.Draw(dc) Laurent@814: if self.Output: Laurent@814: self.Output.Draw(dc) Laurent@814: if self.Action: Laurent@814: self.Action.Draw(dc) andrej@1730: Laurent@814: if not getattr(dc, "printing", False): Laurent@814: DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1]) andrej@1730: Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Sequencial Function Chart Transition andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: Laurent@814: class SFC_Transition(Graphic_Element, DebugDataConsumer): andrej@1736: """ andrej@1736: Class that implements the graphic representation of a transition andrej@1736: """ andrej@1730: Laurent@814: # Create a new transition andrej@1744: def __init__(self, parent, type="reference", condition=None, priority=0, id=None): Laurent@814: Graphic_Element.__init__(self, parent) Laurent@814: DebugDataConsumer.__init__(self) Laurent@814: self.Type = None Laurent@814: self.Id = id Laurent@814: self.Priority = 0 Laurent@814: self.Size = wx.Size(SFC_TRANSITION_SIZE[0], SFC_TRANSITION_SIZE[1]) Laurent@814: # Create an input and output connector andrej@1758: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) andrej@1744: self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) Laurent@814: self.SetType(type, condition) Laurent@814: self.SetPriority(priority) Laurent@814: self.Highlights = {} Laurent@814: self.PreviousValue = None Laurent@814: self.PreviousSpreading = False andrej@1730: Laurent@814: def Flush(self): Laurent@814: if self.Input is not None: Laurent@814: self.Input.Flush() Laurent@814: self.Input = None Laurent@814: if self.Output is not None: Laurent@814: self.Output.Flush() Laurent@814: self.Output = None Laurent@814: if self.Type == "connection" and self.Condition is not None: Laurent@814: self.Condition.Flush() Laurent@814: self.Condition = None andrej@1730: Laurent@814: def SetForced(self, forced): Laurent@814: if self.Forced != forced: Laurent@814: self.Forced = forced Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) andrej@1730: Laurent@814: def SetValue(self, value): Laurent@814: self.PreviousValue = self.Value Laurent@814: self.Value = value Laurent@814: if self.Value != self.PreviousValue: Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) Laurent@814: self.SpreadCurrent() andrej@1730: Laurent@814: def SpreadCurrent(self): Laurent@814: if self.Parent.Debug: Laurent@814: if self.Value is None: Laurent@814: self.Value = False Laurent@814: spreading = self.Input.ReceivingCurrent() & self.Value Laurent@814: if spreading and not self.PreviousSpreading: Laurent@814: self.Output.SpreadCurrent(True) Laurent@814: elif not spreading and self.PreviousSpreading: Laurent@814: self.Output.SpreadCurrent(False) Laurent@814: self.PreviousSpreading = spreading andrej@1730: Laurent@814: # Make a clone of this SFC_Transition andrej@1744: def Clone(self, parent, id=None, pos=None): Laurent@814: transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id) Laurent@814: transition.SetSize(self.Size[0], self.Size[1]) Laurent@814: if pos is not None: Laurent@814: transition.SetPosition(pos.x, pos.y) Laurent@814: else: Laurent@814: transition.SetPosition(self.Pos.x, self.Pos.y) Laurent@814: transition.Input = self.Input.Clone(transition) Laurent@814: transition.Output = self.Output.Clone(transition) Laurent@814: if self.Type == "connection": Laurent@814: transition.Condition = self.Condition.Clone(transition) Laurent@814: return transition andrej@1730: Laurent@814: def GetConnectorTranslation(self, element): andrej@1739: connectors = {self.Input: element.Input, self.Output: element.Output} Laurent@814: if self.Type == "connection" and self.Condition is not None: Laurent@814: connectors[self.Condition] = element.Condition Laurent@814: return connectors andrej@1730: Laurent@814: # Returns the RedrawRect andrej@1744: def GetRedrawRect(self, movex=0, movey=0): Laurent@814: rect = Graphic_Element.GetRedrawRect(self, movex, movey) surkovsv93@1652: if self.Input: surkovsv93@1652: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) surkovsv93@1652: if self.Output: surkovsv93@1652: rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) Laurent@814: if movex != 0 or movey != 0: Laurent@814: if self.Input.IsConnected(): Laurent@814: rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) Laurent@814: if self.Output.IsConnected(): Laurent@814: rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) Laurent@814: if self.Type == "connection" and self.Condition.IsConnected(): Laurent@814: rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey)) Laurent@814: return rect andrej@1730: Laurent@814: # Forbids to change the transition size Laurent@814: def SetSize(self, width, height): Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: Graphic_Element.SetSize(self, width, height) andrej@1730: Laurent@814: # Forbids to resize the transition Laurent@814: def Resize(self, x, y, width, height): Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: Graphic_Element.Resize(self, x, y, width, height) andrej@1730: Laurent@814: # Refresh the size of text for name Laurent@814: def RefreshConditionSize(self): Laurent@814: if self.Type != "connection": Laurent@814: if self.Condition != "": Laurent@814: self.ConditionSize = self.Parent.GetTextExtent(self.Condition) Laurent@814: else: Laurent@814: self.ConditionSize = self.Parent.GetTextExtent("Transition") andrej@1730: Laurent@814: # Refresh the size of text for name Laurent@814: def RefreshPrioritySize(self): Laurent@814: if self.Priority != "": Laurent@814: self.PrioritySize = self.Parent.GetTextExtent(str(self.Priority)) Laurent@814: else: Laurent@814: self.PrioritySize = None Laurent@814: Laurent@814: # Delete this transition by calling the appropriate method Laurent@814: def Delete(self): Laurent@814: self.Parent.DeleteTransition(self) andrej@1730: Laurent@814: # Unconnect input and output Laurent@814: def Clean(self): andrej@1744: self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1744: self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: if self.Type == "connection": andrej@1744: self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1730: Laurent@814: # Returns if the point given is in the bounding box Laurent@814: def HitTest(self, pt, connectors=True): Laurent@814: if self.Type != "connection": Laurent@814: # Calculate the bounding box of the condition outside the transition Laurent@814: text_width, text_height = self.ConditionSize Laurent@814: text_bbx = wx.Rect(self.Pos.x + self.Size[0] + 5, Laurent@814: self.Pos.y + (self.Size[1] - text_height) / 2, Laurent@814: text_width, Laurent@814: text_height) Laurent@814: test_text = text_bbx.InsideXY(pt.x, pt.y) Laurent@814: else: Laurent@814: test_text = False Laurent@814: return test_text or Graphic_Element.HitTest(self, pt, connectors) andrej@1730: Laurent@814: # Refresh the transition bounding box Laurent@814: def RefreshBoundingBox(self): Laurent@814: bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1] Laurent@814: if self.Priority != 0: Laurent@814: bbx_y = self.Pos.y - self.PrioritySize[1] - 2 Laurent@814: bbx_width = max(self.Size[0], self.PrioritySize[0]) Laurent@814: bbx_height = self.Size[1] + self.PrioritySize[1] + 2 Laurent@814: if self.Type == "connection": Laurent@814: bbx_x = self.Pos.x - CONNECTOR_SIZE Laurent@814: bbx_width = bbx_width + CONNECTOR_SIZE Laurent@814: else: Laurent@814: text_width, text_height = self.ConditionSize Laurent@814: # Calculate the bounding box size Laurent@814: bbx_width = max(bbx_width, self.Size[0] + 5 + text_width) Laurent@814: bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2)) Laurent@814: bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2) Laurent@814: self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) andrej@1730: Laurent@814: # Returns the connector connected to input Laurent@814: def GetPreviousConnector(self): Laurent@814: wires = self.Input.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Input) Laurent@814: return None andrej@1730: Laurent@814: # Returns the connector connected to output Laurent@814: def GetNextConnector(self): Laurent@814: wires = self.Output.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Output) Laurent@814: return None andrej@1730: Laurent@814: # Refresh the positions of the transition connectors Laurent@814: def RefreshConnectors(self): Laurent@814: scaling = self.Parent.GetScaling() Laurent@814: horizontal_pos = self.Size[0] / 2 Laurent@814: vertical_pos = self.Size[1] / 2 Laurent@814: if scaling is not None: Laurent@814: horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y Laurent@814: # Update input position Laurent@814: self.Input.SetPosition(wx.Point(horizontal_pos, 0)) Laurent@814: # Update output position Laurent@814: self.Output.SetPosition(wx.Point(horizontal_pos, self.Size[1])) Laurent@814: if self.Type == "connection": Laurent@814: self.Condition.SetPosition(wx.Point(0, vertical_pos)) Laurent@814: self.RefreshConnected() andrej@1730: Laurent@814: # Refresh the position of the wires connected to transition andrej@1744: def RefreshConnected(self, exclude=[]): Laurent@814: self.Input.MoveConnected(exclude) Laurent@814: self.Output.MoveConnected(exclude) Laurent@814: if self.Type == "connection": Laurent@814: self.Condition.MoveConnected(exclude) andrej@1730: andrej@1730: # Returns the transition connector that starts with the point given if it exists andrej@1744: def GetConnector(self, position, name=None): Laurent@814: # if a name is given Laurent@814: if name is not None: Laurent@814: # Test input and output connector andrej@1782: # if name == self.Input.GetName(): Laurent@814: # return self.Input Laurent@814: if name == self.Output.GetName(): Laurent@814: return self.Output Laurent@814: if self.Type == "connection" and name == self.Condition.GetName(): Laurent@814: return self.Condition Laurent@814: connectors = [self.Input, self.Output] Laurent@814: if self.Type == "connection": Laurent@814: connectors.append(self.Condition) Laurent@814: return self.FindNearestConnector(position, connectors) andrej@1730: Laurent@814: # Returns the transition condition connector Laurent@814: def GetConditionConnector(self): Laurent@814: if self.Type == "connection": Laurent@814: return self.Condition Laurent@814: return None andrej@1730: Laurent@814: # Returns input and output transition connectors Laurent@814: def GetConnectors(self): Laurent@814: return {"inputs": [self.Input], "outputs": [self.Output]} andrej@1730: Laurent@814: # Test if point given is on transition input or output connector andrej@1744: def TestConnector(self, pt, direction=None, exclude=True): Laurent@814: # Test input connector Laurent@814: if self.Input.TestPoint(pt, direction, exclude): Laurent@814: return self.Input Laurent@814: # Test output connector Laurent@814: if self.Output.TestPoint(pt, direction, exclude): Laurent@814: return self.Output Laurent@814: # Test condition connector Laurent@814: if self.Type == "connection" and self.Condition.TestPoint(pt, direction, exclude): Laurent@814: return self.Condition Laurent@814: return None Laurent@814: Laurent@814: # Changes the transition type andrej@1744: def SetType(self, type, condition=None): Laurent@814: if self.Type != type: Laurent@814: if self.Type == "connection": andrej@1757: self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: self.Type = type Laurent@814: if type == "connection": Laurent@814: self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST) Laurent@814: else: andrej@1743: if condition is None: Laurent@814: condition = "" Laurent@814: self.Condition = condition Laurent@814: self.RefreshConditionSize() Laurent@814: elif self.Type != "connection": andrej@1743: if condition is None: Laurent@814: condition = "" Laurent@814: self.Condition = condition Laurent@814: self.RefreshConditionSize() Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Returns the transition type Laurent@814: def GetType(self): Laurent@814: return self.Type Laurent@814: Laurent@814: # Changes the transition priority Laurent@814: def SetPriority(self, priority): Laurent@814: self.Priority = priority Laurent@814: self.RefreshPrioritySize() Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Returns the transition type Laurent@814: def GetPriority(self): Laurent@814: return self.Priority Laurent@814: Laurent@814: # Returns the transition condition Laurent@814: def GetCondition(self): Laurent@814: if self.Type != "connection": Laurent@814: return self.Condition Laurent@814: return None andrej@1730: Laurent@814: # Returns the transition minimum size Laurent@814: def GetMinSize(self): Laurent@814: return SFC_TRANSITION_SIZE andrej@1730: Laurent@814: # Align input element with this step Laurent@814: def RefreshInputPosition(self): Laurent@814: wires = self.Input.GetWires() Laurent@814: current_pos = self.Input.GetPosition(False) Laurent@814: input = self.GetPreviousConnector() Laurent@814: if input: Laurent@814: input_pos = input.GetPosition(False) Laurent@814: diffx = current_pos.x - input_pos.x Laurent@814: input_block = input.GetParentBlock() Laurent@814: if isinstance(input_block, SFC_Divergence): Laurent@814: input_block.MoveConnector(input, diffx) Laurent@814: else: Laurent@814: if isinstance(input_block, SFC_Step): Laurent@814: input_block.MoveActionBlock((diffx, 0)) Laurent@814: input_block.Move(diffx, 0) Laurent@814: input_block.RefreshInputPosition() andrej@1730: Laurent@814: # Align output element with this step andrej@1744: def RefreshOutputPosition(self, move=None): Laurent@814: wires = self.Output.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: current_pos = self.Output.GetPosition(False) Laurent@814: output = wires[0][0].GetOtherConnected(self.Output) Laurent@814: output_pos = output.GetPosition(False) Laurent@814: diffx = current_pos.x - output_pos.x Laurent@814: output_block = output.GetParentBlock() Laurent@814: if move: Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock(move) Laurent@814: wires[0][0].Move(move[0], move[1], True) Laurent@814: if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: Laurent@814: output_block.Move(move[0], move[1], self.Parent.Wires) Laurent@814: output_block.RefreshOutputPosition(move) Laurent@814: else: Laurent@814: output_block.RefreshPosition() Laurent@814: elif isinstance(output_block, SFC_Divergence): Laurent@814: output_block.MoveConnector(output, diffx) Laurent@814: else: Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock((diffx, 0)) Laurent@814: output_block.Move(diffx, 0) Laurent@814: output_block.RefreshOutputPosition() Laurent@814: Laurent@814: # Method called when a LeftDClick event have been generated Laurent@814: def OnLeftDClick(self, event, dc, scaling): Laurent@814: # Edit the transition properties Laurent@814: self.Parent.EditTransitionContent(self) andrej@1730: Laurent@814: # Method called when a RightUp event have been generated Laurent@814: def OnRightUp(self, event, dc, scaling): Laurent@814: # Popup the menu with special items for a step Laurent@814: self.Parent.PopupDefaultMenu() andrej@1730: Laurent@814: # Refreshes the transition state according to move defined and handle selected Laurent@814: def ProcessDragging(self, movex, movey, event, scaling): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: movex = max(-self.BoundingBox.x, movex) Laurent@814: if scaling is not None: Laurent@814: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: self.Move(movex, 0) Laurent@814: self.RefreshInputPosition() Laurent@814: self.RefreshOutputPosition() Laurent@814: return movex, 0 Laurent@814: else: andrej@1744: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2, height_fac=2) andrej@1730: Laurent@814: # Refresh input element model Laurent@814: def RefreshInputModel(self): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: input = self.GetPreviousConnector() Laurent@814: if input: Laurent@814: input_block = input.GetParentBlock() Laurent@814: input_block.RefreshModel(False) Laurent@814: if not isinstance(input_block, SFC_Divergence): Laurent@814: input_block.RefreshInputModel() andrej@1730: Laurent@814: # Refresh output element model Laurent@814: def RefreshOutputModel(self, move=False): Laurent@814: output = self.GetNextConnector() Laurent@814: if output: Laurent@814: output_block = output.GetParentBlock() Laurent@814: output_block.RefreshModel(False) Laurent@814: if not isinstance(output_block, SFC_Divergence) or move: Laurent@814: output_block.RefreshOutputModel(move) andrej@1730: Laurent@814: # Refreshes the transition model Laurent@814: def RefreshModel(self, move=True): Laurent@814: self.Parent.RefreshTransitionModel(self) Laurent@814: # If transition has moved, refresh the model of wires connected to output Laurent@814: if move: Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.RefreshInputModel() Laurent@814: self.RefreshOutputModel() Laurent@814: else: Laurent@814: self.Output.RefreshWires() andrej@1730: Laurent@814: # Adds an highlight to the block andrej@1739: def AddHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0: Laurent@814: highlights = self.Highlights.setdefault(infos[0], []) Laurent@814: AddHighlight(highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes an highlight from the block Laurent@814: def RemoveHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] in ["reference", "inline", "priority"]: Laurent@814: highlights = self.Highlights.get(infos[0], []) Laurent@814: if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: Laurent@814: self.Highlights.pop(infos[0]) andrej@1730: Laurent@814: # Removes all the highlights of one particular type from the block Laurent@814: def ClearHighlight(self, highlight_type=None): Laurent@814: if highlight_type is None: Laurent@814: self.Highlights = {} Laurent@814: else: Laurent@814: highlight_items = self.Highlights.items() Laurent@814: for name, highlights in highlight_items: Laurent@814: highlights = ClearHighlights(highlight, highlight_type) Laurent@814: if len(highlights) == 0: Laurent@814: self.Highlights.pop(name) andrej@1730: Laurent@814: # Draws transition Laurent@814: def Draw(self, dc): Laurent@814: Graphic_Element.Draw(self, dc) Laurent@814: if self.Value: Laurent@814: if self.Forced: Laurent@814: dc.SetPen(MiterPen(wx.CYAN)) Laurent@814: dc.SetBrush(wx.CYAN_BRUSH) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.GREEN)) Laurent@814: dc.SetBrush(wx.GREEN_BRUSH) Laurent@814: elif self.Forced: Laurent@814: dc.SetPen(MiterPen(wx.BLUE)) Laurent@814: dc.SetBrush(wx.BLUE_BRUSH) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.BLACK_BRUSH) andrej@1730: Laurent@814: if getattr(dc, "printing", False): Laurent@814: if self.Type != "connection": Laurent@814: condition_size = dc.GetTextExtent(self.Condition) Laurent@814: if self.Priority != 0: Laurent@814: priority_size = dc.GetTextExtent(str(self.Priority)) Laurent@814: else: Laurent@814: if self.Type != "connection": Laurent@814: condition_size = self.ConditionSize Laurent@814: if self.Priority != 0: Laurent@814: priority_size = self.PrioritySize andrej@1730: Laurent@814: # Draw plain rectangle for representing the transition andrej@1730: dc.DrawRectangle(self.Pos.x, andrej@1730: self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, Laurent@814: self.Size[0] + 1, Laurent@814: SFC_TRANSITION_SIZE[1] + 1) Laurent@814: vertical_line_x = self.Input.GetPosition()[0] andrej@1730: dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) Laurent@814: # Draw transition condition Laurent@814: if self.Type != "connection": Laurent@814: if self.Condition != "": Laurent@814: condition = self.Condition Laurent@814: else: Laurent@814: condition = "Transition" Laurent@814: condition_pos = (self.Pos.x + self.Size[0] + 5, Laurent@814: self.Pos.y + (self.Size[1] - condition_size[1]) / 2) Laurent@814: dc.DrawText(condition, condition_pos[0], condition_pos[1]) Laurent@814: # Draw priority number Laurent@814: if self.Priority != 0: Laurent@814: priority_pos = (self.Pos.x, self.Pos.y - priority_size[1] - 2) Laurent@814: dc.DrawText(str(self.Priority), priority_pos[0], priority_pos[1]) Laurent@814: # Draw input and output connectors Laurent@814: self.Input.Draw(dc) Laurent@814: self.Output.Draw(dc) Laurent@814: if self.Type == "connection": Laurent@814: self.Condition.Draw(dc) andrej@1730: Laurent@814: if not getattr(dc, "printing", False): Laurent@814: for name, highlights in self.Highlights.iteritems(): Laurent@814: if name == "priority": Laurent@814: DrawHighlightedText(dc, str(self.Priority), highlights, priority_pos[0], priority_pos[1]) Laurent@814: else: Laurent@814: DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1]) Laurent@814: andrej@1782: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Sequencial Function Chart Divergence and Convergence andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: Laurent@814: class SFC_Divergence(Graphic_Element): andrej@1736: """ andrej@1736: Class that implements the graphic representation of a divergence or convergence, andrej@1736: selection or simultaneous andrej@1736: """ andrej@1730: Laurent@814: # Create a new divergence andrej@1744: def __init__(self, parent, type, number=2, id=None): Laurent@814: Graphic_Element.__init__(self, parent) Laurent@814: self.Type = type Laurent@814: self.Id = id Laurent@814: self.RealConnectors = None Laurent@814: number = max(2, number) Laurent@814: self.Size = wx.Size((number - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL, self.GetMinSize()[1]) Laurent@814: # Create an input and output connector Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: andrej@1744: self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True)] Laurent@814: self.Outputs = [] Laurent@814: for i in xrange(number): andrej@1744: self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True)) Laurent@814: elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: self.Inputs = [] Laurent@814: for i in xrange(number): andrej@1744: self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True)) andrej@1744: self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True)] Laurent@814: self.Value = None Laurent@814: self.PreviousValue = None andrej@1730: Laurent@814: def Flush(self): Laurent@814: for input in self.Inputs: Laurent@814: input.Flush() Laurent@814: self.Inputs = [] Laurent@814: for output in self.Outputs: Laurent@814: output.Flush() Laurent@814: self.Outputs = [] andrej@1730: Laurent@814: def SpreadCurrent(self): Laurent@814: if self.Parent.Debug: Laurent@814: self.PreviousValue = self.Value Laurent@814: if self.Type == SELECTION_CONVERGENCE: Laurent@814: self.Value = False Laurent@814: for input in self.Inputs: Laurent@814: self.Value |= input.ReceivingCurrent() Laurent@814: elif self.Type == SIMULTANEOUS_CONVERGENCE: Laurent@814: self.Value = True Laurent@814: for input in self.Inputs: Laurent@814: self.Value &= input.ReceivingCurrent() Laurent@814: elif self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: Laurent@814: self.Value = self.Inputs[0].ReceivingCurrent() Laurent@814: else: Laurent@814: self.Value = False Laurent@814: if self.Value and not self.PreviousValue: Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) Laurent@814: for output in self.Outputs: Laurent@814: output.SpreadCurrent(True) Laurent@814: elif not self.Value and self.PreviousValue: Laurent@814: if self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) Laurent@814: for output in self.Outputs: Laurent@814: output.SpreadCurrent(False) andrej@1730: Laurent@814: # Make a clone of this SFC_Divergence andrej@1744: def Clone(self, parent, id=None, pos=None): Laurent@814: divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id) Laurent@814: divergence.SetSize(self.Size[0], self.Size[1]) Laurent@814: if pos is not None: Laurent@814: divergence.SetPosition(pos.x, pos.y) Laurent@814: else: Laurent@814: divergence.SetPosition(self.Pos.x, self.Pos.y) Laurent@814: divergence.Inputs = [input.Clone(divergence) for input in self.Inputs] Laurent@814: divergence.Outputs = [output.Clone(divergence) for output in self.Outputs] Laurent@814: return divergence andrej@1730: Laurent@814: def GetConnectorTranslation(self, element): Laurent@814: return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) andrej@1730: Laurent@814: # Returns the RedrawRect andrej@1744: def GetRedrawRect(self, movex=0, movey=0): Laurent@814: rect = Graphic_Element.GetRedrawRect(self, movex, movey) Laurent@814: if movex != 0 or movey != 0: Laurent@814: for input in self.Inputs: Laurent@814: if input.IsConnected(): Laurent@814: rect = rect.Union(input.GetConnectedRedrawRect(movex, movey)) Laurent@814: for output in self.Outputs: Laurent@814: if output.IsConnected(): Laurent@814: rect = rect.Union(output.GetConnectedRedrawRect(movex, movey)) Laurent@814: return rect andrej@1730: Laurent@814: # Forbids to resize the divergence Laurent@814: def Resize(self, x, y, width, height): Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1]) andrej@1730: Laurent@814: # Delete this divergence by calling the appropriate method Laurent@814: def Delete(self): Laurent@814: self.Parent.DeleteDivergence(self) andrej@1730: Laurent@814: # Returns the divergence type Laurent@814: def GetType(self): Laurent@814: return self.Type andrej@1730: Laurent@814: # Unconnect input and output Laurent@814: def Clean(self): Laurent@814: for input in self.Inputs: andrej@1744: input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) Laurent@814: for output in self.Outputs: andrej@1744: output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1730: Laurent@814: # Add a branch to the divergence Laurent@814: def AddBranch(self): Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: Laurent@814: maxx = 0 Laurent@814: for output in self.Outputs: Laurent@814: pos = output.GetRelPosition() Laurent@814: maxx = max(maxx, pos.x) andrej@1744: connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True) Laurent@814: self.Outputs.append(connector) Laurent@814: self.MoveConnector(connector, 0) Laurent@814: elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: maxx = 0 Laurent@814: for input in self.Inputs: Laurent@814: pos = input.GetRelPosition() Laurent@814: maxx = max(maxx, pos.x) andrej@1744: connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True) Laurent@814: self.Inputs.append(connector) Laurent@814: self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL) andrej@1730: Laurent@814: # Remove a branch from the divergence Laurent@814: def RemoveBranch(self, connector): Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: Laurent@814: if connector in self.Outputs and len(self.Outputs) > 2: Laurent@814: self.Outputs.remove(connector) Laurent@814: self.MoveConnector(self.Outputs[0], 0) Laurent@814: elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: if connector in self.Inputs and len(self.Inputs) > 2: Laurent@814: self.Inputs.remove(connector) Laurent@814: self.MoveConnector(self.Inputs[0], 0) andrej@1730: Laurent@814: # Remove the handled branch from the divergence Laurent@814: def RemoveHandledBranch(self): Laurent@814: handle_type, handle = self.Handle Laurent@814: if handle_type == HANDLE_CONNECTOR: Laurent@814: handle.UnConnect(delete=True) Laurent@814: self.RemoveBranch(handle) andrej@1730: Laurent@814: # Return the number of branches for the divergence Laurent@814: def GetBranchNumber(self): Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: Laurent@814: return len(self.Outputs) Laurent@814: elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: return len(self.Inputs) andrej@1730: Laurent@814: # Returns if the point given is in the bounding box Laurent@814: def HitTest(self, pt, connectors=True): andrej@1743: return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) is not None andrej@1730: Laurent@814: # Refresh the divergence bounding box Laurent@814: def RefreshBoundingBox(self): Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: andrej@1768: self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, andrej@1768: self.Size[0] + 1, self.Size[1] + 1) Laurent@814: elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: andrej@1768: self.BoundingBox = wx.Rect( andrej@1768: self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, Laurent@814: self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1) andrej@1730: Laurent@814: # Refresh the position of wires connected to divergence andrej@1744: def RefreshConnected(self, exclude=[]): Laurent@814: for input in self.Inputs: Laurent@814: input.MoveConnected(exclude) Laurent@814: for output in self.Outputs: Laurent@814: output.MoveConnected(exclude) andrej@1730: Laurent@814: # Moves the divergence connector given Laurent@814: def MoveConnector(self, connector, movex): Laurent@814: position = connector.GetRelPosition() Laurent@814: connector.SetPosition(wx.Point(position.x + movex, position.y)) Laurent@814: minx = self.Size[0] Laurent@814: maxx = 0 Laurent@814: for input in self.Inputs: Laurent@814: input_pos = input.GetRelPosition() Laurent@814: minx = min(minx, input_pos.x) Laurent@814: maxx = max(maxx, input_pos.x) Laurent@814: for output in self.Outputs: Laurent@814: output_pos = output.GetRelPosition() Laurent@814: minx = min(minx, output_pos.x) Laurent@814: maxx = max(maxx, output_pos.x) Laurent@814: if minx != 0: Laurent@814: for input in self.Inputs: Laurent@814: input_pos = input.GetRelPosition() Laurent@814: input.SetPosition(wx.Point(input_pos.x - minx, input_pos.y)) Laurent@814: for output in self.Outputs: Laurent@814: output_pos = output.GetRelPosition() Laurent@814: output.SetPosition(wx.Point(output_pos.x - minx, output_pos.y)) Laurent@814: self.Inputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x)) Laurent@814: self.Outputs.sort(lambda x, y: cmp(x.Pos.x, y.Pos.x)) Laurent@814: self.Pos.x += minx Laurent@814: self.Size[0] = maxx - minx Laurent@814: connector.MoveConnected() Laurent@814: self.RefreshBoundingBox() andrej@1730: andrej@1730: # Returns the divergence connector that starts with the point given if it exists andrej@1744: def GetConnector(self, position, name=None): Laurent@814: # if a name is given Laurent@814: if name is not None: Laurent@814: # Test each input and output connector andrej@1782: # for input in self.Inputs: Laurent@814: # if name == input.GetName(): Laurent@814: # return input Laurent@814: for output in self.Outputs: Laurent@814: if name == output.GetName(): Laurent@814: return output Laurent@814: return self.FindNearestConnector(position, self.Inputs + self.Outputs) andrej@1730: andrej@1730: # Returns input and output divergence connectors Laurent@814: def GetConnectors(self): Laurent@814: return {"inputs": self.Inputs, "outputs": self.Outputs} andrej@1730: Laurent@814: # Test if point given is on divergence input or output connector andrej@1744: def TestConnector(self, pt, direction=None, exclude=True): Laurent@814: # Test input connector Laurent@814: for input in self.Inputs: Laurent@814: if input.TestPoint(pt, direction, exclude): Laurent@814: return input Laurent@814: # Test output connector Laurent@814: for output in self.Outputs: Laurent@814: if output.TestPoint(pt, direction, exclude): Laurent@814: return output Laurent@814: return None andrej@1730: Laurent@814: # Changes the divergence size Laurent@814: def SetSize(self, width, height): Laurent@814: height = self.GetMinSize()[1] Laurent@814: for i, input in enumerate(self.Inputs): Laurent@814: position = input.GetRelPosition() Laurent@814: if self.RealConnectors: Laurent@814: input.SetPosition(wx.Point(int(round(self.RealConnectors["Inputs"][i] * width)), 0)) Laurent@814: else: Laurent@814: input.SetPosition(wx.Point(int(round(float(position.x)*float(width)/float(self.Size[0]))), 0)) Laurent@814: input.MoveConnected() Laurent@814: for i, output in enumerate(self.Outputs): Laurent@814: position = output.GetRelPosition() Laurent@814: if self.RealConnectors: Laurent@814: output.SetPosition(wx.Point(int(round(self.RealConnectors["Outputs"][i] * width)), height)) Laurent@814: else: Laurent@814: output.SetPosition(wx.Point(int(round(float(position.x)*float(width)/float(self.Size[0]))), height)) Laurent@814: output.MoveConnected() Laurent@814: self.Size = wx.Size(width, height) Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Returns the divergence minimum size Laurent@814: def GetMinSize(self, default=False): Laurent@814: width = 0 Laurent@814: if default: Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: Laurent@814: width = (len(self.Outputs) - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL Laurent@814: elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: width = (len(self.Inputs) - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: Laurent@814: return width, 1 Laurent@814: elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: return width, 3 Laurent@814: return 0, 0 andrej@1730: Laurent@814: # Refresh the position of the block connected to connector Laurent@814: def RefreshConnectedPosition(self, connector): Laurent@814: wires = connector.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: current_pos = connector.GetPosition(False) Laurent@814: next = wires[0][0].GetOtherConnected(connector) Laurent@814: next_pos = next.GetPosition(False) Laurent@814: diffx = current_pos.x - next_pos.x Laurent@814: next_block = next.GetParentBlock() Laurent@814: if isinstance(next_block, SFC_Divergence): Laurent@814: next_block.MoveConnector(next, diffx) Laurent@814: else: Laurent@814: next_block.Move(diffx, 0) Laurent@814: if connector in self.Inputs: Laurent@814: next_block.RefreshInputPosition() Laurent@814: else: Laurent@814: next_block.RefreshOutputPosition() Laurent@814: Laurent@814: # Refresh the position of this divergence Laurent@814: def RefreshPosition(self): Laurent@814: y = 0 Laurent@814: for input in self.Inputs: Laurent@814: wires = input.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: previous = wires[0][0].GetOtherConnected(input) Laurent@814: previous_pos = previous.GetPosition(False) Laurent@814: y = max(y, previous_pos.y + GetWireSize(previous.GetParentBlock())) Laurent@814: diffy = y - self.Pos.y Laurent@814: if diffy != 0: Laurent@814: self.Move(0, diffy, self.Parent.Wires) Laurent@814: self.RefreshOutputPosition((0, diffy)) Laurent@814: for input in self.Inputs: Laurent@814: input.MoveConnected() andrej@1730: Laurent@814: # Align output element with this divergence andrej@1744: def RefreshOutputPosition(self, move=None): Laurent@814: if move: Laurent@814: for output_connector in self.Outputs: Laurent@814: wires = output_connector.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: current_pos = output_connector.GetPosition(False) Laurent@814: output = wires[0][0].GetOtherConnected(self.Output) Laurent@814: output_pos = output.GetPosition(False) Laurent@814: diffx = current_pos.x - output_pos.x Laurent@814: output_block = output.GetParentBlock() Laurent@814: if isinstance(output_block, SFC_Step): Laurent@814: output_block.MoveActionBlock(move) Laurent@814: wires[0][0].Move(move[0], move[1], True) Laurent@814: if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: Laurent@814: output_block.Move(move[0], move[1], self.Parent.Wires) Laurent@814: output_block.RefreshOutputPosition(move) andrej@1730: Laurent@814: # Method called when a LeftDown event have been generated Laurent@814: def OnLeftDown(self, event, dc, scaling): andrej@1740: self.RealConnectors = {"Inputs": [], "Outputs": []} Laurent@1258: for input in self.Inputs: Laurent@1258: position = input.GetRelPosition() Laurent@1258: self.RealConnectors["Inputs"].append(float(position.x)/float(self.Size[0])) Laurent@1258: for output in self.Outputs: Laurent@1258: position = output.GetRelPosition() Laurent@1258: self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0])) Laurent@1258: Graphic_Element.OnLeftDown(self, event, dc, scaling) andrej@1730: Laurent@1258: # Method called when a LeftUp event have been generated Laurent@1258: def OnLeftUp(self, event, dc, scaling): Laurent@1258: Graphic_Element.OnLeftUp(self, event, dc, scaling) Laurent@1258: self.RealConnectors = None andrej@1730: Laurent@1258: # Method called when a RightDown event have been generated Laurent@1258: def OnRightDown(self, event, dc, scaling): Laurent@1258: pos = GetScaledEventPosition(event, dc, scaling) Laurent@1258: # Test if a connector have been handled Laurent@1258: connector = self.TestConnector(pos, exclude=False) Laurent@814: if connector: Laurent@814: self.Handle = (HANDLE_CONNECTOR, connector) Laurent@814: wx.CallAfter(self.Parent.SetCurrentCursor, 1) Laurent@814: self.Selected = False Laurent@814: # Initializes the last position Laurent@814: self.oldPos = GetScaledEventPosition(event, dc, scaling) Laurent@814: else: Laurent@1258: Graphic_Element.OnRightDown(self, event, dc, scaling) andrej@1730: Laurent@1258: # Method called when a RightUp event have been generated Laurent@1258: def OnRightUp(self, event, dc, scaling): Laurent@1258: pos = GetScaledEventPosition(event, dc, scaling) Laurent@814: handle_type, handle = self.Handle Laurent@814: if handle_type == HANDLE_CONNECTOR and self.Dragging and self.oldPos: Laurent@814: wires = handle.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: block = wires[0][0].GetOtherConnected(handle).GetParentBlock() Laurent@814: block.RefreshModel(False) Laurent@814: if not isinstance(block, SFC_Divergence): Laurent@814: if handle in self.Inputs: Laurent@814: block.RefreshInputModel() Laurent@814: else: Laurent@814: block.RefreshOutputModel() Laurent@1258: Graphic_Element.OnRightUp(self, event, dc, scaling) Laurent@1258: else: Laurent@1258: # Popup the menu with special items for a block and a connector if one is handled Laurent@1258: connector = self.TestConnector(pos, exclude=False) Laurent@1258: if connector: Laurent@1258: self.Handle = (HANDLE_CONNECTOR, connector) Laurent@1258: self.Parent.PopupDivergenceMenu(True) Laurent@1258: else: Laurent@1258: # Popup the divergence menu without delete branch Laurent@1258: self.Parent.PopupDivergenceMenu(False) andrej@1730: Laurent@814: # Refreshes the divergence state according to move defined and handle selected Laurent@814: def ProcessDragging(self, movex, movey, event, scaling): Laurent@814: handle_type, handle = self.Handle Laurent@814: # A connector has been handled Laurent@814: if handle_type == HANDLE_CONNECTOR: Laurent@814: movex = max(-self.BoundingBox.x, movex) Laurent@814: if scaling is not None: Laurent@814: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: self.MoveConnector(handle, movex) Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.RefreshConnectedPosition(handle) Laurent@814: return movex, 0 Laurent@814: elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) Laurent@814: return 0, 0 andrej@1730: Laurent@814: # Refresh output element model Laurent@814: def RefreshOutputModel(self, move=False): Laurent@814: if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: for output in self.Outputs: Laurent@814: wires = output.GetWires() Laurent@814: if len(wires) != 1: Laurent@814: return Laurent@814: output_block = wires[0][0].GetOtherConnected(output).GetParentBlock() Laurent@814: output_block.RefreshModel(False) Laurent@814: if not isinstance(output_block, SFC_Divergence) or move: Laurent@814: output_block.RefreshOutputModel(move) andrej@1730: Laurent@814: # Refreshes the divergence model Laurent@814: def RefreshModel(self, move=True): Laurent@814: self.Parent.RefreshDivergenceModel(self) Laurent@814: # If divergence has moved, refresh the model of wires connected to outputs Laurent@814: if move: Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.RefreshOutputModel() Laurent@814: else: Laurent@814: for output in self.Outputs: Laurent@814: output.RefreshWires() andrej@1730: Laurent@814: # Draws the highlightment of this element if it is highlighted Laurent@814: def DrawHighlightment(self, dc): Laurent@814: scalex, scaley = dc.GetUserScale() Laurent@814: dc.SetUserScale(1, 1) Laurent@814: dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) Laurent@814: dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) Laurent@814: dc.SetLogicalFunction(wx.AND) Laurent@814: # Draw two rectangles for representing the contact Laurent@814: posx = self.Pos.x Laurent@814: width = self.Size[0] Laurent@814: if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: Laurent@814: posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA Laurent@814: width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2 andrej@1730: dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, andrej@1730: int(round((self.Pos.y - 1) * scaley)) - 2, andrej@1730: int(round((width + 3) * scalex)) + 5, Laurent@814: int(round((self.Size.height + 3) * scaley)) + 5) Laurent@814: dc.SetLogicalFunction(wx.COPY) Laurent@814: dc.SetUserScale(scalex, scaley) andrej@1730: Laurent@814: # Draws divergence Laurent@814: def Draw(self, dc): Laurent@814: Graphic_Element.Draw(self, dc) Laurent@814: if self.Value: Laurent@814: dc.SetPen(MiterPen(wx.GREEN)) Laurent@814: dc.SetBrush(wx.GREEN_BRUSH) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.BLACK_BRUSH) Laurent@814: # Draw plain rectangle for representing the divergence Laurent@814: if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: Laurent@814: dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) Laurent@814: elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: andrej@1730: dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, Laurent@814: self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y) andrej@1730: dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], Laurent@814: self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1]) Laurent@814: # Draw inputs and outputs connectors Laurent@814: for input in self.Inputs: Laurent@814: input.Draw(dc) Laurent@814: for output in self.Outputs: Laurent@814: output.Draw(dc) andrej@1730: Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Sequencial Function Chart Jump to Step andrej@1782: # ------------------------------------------------------------------------------- andrej@1782: Laurent@814: Laurent@814: class SFC_Jump(Graphic_Element): andrej@1736: """ andrej@1736: Class that implements the graphic representation of a jump to step andrej@1736: """ andrej@1730: Laurent@814: # Create a new jump andrej@1744: def __init__(self, parent, target, id=None): Laurent@814: Graphic_Element.__init__(self, parent) Laurent@814: self.SetTarget(target) Laurent@814: self.Id = id Laurent@814: self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1]) Laurent@814: self.Highlights = [] Laurent@814: # Create an input and output connector andrej@1744: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) Laurent@814: self.Value = None Laurent@814: self.PreviousValue = None andrej@1730: Laurent@814: def Flush(self): Laurent@814: if self.Input is not None: Laurent@814: self.Input.Flush() Laurent@814: self.Input = None andrej@1730: Laurent@814: def SpreadCurrent(self): Laurent@814: if self.Parent.Debug: Laurent@814: self.PreviousValue = self.Value Laurent@814: self.Value = self.Input.ReceivingCurrent() Laurent@814: if self.Value != self.PreviousValue and self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) andrej@1730: Laurent@814: # Make a clone of this SFC_Jump andrej@1744: def Clone(self, parent, id=None, pos=None): Laurent@814: jump = SFC_Jump(parent, self.Target, id) Laurent@814: jump.SetSize(self.Size[0], self.Size[1]) Laurent@814: if pos is not None: Laurent@814: jump.SetPosition(pos.x, pos.y) Laurent@814: else: Laurent@814: jump.SetPosition(self.Pos.x, self.Pos.y) Laurent@814: jump.Input = self.Input.Clone(jump) Laurent@814: return jump andrej@1730: Laurent@814: def GetConnectorTranslation(self, element): andrej@1739: return {self.Input: element.Input} andrej@1730: Laurent@814: # Returns the RedrawRect andrej@1744: def GetRedrawRect(self, movex=0, movey=0): Laurent@814: rect = Graphic_Element.GetRedrawRect(self, movex, movey) surkovsv93@1652: if self.Input: surkovsv93@1652: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) Laurent@814: if movex != 0 or movey != 0: Laurent@814: if self.Input.IsConnected(): Laurent@814: rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) Laurent@814: return rect andrej@1730: Laurent@814: # Forbids to change the jump size Laurent@814: def SetSize(self, width, height): Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: Graphic_Element.SetSize(self, width, height) andrej@1730: Laurent@814: # Forbids to resize jump Laurent@814: def Resize(self, x, y, width, height): Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: Graphic_Element.Resize(self, x, y, width, height) andrej@1730: Laurent@814: # Delete this jump by calling the appropriate method Laurent@814: def Delete(self): Laurent@814: self.Parent.DeleteJump(self) andrej@1730: Laurent@814: # Unconnect input Laurent@814: def Clean(self): andrej@1744: self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1730: Laurent@814: # Refresh the size of text for target Laurent@814: def RefreshTargetSize(self): Laurent@814: self.TargetSize = self.Parent.GetTextExtent(self.Target) andrej@1730: Laurent@814: # Returns if the point given is in the bounding box Laurent@814: def HitTest(self, pt, connectors=True): Laurent@814: # Calculate the bounding box of the condition outside the transition Laurent@814: text_width, text_height = self.TargetSize Laurent@814: text_bbx = wx.Rect(self.Pos.x + self.Size[0] + 2, Laurent@814: self.Pos.y + (self.Size[1] - text_height) / 2, Laurent@814: text_width, Laurent@814: text_height) Laurent@814: return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors) andrej@1730: Laurent@814: # Refresh the jump bounding box Laurent@814: def RefreshBoundingBox(self): Laurent@814: text_width, text_height = self.Parent.GetTextExtent(self.Target) Laurent@814: # Calculate the bounding box size Laurent@814: bbx_width = self.Size[0] + 2 + text_width andrej@1768: self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, andrej@1768: bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1) andrej@1730: Laurent@814: # Returns the connector connected to input Laurent@814: def GetPreviousConnector(self): Laurent@814: wires = self.Input.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: return wires[0][0].GetOtherConnected(self.Input) Laurent@814: return None andrej@1730: Laurent@814: # Refresh the element connectors position Laurent@814: def RefreshConnectors(self): Laurent@814: scaling = self.Parent.GetScaling() Laurent@814: horizontal_pos = self.Size[0] / 2 Laurent@814: if scaling is not None: Laurent@814: horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: self.Input.SetPosition(wx.Point(horizontal_pos, 0)) Laurent@814: self.RefreshConnected() andrej@1730: Laurent@814: # Refresh the position of wires connected to jump andrej@1744: def RefreshConnected(self, exclude=[]): Laurent@814: if self.Input: Laurent@814: self.Input.MoveConnected(exclude) andrej@1730: andrej@1730: # Returns input jump connector andrej@1744: def GetConnector(self, position=None, name=None): Laurent@814: return self.Input andrej@1730: andrej@1730: # Returns all the jump connectors Laurent@814: def GetConnectors(self): Laurent@814: return {"inputs": [self.Input], "outputs": []} andrej@1730: Laurent@814: # Test if point given is on jump input connector andrej@1744: def TestConnector(self, pt, direction=None, exclude=True): Laurent@814: # Test input connector Laurent@814: if self.Input and self.Input.TestPoint(pt, direction, exclude): Laurent@814: return self.Input Laurent@814: return None andrej@1730: Laurent@814: # Changes the jump target Laurent@814: def SetTarget(self, target): Laurent@814: self.Target = target Laurent@814: self.RefreshTargetSize() Laurent@814: self.RefreshBoundingBox() andrej@1730: Laurent@814: # Returns the jump target Laurent@814: def GetTarget(self): Laurent@814: return self.Target andrej@1730: Laurent@814: # Returns the jump minimum size Laurent@814: def GetMinSize(self): Laurent@814: return SFC_JUMP_SIZE andrej@1730: Laurent@814: # Align input element with this jump Laurent@814: def RefreshInputPosition(self): Laurent@814: if self.Input: Laurent@814: current_pos = self.Input.GetPosition(False) Laurent@814: input = self.GetPreviousConnector() Laurent@814: if input: Laurent@814: input_pos = input.GetPosition(False) Laurent@814: diffx = current_pos.x - input_pos.x Laurent@814: input_block = input.GetParentBlock() Laurent@814: if isinstance(input_block, SFC_Divergence): Laurent@814: input_block.MoveConnector(input, diffx) Laurent@814: else: Laurent@814: if isinstance(input_block, SFC_Step): Laurent@814: input_block.MoveActionBlock((diffx, 0)) Laurent@814: input_block.Move(diffx, 0) Laurent@814: input_block.RefreshInputPosition() andrej@1730: Laurent@814: # Can't align output element, because there is no output andrej@1744: def RefreshOutputPosition(self, move=None): Laurent@814: pass andrej@1730: Laurent@814: # Method called when a LeftDClick event have been generated Laurent@814: def OnLeftDClick(self, event, dc, scaling): Laurent@814: # Edit the jump properties Laurent@814: self.Parent.EditJumpContent(self) andrej@1730: Laurent@814: # Method called when a RightUp event have been generated Laurent@814: def OnRightUp(self, event, dc, scaling): Laurent@814: # Popup the default menu Laurent@814: self.Parent.PopupDefaultMenu() andrej@1730: Laurent@814: # Refreshes the jump state according to move defined and handle selected Laurent@814: def ProcessDragging(self, movex, movey, event, scaling): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: movex = max(-self.BoundingBox.x, movex) Laurent@814: if scaling is not None: Laurent@814: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: self.Move(movex, 0) Laurent@814: self.RefreshInputPosition() Laurent@814: return movex, 0 Laurent@814: else: andrej@1744: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2) andrej@1730: Laurent@814: # Refresh input element model Laurent@814: def RefreshInputModel(self): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: input = self.GetPreviousConnector() Laurent@814: if input: Laurent@814: input_block = input.GetParentBlock() Laurent@814: input_block.RefreshModel(False) Laurent@814: if not isinstance(input_block, SFC_Divergence): Laurent@814: input_block.RefreshInputModel() andrej@1730: Laurent@814: # Refresh output element model Laurent@814: def RefreshOutputModel(self, move=False): Laurent@814: pass andrej@1730: Laurent@814: # Refreshes the jump model Laurent@814: def RefreshModel(self, move=True): Laurent@814: self.Parent.RefreshJumpModel(self) Laurent@814: if move: Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: self.RefreshInputModel() andrej@1730: Laurent@814: # Adds an highlight to the variable Laurent@814: def AddHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "target" and start[0] == 0 and end[0] == 0: Laurent@814: AddHighlight(self.Highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes an highlight from the variable Laurent@814: def RemoveHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "target": Laurent@814: RemoveHighlight(self.Highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes all the highlights of one particular type from the variable Laurent@814: def ClearHighlight(self, highlight_type=None): Laurent@814: ClearHighlights(self.Highlights, highlight_type) andrej@1730: Laurent@814: # Draws the highlightment of this element if it is highlighted Laurent@814: def DrawHighlightment(self, dc): Laurent@814: scalex, scaley = dc.GetUserScale() Laurent@814: dc.SetUserScale(1, 1) Laurent@814: dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) Laurent@814: dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) Laurent@814: dc.SetLogicalFunction(wx.AND) andrej@1730: points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, Laurent@814: int(round((self.Pos.y - 2) * scaley)) - 2), andrej@1730: wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, Laurent@814: int(round((self.Pos.y - 2) * scaley)) - 2), andrej@1730: wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), Laurent@814: int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)] Laurent@814: dc.DrawPolygon(points) Laurent@814: dc.SetLogicalFunction(wx.COPY) Laurent@814: dc.SetUserScale(scalex, scaley) andrej@1730: Laurent@814: # Draws divergence Laurent@814: def Draw(self, dc): Laurent@814: Graphic_Element.Draw(self, dc) Laurent@814: if self.Value: Laurent@814: dc.SetPen(MiterPen(wx.GREEN)) Laurent@814: dc.SetBrush(wx.GREEN_BRUSH) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.BLACK_BRUSH) andrej@1730: Laurent@814: if getattr(dc, "printing", False): Laurent@814: target_size = dc.GetTextExtent(self.Target) Laurent@814: else: Laurent@814: target_size = self.TargetSize andrej@1730: Laurent@814: # Draw plain rectangle for representing the divergence Laurent@814: dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1]) Laurent@814: points = [wx.Point(self.Pos.x, self.Pos.y), Laurent@814: wx.Point(self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1] / 3), Laurent@814: wx.Point(self.Pos.x + self.Size[0], self.Pos.y), Laurent@814: wx.Point(self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1])] Laurent@814: dc.DrawPolygon(points) Laurent@814: target_pos = (self.Pos.x + self.Size[0] + 2, Laurent@814: self.Pos.y + (self.Size[1] - target_size[1]) / 2) Laurent@814: dc.DrawText(self.Target, target_pos[0], target_pos[1]) Laurent@814: # Draw input connector Laurent@814: if self.Input: Laurent@814: self.Input.Draw(dc) andrej@1730: Laurent@814: if not getattr(dc, "printing", False): Laurent@814: DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1]) andrej@1730: Laurent@814: andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: # Sequencial Function Chart Action Block andrej@1782: # ------------------------------------------------------------------------------- Laurent@814: Laurent@814: Laurent@814: class SFC_ActionBlock(Graphic_Element): andrej@1736: """ andrej@1736: Class that implements the graphic representation of an action block andrej@1736: """ andrej@1730: Laurent@814: # Create a new action block andrej@1744: def __init__(self, parent, actions=[], id=None): Laurent@814: Graphic_Element.__init__(self, parent) Laurent@814: self.Id = id Laurent@814: self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) Laurent@814: self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) Laurent@814: self.Highlights = {} Laurent@814: # Create an input and output connector andrej@1744: self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone=True) Laurent@814: self.SetActions(actions) Laurent@814: self.Value = None Laurent@814: self.PreviousValue = None andrej@1730: Laurent@814: def Flush(self): Laurent@814: if self.Input is not None: Laurent@814: self.Input.Flush() Laurent@814: self.Input = None andrej@1730: Laurent@814: def SpreadCurrent(self): Laurent@814: if self.Parent.Debug: Laurent@814: self.PreviousValue = self.Value Laurent@814: self.Value = self.Input.ReceivingCurrent() Laurent@814: if self.Value != self.PreviousValue and self.Visible: Laurent@814: self.Parent.ElementNeedRefresh(self) andrej@1730: Laurent@814: # Make a clone of this SFC_ActionBlock andrej@1744: def Clone(self, parent, id=None, pos=None): Laurent@814: actions = [action.copy() for action in self.Actions] Laurent@814: action_block = SFC_ActionBlock(parent, actions, id) Laurent@814: action_block.SetSize(self.Size[0], self.Size[1]) Laurent@814: if pos is not None: Laurent@814: action_block.SetPosition(pos.x, pos.y) Laurent@814: else: Laurent@814: action_block.SetPosition(self.Pos.x, self.Pos.y) Laurent@814: action_block.Input = self.Input.Clone(action_block) Laurent@814: return action_block andrej@1730: Laurent@814: def GetConnectorTranslation(self, element): andrej@1739: return {self.Input: element.Input} andrej@1730: Laurent@814: # Returns the RedrawRect andrej@1744: def GetRedrawRect(self, movex=0, movey=0): Laurent@814: rect = Graphic_Element.GetRedrawRect(self, movex, movey) surkovsv93@1652: if self.Input: surkovsv93@1652: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) Laurent@814: if movex != 0 or movey != 0: Laurent@814: if self.Input.IsConnected(): Laurent@814: rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) Laurent@814: return rect andrej@1730: Laurent@814: # Returns the number of action lines Laurent@814: def GetLineNumber(self): Laurent@814: return len(self.Actions) andrej@1730: Laurent@814: def GetLineSize(self): Laurent@814: if len(self.Actions) > 0: Laurent@814: return self.Size[1] / len(self.Actions) Laurent@814: else: Laurent@814: return SFC_ACTION_MIN_SIZE[1] andrej@1730: Laurent@814: # Forbids to resize the action block Laurent@814: def Resize(self, x, y, width, height): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: if x == 0: Laurent@814: self.SetSize(width, self.Size[1]) Laurent@814: else: Laurent@814: Graphic_Element.Resize(self, x, y, width, height) andrej@1730: Laurent@814: # Delete this action block by calling the appropriate method Laurent@814: def Delete(self): Laurent@814: self.Parent.DeleteActionBlock(self) andrej@1730: Laurent@814: # Unconnect input and output Laurent@814: def Clean(self): andrej@1744: self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) andrej@1730: Laurent@814: # Refresh the action block bounding box Laurent@814: def RefreshBoundingBox(self): Laurent@814: self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) andrej@1730: Laurent@814: # Refresh the position of wires connected to action block andrej@1744: def RefreshConnected(self, exclude=[]): Laurent@814: self.Input.MoveConnected(exclude) andrej@1730: andrej@1730: # Returns input action block connector andrej@1744: def GetConnector(self, position=None, name=None): Laurent@814: return self.Input andrej@1730: andrej@1730: # Returns all the action block connectors Laurent@814: def GetConnectors(self): Laurent@814: return {"inputs": [self.Input], "outputs": []} andrej@1730: Laurent@814: # Test if point given is on action block input connector andrej@1744: def TestConnector(self, pt, direction=None, exclude=True): Laurent@814: # Test input connector Laurent@814: if self.Input.TestPoint(pt, direction, exclude): Laurent@814: return self.Input Laurent@814: return None andrej@1730: Laurent@814: # Refresh the element connectors position Laurent@814: def RefreshConnectors(self): Laurent@814: scaling = self.Parent.GetScaling() Laurent@814: vertical_pos = SFC_ACTION_MIN_SIZE[1] / 2 Laurent@814: if scaling is not None: Laurent@814: vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y Laurent@814: self.Input.SetPosition(wx.Point(0, vertical_pos)) Laurent@814: self.RefreshConnected() andrej@1730: Laurent@814: # Changes the action block actions Laurent@814: def SetActions(self, actions): Laurent@814: self.Actions = actions Laurent@814: self.ColSize = [0, 0, 0] Laurent@814: min_height = 0 Laurent@814: for action in self.Actions: Laurent@1339: width, height = self.Parent.GetTextExtent( Laurent@1339: action.qualifier if action.qualifier != "" else "N") Laurent@814: self.ColSize[0] = max(self.ColSize[0], width + 10) Laurent@814: row_height = height Laurent@1339: if action.duration != "": Laurent@1338: width, height = self.Parent.GetTextExtent(action.duration) Laurent@814: row_height = max(row_height, height) Laurent@814: self.ColSize[0] = max(self.ColSize[0], width + 10) Laurent@1338: width, height = self.Parent.GetTextExtent(action.value) Laurent@814: row_height = max(row_height, height) Laurent@814: self.ColSize[1] = max(self.ColSize[1], width + 10) Laurent@1339: if action.indicator != "": Laurent@1338: width, height = self.Parent.GetTextExtent(action.indicator) Laurent@814: row_height = max(row_height, height) Laurent@814: self.ColSize[2] = max(self.ColSize[2], width + 10) Laurent@814: min_height += row_height + 5 Laurent@814: if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Laurent@814: self.Size = wx.Size(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], max(min_height, SFC_ACTION_MIN_SIZE[1], self.Size[1])) Laurent@814: self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], andrej@1768: SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height) Laurent@814: self.RefreshBoundingBox() Laurent@814: else: Laurent@814: self.Size = wx.Size(max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], andrej@1768: SFC_ACTION_MIN_SIZE[0]), andrej@1768: len(self.Actions) * SFC_ACTION_MIN_SIZE[1]) Laurent@814: self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], andrej@1768: SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1] Laurent@814: self.RefreshBoundingBox() Laurent@1339: if self.Input is not None: Laurent@814: wires = self.Input.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock() Laurent@814: input_block.RefreshOutputPosition() Laurent@814: input_block.RefreshOutputModel(True) andrej@1730: Laurent@814: # Returns the action block actions Laurent@814: def GetActions(self): Laurent@814: return self.Actions andrej@1730: Laurent@814: # Returns the action block minimum size Laurent@814: def GetMinSize(self): Laurent@814: return self.MinSize andrej@1730: Laurent@814: # Method called when a LeftDClick event have been generated Laurent@814: def OnLeftDClick(self, event, dc, scaling): Laurent@814: # Edit the action block properties Laurent@814: self.Parent.EditActionBlockContent(self) andrej@1730: Laurent@814: # Method called when a RightUp event have been generated Laurent@814: def OnRightUp(self, event, dc, scaling): Laurent@814: # Popup the default menu Laurent@814: self.Parent.PopupDefaultMenu() andrej@1730: Laurent@814: # Refreshes the action block state according to move defined and handle selected Laurent@814: def ProcessDragging(self, movex, movey, event, scaling): Laurent@814: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: Laurent@814: handle_type, handle = self.Handle Laurent@814: if handle_type == HANDLE_MOVE: Laurent@814: movex = max(-self.BoundingBox.x, movex) Laurent@814: if scaling is not None: Laurent@814: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x Laurent@814: wires = self.Input.GetWires() Laurent@814: if len(wires) == 1: Laurent@814: input_pos = wires[0][0].GetOtherConnected(self.Input).GetPosition(False) Laurent@814: if self.Pos.x - input_pos.x + movex >= SFC_WIRE_MIN_SIZE: Laurent@814: self.Move(movex, 0) Laurent@814: return movex, 0 Laurent@814: return 0, 0 Laurent@814: else: Laurent@814: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) Laurent@814: else: Laurent@814: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) Laurent@814: Laurent@814: # Refreshes the action block model Laurent@814: def RefreshModel(self, move=True): Laurent@814: self.Parent.RefreshActionBlockModel(self) andrej@1730: Laurent@814: # Adds an highlight to the variable Laurent@814: def AddHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "action" and infos[1] < len(self.Actions): Laurent@814: action_highlights = self.Highlights.setdefault(infos[1], {}) Laurent@814: attribute_highlights = action_highlights.setdefault(infos[2], []) Laurent@814: AddHighlight(attribute_highlights, (start, end, highlight_type)) andrej@1730: Laurent@814: # Removes an highlight from the block Laurent@814: def RemoveHighlight(self, infos, start, end, highlight_type): Laurent@814: if infos[0] == "action" and infos[1] < len(self.Actions): Laurent@814: action_highlights = self.Highlights.get(infos[1], {}) Laurent@814: attribute_highlights = action_highlights.setdefault(infos[2], []) Laurent@814: if RemoveHighlight(attribute_highlights, (start, end, highlight_type)) and len(attribute_highlights) == 0: Laurent@814: action_highlights.pop(infos[2]) Laurent@814: if len(action_highlights) == 0: Laurent@814: self.Highlights.pop(infos[1]) andrej@1730: Laurent@814: # Removes all the highlights of one particular type from the block Laurent@814: def ClearHighlight(self, highlight_type=None): Laurent@814: if highlight_type is None: Laurent@814: self.Highlights = {} Laurent@814: else: Laurent@814: highlight_items = self.Highlights.items() Laurent@814: for number, action_highlights in highlight_items: Laurent@814: action_highlight_items = action_highlights.items() Laurent@814: for name, attribute_highlights in action_highlights: Laurent@814: attribute_highlights = ClearHighlights(attribute_highlights, highlight_type) Laurent@814: if len(attribute_highlights) == 0: Laurent@814: action_highlights.pop(name) Laurent@814: if len(action_highlights) == 0: Laurent@814: self.Highlights.pop(number) andrej@1730: Laurent@814: # Draws divergence Laurent@814: def Draw(self, dc): Laurent@814: Graphic_Element.Draw(self, dc) Laurent@814: if self.Value: Laurent@814: dc.SetPen(MiterPen(wx.GREEN)) Laurent@814: else: Laurent@814: dc.SetPen(MiterPen(wx.BLACK)) Laurent@814: dc.SetBrush(wx.WHITE_BRUSH) Laurent@814: colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]] Laurent@814: # Draw plain rectangle for representing the action block Laurent@814: dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) andrej@1730: dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, andrej@1768: self.Pos.x + colsize[0], self.Pos.y + self.Size[1]) andrej@1730: dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, andrej@1768: self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1]) Laurent@814: line_size = self.GetLineSize() Laurent@814: for i, action in enumerate(self.Actions): Laurent@814: if i != 0: andrej@1730: dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, andrej@1768: self.Pos.x + self.Size[0], self.Pos.y + i * line_size) Laurent@1338: qualifier_size = dc.GetTextExtent(action.qualifier) Laurent@1339: if action.duration != "": Laurent@814: qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2, Laurent@814: self.Pos.y + i * line_size + line_size / 2 - qualifier_size[1]) Laurent@1338: duration_size = dc.GetTextExtent(action.duration) Laurent@814: duration_pos = (self.Pos.x + (colsize[0] - duration_size[0]) / 2, Laurent@814: self.Pos.y + i * line_size + line_size / 2) Laurent@1338: dc.DrawText(action.duration, duration_pos[0], duration_pos[1]) Laurent@814: else: Laurent@814: qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2, Laurent@814: self.Pos.y + i * line_size + (line_size - qualifier_size[1]) / 2) Laurent@1338: dc.DrawText(action.qualifier, qualifier_pos[0], qualifier_pos[1]) Laurent@1338: content_size = dc.GetTextExtent(action.value) Laurent@814: content_pos = (self.Pos.x + colsize[0] + (colsize[1] - content_size[0]) / 2, Laurent@814: self.Pos.y + i * line_size + (line_size - content_size[1]) / 2) Laurent@1338: dc.DrawText(action.value, content_pos[0], content_pos[1]) Laurent@1339: if action.indicator != "": Laurent@1338: indicator_size = dc.GetTextExtent(action.indicator) Laurent@814: indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2, Laurent@814: self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2) Laurent@1338: dc.DrawText(action.indicator, indicator_pos[0], indicator_pos[1]) andrej@1730: Laurent@814: if not getattr(dc, "printing", False): Laurent@814: action_highlights = self.Highlights.get(i, {}) Laurent@814: for name, attribute_highlights in action_highlights.iteritems(): Laurent@814: if name == "qualifier": Laurent@1338: DrawHighlightedText(dc, action.qualifier, attribute_highlights, qualifier_pos[0], qualifier_pos[1]) Laurent@814: elif name == "duration": Laurent@1338: DrawHighlightedText(dc, action.duration, attribute_highlights, duration_pos[0], duration_pos[1]) Laurent@814: elif name in ["reference", "inline"]: Laurent@1338: DrawHighlightedText(dc, action.value, attribute_highlights, content_pos[0], content_pos[1]) Laurent@814: elif name == "indicator": Laurent@1338: DrawHighlightedText(dc, action.indicator, attribute_highlights, indicator_pos[0], indicator_pos[1]) andrej@1730: Laurent@814: # Draw input connector Laurent@814: self.Input.Draw(dc)