graphics/LD_Objects.py
changeset 0 b622defdfd98
child 5 f8652b073e84
equal deleted inserted replaced
-1:000000000000 0:b622defdfd98
       
     1 #!/usr/bin/env python
       
     2 # -*- coding: utf-8 -*-
       
     3 
       
     4 #This file is part of PLCOpenEditor, a library implementing an IEC 61131-3 editor
       
     5 #based on the plcopen standard. 
       
     6 #
       
     7 #Copyright (C): Edouard TISSERANT and Laurent BESSARD
       
     8 #
       
     9 #See COPYING file for copyrights details.
       
    10 #
       
    11 #This library is free software; you can redistribute it and/or
       
    12 #modify it under the terms of the GNU Lesser General Public
       
    13 #License as published by the Free Software Foundation; either
       
    14 #version 2.1 of the License, or (at your option) any later version.
       
    15 #
       
    16 #This library is distributed in the hope that it will be useful,
       
    17 #but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    18 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    19 #Lesser General Public License for more details.
       
    20 #
       
    21 #You should have received a copy of the GNU Lesser General Public
       
    22 #License along with this library; if not, write to the Free Software
       
    23 #Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    24 
       
    25 from wxPython.wx import *
       
    26 import wx
       
    27 
       
    28 from GraphicCommons import *
       
    29 from plcopen.structures import *
       
    30 
       
    31 #-------------------------------------------------------------------------------
       
    32 #                         Ladder Diagram PowerRail
       
    33 #-------------------------------------------------------------------------------
       
    34 
       
    35 """
       
    36 Class that implements the graphic representation of a power rail
       
    37 """
       
    38 
       
    39 class LD_PowerRail(Graphic_Element):
       
    40     
       
    41     # Create a new power rail
       
    42     def __init__(self, parent, type, id = None, connectors = [True]):
       
    43         Graphic_Element.__init__(self, parent)
       
    44         self.Type = type
       
    45         self.Id = id
       
    46         # Create a connector or a blank according to 'connectors' and add it in
       
    47         # the connectors list
       
    48         self.Connectors = []
       
    49         for connector in connectors:
       
    50             self.AddConnector(connector)
       
    51         self.RefreshSize()
       
    52         
       
    53     # Destructor
       
    54     def __del__(self):
       
    55         self.Connectors = []
       
    56     
       
    57     # Forbids to change the power rail size
       
    58     def SetSize(self, width, height):
       
    59         pass
       
    60     
       
    61     # Forbids to select a power rail
       
    62     def HitTest(self, pt):
       
    63         return False
       
    64     
       
    65     # Deletes this power rail by calling the appropriate method
       
    66     def Delete(self):
       
    67         self.Parent.DeletePowerRail(self)
       
    68     
       
    69     # Unconnect all connectors
       
    70     def Clean(self):
       
    71         for connector in self.Connectors:
       
    72             if connector:
       
    73                 connector.UnConnect()
       
    74                 
       
    75     # Refresh the power rail bounding box
       
    76     def RefreshBoundingBox(self):
       
    77         dc = wxClientDC(self.Parent)
       
    78         if self.Type == LEFTRAIL:
       
    79             bbx_x = self.Pos.x
       
    80         elif self.Type == RIGHTRAIL:
       
    81             bbx_x = self.Pos.x - CONNECTOR_SIZE
       
    82         self.BoundingBox = wxRect(bbx_x, self.Pos.y, self.Size[0] + CONNECTOR_SIZE + 1, self.Size[1] + 1)
       
    83     
       
    84     # Refresh the power rail size
       
    85     def RefreshSize(self):
       
    86         self.Size = wxSize(2, LD_LINE_SIZE * len(self.Connectors))
       
    87         self.RefreshBoundingBox()
       
    88     
       
    89     # Add a connector or a blank to this power rail at the last place
       
    90     def AddConnector(self, connector = True):
       
    91         self.InsertConnector(len(self.Connectors), connector)
       
    92     
       
    93     # Add a connector or a blank to this power rail at the place given
       
    94     def InsertConnector(self, idx, connector = True):
       
    95         if connector:
       
    96             if self.Type == LEFTRAIL:
       
    97                 connector = Connector(self, "", "BOOL", wxPoint(2, 0), EAST)
       
    98             elif self.Type == RIGHTRAIL:
       
    99                 connector = Connector(self, "", "BOOL", wxPoint(0, 0), WEST)
       
   100             self.Connectors.insert(idx, connector)
       
   101         else:
       
   102             self.Connectors.insert(idx, None)
       
   103         self.RefreshSize()
       
   104         self.RefreshConnectors()
       
   105     
       
   106     # Returns the index in connectors list for the connector given
       
   107     def GetConnectorIndex(self, connector):
       
   108         if connector in self.Connectors:
       
   109             return self.Connectors.index(connector)
       
   110         return None
       
   111     
       
   112     # Returns if there is a connector in connectors list at the index given
       
   113     def IsNullConnector(self, idx):
       
   114         if idx < len(self.Connectors):
       
   115             return self.Connectors[idx] == None
       
   116         return False
       
   117     
       
   118     # Delete the connector or blank from connectors list at the index given
       
   119     def DeleteConnector(self, idx):
       
   120         self.Connectors.pop(idx)
       
   121         self.RefreshConnectors()
       
   122         self.RefreshSize()
       
   123     
       
   124     # Refresh the positions of the power rail connectors
       
   125     def RefreshConnectors(self):
       
   126         position = LD_LINE_SIZE / 2
       
   127         for connector in self.Connectors:
       
   128             if connector:
       
   129                 if self.Type == LEFTRAIL:
       
   130                     connector.SetPosition(wxPoint(self.Size[0], position))
       
   131                 elif self.Type == RIGHTRAIL:
       
   132                     connector.SetPosition(wxPoint(0, position))
       
   133             position += LD_LINE_SIZE
       
   134         self.RefreshConnected()
       
   135     
       
   136     # Refresh the position of wires connefcted to power rail
       
   137     def RefreshConnected(self, exclude = []):
       
   138         for connector in self.Connectors:
       
   139             connector.MoveConnected(exclude)
       
   140     
       
   141     # Returns the power rail connector that starts with the point given if it exists 
       
   142     def GetConnector(self, position):
       
   143         for connector in self.Connectors:
       
   144             if connector:
       
   145                 connector_pos = connector.GetRelPosition()
       
   146                 if position.x == self.Pos.x + connector_pos.x and position.y == self.Pos.y + connector_pos.y:
       
   147                     return connector
       
   148         return None
       
   149     
       
   150     # Returns all the power rail connectors 
       
   151     def GetConnectors(self):
       
   152         return [connector for connector in self.Connectors if connector]
       
   153     
       
   154     # Test if point given is on one of the power rail connectors
       
   155     def TestConnector(self, pt, exclude=True):
       
   156         for connector in self.Connectors:
       
   157             if connector and connector.TestPoint(pt, exclude):
       
   158                 return connector
       
   159         return None
       
   160     
       
   161     # Returns the power rail type
       
   162     def GetType(self):
       
   163         return self.Type
       
   164     
       
   165     # Refreshes the power rail model
       
   166     def RefreshModel(self, move=True):
       
   167         self.Parent.RefreshPowerRailModel(self)
       
   168         # If power rail has moved and power rail is of type LEFT, refresh the model 
       
   169         # of wires connected to connectors
       
   170         if move and self.Type == LEFTRAIL:
       
   171             for connector in self.Connectors:
       
   172                 if connector:
       
   173                     connector.RefreshWires()
       
   174     
       
   175     # Draws power rail
       
   176     def Draw(self, dc):
       
   177         dc.SetPen(wxBLACK_PEN)
       
   178         dc.SetBrush(wxBLACK_BRUSH)
       
   179         # Draw a rectangle with the power rail size
       
   180         dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1)
       
   181         # Draw connectors
       
   182         for connector in self.Connectors:
       
   183             if connector:
       
   184                 connector.Draw(dc)
       
   185         Graphic_Element.Draw(self, dc)
       
   186 
       
   187 
       
   188 #-------------------------------------------------------------------------------
       
   189 #                         Ladder Diagram Contact
       
   190 #-------------------------------------------------------------------------------
       
   191 
       
   192 """
       
   193 Class that implements the graphic representation of a contact
       
   194 """
       
   195 
       
   196 class LD_Contact(Graphic_Element):
       
   197     
       
   198     # Create a new contact
       
   199     def __init__(self, parent, type, name, id = None):
       
   200         Graphic_Element.__init__(self, parent)
       
   201         self.Type = type
       
   202         self.Name = name
       
   203         self.Id = id
       
   204         self.Size = wxSize(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1])
       
   205         # Create an input and output connector
       
   206         self.Input = Connector(self, "", "BOOL", wxPoint(0, self.Size[1] / 2 + 1), WEST)
       
   207         self.Output = Connector(self, "", "BOOL", wxPoint(self.Size[0], self.Size[1] / 2 + 1), EAST)
       
   208     
       
   209     # Destructor
       
   210     def __del__(self):
       
   211         self.Input = None
       
   212         self.Output = None
       
   213     
       
   214     # Forbids to change the contact size
       
   215     def SetSize(self, width, height):
       
   216         pass
       
   217     
       
   218     # Delete this contact by calling the appropriate method
       
   219     def Delete(self):
       
   220         self.Parent.DeleteContact(self)
       
   221     
       
   222     # Unconnect input and output
       
   223     def Clean(self):
       
   224         self.Input.UnConnect()
       
   225         self.Output.UnConnect()
       
   226     
       
   227     # Refresh the contact bounding box
       
   228     def RefreshBoundingBox(self):
       
   229         dc = wxClientDC(self.Parent)
       
   230         # Calculate the size of the name outside the contact
       
   231         text_width, text_height = dc.GetTextExtent(self.Name)
       
   232         # Calculate the bounding box size
       
   233         if self.Name != "":
       
   234             bbx_x = self.Pos.x - max(0, (text_width - self.Size[0]) / 2)
       
   235             bbx_width = max(self.Size[0], text_width)
       
   236             bbx_y = self.Pos.y - (text_height + 2)
       
   237             bbx_height = self.Size[1] + (text_height + 2)
       
   238         else:
       
   239             bbx_x = self.Pos.x
       
   240             bbx_width = self.Size[0]
       
   241             bbx_y = self.Pos.y
       
   242             bbx_height = self.Size[1]
       
   243         self.BoundingBox = wxRect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
       
   244     
       
   245     # Refresh the position of wire connected to contact
       
   246     def RefreshConnected(self, exclude = []):
       
   247         self.Input.MoveConnected(exclude)
       
   248         self.Output.MoveConnected(exclude)
       
   249     
       
   250     # Returns the contact connector that starts with the point given if it exists 
       
   251     def GetConnector(self, position):
       
   252         # Test input connector
       
   253         input_pos = self.Input.GetRelPosition()
       
   254         if position.x == self.Pos.x + input_pos.x and position.y == self.Pos.y + input_pos.y:
       
   255             return self.Input
       
   256         # Test output connector
       
   257         output_pos = self.Output.GetRelPosition()
       
   258         if position.x == self.Pos.x + output_pos.x and position.y == self.Pos.y + output_pos.y:
       
   259             return self.Output
       
   260         return None
       
   261     
       
   262     # Returns input and output contact connectors 
       
   263     def GetConnectors(self):
       
   264         return {"input":self.Input,"output":self.Output}
       
   265     
       
   266     # Test if point given is on contact input or output connector
       
   267     def TestConnector(self, pt, exclude=True):
       
   268         # Test input connector
       
   269         if self.Input.TestPoint(pt, exclude):
       
   270             return self.Input
       
   271         # Test output connector
       
   272         if self.Output.TestPoint(pt, exclude):
       
   273             return self.Output
       
   274         return None
       
   275 
       
   276     # Changes the contact name
       
   277     def SetName(self, name):
       
   278         self.Name = name
       
   279 
       
   280     # Returns the contact name
       
   281     def GetName(self):
       
   282         return self.Name
       
   283 
       
   284     # Changes the contact type
       
   285     def SetType(self, type):
       
   286         self.Type = type
       
   287 
       
   288     # Returns the contact type
       
   289     def GetType(self):
       
   290         return self.Type
       
   291     
       
   292     # Method called when a LeftDClick event have been generated
       
   293     def OnLeftDClick(self, event, scaling):
       
   294         # Edit the contact properties
       
   295         self.Parent.EditContactContent(self)
       
   296     
       
   297     # Refreshes the contact model
       
   298     def RefreshModel(self, move=True):
       
   299         self.Parent.RefreshContactModel(self)
       
   300         # If contact has moved, refresh the model of wires connected to output
       
   301         if move:
       
   302             self.Output.RefreshWires()
       
   303     
       
   304     # Draws contact
       
   305     def Draw(self, dc):
       
   306         dc.SetPen(wxBLACK_PEN)
       
   307         dc.SetBrush(wxBLACK_BRUSH)
       
   308         # Draw two rectangles for representing the contact
       
   309         dc.DrawRectangle(self.Pos.x, self.Pos.y, 2, self.Size[1] + 1)
       
   310         dc.DrawRectangle(self.Pos.x + self.Size[0] - 1, self.Pos.y, 2, self.Size[1] + 1)
       
   311         # Draw contact name
       
   312         namewidth, nameheight = dc.GetTextExtent(self.Name)
       
   313         dc.DrawText(self.Name, self.Pos.x + (self.Size[0] - namewidth) / 2,
       
   314                 self.Pos.y - (nameheight + 2))
       
   315         # Draw the modifier symbol in the middle of contact
       
   316         typetext = ""
       
   317         if self.Type == CONTACT_REVERSE:
       
   318             typetext = "/"
       
   319         elif self.Type == CONTACT_RISING:
       
   320             typetext = "P"
       
   321         elif self.Type == CONTACT_FALLING:
       
   322             typetext = "N"
       
   323         if typetext != "":
       
   324             typewidth, typeheight = dc.GetTextExtent(typetext)
       
   325             dc.DrawText(typetext, self.Pos.x + (self.Size[0] - typewidth) / 2 + 1,
       
   326                     self.Pos.y + (self.Size[1] - typeheight) / 2)
       
   327         # Draw input and output connectors
       
   328         self.Input.Draw(dc)
       
   329         self.Output.Draw(dc)
       
   330         Graphic_Element.Draw(self, dc)
       
   331 
       
   332 
       
   333 #-------------------------------------------------------------------------------
       
   334 #                         Ladder Diagram Coil
       
   335 #-------------------------------------------------------------------------------
       
   336 
       
   337 """
       
   338 Class that implements the graphic representation of a coil
       
   339 """
       
   340 
       
   341 class LD_Coil(Graphic_Element):
       
   342     
       
   343     # Create a new coil
       
   344     def __init__(self, parent, type, name, id = None):
       
   345         Graphic_Element.__init__(self, parent)
       
   346         self.Type = type
       
   347         self.Name = name
       
   348         self.Id = id
       
   349         self.Size = wxSize(LD_ELEMENT_SIZE[0], LD_ELEMENT_SIZE[1])
       
   350         # Create an input and output connector
       
   351         self.Input = Connector(self, "", "BOOL", wxPoint(0, self.Size[1] / 2 + 1), WEST)
       
   352         self.Output = Connector(self, "", "BOOL", wxPoint(self.Size[0], self.Size[1] / 2 + 1), EAST)
       
   353         
       
   354     # Destructor
       
   355     def __del__(self):
       
   356         self.Input = None
       
   357         self.Output = None
       
   358     
       
   359     # Forbids to change the contact size
       
   360     def SetSize(self, width, height):
       
   361         pass
       
   362     
       
   363     # Delete this coil by calling the appropriate method
       
   364     def Delete(self):
       
   365         self.Parent.DeleteCoil(self)
       
   366     
       
   367     # Unconnect input and output
       
   368     def Clean(self):
       
   369         self.Input.UnConnect()
       
   370         self.Output.UnConnect()
       
   371                 
       
   372     # Refresh the coil bounding box
       
   373     def RefreshBoundingBox(self):
       
   374         dc = wxClientDC(self.Parent)
       
   375         # Calculate the size of the name outside the coil
       
   376         text_width, text_height = dc.GetTextExtent(self.Name)
       
   377         # Calculate the bounding box size
       
   378         if self.Name != "":
       
   379             bbx_x = self.Pos.x - max(0, (text_width - self.Size[0]) / 2)
       
   380             bbx_width = max(self.Size[0], text_width)
       
   381             bbx_y = self.Pos.y - (text_height + 2)
       
   382             bbx_height = self.Size[1] + (text_height + 2)
       
   383         else:
       
   384             bbx_x = self.Pos.x
       
   385             bbx_width = self.Size[0]
       
   386             bbx_y = self.Pos.y
       
   387             bbx_height = self.Size[1]
       
   388         self.BoundingBox = wxRect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1)
       
   389     
       
   390     # Refresh the position of wire connected to coil
       
   391     def RefreshConnected(self, exclude = []):
       
   392         self.Input.MoveConnected(exclude)
       
   393         self.Output.MoveConnected(exclude)
       
   394     
       
   395     # Returns the coil connector that starts with the point given if it exists 
       
   396     def GetConnector(self, position):
       
   397         # Test input connector
       
   398         input_pos = self.Input.GetRelPosition()
       
   399         if position.x == self.Pos.x + input_pos.x and position.y == self.Pos.y + input_pos.y:
       
   400             return self.Input
       
   401         # Test output connector
       
   402         output_pos = self.Output.GetRelPosition()
       
   403         if position.x == self.Pos.x + output_pos.x and position.y == self.Pos.y + output_pos.y:
       
   404             return self.Output
       
   405         return None
       
   406     
       
   407     # Returns input and output coil connectors 
       
   408     def GetConnectors(self):
       
   409         return {"input":self.Input,"output":self.Output}
       
   410     
       
   411     # Test if point given is on coil input or output connector
       
   412     def TestConnector(self, pt, exclude=True):
       
   413         # Test input connector
       
   414         if self.Input.TestPoint(pt, exclude):
       
   415             return self.Input
       
   416         # Test output connector
       
   417         if self.Output.TestPoint(pt, exclude):
       
   418             return self.Output
       
   419         return None
       
   420     
       
   421     # Changes the coil name
       
   422     def SetName(self, name):
       
   423         self.Name = name
       
   424 
       
   425     # Returns the coil name
       
   426     def GetName(self):
       
   427         return self.Name
       
   428     
       
   429     # Changes the coil type
       
   430     def SetType(self, type):
       
   431         self.Type = type
       
   432     
       
   433     # Returns the coil type
       
   434     def GetType(self):
       
   435         return self.Type
       
   436     
       
   437     # Method called when a LeftDClick event have been generated
       
   438     def OnLeftDClick(self, event, scaling):
       
   439         # Edit the coil properties
       
   440         self.Parent.EditCoilContent(self)
       
   441     
       
   442     # Refreshes the coil model
       
   443     def RefreshModel(self, move=True):
       
   444         self.Parent.RefreshCoilModel(self)
       
   445         # If coil has moved, refresh the model of wires connected to output
       
   446         if move:
       
   447             self.Output.RefreshWires()
       
   448     
       
   449     # Draws coil
       
   450     def Draw(self, dc):
       
   451         dc.SetPen(wxPen(wxBLACK, 2, wxSOLID))
       
   452         dc.SetBrush(wxTRANSPARENT_BRUSH)
       
   453         # Draw a two circle arcs for representing the coil
       
   454         dc.DrawEllipticArc(self.Pos.x, self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1, self.Size[0], int(self.Size[1] * sqrt(2)) - 1, 135, 225)
       
   455         dc.DrawEllipticArc(self.Pos.x, self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1, self.Size[0], int(self.Size[1] * sqrt(2)) - 1, -45, 45)
       
   456         dc.SetPen(wxBLACK_PEN)
       
   457         dc.DrawPoint(self.Pos.x + 1, self.Pos.y + self.Size[1] / 2 + 1)
       
   458         # Draw coil name
       
   459         namewidth, nameheight = dc.GetTextExtent(self.Name)
       
   460         dc.DrawText(self.Name, self.Pos.x + (self.Size[0] - namewidth) / 2,
       
   461                 self.Pos.y - (nameheight + 2))
       
   462         # Draw the modifier symbol in the middle of coil
       
   463         typetext = ""
       
   464         if self.Type == COIL_REVERSE:
       
   465             typetext = "/"
       
   466         elif self.Type == COIL_SET:
       
   467             typetext = "S"
       
   468         elif self.Type == COIL_RESET:
       
   469             typetext = "R"
       
   470         if typetext != "":
       
   471             typewidth, typeheight = dc.GetTextExtent(typetext)
       
   472             dc.DrawText(typetext, self.Pos.x + (self.Size[0] - typewidth) / 2 + 1,
       
   473                     self.Pos.y + (self.Size[1] - typeheight) / 2)
       
   474         # Draw input and output connectors
       
   475         self.Input.Draw(dc)
       
   476         self.Output.Draw(dc)
       
   477         Graphic_Element.Draw(self, dc)