blocks/BlockLinkChecker.py
class BlockLinkChecker():

   MAX_LINK_DISTANCE = 20.0;
   rules = []

   def hasPlugEquivalent(b):
      if (b == None):
         return False;
      hasPlug = b.hasPlug();
      hasBefore = b.hasBeforeConnector();
      # Should have at most one plug-type connector
      assert( not (hasPlug & hasBefore));
      return hasPlug | hasBefore;

   def getPlugEquivalent(b):
      if (not BlockLinkChecker.hasPlugEquivalent(b)):
         return None;
      if (b.hasPlug()):
         return b.getPlug(); 
      return b.getBeforeConnector();

   def getSocketEquivalents(b):
      if (b == None):
         return []
      if (not b.hasAfterConnector()):
      	return b.getSockets();
      socketEquivalents = [];
      for socket in b.getSockets():
         socketEquivalents.append(socket);

      socketEquivalents.append(b.getAfterConnector());
      return socketEquivalents


   def addRule(rule):
      BlockLinkChecker.rules.append(rule);
      #if (rule instanceof WorkspaceListener)
     	#	Workspace.getInstance().addWorkspaceListener((WorkspaceListener)rule);

   '''
    * Factory method for creating BlockLink objects
    * @param block1 one of the Block objects in the potential link
    * @param block2 the other Block object
    * @param socket1 the BlockConnector from block1
    * @param socket2 the BlockConnector from block2
    * @return a BlockLink object storing the potential link between block1 and block2
   '''
   def getBlockLink(block1, block2, socket1, socket2):
      # If these arguments are the same as the last call to getBlockLink, return the old object instead of creating a new one
      if (not
         ((block1.blockID == lastPlugID  and block2.blockID == lastSocketID and socket1 == lastPlug and socket2 == lastSocket) or
   		 (block2.blockID == lastPlugID  and block1.blockID == lastSocketID and socket2 == lastPlug and socket1 == lastSocket))):
         lastLink = BlockLink(block1, block2, socket1, socket2);

      return lastLink;

   '''
   * Gets the screen coordinate of the center of a socket.
   * @param block the RenderableBlock containting the socket
   * @param socket the desired socket
   * @return a Point2D that represents the center of the socket on the screen.
   '''
   def getAbsoluteSocketPoint(block, socket):
      relativePoint = block.getSocketPixelPoint(socket);
      blockPosition = block.getLocationOnScreen();
      return QtCore.QPointF(relativePoint.x() + blockPosition.x(), relativePoint.y() + blockPosition.y());


   def distance(p, q):
      import math
      dx   = p.x() - q.x();         #horizontal difference
      dy   = p.y() - q.y();         #vertical difference
      dist = math.sqrt( dx*dx + dy*dy ); #distance using Pythagoras theorem
      return dist;


   '''
   * Checks if a potential link satisfies ANY of the rules loaded into the link checker
   * @param block1 one Block in the potential link
   * @param block2 the other Block
   * @param socket1 the BlockConnector from block1 in the potential link
   * @param socket2 the BlockConnector from block2
   * @return true if the pairing of block1 and block2 at socket1 and socket2 passes any rules, false otherwise
   '''
   def checkRules(block1, block2, socket1, socket2):
      rulesList = BlockLinkChecker.rules
      currentRule = None;
      foundRule = False;
      for currentRule in rulesList:
         canLink = currentRule.canLink(block1, block2, socket1, socket2);
         if (not currentRule.isMandatory()):
            foundRule |= canLink;
         elif (not canLink):
            return False;

      return foundRule;


   '''
   * Checks to see if a <code>RenderableBlock</code>s can connect to other <code>RenderableBlock</code>s.
   * This would mean that they have <code>BlockConnector</code>s that satisfy at least one of the <code>LinkRule</code>s,
   * and that these sockets are in close proximity.
   * @param rblock1 one of the blocks to check
   * @param otherBlocks the other blocks to check against
   * @return a <code>BlockLink</code> object that gives the two closest matching <code>BlockConnector</code>s in these blocks,
   * or null if no such matching exists.
   '''
   def getLink(rblock1, otherBlocks):
      from blocks.BlockLink import BlockLink

      block1 = Block.getBlock(rblock1.blockID);
      closestSocket1 = None;
      closestSocket2 = None;
      closestBlock2 = None;
      closestDistance = BlockLinkChecker.MAX_LINK_DISTANCE;
      #currentDistance;

      for rblock2 in otherBlocks:
         currentPlug = BlockLinkChecker.getPlugEquivalent(block1);

         block2 = Block.getBlock(rblock2.blockID);
         if (block1 == block2 or not rblock1.isVisible() or not rblock2.isVisible() or rblock1.isCollapsed() or rblock2.isCollapsed()):
            continue;

         currentPlugPoint = None;
         currentSocketPoint = None;
         if (currentPlug != None):
            currentPlugPoint = BlockLinkChecker.getAbsoluteSocketPoint(rblock1, currentPlug);

            for currentSocket in BlockLinkChecker.getSocketEquivalents(block2):
               currentSocketPoint = BlockLinkChecker.getAbsoluteSocketPoint(rblock2, currentSocket);
               currentDistance = BlockLinkChecker.distance(currentPlugPoint,currentSocketPoint);

               if ((currentDistance < closestDistance) and BlockLinkChecker.checkRules(block1, block2, currentPlug, currentSocket)):
                  closestBlock2 = block2;
                  closestSocket1 = currentPlug;
                  closestSocket2 = currentSocket;
                  closestDistance = currentDistance;


         currentPlug = BlockLinkChecker.getPlugEquivalent(block2);
         if (currentPlug != None) :
            currentPlugPoint = BlockLinkChecker.getAbsoluteSocketPoint(rblock2, currentPlug);
            for currentSocket in BlockLinkChecker.getSocketEquivalents(block1):
               currentSocketPoint = BlockLinkChecker.getAbsoluteSocketPoint(rblock1, currentSocket);
               currentDistance = BlockLinkChecker.distance(currentPlugPoint,currentSocketPoint);
               if ((currentDistance < closestDistance) and BlockLinkChecker.checkRules(block1, block2, currentSocket, currentPlug)):
                  closestBlock2 = block2;
                  closestSocket1 = currentSocket;
                  closestSocket2 = currentPlug;
                  closestDistance = currentDistance;

      if (closestSocket1 == None):
         return None;


      return BlockLink.getBlockLink(block1, closestBlock2, closestSocket1, closestSocket2);
blocks/Block.py
class Block():

    # The ID that is to be assigned to the next new block
    NEXT_ID = 1
    MAX_RESERVED_ID = -1

    # Defines a NULL id for a Block
    NULL = ""

    # A universal hashmap of all the Block instances
    ALL_BLOCKS= {}
    tmpObj = None
   
    def __init__(self, canvas):
        self.canvas = canvas
        self.pageLabel = ""
        self.hasFocus = False;
        self.isBad = False;
        self.disabled = False
        self.sockets = []
        self.argumentDescriptions = []
        self.properties = {}
        self.outputConnection = None
  
    def __del__(self):
        pass

    @classmethod
    def createBlockFromID(cls, canvas, genusName, linkToStubs=True, id=-1,label=None):
      obj = cls(canvas)
      obj.linkToStubs = linkToStubs
      obj._blockID = id
    
      genus = BlockGenus.getGenusWithName(genusName)
      
      if(genus == None):
          return None
          raise Exception("genusName: "+genusName+" does not exist.")

      if (label == None):
         label = BlockGenus.getGenusWithName(genusName).getInitialLabel()

      #if (id in Block.ALL_BLOCKS):
      #  dup = Block.ALL_BLOCKS.get(id);
      #  print("pre-existing block is: {0} with genus {1} and label {2}".format(id,dup.getGenusName(),dup.getBlockLabel()));
      #  raise Exception("Block id: {0} already exists!  BlockGenus {1}, label: {2}".format(id,genusName,label))

      # copy the block connectors from block genus
      iter = genus.getInitSockets();
      for con in iter:
         obj.sockets.append(BlockConnector(         
            con.kind,
            con.type,
            con.positionType,
            con.label,
            con.isLabelEditable,
            con.isExpandable,
            con.connBlockID,
            con.expandGroup))


      if(genus.getInitPlug() != None):
         obj.plug = BlockConnector(
            genus.getInitPlug().kind,
            genus.getInitPlug().type,
            genus.getInitPlug().positionType,
            genus.getInitPlug().label,
            genus.getInitPlug().isLabelEditable,
            genus.getInitPlug().isExpandable,
            genus.getInitPlug().connBlockID,
            genus.getInitPlug().expandGroup);
      else:
         obj.plug = None

      if(genus.getInitBefore() != None):
         obj.before = BlockConnector(
            genus.getInitBefore().kind,
            genus.getInitBefore().type,
            genus.getInitBefore().positionType,
            genus.getInitBefore().label,
            genus.getInitBefore().isLabelEditable,
            genus.getInitBefore().isExpandable,
            genus.getInitBefore().connBlockID,
            genus.getInitBefore().expandGroup);
      else:
         obj.before = None

      if(genus.getInitAfter() != None):
         obj.after = BlockConnector(
            genus.getInitAfter().kind,
            genus.getInitAfter().type,
            genus.getInitAfter().positionType,
            genus.getInitAfter().label,
            genus.getInitAfter().isLabelEditable,
            genus.getInitAfter().isExpandable,
            genus.getInitAfter().connBlockID,
            genus.getInitAfter().expandGroup);
      else:
         obj.after = None

      obj.genusName = genusName;

      obj.label = label;

      obj.expandGroups = []
     
      if(obj.blockID != None and obj.blockID != -1 and obj.blockID != ""):
        Block.ALL_BLOCKS[obj.blockID] = obj

      Block.tmpObj = obj
      
      return obj
      
   
    @classmethod
    def createBlock(cls, canvas,  genusName, linkToStubs, label=None):
     id = Block.generateId()
     return Block.createBlockFromID(canvas, genusName, linkToStubs, id,label)

    @property
    def blockID(self):
      """I'm the 'x' property."""
      return self._blockID

    @blockID.setter
    def blockID(self, value):
      self._blockID = value

    @blockID.deleter
    def blockID(self):
      del self._blockID       
      
    def generateId():
       return str(uuid.uuid1())
       
    def getInputTargetBlock(self, name):
        socket = self.getSocketByName(name.lower())
        if(socket == None):
          return None
          
        blockID = socket.blockID
        if(blockID == ""):
          return None
        else:
          return Block.getBlock(blockID)
      
    def getBlock(blockID):
     
      if(blockID == -1): return Block.tmpObj 
      if(blockID == ""): return None       
      if(blockID in Block.ALL_BLOCKS):
         return Block.ALL_BLOCKS[blockID]
      else:
         #print("Not found")
         return None

    def isInfix(self):
        genus = self.getGenus()
        return genus.isInfix()

    def getGenus(self):
        return BlockGenus.getGenusWithName(self.genusName)

    def changeLabelTo(self,  label):
        self.label = label 

    def hasPlug(self):
        return not (self.plug == None);

    def getPlug(self):
        return self.plug;


    def hasPageLabel(self):
      return self.pageLabel != None and self.pageLabel != ""

    def isCommandBlock(self):
      return self.getGenus().isCommandBlock()

    def hasSiblings(self):
     return self.getGenus().hasSiblings();

    def getSiblingsList(self):
      return self.getGenus().getSiblingsList()

    def getCustomerFamily(self):
        familyName = self.getGenus().familyName
        if familyName not in self.canvas.families:
            self.canvas.families[familyName] = {}
            
        return self.canvas.families[familyName]

    def hasBeforeConnector(self):
      return self.before != None;

    def hasAfterConnector(self):
      return self.after != None;

    def getSockets(self):
      return self.sockets
      
    def getSocketByName(self, name):
      for socket in self.getSockets():
        if(socket.getLabel() == name):
          return socket
      return None

    def isProcedureDeclBlock(self):
      return self.getGenus().isProcedureDeclBlock();

    def getAfterConnector(self):
      return self.after;

    def getBeforeConnector(self):
      return self.before;

    def getBlockFullLabel(self):
      return self.getGenus().getLabelPrefix()+self.label+self.getGenus().getLabelSuffix();
      
    def getBlockLabel(self):
      return self.label

    def getLabelPrefix(self):
      return self.getGenus().getLabelPrefix()
      
    def getLabelSuffix(self):
      return self.getGenus().getLabelSuffix()
      
    def setBlockLabel(self, newLabel):
      from blocks.BlockStub import BlockStub
      if (self.linkToStubs and self.hasStubs()):
          BlockStub.parentNameChanged(self.label, newLabel, self.blockID);

      self.label = newLabel;

    def isLabelEditable(self):
      return self.getGenus()._isLabelEditable;

    def getPageLabel(self):
      return self.pageLabel;

    def getNumSockets(self):
      return len(self.sockets);

    def getSocketAt(self, index):
        if(index<len(self.sockets)):
           return self.sockets[index];
        else:
          return None
        #assert index < sockets.size() : "Index "+index+" is greater than the num of sockets: "+sockets.size()+" of "+this;
      

    def getInitBlockImages(self):
      return self.getGenus().getInitBlockImages();

    def isDeclaration(self):
      return self.getGenus().isDeclaration();

    def getColor(self):
      return self.getGenus().getColor();

    def isDataBlock(self):
      return self.getGenus().isDataBlock();

    def isFunctionBlock(self):
      return self.getGenus().isFunctionBlock();
      
    def isVariable(self):
      return self.getGenus().isVariableDeclBlock();      

    def getArgumentDescription(self, index):
      if (index < len(self.argumentDescriptions) and index >= 0):
         return self.argumentDescriptions[index];

      return None;

    def getInitialLabel(self):
     return self.getGenus().getInitialLabel();

    def labelMustBeUnique(self):
      return self.getGenus().labelMustBeUnique;

    def hasDefaultArgs(self):
      return self.getGenus().hasDefaultArgs();

    def linkAllDefaultArgs(self):
      if(self.getGenus().hasDefaultArgs()):
         defargIDs = []
         for con in self.sockets:
            id = con.linkDefArgument();
            defargIDs.append(id);
            # if id not null, then connect def arg's plug to this block
            if(id != Block.NULL):
               Block.getBlock(id).getPlug().setConnectorBlockID(self.blockID);

         return defargIDs;

      return None;

    def blockConnected(self,connectedSocket, connectedBlockID):
      from blocks.BlockStub import BlockStub
      if (connectedSocket.isExpandable):
         if (connectedSocket.getExpandGroup().length() > 0):
             # Part of an expand group
            self.expandSocketGroup(connectedSocket.getExpandGroup());

         else:
            #expand into another one
            index = self.getSocketIndex(connectedSocket);
            if(self.isProcedureDeclBlock()):
               self.addSocket(index+1, connectedSocket.initKind(), connectedSocket.getPositionType(), "", connectedSocket.isLabelEditable(), connectedSocket.isExpandable(), Block.NULL);
            else:
               self.addSocket(index+1, connectedSocket.initKind(), connectedSocket.getPositionType(), connectedSocket.getLabel(), connectedSocket.isLabelEditable(), connectedSocket.isExpandable(), Block.NULL);


      # NOTE: must update the sockets of this before updating its stubs as stubs use this as a reference to update its own sockets
      # if block has stubs, update its stubs as well
      if(self.hasStubs()):
         BlockStub.parentConnectorsChanged(self.blockID);

    def hasStubs(self):
      return self.linkToStubs and  self.getGenus().hasStubs();

    def getBeforeBlockID(self):
      if (self.before == None):
         return '' ;
      return self.before.blockID;

    def getAfterBlockID(self):
      if (self.after == None):
         return '';
         
      print(self.after.blockID)         
      return self.after.blockID;

    def getPlugBlockID(self):
      if(self.plug == None):
         return '';

      return self.plug.blockID;


    def getExpandGroup(groups, group):
      for list in groups:
         # Always at least one element in the group.
         if (list[0].getExpandGroup() == group):
            return list;

      return None;

    def expandSocketGroup(self,group):
      '''
      * Expand a socket group in this block. For now, all new sockets will
      * be added after the last socket in the group.
      '''
      expandSockets = self.getExpandGroup(self.expandGroups, group);
      #assert expandSockets != null;

      # Search for the socket to insert after.
      index = len(self.sockets) - 1;
      label = expandSockets[len(expandSockets) - 1].getLabel();
      while(index  >= 0):
         conn = self.sockets[index]
         if (conn.getLabel()==label and conn.getExpandGroup() ==(group)):
             break;
         index -= 1

     #assert index >= 0;

      # Insert all the new sockets
      for conn in  expandSockets:
         index+=1;
         newConn = BlockConnector(conn);
         self.sockets.add(index, newConn);

    def shrinkSocketGroup(self, socket):
      '''
      * Shrink a socket group (un-expand it).
      '''
      group = socket.getExpandGroup();
      expandSockets = self.getExpandGroup(self.expandGroups, group);
      #assert expandSockets != null;

      # Search for the first socket in the group, if not the expandable
      # one.
      label = expandSockets.get(0).getLabel();
      index = self.getSocketIndex(self.socket);
      while(index  >= 0):
         con = self.sockets[index];
         if (con.getLabel() == label and con.getExpandGroup() == group):
             break;
         index -= 1

      #assert index >= 0;

      # Remove all the sockets.
      self.removeSocket(index);
      total = len(expandSockets);
      for i in range(1,total):
         con = self.sockets[index];
         if (con.getLabel() == expandSockets[i].getLabel() and con.getExpandGroup() == group):
            self.removeSocket(index);
            i+=1;
         else:
             index+=1;

    def canRemoveSocket(self, socket):
      '''
      * Returns true if the given expandable socket can be removed.
      '''
      total = len(self.sockets);
      first = -1;
      for i in range(0,total):
         conn = self.sockets.get(i);
         if (conn == self.socket):
             if (first == -1):first = i;
             else: return True;
         elif (conn.getPositionType().equals(socket.getPositionType()) and
                  conn.isExpandable() == socket.isExpandable() and
                  conn.initKind().equals(socket.initKind()) and
                  conn.getExpandGroup().equals(socket.getExpandGroup())):
             if (first == -1): first = i;
             else: return True;


      # If the socket is the first and last of its kind, then we can NOT
      # remove it. (We also can't remove it if they're both -1, obviously.)
      return False;

    def blockDisconnected(self,disconnectedSocket):
      '''
      * Informs this Block that a block has disconnected from the specified disconnectedSocket
      * @param disconnectedSocket
      '''
      from blocks.BlockStub import BlockStub
      
      if (disconnectedSocket.isExpandable and self.canRemoveSocket(disconnectedSocket)):
         if (disconnectedSocket.getExpandGroup().length() > 0):
            self.shrinkSocketGroup(disconnectedSocket);
         else:
            self.removeSocket(disconnectedSocket);


      # NOTE: must update the sockets of this before updating its stubs as stubs use this as a reference to update its own sockets
      # if block has stubs, update its stubs as well
      if(self.hasStubs()):
         BlockStub.parentConnectorsChanged(self.blockID);

    def getConnectorTo(self,otherBlockID):
      '''
      * Searches for the BlockConnector linking this block to another block
      * @param otherBlockID the Block ID if the other block
      * @return the BlockConnector linking this block to the other block
      '''
      if (otherBlockID == None or otherBlockID == Block.NULL):
         return None;
      if (self.getPlugBlockID() == otherBlockID):
         return self.plug;
      if (self.getBeforeBlockID() == otherBlockID):
         return self.before;
      if (self.getAfterBlockID() == otherBlockID):
         return self.after;
      for socket in self.getSockets():
         if (socket.blockID == otherBlockID):
            return socket;
      return None;

    def getGenusName(self):
      '''
      * Returns the name of this genus
      * FORWARDED FROM BLOCK GENUS
      * @return the name of this genus
      '''
      return self.genusName

    def getBlockInfo(self):
        blockInfo = {}
        blockInfo['id'] = self.blockID
        blockInfo['genus-name'] = self.getGenusName()
        blockInfo['has-focus'] =self.hasFocus
        
        if (not self.label == (self.getInitialLabel())):
            blockInfo['Label'] = self.label    
    
        blockInfo['PageLabel'] = self.pageLabel    
        
        if (self.isBad):
            blockInfo['CompilerErrorMsg'] = self.badMsg    

        blockInfo['BeforeBlockId'] = self.getBeforeBlockID()    
        blockInfo['AfterBlockId'] = self.getAfterBlockID()    

        if (self.plug != None):
            blockInfo['plug'] = self.plug.getConnectorInfo();

        if (len(self.sockets) > 0) :
            socket_list = []
            for con in self.getSockets():
                socket_list.append(con.getConnectorInfo())
            blockInfo['socket_list'] = socket_list

        # save block properties that are not specified within genus
        # i.e. properties that were created/specified during runtime 
        if (len(self.properties) > 0):
            prop_list = []
            for key in self.properties:
                prop_info = {}
                prop_info['key']=  key
                prop_info['value']=  self.properties[key]
                prop_list.append(prop_info)
            blockInfo['prop_list'] = prop_list
        return blockInfo

    def getSaveNode(self, document, x, y, commentNode, isCollapsed):
      blockElement = document.createElement("Block");

      blockElement.setAttribute("id", str(self.blockID));

      blockElement.setAttribute("genus-name", self.getGenusName());
      if (self.hasFocus):
         blockElement.setAttribute("has-focus", "yes");


      if (not self.label == (self.getInitialLabel())):
         labelElement = document.createElement("Label");
         labelElement.appendChild(document.createTextNode(self.label ));
         blockElement.appendChild(labelElement);


      if (self.pageLabel != None and self.pageLabel != ""):
         pageLabelElement = document.createElement("PageLabel");
         pageLabelElement.appendChild(document.createTextNode(self.pageLabel));
         blockElement.appendChild(pageLabelElement);


      if (self.isBad):
         msgElement = document.createElement("CompilerErrorMsg");
         msgElement.appendChild(document.createTextNode(self.badMsg));
         blockElement.appendChild(msgElement);


      # Location
      locationElement = document.createElement("Location");
      xElement = document.createElement("X");
      xElement.appendChild(document.createTextNode(str(x)));
      locationElement.appendChild(xElement);

      yElement = document.createElement("Y");
      yElement.appendChild(document.createTextNode(str(y)));
      locationElement.appendChild(yElement);
      blockElement.appendChild(locationElement);

      if (isCollapsed):
         collapsedElement = document.createElement("Collapsed");
         blockElement.appendChild(collapsedElement);


      if (commentNode != None):
         blockElement.appendChild(commentNode);


      if (self.hasBeforeConnector() and self.getBeforeBlockID() != Block.NULL):
         blockIdElement = document.createElement("BeforeBlockId");
         blockIdElement.appendChild(document.createTextNode(self.getBeforeBlockID() ));
         blockElement.appendChild(blockIdElement);


      if (self.hasAfterConnector() and self.getAfterBlockID() != Block.NULL):
         blockIdElement = document.createElement("AfterBlockId");
         blockIdElement.appendChild(document.createTextNode(self.getAfterBlockID()));
         blockElement.appendChild(blockIdElement);


      if (self.plug != None):
            plugElement = document.createElement("Plug");
            blockConnectorNode = self.plug.getSaveNode(document, "plug");
            plugElement.appendChild(blockConnectorNode);
            blockElement.appendChild(plugElement);

      if (len(self.sockets) > 0) :
         socketsElement = document.createElement("Sockets");
         socketsElement.setAttribute("num-sockets", str(len(self.sockets)));
         for con in self.getSockets():
            blockConnectorNode = con.getSaveNode(document, "socket");
            socketsElement.appendChild(blockConnectorNode);

         blockElement.appendChild(socketsElement);
         # sockets tricky... because what if
         # one of the sockets is expanded?  should the socket keep a reference
         # to their genus socket?  and so should the expanded one?


      # save block properties that are not specified within genus
      # i.e. properties that were created/specified during runtime
      print(self.properties)     
      if (len(self.properties) > 0):
         propertiesElement = document.createElement("LangSpecProperties");
         for key in self.properties:
            propertyElement = document.createElement("LangSpecProperty");
            propertyElement.setAttribute("key", key);
            propertyElement.setAttribute("value", self.properties[key]);

            propertiesElement.appendChild(propertyElement);

         blockElement.appendChild(propertiesElement);


      return blockElement;



    def loadBlockFrom(block_info, canvas):
        '''
        * Loads Block information from the specified node and return a Block
        * instance with the loaded information
        * @param workspace The workspace in use
        * @param node Node cantaining desired information
        * @return Block instance containing loaded information
        '''
        from blocks.BlockStub import BlockStub

        block = None;
        id = None;
        genusName = None;
        label = None;
        pagelabel = None;
        badMsg = None;
        beforeID = None;
        afterID = None;
        plug = None;
        sockets = []
        blockLangProperties = {};
        hasFocus = False;

        # stub information if this node contains a stub
        isStubBlock = False;
        stubParentName = None;
        stubParentGenus = None;
      
        '''
        if (node.tag == ("BlockStub")):
            isStubBlock = True;
            blockNode = None;
            stubChildren = node.getchildren();
            for infoNode in stubChildren:
                if (infoNode.tag == ("StubParentName")):
                    stubParentName = infoNode.getTextContent();
                elif (infoNode.tag ==("StubParentGenus")) :
                   stubParentGenus = infoNode.getTextContent();
                elif (infoNode.tag == ("Block")):
                   blockNode = infoNode;

            node = blockNode;
        '''

        #if (node.tag == ("Block")):
        # load attributes
        if("id" in block_info):
            id = block_info["id"]  #+ canvas.getMaxReservedID() #translateLong(workspace, long(nameMatcher.group(1)), idMapping);

        if("genus-name" in block_info):
            genusName = block_info["genus-name"]

        # load optional items
        if("has-focus" in block_info):
            hasFocus = block_info['has-focus']

        if('Label' in block_info):
            label = block_info['Label']
        if('PageLabel' in block_info):   
            pagelabel = block_info['PageLabel']
            
        if('CompilerErrorMsg' in block_info):
            badMsg = block_info['CompilerErrorMsg']
        beforeID = block_info['BeforeBlockId']
        afterID = block_info['AfterBlockId']
        
        if  ('plug' in block_info):
            plug_info = block_info['plug']
            plug = BlockConnector.loadBlockConnector(plug_info,);

        if  ('socket_list' in block_info) :
            for  socket_info in block_info['socket_list']:
                sockets.append(BlockConnector.loadBlockConnector(socket_info));

        if  ('prop_list' in block_info):
            prop_list = block_info['prop_list']

            for prop_info in prop_list:
                key = prop_info['key']                
                value = prop_info['value']               ;                  
                if(key != None and value != None):
                    blockLangProperties[key] = value

         #create block or block stub instance
        if (not isStubBlock):
            if (label == None):
               block = Block.createBlockFromID(canvas, genusName, True, id);
            else:
               block = Block.createBlockFromID(canvas,  genusName,True, id,label);

        else:
            #assert label != null : "Loading a block stub, but has a null label!";
            block = BlockStub(node.workspace, id, genusName, label, stubParentName, stubParentGenus);


        if (plug != None):
            # Some callers can change before/after/plug types. We have
            # to synchronize so that we never have both.
            # assert beforeID == null && afterID == null;
            block.plug = plug;
            block.removeBeforeAndAfter();

        if (len(sockets) > 0):
            block.sockets = sockets;

        if (beforeID != ''):
            block.before.setConnectorBlockID(beforeID);

        if (afterID != ''):
            block.after.setConnectorBlockID(afterID);

        if (pagelabel != None):
            block.pageLabel = pagelabel;

        if (badMsg != None):
            block.isBad = True;
            block.badMsg = badMsg;

        block.hasFocus = hasFocus;

        #load language dependent properties
        if (blockLangProperties != None and len(blockLangProperties) > 0):
            block.properties = blockLangProperties;

        return block;

        #return None

    def removeBeforeAndAfter(self):
        self.before = None;
        self.after = None;

    def getProperty(self, property) :
        if(property in self.properties):
          return self.properties[property]
        else:
          return ''
blocks/BlockShapeUtil.py
class BlockShapeUtil():

   def cornerTo( gp, cornerPoint, nextCornerPoint,  radius):
      if (radius < 0.001):
         #if a small radius, just draw a line
         BlockShapeUtil.lineTo(gp,cornerPoint.x(), cornerPoint.y());
      else:
         BlockShapeUtil.makeCornerTo(gp, cornerPoint, nextCornerPoint, radius);


   def printPos(p):
      print("X="+ str(p.currentPosition().x()) + ", Y=" + str(p.currentPosition().y()))

   def dist(x0, y0, x1, y1):
       "Distance between two points"
       return ((x1 - x0)**2 + (y1 - y0)**2) ** 0.5

   def lineToRelative(gp, x, y):
      currentPoint = gp.currentPosition();
      BlockShapeUtil.lineTo(gp, currentPoint.x() + x, currentPoint.y() + y)

   def lineTo(gp, x, y):
      if debug:
         print("Line to point({0},{1})".format(x,y))
      gp.lineTo(x, y);

   def cornerShape(gp, x1, y1, x2, y2, x3, y3):
      BlockShapeUtil.cubicTo(gp, x1, y1, x2, y2, x3, y3)
   def cubicTo(gp, x1, y1, x2, y2, x3, y3):
      if debug:
         currentPoint = gp.currentPosition();
         print("Draw cubic from point({0},{1}) to point({2},{3}) via control point({4},{5} and {6},{7})".format(currentPoint.x(), currentPoint.y(), x3,y3,x1,y1,x2,y2))
      gp.cubicTo( x1, y1, x2, y2, x3, y3);
   def moveTo(gp, x, y):
      currentPoint = gp.currentPosition();
      if debug:
         print("Move from point({0},{1}) to point({2},{3})".format(currentPoint.x(),currentPoint.y(),x,y))
      gp.moveTo(currentPoint.x() + x, currentPoint.y() + y);


   #def appendPath2(gp1, gp2, reversed):
   #   if(not reversed):
   #      gp1.connectPath(gp2)
   #   else:
   #      gp1.connectPath(gp2.toReversed())

   def appendPath(gp1, gp2, reversed):

      leftmost = 0xffffffff

      type_index_list = []
      for index in range(0,gp2.elementCount()):
         element = gp2.elementAt(index)
         if(element.type == QtGui.QPainterPath.CurveToElement or
            element.type == QtGui.QPainterPath.MoveToElement  or
            element.type == QtGui.QPainterPath.LineToElement):
            type_index_list.append(index)
      #print(type_index_list)
      if(reversed):
         deltaX = gp1.currentPosition().x();
         deltaY = gp1.currentPosition().y();

         last_type_index = type_index_list[len(type_index_list) - 1]
         last_type = gp2.elementAt(last_type_index).type

         if(last_type == QtGui.QPainterPath.MoveToElement):
            last_p1 = QtCore.QPointF(gp2.elementAt(last_type_index))
            deltaX -= last_p1.x()
            deltaY -= last_p1.y()
         elif(last_type == QtGui.QPainterPath.LineToElement):
            last_p1 = QtCore.QPointF(gp2.elementAt(last_type_index))
            deltaX -= last_p1.x()
            deltaY -= last_p1.y()
         elif(last_type == QtGui.QPainterPath.CurveToElement):
            last_p3 = QtCore.QPointF(gp2.elementAt(last_type_index+2))
            deltaX -= last_p3.x()
            deltaY -= last_p3.y()
         else:
            print("Incorrect type")

         j = len(type_index_list)-1
         while(j>0):
            type_index = type_index_list[j]
            element = gp2.elementAt(type_index)

            pre_type_index = type_index_list[j-1]
            pre_element = gp2.elementAt(pre_type_index)

            type = element.type
            prevType = pre_element.type

            prevX = 0.0
            prevY = 0.0

            if(prevType == QtGui.QPainterPath.MoveToElement or prevType == QtGui.QPainterPath.LineToElement):
               pre_p1 = QtCore.QPointF(gp2.elementAt(pre_type_index))
               prevX = pre_p1.x()
               prevY = pre_p1.y()
            elif(prevType == QtGui.QPainterPath.CurveToElement):
               pre_p3 = QtCore.QPointF(gp2.elementAt(pre_type_index+2))
               prevX = pre_p3.x()
               prevY = pre_p3.y()
            else:
               print("Incorrect type")
               continue

            leftmost = min(leftmost, prevX + deltaX);

            if(type == QtGui.QPainterPath.MoveToElement or type == QtGui.QPainterPath.LineToElement):
               gp1.lineTo(prevX + deltaX, prevY + deltaY);
            elif(type == QtGui.QPainterPath.CurveToElement):
               #print(type_index)
               p1 = QtCore.QPointF(gp2.elementAt(type_index))
               p2 = QtCore.QPointF(gp2.elementAt(type_index+1))
               p3 = QtCore.QPointF(gp2.elementAt(type_index+2))
               gp1.cubicTo(p2.x() + deltaX, p2.y() + deltaY, p1.x() + deltaX, p1.y() + deltaY, prevX + deltaX, prevY + deltaY);
            else:
               print("Incorrect type")

            j -= 1
      else: # Not reversed
         #print(type_index_list)
         element = gp2.elementAt(type_index_list[0])
         p0 = QtCore.QPointF(element)
         deltaX = gp1.currentPosition().x() - p0.x()
         deltaY = gp1.currentPosition().y() - p0.y()

         for j in range(1,len(type_index_list)):
            type_index = type_index_list[j]
            #print(type_index)
            element = gp2.elementAt(type_index)
            type = element.type

            if(type == QtGui.QPainterPath.MoveToElement):
               pass
            elif(type == QtGui.QPainterPath.LineToElement):
               p1 = QtCore.QPointF(gp2.elementAt(type_index))
               gp1.lineTo(p1.x() + deltaX, p1.y() + deltaY);
               leftmost = min(leftmost, p1.x() + deltaX);
            elif(type == QtGui.QPainterPath.CurveToElement):
               p1 = QtCore.QPointF(gp2.elementAt(type_index))
               p2 = QtCore.QPointF(gp2.elementAt(type_index+1))
               p3 = QtCore.QPointF(gp2.elementAt(type_index+2))
               gp1.cubicTo(p1.x() + deltaX, p1.y() + deltaY, p2.x() + deltaX, p2.y() + deltaY, p3.x() + deltaX, p3.y() + deltaY);

               leftmost = min(leftmost, p3.x() + deltaX);
            else:
               print("Incorrect type")


   def makeCornerTo(gp, cornerPoint, nextCornerPoint, radius):
      currentPoint = gp.currentPosition();

      # get fractional to the corner where the line first starts to curve
      distance = BlockShapeUtil.dist(currentPoint.x(),currentPoint.y(),cornerPoint.x(),cornerPoint.y());
      fraction = (distance - radius) / distance;

      # calculate these distance from the current point
      xDistance = (cornerPoint.x() - currentPoint.x()) * fraction;
      yDistance = (cornerPoint.y() - currentPoint.y()) * fraction;

      # draw a line to the point where the line first starts to curve
      BlockShapeUtil.lineToRelative(gp, xDistance, yDistance);

      startCurvePoint = gp.currentPosition();


      # get fractional to the corner where the line first starts to curve
      distanceFromCornerToNextCorner = BlockShapeUtil.dist(cornerPoint.x(),cornerPoint.y(),nextCornerPoint.x(),nextCornerPoint.y());
      fractionToNextCorner = radius / distanceFromCornerToNextCorner;

      # calculate these distance from the current point
      xDistanceFromCornerToEndCurve = (nextCornerPoint.x() - cornerPoint.x()) * fractionToNextCorner;
      yDistanceFromCornerToEndCurve = (nextCornerPoint.y() - cornerPoint.y()) * fractionToNextCorner;


      endCurvePoint = QtCore.QPoint(cornerPoint.x() + xDistanceFromCornerToEndCurve,
        cornerPoint.y() + yDistanceFromCornerToEndCurve);


      # finally draw the cornerShape
      BlockShapeUtil.cornerShape(gp,
        #start at:
        startCurvePoint.x(), startCurvePoint.y(),
        #corner at:
        cornerPoint.x(), cornerPoint.y(),
        #end at:
        endCurvePoint.x(), endCurvePoint.y());

      #         System.out.println("StartCurve at: " + startCurvePoint);
      #         System.out.println("Corner at: " + cornerPoint);
      #         System.out.println("EndCurve at: " + endCurvePoint);
      #         System.out.println("NextCorner at: " + nextCornerPoint);



   class BevelCacheKey():

      def __init__(self, width, height, area):
         self.width = width;
         self.height = height;
         self.area = area;
         self.hashCode = computeHashCode();


      def computeHashCode(self):
         hash = self.width * 1313 + self.height * 71;
         # Area.hashCode() is not implemented, so we have to do it ourselves..
         pi = area.getPathIterator(null);
         arg = double[6];
         while (not pi.isDone()):

            for i in range(0,6): arg[i] = 0;

            val = pi.getWindingRule();
            val += 3 * pi.currentSegment(arg);
            for i in range(0,6):
               val = (val * 5) + floor(arg[i] * 1000);

            hash = hash * 7 + val;
            pi.next();


         return hash;


      def equals(self, o):
         if not (instanceof(o,BevelCacheKey) or o.hashCode() != hashCode):
            return false;
         b = o;
         return width == b.width and height == b.height and area.equals(b.area);


      def hashCode():
         return self.hashCode;


   BEVEL_CACHE_SIZE = 200;
   bevelCache = {}


   def getBevelImage(width, height, s):
      #key = BlockShapeUtil.BevelCacheKey(width, height, s);

      #img = bevelCache.get(key);
      #if (img != None):
      #   # System.out.println("Found cached bevel!");
      #   return img;

      # System.out.println("Not found cached bevel!");
      # generic light vector - "chosen to look good"
      light = ShapeBevel.getLightVector(-1,-2,2);
      bevelSize = 3;
      # create image
      img = QtGui.QImage(
            width,
            height,
            QtGui.QImage.Format_ARGB32);
            #(width, height, Transparency.TRANSLUCENT);
      img.fill(255);
      painter = QtGui.QPainter(img);
      painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
      #g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      #brush = QtGui.QBrush(ShapeBevel.getFrontFaceOverlay(light));
      #painter.setColor(ShapeBevel.getFrontFaceOverlay(light));
      #painter.fillPath(s,brush);
      ShapeBevel.createShapeBevel(painter, s, 0.1, bevelSize, bevelSize, light);
      # Make a copy of the Area to prevent aliasing.
      #key2 = BevelCacheKey(width, height, s);
      #bevelCache.put(key2, img);
      return img;
blocks/GraphicsManager.py
class GraphicsManager():

   # get GraphicConfiguration for default screen - this should only be done once at startup **/
   #GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
   #private static GraphicsDevice gs = ge.getDefaultScreenDevice();
   #public static GraphicsConfiguration gc = gs.getDefaultConfiguration();

   MAX_RECYCLED_IMAGES = 50
   numRecycledImages = 0;
   recycledImages = []
   '''
   * Functionally equivalent to
   *   gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT)
   * but allows reusing released images.
   '''
   def getGCCompatibleImage(width, height,back_color):
      size = QtCore.QSize(width, height)
      imgList = None
      if(size in GraphicsManager.recycledImages):
         imgList = GraphicsManager.recycledImages[size]

      if (imgList == None or len(imgList) ==0):
         img = QtGui.QImage(
            width,
            height,
            QtGui.QImage.Format_ARGB32);
         img.fill(0);
         return img;

      GraphicsManager.numRecycledImages-=1;
      img = imgList.pop();

      # Clear the image
      img.fill(0);
      return img;


   '''
   * Add an image to the recycled images list (or if img = null, does nothing).
   * Note: the passed variable should be immediately set to null to avoid aliasing bugs.
   '''
   def recycleGCCompatibleImage(img):
      return;
      if (img == None):
         return;
      # Make sure we don't waste too much memory
      if (GraphicsManager.numRecycledImages >= GraphicsManager.MAX_RECYCLED_IMAGES):
         GraphicsManager.recycledImages= []

      size = QtCore.QSize(img.width(), img.height())

      imgList = GraphicsManager.recycledImages[size];
      if (imgList == None):
         imgList = []
         GraphicsManager.recycledImages[size] = imgList;

      imgList.append(img);
blocks/ConnectorTag.py
class ConnectorTag():
    def __init__(self,connector):
        self.aLoc = QtCore.QPoint(0,0);
        self.dimension = None;
        self.connector = connector;
        self.label = None
        self.zoom = 1.0;

    def getSocket(self):
        return self.connector;

    def setLabel(self,label):
        self.label = label;

    def getLabel(self):
        return self.label;

    def getDimension(self):
        if(self.dimension == None):
            return None;
        else:
            return QtCore.QSize((self.dimension.width()/self.zoom), (int)(self.dimension.height()/self.zoom));

    def setDimension(self,dimension):
        self.dimension=dimension

    def setAbstractLocation(self, p):
        # we can't do aLoc=loc because then we can mutate aLoc by mutating loc
        self.aLoc = p;

    def getAbstractLocation(self):
        #w e can't do aLoc=loc because then we can mutate aLoc by mutating loc
        return self.aLoc;

    def rescale(self, x):
        return x*self.zoom

    def getPixelLocation(self):
        return QtCore.QPoint(self.rescale(self.aLoc.x()), self.rescale(self.aLoc.y()));
blocks/BlockShape.py
class BlockShape():

    # radius of rounded corners
    CORNER_RADIUS = 3.0;
    # variable declaration spacer
    VARIABLE_DECLARATION_SPACER = 10;
    # spacer for bottom sockets to block sides and other bottom sockets
    BOTTOM_SOCKET_SIDE_SPACER = 10;
    # spacer for in between bottom sockets
    BOTTOM_SOCKET_MIDDLE_SPACER = 16;
    # spacer on top of bottom sockets to give continuous top
    BOTTOM_SOCKET_UPPER_SPACER = 4;

    customBlockShapeSets= []
    BCS = BlockConnectorShape()

    def __init__(self,rb):

        if (rb != None):
             self.rb = rb
             self.blockID = rb.blockID
             self.block = Block.getBlock(self.blockID)
        else:
             print("Cannot create shape of null RenderableBlock.");

            #initialize gernal path segements around the block shape
        #self.painterPath = None

        self.setupProperties();


    def setupProperties(self):
        '''
        Determine charactoristics of the block shape depending on properties of the block
        '''
        # if isCommandBlock than it has curved corners, else sharp corners
        # note: it won't actually waste time drawing sharp corners,
        # but cornering method will know to draw a line instead

        self.hasCurvedCorners = self.block.hasBeforeConnector() or self.block.hasAfterConnector() or self.block.isCommandBlock()
        self.blockCornerRadius = BlockShape.CORNER_RADIUS if self.hasCurvedCorners else 0.0


    def setupDimensions(self):
        '''
        Determine the dimensions of the block shape so there is enough space to
        draw all of the shape's features.
        '''
            # if it has a plug, then offset the start of drawing the block
        initX = 0
        initY = 0;

        if self.block.hasPlug():
             initX = BlockConnectorShape.getConnectorDimensions(self.block.getPlug()).width()

            ###CHECK FOR CUSTOM SHAPES###
            # for every customBlockShapes
        cornerPoints =    [None,None,None,None];

        # System.out.println(block.getGenusName() + " custom size: " + customBlockShapeSets.size());
        for customBlockShapeSet    in self.customBlockShapeSets:
             # returns if custom shape is within this set, break for first one found
             if (customBlockShapeSet.checkCustomShapes(block, cornerPoints, rb.accomodateLabelsWidth(), getTotalHeightOfSockets())):
                    self.topLeftCorner = QPointF (cornerPoints[0].getX() + initX, cornerPoints[0].getY() + initY)
                    self.topRightCorner = QPointF (cornerPoints[1].getX() + initX, cornerPoints[1].getY() + initY)
                    self.botLeftCorner = QPointF (cornerPoints[2].getX() + initX, cornerPoints[2].getY() + initY)
                    self.botRightCorner = QPointF (cornerPoints[3].getX() + initX, cornerPoints[3].getY() + initY)
                    return


            # if custom shape not found, then continue to derive it
            # as determined by the renderable block
            # POSITIONING FACTORS:
            # initial x: plug on left side
            # initial y: should always be 0 so top is at highest point
            # SIZE FACTORS:
            # width: default size of block, size of text
            # height: how many sockets

        width = self.determineBlockWidth()
        height = self.determineBlockHeight()

        self.blockBody = QtCore.QRect( initX, initY, width, height)

            # derived fields
        self.topLeftCorner    = QtCore.QPoint(self.blockBody.x(),    self.blockBody.y());
        self.topRightCorner = QtCore.QPoint(self.blockBody.x() + self.blockBody.width(), self.blockBody.y());
        self.botLeftCorner    = QtCore.QPoint(self.blockBody.x(),    self.blockBody.y() +        self.blockBody.height());
        self.botRightCorner = QtCore.QPoint(self.blockBody.x() + self.blockBody.width(), self.blockBody.y() + self.blockBody.height());

        pass


    def determineBlockWidth(self):
        '''
        Determines the width of the block by checking for numerous block characteristics
        TODO: this contains a lot of starlogo specific checks - should be refactored into slcodeblocks?
        '''
        width = 0;

        # add width for labels
        width += self.rb.accomodateLabelsWidth();
        #print(self.rb.accomodateLabelsWidth())
        # add width for sockets
        width += self.rb.getMaxSocketShapeWidth();

        if (self.block.isCommandBlock()) :
             width +=    10;
        elif (self.block.isDataBlock() or self.block.isFunctionBlock()):
             width += 8;
        #elif (self.block.isDeclaration()):
        #     width += 20;
        else:
             #assert false : "Block type not found." + block;
             # treat like a command block
             width += 10;


        # add some width for the drop down triangle if it has siblings
        if(self.block.hasSiblings()):
             width +=5;


        # add image width if the width calculated so far is less than the image width
        if(width < self.rb.accomodateImagesWidth()):
             width += (self.rb.accomodateImagesWidth() - width) + 10 ;

        # This forces the block to be an even number of pixels wide
        # Doing so ensures that command ports don't occur at half-pixel locations
        if (width % 2 == 1): width+=1;

        width = max(width, self.rb.getBlockWidgetDimension().width());

        return width;

    def determineBlockHeight(self):
        heightSum = 0;

        # has cornered edges?
        heightSum += 2 * BlockShape.CORNER_RADIUS if self.hasCurvedCorners else 0

        # determine and add socket heights
        heightSum += self.getTotalHeightOfSockets();

        # System.out.println("height sum after getting total height of sockets: "+heightSum);

        # ensure at least height of one data plug
        if(heightSum < BlockConnectorShape.DATA_PLUG_HEIGHT):
             heightSum = BlockConnectorShape.DATA_PLUG_HEIGHT;


        # get any height of labels other than sockets
        # page label height
        heightSum += self.rb.accomodatePageLabelHeight();

        # total image height if height so far is less than the total height of images
        if(heightSum < self.rb.accomodateImagesHeight()):
             heightSum += (self.rb.accomodateImagesHeight() - heightSum) + 10;

        # System.out.println("returned heightSum: "+heightSum);

        if(self.block.isInfix()):
            heightSum += BlockShape.BOTTOM_SOCKET_UPPER_SPACER;

        heightSum = max(heightSum, self.rb.getBlockWidgetDimension().height());

        return heightSum;

    def getBottomSide(self):
        return self.gpBottomClockwise;

    def getLeftSide(self):
        return self.gpLeftClockwise;

    def getTopSide(self):
        return self.gpTop;

    def getRightSide(self):
        return self.gpRight;

    def reformArea(self):
        # hopefully reseting is less costly than creating new ones

        self.gpTop = QtGui.QPainterPath()
        self.gpRight = QtGui.QPainterPath()
        self.gpBottom = QtGui.QPainterPath()
        self.gpLeft = QtGui.QPainterPath()

        self.setupDimensions();

        # make all of the sides
        self.makeTopSide();
        self.makeRightSide();
        self.makeBottomSide();
        self.makeLeftSide();

        # corrected (clockwise) left and bottom
        self.gpBottomClockwise = QtGui.QPainterPath ()
        self.gpLeftClockwise = QtGui.QPainterPath ()
        #self.gpBottomClockwise.moveTo(self.gpBottom.currentPosition().x(),
        #                                                     self.gpBottom.currentPosition().y());
        #self.gpLeftClockwise.moveTo(self.gpLeft.currentPosition().x(),
        #                                                     self.gpLeft.currentPosition().y());.2
        BlockShapeUtil.appendPath(self.gpBottomClockwise, self.gpBottom, True);
        BlockShapeUtil.appendPath(self.gpLeftClockwise, self.gpLeft, True);


        # create direction specific paths
        gpClockwise = QtGui.QPainterPath()
        gpCounterClockwise = QtGui.QPainterPath()

        # add to the direction specific paths
        gpCounterClockwise.connectPath(self.gpLeft);
        gpCounterClockwise.connectPath(self.gpBottom);
        gpClockwise.connectPath(self.gpTop);
        gpClockwise.connectPath(self.gpRight);

        # connect so gpCounterClockwise is the full path
        # it must be counter-clockwise for the bevel to be able to use it
        BlockShapeUtil.appendPath(gpCounterClockwise, gpClockwise, True);

        # convert it to an area
        self.blockArea = gpCounterClockwise

        return self.blockArea;


    def setEndPoint(self, gp, currentCorner, otherCorner, firstPointOnSide):

        # save calculation time if cornerRadius is zero
        if(self.blockCornerRadius == 0):
             if(firstPointOnSide):
                    BlockShapeUtil.moveTo(gp,currentCorner.x(), currentCorner.y());
             else:
                    BlockShapeUtil.lineTo(gp,currentCorner.x(), currentCorner.y());

        else:
             # corner radius > 0

             # find theta from line from current corner to other corner
             theta = math.atan2(otherCorner.x() - currentCorner.x(),
                                                            #negate since (0,0) at upper left
                                                            -(otherCorner.y() - currentCorner.y()));

             dx = self.blockCornerRadius * math.cos(math.pi/2 - theta);
             dy = self.blockCornerRadius * math.sin(math.pi/2 - theta);

             # System.out.println("theta: " + theta + "    xdiff: " + Xdiff + "    Ydiff: " + Ydiff);

             if(firstPointOnSide):
                    BlockShapeUtil.moveTo(gp, (currentCorner.x() + dx), (currentCorner.y() - dy));
             else:
                    BlockShapeUtil.lineTo(gp, (currentCorner.x() + dx), (currentCorner.y() - dy));


    def makeTopSide(self):

        # starting point of the block
        # gpTop.moveTo((float) topLeftCorner.getX(), (float) topLeftCorner.getY() + blockCornerRadius);
        self.setEndPoint(self.gpTop, self.topLeftCorner, self.botLeftCorner, True);

        # curve up and right
        BlockShapeUtil.cornerTo(self.gpTop, self.topLeftCorner, self.topRightCorner, self.blockCornerRadius);

        # command socket if necessary
        if (self.block.isCommandBlock() and self.block.hasBeforeConnector()):
             # params: path, distance to center of block, going right
             # Old center-aligned ports
             # Point2D p = BCS.addControlConnectorShape(gpTop, (float) topLeftCorner.distance(topRightCorner) / 2 - blockCornerRadius, true);
             # Trying left-aligned ports for now
             p = BlockShape.BCS.addControlConnectorShape(self.gpTop, BlockConnectorShape.COMMAND_PORT_OFFSET + self.blockCornerRadius, True);

             self.rb.updateSocketPoint(self.block.getBeforeConnector(), p);

        # curve down
        BlockShapeUtil.cornerTo(self.gpTop, self.topRightCorner, self.botRightCorner, self.blockCornerRadius);
        #BlockShapeUtil.lineTo(self.gpTop, self.topRightCorner.x(),self.topRightCorner.y())
        # end topside
    # gpTop.lineTo(blockBody.x + blockBody.width, blockBody.y + blockCornerRadius);
    # gpTop.lineTo((float) topRightCorner.getX(), (float) topRightCorner.getY() + blockCornerRadius);
        self.setEndPoint(self.gpTop, self.topRightCorner, self.botRightCorner, False);

    def makeRightSide(self):

        # move to the end of the TopSide
        # gpRight.moveTo(blockBody.x + blockBody.width, blockBody.y + blockCornerRadius);
        # gpRight.moveTo((float) topRightCorner.getX(), (float) topRightCorner.getY() + blockCornerRadius);
        self.setEndPoint(self.gpRight, self.topRightCorner, self.botRightCorner, True);
        #BlockShapeUtil.moveTo(self.gpRight,self.topRightCorner.x(), self.topRightCorner.y());
        # if page label enabled, extra height
        if(self.block.hasPageLabel()):
             BlockShapeUtil.lineToRelative(self.gpRight, 0, self.rb.accomodatePageLabelHeight()/2);

        # ADD MIRRORED PLUG ////
        # if it has a mirrored plug, then add it
        if((self.block.getPlug() != None) and
            (self.block.getPlug().getPositionType() == (BlockConnector.PositionType.MIRROR))):
             # add the plug to the gpRight
             BlockShape.BCS.addDataPlug(self.gpRight, self.block.getPlug().type, True, True);


        # ADD SOCKETS ////
        # for each socket in the iterator
        for curSocket in self.block.getSockets() :

             # if it is a single socket (there are no mirrored sockets)
             if (curSocket.getPositionType() == (BlockConnector.PositionType.SINGLE)):
                    # add the socket shape to the gpRight

                    # if it's a command socket
                    if (BlockConnectorShape.isCommandConnector(curSocket)):
                         spacerHeight = self.getSocketSpacerHeight(curSocket, BlockConnectorShape.DEFAULT_COMMAND_INPUT_HEIGHT);
                         # draw the command socket bar and such
                         p = BlockShape.BCS.addCommandSocket(self.gpRight, spacerHeight);
                         self.rb.updateSocketPoint(curSocket, p);

                    else:
                         self.appendConnectorOffset(self.gpRight, self.topRightCorner, self.botRightCorner, curSocket, True);
                         # it's a data socket
                         p = BlockShape.BCS.addDataSocket(self.gpRight, curSocket.type, True);
                         self.rb.updateSocketPoint(curSocket, p);

                         spacerHeight = self.getSocketSpacerHeight(curSocket, BlockConnectorShape.DATA_PLUG_HEIGHT);
                         socketHeight = BlockConnectorShape.getConnectorDimensions(curSocket).height();
                         BlockShapeUtil.lineToRelative(self.gpRight, 0, spacerHeight - socketHeight);

                         self.appendConnectorOffset(self.gpRight, self.topRightCorner, self.botRightCorner, curSocket, False);


        # if (block.getPlug() != null) System.out.println(block.getPlug().getPositionType());

        # line to the bottom right
        # gpRight.lineTo(blockBody.x + blockBody.width, blockBody.y + blockBody.height - blockCornerRadius);
        # gpRight.lineTo((float) botRightCorner.getX(), (float) botRightCorner.getY() - blockCornerRadius);
        self.setEndPoint(self.gpRight, self.botRightCorner, self.topRightCorner, False);


    def makeBottomSide(self):

        # start bottom-right
        # gpBottom.moveTo(blockBody.x, blockBody.y + blockBody.height - blockCornerRadius);
        #gpBottom.moveTo((float) botLeftCorner.getX(), (float) botLeftCorner.getY() - blockCornerRadius);
        self.setEndPoint(self.gpBottom, self.botLeftCorner, self.topLeftCorner, True);

        # BlockShapeUtil.lineToRelative(gpLeft, 0, 10);

        # curve down and right
        BlockShapeUtil.cornerTo(self.gpBottom, self.botLeftCorner, self.botRightCorner, self.blockCornerRadius);

        # CONTROL CONNECTOR
        # Removing the isCommandBlock requirement for now because procedure block has an after connector
        # if (block.isCommandBlock() && block.hasAfterConnector()) {
        if (self.block.hasAfterConnector() and (not self.rb.isCollapsed())):

             # control connector if necessary
             # Old center-aligned port
             # Point2D p = BCS.addControlConnectorShape(gpBottom, (float) botLeftCorner.distance(botRightCorner) / 2 - blockCornerRadius, true);
             # Trying left-aligned ports
             p = BlockShape.BCS.addControlConnectorShape(self.gpBottom, BlockConnectorShape.COMMAND_PORT_OFFSET + self.blockCornerRadius, True);
             self.rb.updateSocketPoint(self.block.getAfterConnector(), p);

        # curve right and up
        BlockShapeUtil.cornerTo(self.gpBottom, self.botRightCorner, self.topRightCorner, self.blockCornerRadius);

        # end bottom
        # gpBottom.lineTo(blockBody.x + blockBody.width, blockBody.y + blockBody.height - blockCornerRadius);
        # gpBottom.lineTo((float) botRightCorner.getX(), (float) botRightCorner.getY() - blockCornerRadius);
        self.setEndPoint(self.gpBottom, self.botRightCorner, self.topRightCorner, False);

    def makeLeftSide(self):

        # starting point of the block
        # gpLeft.moveTo(blockBody.x, blockBody.y + blockCornerRadius);
        # gpLeft.moveTo((float) topLeftCorner.getX(), (float) topLeftCorner.getY() + blockCornerRadius);
        self.setEndPoint(self.gpLeft, self.topLeftCorner, self.botLeftCorner, True);

        #//// ADD PLUG ////
        if (self.block.getPlug() != None):

             self.appendConnectorOffset(self.gpLeft, self.botLeftCorner, self.topLeftCorner, self.block.getPlug(), False);
             # add the plug shape to the gpLeft
             p = BlockShape.BCS.addDataPlug(self.gpLeft, self.block.getPlug().type, True,False);
             self.rb.updateSocketPoint(self.block.getPlug(), p);

             self.appendConnectorOffset(self.gpLeft, self.botLeftCorner, self.topLeftCorner, self.block.getPlug(), True);

        # end left side
        # gpLeft.lineTo(blockBody.x, blockBody.y + blockBody.height - blockCornerRadius);

        self.setEndPoint(self.gpLeft, self.botLeftCorner, self.topLeftCorner, False);

    def getTotalHeightOfSockets(self):
        heightSum = 0;

        # note if we find a bottom socket
        hasBottomSocket = False;
        maxBottomSocketHeight = 0;

        for socket in self.block.getSockets():

             socketDimension = self.rb.getSocketSpaceDimension(socket);
             # bottom connector stuff
             if (socket.getPositionType() == BlockConnector.PositionType.BOTTOM ):
                    if(socketDimension != None and socketDimension.height() > maxBottomSocketHeight):
                         maxBottomSocketHeight = socketDimension.height();

                    hasBottomSocket = True;
                    continue;

             #if the socket has been assigned a dimension...
             if(socketDimension != None):

                    heightSum += socketDimension.height();
                    # if command, then add other command parts
                    if(BlockConnectorShape.isCommandConnector(socket)):
                        heightSum += BlockConnectorShape.COMMAND_INPUT_BAR_HEIGHT + 2 * BlockShape.CORNER_RADIUS;
                    continue;


             # else use default dimension
             if(BlockConnectorShape.isCommandConnector(socket)):
                    heightSum += BlockConnectorShape.DEFAULT_COMMAND_INPUT_HEIGHT + BlockConnectorShape.COMMAND_INPUT_BAR_HEIGHT + 2 * BlockShape.CORNER_RADIUS;
                    #print("3：  heightSum {0}", heightSum)
             #else normal data connector, so add height
             else:
                    heightSum += BlockConnectorShape.DATA_PLUG_HEIGHT;


        # if it has bottom sockets, add extra height
        if(hasBottomSocket):
            heightSum += maxBottomSocketHeight;
        return heightSum;


    def appendConnectorOffset(self,gp, topPoint, botPoint,
        blockConnector, aboveConnector):

        # if top and bottom are equal, then no offset necessary
        if(topPoint.x() == botPoint.x()): return;

        # if top further right than bottom, then Xdiff is positive
        Xdiff = topPoint.x() - botPoint.x();
        # absolute distance
        Ydiff = math.abs(topPoint.y() - botPoint.y());


        # check to only offset correctly above or below the connector:
        # offset only above connectors on right slanting sides
        if(Xdiff > 0 and not aboveConnector) : return;
        # offset only below connectors on left slanting sides
        if(Xdiff < 0 and    aboveConnector): return;

        # get fraction by dividing connector height by total height of the side
        fraction = BlockConnectorShape.getConnectorDimensions(blockConnector).getHeight()/Ydiff;
        insetDist = Xdiff * fraction;

        # if top further out, then inset left - else move right
        BlockShapeUtil.lineToRelative(gp, -insetDist, 0);

    def getSocketSpacerHeight(self,socket, defaultHeight):
        # spacer for block connected to socket
        # default spacer height if no block connected
        spacerHeight = defaultHeight;
        # check for socket space from a connected block
        socketSpaceDimension = self.rb.getSocketSpaceDimension(socket);
        # if the socket has been assigned a dimension
        if(socketSpaceDimension != None):
             spacerHeight = socketSpaceDimension.height();

        return spacerHeight;
blocks/BlockImageIcon.py
class BlockImageIcon(QLabel):
    
    def __init__(self, url, img_loc, _icon, width, height, isEditable, wrapText, lockRatio=True):
        QLabel.__init__(self)
        self._url = url
        self._lockRatio = lockRatio
        if(_icon == None):
            self._imgIcon = QPixmap()
            if(self._url != ''):
                self.imgCtrl = FileDownloader(QUrl(url))
                self._imgIcon.loadFromData(self.imgCtrl.downloadedData())
                
            #self._imgIcon = self._imgIcon.scaled(self.width(), self.height())   
        else:
            self._imgIcon = _icon
        
        self.img_loc = img_loc
        self._isEditable = isEditable
        self._wrapText = wrapText
        
        #setPreferredSize(new Dimension(_imgIcon.getIconWidth(), _imgIcon.getIconHeight()));
   
        #store in blockImageMap
        #icon = QPixmap(os.getcwd() +fileLocation)
        if lockRatio:
            height = width * self._imgIcon.height()/self._imgIcon.width()
        
        if(self._imgIcon != None and not self._imgIcon.isNull() and width > 0 and height > 0): 
            #print('scaled in __init__')
            #self._imgIcon = self._imgIcon.scaled(width, height)        
            self.setPixmap(self._imgIcon)
   
        self.resize(QSize(width,height )) 
        
    def setSize(self, width, height,  forceReload=False):
        
        QApplication.setOverrideCursor(Qt.WaitCursor);        
        try:
            if(forceReload or width != self._imgIcon.width() or height != self._imgIcon.height()):
                if(self._url != ''):
                    self.imgCtrl = FileDownloader(QUrl(self._url ))
                    icon = QPixmap()
                    icon.loadFromData(self.imgCtrl.downloadedData())
                
                    if self.lockRatio:
                        #print('icon w:%d,h:%d'%(icon.width(), icon.height()))
                        height = width * icon.height()/icon.width()
                    
                    #print('w:%d,h:%d'%(width, height))
                    self._imgIcon = icon.scaled(width, height)
                    self.setPixmap(self._imgIcon)
                    
            self.resize(QSize(width,height )) 
        except:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno,exc_obj)        
        finally:
            QApplication.restoreOverrideCursor();
    
    def getImageInfo(self):
        import base64
        ImageInfo = {}
        ImageInfo['location'] = self.location
        ImageInfo['isEditable'] = self.isEditable
        ImageInfo['wrapText'] = self.wrapText
        ImageInfo['url'] = self.url
        ImageInfo['width'] = self.width()
        ImageInfo['height'] = self.height()
        ImageInfo['lockRatio'] = self.lockRatio
        
        # Save QPixmap to QByteArray via QBuffer.
        byte_array = QByteArray()
        buffer = QBuffer(byte_array)
        buffer.open(QIODevice.WriteOnly)
        self.icon.save(buffer, 'PNG')
        icon_data = byte_array.data()
        b64_data = base64.b64encode(icon_data)
        icon_b64_str = b64_data.decode("utf-8") 
        ImageInfo['icon'] = icon_b64_str
        return ImageInfo
    
    def loadImage(self, url):
        imageUrl = QUrl(url);
        self.imgCtrl = FileDownloader(imageUrl, self) 
        self.connect(self.imgCtrl , SIGNAL ('downloaded()'), self.downloaded);        
        
    def downloaded(self):
        icon = QPixmap() 
        icon.loadFromData(self.imgCtrl.downloadedData())
        icon = icon.scaled(self.width(), self.height())        
     
    @property
    def icon(self):
        return self._imgIcon

    @icon.setter
    def icon(self, _icon):
        if(_icon != None and not _icon.isNull() and self.width() > 0 and self.height() > 0): 
            if(self.lockRatio):
                height = self.width() * _icon.height()/_icon.width()
            else:
                height = self.height()
                
            self.resize(self.width(), height)
            self._imgIcon = _icon.scaled(self.width(), height)  
            self.setPixmap(self._imgIcon)        
        #else:
        #    print(self.width())
    
    @property
    def location(self):
        return self.img_loc

    @location.setter
    def location(self, value):
        self.img_loc = value        
        
    @property
    def isEditable(self):
        return self._isEditable

    @isEditable.setter
    def isEditable(self, value):
        self._isEditable = value    
  
    @property
    def wrapText(self):
        return self._wrapText

    @wrapText.setter
    def wrapText(self, value):
        self._wrapText = value 
        
    @property
    def lockRatio(self):
        return self._lockRatio

    @lockRatio.setter
    def lockRatio(self, value):
        self._lockRatio = value
        if self._lockRatio:
            height = self.width() * self._imgIcon.height()/self._imgIcon.width()  
            self.resize(QSize(self.width(),height )) 
            #print('w:%d,h:%d'%(self.width(), self.height()))
        
    @property
    def url(self):
        return self._url

    @url.setter
    def url(self, value):
        self._url = value  
blocks/InfixBlockShape.py
class InfixBlockShape(BlockShape):

   def __init__(self,rb):
      BlockShape.__init__(self,rb)
      self.maxX=0

   def makeBottomSide(self):
      '''
      * Overrided from BlockShape.
      * Takes into account the need to resize the dimensions of an infix block for various cases.
      '''
      from blocks.RenderableBlock import RenderableBlock
      from blocks.BlockShapeUtil import BlockShapeUtil
      # Reset the maximum X-coordinate so the infix block can resize if you remove blocks within it
      self.maxX = 0;

      # start bottom-right
      self.setEndPoint(self.gpBottom, self.botLeftCorner, self.topLeftCorner, True);

      #curve down and right
      BlockShapeUtil.cornerTo(self.gpBottom, self.botLeftCorner, self.botRightCorner, self.blockCornerRadius);



      # BOTTOM SOCKETS
      # for each socket in the iterator
      socketCounter = 0; #need to use this to determine which socket we're on
      for curSocket in self.block.getSockets():
         #if bottom socket
         if (curSocket.getPositionType() == BlockConnector.PositionType.BOTTOM):

            # move away from bottom left corner
            if(socketCounter > 0):
               self.gpBottom.lineTo(
                         self.gpBottom.currentPosition().x() + BlockShape.BOTTOM_SOCKET_MIDDLE_SPACER,
                         self.gpBottom.currentPosition().y());
            else:
                 self.gpBottom.lineTo(
                         self.gpBottom.currentPosition().x() + BlockShape.BOTTOM_SOCKET_SIDE_SPACER,
                         self.gpBottom.currentPosition().y());


            # move down so bevel doesn't screw up from connecting infinitely sharp corner
            # as occurs from a curved port
            BlockShapeUtil.lineToRelative(self.gpBottom, 0, -0.1);

            #//////////////////////
            #//begin drawing socket
            #//////////////////////

            if(curSocket.blockID == Block.NULL):
               # draw first socket - up left side
               leftSocket = BlockShape.BCS.addDataSocketUp(self.gpBottom, curSocket.type, True);
               self.rb.updateSocketPoint(curSocket, leftSocket);
               # System.out.println("socket poitn: "+rb.getSocketPoint(curSocket));

               # System.out.println("socket poitn leftsocket: "+leftSocket);

               # draw left standard empty socket space - top side
               self.gpBottom.lineTo(
                         self.gpBottom.currentPosition().x() + BlockShape.BOTTOM_SOCKET_SIDE_SPACER,
                         self.gpBottom.currentPosition().y());


               #draw first socket - down right side
               BlockShape.BCS.addDataSocket(self.gpBottom, curSocket.type, False);
               #rb.updateSocketPoint(curSocket, rightSocket);
            else: # there is a connected block

               connectedBlock = Block.getBlock(curSocket.blockID);
               connectedRBlock = RenderableBlock.getRenderableBlock(curSocket.blockID);
               if(connectedBlock == None or connectedRBlock== None) : continue

               # calculate and update the new socket point
               # update the socket point of this cursocket which should now adopt the plug socket point of its
               # connected block since we're also adopting the left side of its shape

               # Use coordinates when the zoom level is 1.0 to calculate socket point
               unzoomX = connectedRBlock.getSocketPixelPoint(connectedBlock.getPlug()).x()/connectedRBlock.getZoom();
               unzoomY = connectedRBlock.getSocketPixelPoint(connectedBlock.getPlug()).y()/connectedRBlock.getZoom();
               connectedBlockSocketPoint = QtCore.QPoint(unzoomX, unzoomY);
               currentPoint = self.gpBottom.currentPosition();
               newX = connectedBlockSocketPoint.x() + abs(connectedBlockSocketPoint.x() - currentPoint.x());
               newY = connectedBlockSocketPoint.y() + abs(connectedRBlock.getBlockHeight()/connectedRBlock.getZoom() - currentPoint.y());
               self.rb.updateSocketPoint(curSocket, QtCore.QPoint(newX, newY));

               self.gpBottom.currentPosition().x()

               connectedBlockShape = RenderableBlock.getRenderableBlock(curSocket.blockID).getBlockShape();
               #append left side of connected block
               
               connectedBlockShape.reformArea()
               BlockShapeUtil.appendPath(self.gpBottom, connectedBlockShape.getLeftSide(), False);
               connectedBlockShape.getLeftSide().currentPosition()
               self.gpBottom.currentPosition().x()
               # append right side of connected block (more complicated)
               if(connectedBlock.getNumSockets() == 0 or connectedBlock.isInfix()):
                  #  append top side of connected block
                  BlockShapeUtil.appendPath(self.gpBottom, connectedBlockShape.getTopSide(), False);
                  BlockShapeUtil.appendPath(self.gpBottom, connectedBlockShape.getRightSide(), False);
               else:
                  # iterate through the sockets of the connected block, checking if
                  # it has blocks connected to them
                  self.appendRightSidePath(self.gpBottom, connectedBlock, connectedBlockShape);


               # Updates the maximum X-coordinate and sets the current point to self.maxX
               if(self.maxX < self.gpBottom.currentPosition().x()):
                  self.maxX = self.gpBottom.currentPosition().x();

               self.gpBottom.lineTo(self.maxX, self.gpBottom.currentPosition().y());


            # bump down so bevel doesn't screw up
            BlockShapeUtil.lineToRelative(self.gpBottom, 0, 0.1);

            # System.out.println("gpbottom starting point: "+gpBottom.currentPosition());

            # draw RIGHT to create divider ////
            if(socketCounter < self.block.getNumSockets()-1):
               self.gpBottom.lineTo( #need to add the width of the block label.  warning: this assumes that there is only one block label
                      self.gpBottom.currentPosition().x() + BlockShape.BOTTOM_SOCKET_MIDDLE_SPACER + self.rb.accomodateLabelsWidth(),
                      self.gpBottom.currentPosition().y());
            else:
               self.gpBottom.lineTo(
                      self.gpBottom.currentPosition().x() + BlockShape.BOTTOM_SOCKET_SIDE_SPACER,
                      self.gpBottom.currentPosition().y());

            socketCounter+=1;

      #curve right and up
      BlockShapeUtil.cornerTo(self.gpBottom, self.botRightCorner, self.topRightCorner, self.blockCornerRadius);

      #end bottom
      self.setEndPoint(self.gpBottom, self.botRightCorner, self.topRightCorner, False);

   def appendRightSidePath(self,painterPath, connectedBlock, connectedBlockShape):
      '''
        * Appends the right side path of the stack of blocks connected to the specified connectedBlock.  If there are
        * some empty sockets, this method will append empty placeholders.
        * @param painterPath the GeneralPath to append the new path to
        * @param connectedBlock the Block instance whose right side of its stack of connected blocks will be appened to the
        * specified painterPath
        * @param connectedBlockShape the BlockShape of the specified connectedBlock
       '''
      from blocks.RenderableBlock import RenderableBlock
      # int lastBottomPathWidth;


      # append top side of connected block
      BlockShapeUtil.appendPath(painterPath, connectedBlockShape.getTopSide(), False);

      startX = painterPath.currentPosition().x();
      for socket in connectedBlock.getSockets():
         # Sets the current x-coordinate to the start x-coordinate
         # Makes it so path movements created by previous blocks don't affect
         # the subsequent blocks.
         painterPath.lineTo(startX, painterPath.currentPosition().y());
         if(socket.blockID == Block.NULL):
            # just draw an empty socket placeholder
            # if its the first socket, draw a top side
            painterPath.lineTo(
                        painterPath.currentPosition().x() + BlockShape.BOTTOM_SOCKET_SIDE_SPACER,
                        painterPath.currentPosition().y());

            # now draw the empty right socket side
            # draw first socket - down right side
            BlockShape.BCS.addDataSocket(painterPath, socket.getKind(), False);
            # TODO:lastBottomPathWidth = (int)BOTTOM_SOCKET_SIDE_SPACER;
         else:
            # a block is connected to this socket, check if that block has sockets
            # OR if the block is an infix block - if it is infix, then just wrap around the infix block
            block = Block.getBlock(socket.blockID);
            shape = RenderableBlock.getRenderableBlock(socket.blockID).getBlockShape();
            if(block.getNumSockets() == 0 or block.isInfix()):
               # append this block's top and right side
               # TODO instead of just appending the right side...draw line to
               BlockShapeUtil.appendPath(painterPath, shape.getTopSide(), False);
               BlockShapeUtil.appendPath(painterPath, shape.getRightSide(), False);
            else:
               self.appendRightSidePath(painterPath, block, shape);


         # Updates the maximum X-coordinate and sets the current point to self.maxX
         if(self.maxX < painterPath.currentPosition().x()):
            self.maxX =  painterPath.currentPosition().x();

         painterPath.lineTo(self.maxX, painterPath.currentPosition().y());

   def determineBlockWidth(self):
      '''
        * Overrided from BlockShape.
        * Determines the width of the sum of the bottom sockets and uses it if it is
        * greater than the width determined by the determineBlockWidth in BlockShape.
        * Else, it returns the sum of these two values.
      '''
      # System.out.println("determining block width");

      width = BlockShape.determineBlockWidth(self);

      # if the sum of bottom sockets is greater than the calculated width, then use it
      bottomSocketWidth = 0;
      for socket in self.block.getSockets():
         if (socket.getPositionType() == BlockConnector.PositionType.BOTTOM):
            if(socket.blockID == Block.NULL):
               # 3 socket spacers = left of socket, between connectors, right of socket
               bottomSocketWidth += BlockShape.BOTTOM_SOCKET_SIDE_SPACER ;
            else: # a block is connected to socket
               # TODO get their assigned width from rb
               if (self.rb.getSocketSpaceDimension(socket) != None):
                  bottomSocketWidth += self.rb.getSocketSpaceDimension(socket).width();
                  bottomSocketWidth -= BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH;
                  # if it's a mirror plug, subtract for the other side, too.
                  if (Block.getBlock(socket.blockID).getPlug().getPositionType() == BlockConnector.PositionType.MIRROR):
                     bottomSocketWidth -= BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH;



      bottomSocketWidth += 2 * BlockShape.BOTTOM_SOCKET_MIDDLE_SPACER;  # TODO need to decide for a size of the middle spacer and how to place them
      bottomSocketWidth += 2 * BlockShape.BOTTOM_SOCKET_SIDE_SPACER;

      if(bottomSocketWidth > width): return (bottomSocketWidth + self.rb.accomodateLabelsWidth());

      width += bottomSocketWidth;

      # make sure its even
      if(width % 2 == 1): width+=1;

      return width;
blocks/BlockLabel.py
class BlockLabel():
  def enum(*sequential, **named):
       enums = dict(zip(sequential, range(len(sequential))), **named)
       return type('Enum', (), enums)

  Type = enum("NAME_LABEL", "PAGE_LABEL", "PORT_LABEL", "DATA_LABEL")

  blockFontSmall_Bold   = QtGui.QFont("Arial",  6, QtGui.QFont.Bold);
  blockFontMedium_Bold  = QtGui.QFont("Arial",  8, QtGui.QFont.Bold);
  blockFontLarge_Bold   = QtGui.QFont("Arial", 10, QtGui.QFont.Bold);
  blockFontSmall_Plain  = QtGui.QFont("Arial",  6, QtGui.QFont.Normal);
  blockFontMedium_Plain = QtGui.QFont("Arial",  8, QtGui.QFont.Normal);
  blockFontLarge_Plain  = QtGui.QFont("Arial", 10, QtGui.QFont.Normal);

  def __init__(self,parent, initLabelText, prefix, suffix, labelType, isEditable, blockID, hasComboPopup, tooltipBackground):
      from blocks.RenderableBlock import RenderableBlock
      from blocks.FactoryRenderableBlock import FactoryRenderableBlock
      
      self.widget= LabelWidget(parent, blockID, initLabelText, prefix, suffix, Block.getBlock(blockID).getColor().darker(), tooltipBackground)
      self.parent = parent
      self.zoom = 1.0
      self.initLabelText = initLabelText
      self.labelType = labelType
      self.blockID = blockID
      self.hasComboPopup = hasComboPopup
      # Only editable if the isEditable parameter was true, the label is either a Block's name or
      # socket label, the block can edit labels, and the block is not in the factory.
      self.widget.setEditable(
            isEditable and
            (labelType == BlockLabel.Type.NAME_LABEL or labelType == BlockLabel.Type.PORT_LABEL) and
            Block.getBlock(blockID).isLabelEditable() and
            not isinstance(RenderableBlock.getRenderableBlock(blockID),FactoryRenderableBlock));
      if(labelType == None or labelType == (BlockLabel.Type.NAME_LABEL)):
         self.widget.setFont(BlockLabel.blockFontLarge_Bold);
      elif(labelType == (BlockLabel.Type.PAGE_LABEL)):
         self.widget.setFont(BlockLabel.blockFontMedium_Bold);
      elif(labelType ==(BlockLabel.Type.PORT_LABEL)):
        self.widget.setFont(BlockLabel.blockFontMedium_Bold);
      elif(labelType == (BlockLabel.Type.DATA_LABEL)):
        self.widget.setFont(BlockLabel.blockFontMedium_Bold);
      
      # set initial text
      self.widget.updateLabelText(initLabelText);
      # add and show the textLabel initially
      self.widget.setEditingState(False);
      
      #famList = {}
      #if (Block.getBlock(blockID).hasSiblings()) :
      #  famList = Block.getBlock(blockID).getSiblingsList();      

      #self.widget.setMenu();
      
      self.widget.fireTextChanged = self.textChanged
      self.widget.fireGenusChanged = self.labelChanged
      self.widget.fireMenuChanged = self.onRenameVariable
  
  def menuEnabled(self):
      return self.hasComboPopup and Block.getBlock(self.blockID).hasSiblings()

  def labelChanged(self, label):
    from blocks.RenderableBlock import RenderableBlock

    if(self.widget.hasMenu):
      oldBlock = Block.getBlock(self.blockID);
      oldBlock.changeLabelTo(label);
      rb = RenderableBlock.getRenderableBlock(self.blockID);
      rb.repaintBlock();
      #Workspace.getInstance().notifyListeners(new WorkspaceEvent(rb.getParentWidget(), blockID, WorkspaceEvent.BLOCK_GENUS_CHANGED));

  def textChanged(self, text):
    from blocks.RenderableBlock import RenderableBlock
    from blocks.BlockStub import BlockStub
    
    if (self.labelType == BlockLabel.Type.NAME_LABEL or 
        self.labelType == BlockLabel.Type.PORT_LABEL) and  (Block.getBlock(self.blockID).isLabelEditable()):
      if (self.labelType == (BlockLabel.Type.NAME_LABEL)):
        Block.getBlock(self.blockID).setBlockLabel(text);

      plug = Block.getBlock(self.blockID).getPlug();
      # Check if we're connected to a block. If we are and the the block we're connected to
      # has stubs, update them.
      if (plug != None and plug.blockID != Block.NULL):
        if (Block.getBlock(plug.blockID) != None):
          if(Block.getBlock(plug.blockID).isProcedureDeclBlock() and
            Block.getBlock(plug.blockID).hasStubs()):
            # Blocks already store their socket names when saved so it is not necessary
            # nor desired to call the connectors changed event again.
            if (Block.getRenderableBlock(plug.blockID).isLoading()):
              BlockStub.parentConnectorsChanged(self.workspace, plug.blockID);

      rb = RenderableBlock.getRenderableBlock(self.blockID);

      if(rb != None and not rb.isLoading):
        rb.reformBlockShape()
        rb.updateBuffImg()
        #rb.repaint()
        pass
          #workspace.notifyListeners(new WorkspaceEvent(workspace, rb.getParentWidget(), blockID, WorkspaceEvent.BLOCK_RENAMED));

  def onRenameVariable(self, old_name, new_name):
    from blocks.RenderableBlock import RenderableBlock
    from blocks.FactoryRenderableBlock import FactoryRenderableBlock
    from blocks.BlockGenus import BlockGenus
    
    rb = RenderableBlock.getRenderableBlock(self.blockID);
    block = Block.getBlock(self.blockID)
    familyMap = block.getCustomerFamily();
    
    findVar = False
    for key in familyMap:
      if(familyMap[key] == old_name):
        familyMap[key] = new_name
        findVar = True 
        
    if(not findVar):
        familyMap[new_name] = new_name

    #self.labelChanged(new_name)
    
    #return
    #factoryBlock = rb.factoryRB

    #for rb in factoryBlock.child_list:
    #    blockLabel = rb.blockLabel
    #    #blockLabel.widget.setMenu();
    #    if(blockLabel.getText() == old_name):   
    #        blockLabel.labelChanged(new_name)
    
    #factoryBlock.blockLabel.widget.setMenu()
    #if(factoryBlock.blockLabel.getText() == old_name):
    #    factoryBlock.blockLabel.labelChanged(new_name)
    #print(block.getGenus().familyName)
    #print(BlockGenus.familyBlocks)
    if(block.getGenus().familyName in BlockGenus.familyBlocks):
      for genus in  BlockGenus.familyBlocks[block.getGenus().familyName]:
          genusName = genus.genusName

          if genusName not in FactoryRenderableBlock.factoryRBs: continue
          factoryBlock = FactoryRenderableBlock.factoryRBs[genusName]

          for rb in factoryBlock.child_list:
            blockLabel = rb.blockLabel
            #blockLabel.widget.setMenu();
            if(blockLabel.getText() == old_name):
              blockLabel.labelChanged(new_name)
          
          #factoryBlock.blockLabel.widget.setMenu();
          if(factoryBlock.blockLabel.getText() == old_name):
            #print(factoryBlock)
            factoryBlock.blockLabel.labelChanged(new_name)
    
  def getAbstractWidth(self):
    if(self.widget.hasMenu):
      return (self.widget.width()/self.zoom)-9;
    else:
      return (self.widget.width()/self.zoom);

  def getAbstractHeight(self):
      return (self.widget.height()/self.zoom);

  def rescale(self,x):return x*self.zoom
  def descale(self,x):return x/self.zoom
  def setPixelLocation(self,x, y): self.widget.move(x, y)

  def getText(self):
      return self.widget.getText();

  def setZoomLevel(self, newZoom):
      self.zoom = newZoom;
      #self.widget.setZoomLevel(newZoom);

  def setText(self,text):
      #pass
      self.widget.setText(text);

  def setParent(self,parent):
      self.parent = parent
      self.widget.setParent(parent);

  def getParent(self):
      return self.parent

  def raise_(self):
      self.widget.raise_()
blocks/BlockConnectorShape.py
class BlockConnectorShape():

   # left alignment buffer for command ports
   COMMAND_PORT_OFFSET = 15;

   # height of horizontal-plug/socket
   DATA_PLUG_HEIGHT = 24.0;

   # Width of most plug shapes
   NORMAL_DATA_PLUG_WIDTH = 8.0;

   # Width of polymorphic plug shape
   POLYMORPHIC_DATA_PLUG_WIDTH = 8.0;

   # width of vertical control connection
   CONTROL_PLUG_WIDTH = 14.0;

   # height of vertical control connection
   CONTROL_PLUG_HEIGHT = 4.0;

   # width of command input bar
   COMMAND_INPUT_BAR_WIDTH = COMMAND_PORT_OFFSET + 2.0;

   # height of command input bar
   COMMAND_INPUT_BAR_HEIGHT = 5.0;

   # default height of command input
   DEFAULT_COMMAND_INPUT_HEIGHT = DATA_PLUG_HEIGHT

   COMMAND_SHAPE_NAME="";

   '''
   Different styles of SocketShapes:
   1 is the normal shape
   2 is the double stacked shape
   3 is the double inversion
   '''

   TRIANGLE_1 = 1;
   TRIANGLE_2 = 2;
   TRIANGLE_3 = 3;

   CIRCLE_1 = 4;
   CIRCLE_2 = 5;
   CIRCLE_3 = 6;

   SQUARE_1 = 7;
   SQUARE_2 = 8;
   SQUARE_3 = 9;

   POLYMORPHIC_1 = 10;
   POLYMORPHIC_2 = 11;
   POLYMORPHIC_3 = 12;

   PROC_PARAM = 13;

   COMMAND = 14;
   DEBUG_MODE = False;

   SHAPE_MAPPINGS = {}

   def __init__(self):
      pass

   def getConnenctionShapeMapping(shapeName):

      if (shapeName not in BlockConnectorShape.SHAPE_MAPPINGS):
         #assert False, ("Unknown Connection Type: " + shapeName)
         return -1;
      else:
         return BlockConnectorShape.SHAPE_MAPPINGS[shapeName]



   def getConnectorDimensions(blockConnector):
      '''
      Gets the dimension of a given BlockConnector.  Mapping for the connector to a shape must already exist.
      '''
      mappedValue = BlockConnectorShape.getConnenctionShapeMapping(blockConnector.type)

      # if shaped not yet mapped
      # assert (mappedValue != -1) : "Block Connector is not mapped: "+blockConnector;

      # TODO: add proc param dimensions
      if((mappedValue == BlockConnector.POLYMORPHIC_1) or
         (mappedValue == BlockConnector.POLYMORPHIC_2) or
         (mappedValue == BlockConnector.POLYMORPHIC_3)):
         return QtCore.QSizeF(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT)
      else:
         return QtCore.QSizeF(BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT)

   def addConnenctionShapeMapping(shapeName,  integer):
      #print("adding con shape map: "+shapeName+", "+ str(integer));
      BlockConnectorShape.SHAPE_MAPPINGS[shapeName] = integer

      if(integer == BlockConnectorShape.COMMAND):
         BlockConnectorShape.COMMAND_SHAPE_NAME = shapeName;

   #def getConnenctionShapeMapping(shapeName):
    #  if (BlockConnectorShape.SHAPE_MAPPINGS.get(shapeName) == None):
    #     #assert false : ("Unknown Connection Type: " + shapeName);
    #     return -1;
    #  else:
    #     return BlockConnectorShape.SHAPE_MAPPINGS.get(shapeName);


   def isCommandConnector(connector):
      return (BlockConnectorShape.getConnenctionShapeMapping(connector.type) == BlockConnectorShape.COMMAND);

   def getCommandShapeName():
      return BlockConnectorShape.COMMAND_SHAPE_NAME;

   def addDataSocket(self,blockPath, connectionShape,  onRightSide):
      # if onRightSide, socket is convex-left
      return self.addDataConnection(blockPath, connectionShape, True, not onRightSide);


   # assosiated method to draw starting from the bottom of the socket
   def addDataSocketUp(self, blockPath, connectionShape,  onRightSide):
      # if onRightSide, socket is convex-left
      return self.addDataConnection(blockPath, connectionShape, False, not onRightSide);

   def addControlConnectorShape(self,blockPath, distanceToCenter, appendRight):

      # get the initial point info and set the currentConnectorPath to use the self._lineTo self._curveTo methods
      self.startPoint = blockPath.currentPosition();
      self.socketPoint = QtCore.QPointF(self.startPoint.x() + (distanceToCenter if appendRight else -distanceToCenter), self.startPoint.y());

      self.currentConnectorPath = blockPath;

      if (appendRight):
         #then the centerPoint is to the right of the current location on the generalPath
         self._lineTo(distanceToCenter - BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, 0);
         # update starting point for self._curveTo
         self.startPoint = blockPath.currentPosition();

         self._curveTo( BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, BlockConnectorShape.CONTROL_PLUG_HEIGHT * 4/3,
              BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, BlockConnectorShape.CONTROL_PLUG_HEIGHT * 4/3,
              BlockConnectorShape.CONTROL_PLUG_WIDTH, 0);
      else:
         # then the centerPoint is to the left of the current location on the generalPath
         self._lineTo(- distanceToCenter + BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, 0);
         #update starting point for self._curveTo
         self.startPoint = blockPath.currentPosition();

         self._curveTo( - BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, BlockConnectorShape.CONTROL_PLUG_HEIGHT * 4/3,
             - BlockConnectorShape.CONTROL_PLUG_WIDTH / 2, BlockConnectorShape.CONTROL_PLUG_HEIGHT * 4/3,
             - BlockConnectorShape.CONTROL_PLUG_WIDTH, 0);

      # to catch bugs
      self.currentConnectorPath = None;

      return self.socketPoint;

   def _lineTo(self,x, y):
      BlockShapeUtil.lineTo(self.currentConnectorPath, x + self.startPoint.x(), y + self.startPoint.y());


   def _curveTo(self, x1, y1,  x2,  y2, x3,  y3):
      #print('x0=%d,y0=%d,x1=%d,y1=%d,x2=%d,y2=%d,x3=%d,y3=%d'%(self.startPoint.x(), self.startPoint.y(), x1, y1,  x2,  y2, x3,  y3)) 
      BlockShapeUtil.cubicTo (
         self.currentConnectorPath,
         x1 + self.startPoint.x(), y1 + self.startPoint.y(),
         x2 + self.startPoint.x(), y2 + self.startPoint.y(),
         x3 + self.startPoint.x(), y3 + self.startPoint.y());


   def addDataConnection(self,blockPath,  connectionShape,  startFromTop, convexRight):
      # get the associated connection shape value
      connectionShapeInt = BlockConnectorShape.getConnenctionShapeMapping(connectionShape);

      # get the initial point info and set the currentConnectorPath to use the self._lineTo self._curveTo methods
      self.startPoint = blockPath.currentPosition();
      xStart = self.startPoint.x();
      yStart = self.startPoint.y();
      self.currentConnectorPath = QtGui.QPainterPath();
      self.currentConnectorPath.moveTo(xStart, yStart);

      socketPoint = QtCore.QPoint(self.startPoint.x(), (self.startPoint.y() + ( BlockConnectorShape.DATA_PLUG_HEIGHT / 2)) if startFromTop else (self.startPoint.y() - ( BlockConnectorShape.DATA_PLUG_HEIGHT / 2)));

      if (connectionShapeInt == BlockConnectorShape.TRIANGLE_1):
         # Starlogo Number
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.TRIANGLE_2):
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT / 4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         # shifted duplicate
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 3.0/4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.TRIANGLE_3):
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT / 4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         # inversion
         self._lineTo( -BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 3.0/4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
         #Starlogo Boolean
      elif (connectionShapeInt == BlockConnectorShape.CIRCLE_1):
         #print('CIRCLE_1') 
         self._curveTo(
         		(BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH) * 4.0 / 4, 0,
         		(BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH) * 4.0 / 4, BlockConnectorShape.DATA_PLUG_HEIGHT,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.CIRCLE_2):
         self._curveTo(
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, 0,
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 1/4,
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH * 1/2, BlockConnectorShape.DATA_PLUG_HEIGHT * 1.0/2);
         self._curveTo(BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 4.0/4,
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.CIRCLE_3):
         self._curveTo(
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, 0,
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 1/4,
         		BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH * 1/2, BlockConnectorShape.DATA_PLUG_HEIGHT * 1.0/2);
         # inversion
         self._curveTo(-BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 4.0/4,
         		-BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT);
         #Starlogo String
      elif (connectionShapeInt == BlockConnectorShape.SQUARE_1):
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.SQUARE_2):
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.45);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.45);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.55);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.55);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.SQUARE_3):
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.50);
         self._lineTo( -BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.50);
         self._lineTo( -BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.85);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.POLYMORPHIC_1):
         self._curveTo(0, BlockConnectorShape.DATA_PLUG_HEIGHT/3,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT*1.0/3,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT*1.0/4);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT*1.0/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT*1.0/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT*1.0/2);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT *5.0/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT *5.0/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT *3.0/4);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT *2/3,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT *2/3,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT);
      elif (connectionShapeInt == BlockConnectorShape.POLYMORPHIC_2):
         self._curveTo(0, BlockConnectorShape.DATA_PLUG_HEIGHT/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT/8);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT/4);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT *5/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT *5/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT *3/8);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT *2/6,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT *2/6,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT/2);
         # shifted duplicate
         self._curveTo(0, BlockConnectorShape.DATA_PLUG_HEIGHT *4/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT *4/6,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT *5/8);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT *7/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT *7/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT *3/4);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT *11/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH *2/3, BlockConnectorShape.DATA_PLUG_HEIGHT *11/12,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT *7/8);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT *5/6,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT *5/6,
         		0, BlockConnectorShape.DATA_PLUG_HEIGHT);

      elif (connectionShapeInt == BlockConnectorShape.POLYMORPHIC_3):
         self._lineTo( BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT / 8);
         self._lineTo( BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.025);
         self._lineTo( BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 3/4, BlockConnectorShape.DATA_PLUG_HEIGHT/8);
         self._curveTo(BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 10 / 9, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 10 / 9, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.35,
         		BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 3 / 4, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 8);
         self._lineTo( BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH / 2, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.475);
         self._lineTo( BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH / 3, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 8);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         # inversion
         self._lineTo( -BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/3, BlockConnectorShape.DATA_PLUG_HEIGHT / 8 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( -BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH/2, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.025 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( -BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 3/4, BlockConnectorShape.DATA_PLUG_HEIGHT/8 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._curveTo(-BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 10 / 9, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.15 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2,
         		-BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 10 / 9, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.35 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2,
         		-BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH * 3 / 4, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 8 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( -BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH / 2, BlockConnectorShape.DATA_PLUG_HEIGHT * 0.475 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( -BlockConnectorShape.POLYMORPHIC_DATA_PLUG_WIDTH / 3, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 8 + BlockConnectorShape.DATA_PLUG_HEIGHT / 2);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);


      # formally BlockGenus.PROC_PARAM
      elif (connectionShapeInt == BlockConnectorShape.PROC_PARAM):
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 1 / 4);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 1 / 4);
         self._lineTo( BlockConnectorShape.NORMAL_DATA_PLUG_WIDTH, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT * 3 / 4);
         self._lineTo( 0, BlockConnectorShape.DATA_PLUG_HEIGHT);

      # look for additional 3rd party shapes here...

      else:
         # System.out.println("Connection Type Not Identified: " + connectionShape);
         pass



      # flip the path if starting from the bottom or changing convex direction
      if (not startFromTop or not convexRight):
         self.currentConnectorPath = self.transformGeneralPath(self.currentConnectorPath, not convexRight, not startFromTop);

      #pos1 = blockPath.currentPosition()
      # finally append the correctly oriented currentConnectorPath to the blockPath
      blockPath.connectPath(self.currentConnectorPath);
      #pos2 = blockPath.currentPosition()
      # to catch bugs
      self.currentConnectorPath = None;

      return socketPoint;

   def addCommandSocket(self, blockPath, commandSocketHeight):
      from blocks.BlockShape import BlockShape

      # draw bar
      BlockShapeUtil.lineToRelative(blockPath, BlockConnectorShape.COMMAND_INPUT_BAR_WIDTH, 0);
      BlockShapeUtil.lineToRelative(blockPath, 0, BlockConnectorShape.COMMAND_INPUT_BAR_HEIGHT);
      socketPoint = self.addControlConnectorShape(blockPath, BlockConnectorShape.CONTROL_PLUG_WIDTH / 2,False);

      # first corner inside command input
      BlockShapeUtil.cornerTo(blockPath,
      QtCore.QPointF(
          blockPath.currentPosition().x() - BlockConnectorShape.COMMAND_INPUT_BAR_WIDTH +  BlockShape.CORNER_RADIUS,
          blockPath.currentPosition().y()),
      QtCore.QPointF(
          blockPath.currentPosition().x() - BlockConnectorShape.COMMAND_INPUT_BAR_WIDTH +  BlockShape.CORNER_RADIUS,
          blockPath.currentPosition().y() + BlockShape.CORNER_RADIUS),
      BlockShape.CORNER_RADIUS);



      # insert dynamic command input height between these two methods
      BlockShapeUtil.lineToRelative(blockPath, 0, commandSocketHeight);

      # second corner at bottom of command input
      BlockShapeUtil.cornerTo(blockPath,
      QtCore.QPointF(
          blockPath.currentPosition().x(),
          blockPath.currentPosition().y() + BlockShape.CORNER_RADIUS),
      QtCore.QPointF(
          blockPath.currentPosition().x() + BlockShape.CORNER_RADIUS,
          blockPath.currentPosition().y() + BlockShape.CORNER_RADIUS),
      BlockShape.CORNER_RADIUS);


      # extend left to match y coordinate of initial point
      BlockShapeUtil.lineToRelative(blockPath, BlockConnectorShape.CONTROL_PLUG_WIDTH/2, 0);

      return socketPoint;

   '''
   * Flips a GeneralPath and translates it so the starting point is in the correct place.
   *
   * @param gp the GeneralPath to be transformed
   * @param horzFlip true if flipped horizontally
   * @param vertFlip true if flipped vertically
   '''
   def transformGeneralPath(self,gp, horzFlip, vertFlip):
      xScale = 0
      yScale = 0
      xTranslate = 0
      yTranslate = 0

      if (horzFlip):
         xScale = -1;
         xTranslate = 2*self.startPoint.x();
      else:
         xScale = 1;
         xTranslate = 0;

      if (vertFlip):
         yScale = -1;
         yTranslate = 2*self.startPoint.y();
      else:
         yScale = 1;
         yTranslate = 0;


      # scale (flip)
      scale_transform = QtGui.QTransform()
      scale_transform.scale(xScale, yScale);
      # translate across the origin
      translate_transform = QtGui.QTransform()
      translate_transform.translate (xTranslate, yTranslate);
      # apply the transforms
      gp = scale_transform.map(gp)
      gp = translate_transform.map(gp)

      return gp



   def addDataPlug(self,blockPath, connectionShape, startFromTop, onRightSide):
      # if onRightSide, plug is convex-right
      return self.addDataConnection(blockPath, connectionShape, startFromTop, onRightSide);

   def loadBlockConnectorShapes(root):

      attrExtractor="(.*)"
      drawerNodes=root.findall("BlockConnectorShapes/BlockConnectorShape");
      for drawerNode in drawerNodes:
         shapeType = None;
         shapeNumber = None;
         if 'shape-type' in drawerNode.attrib:
            shapeType = drawerNode.attrib['shape-type']

         if 'shape-number' in drawerNode.attrib:
            shapeNumber = drawerNode.attrib['shape-number']

         if(shapeType != None and shapeNumber != None):
            # create shape to number mapping here
            BlockConnectorShape.addConnenctionShapeMapping(shapeType, int(shapeNumber));
blocks/SocketLabel.py
class SocketLabel(BlockLabel):


   def __init__(self, parent, socket, initLabelText, labelType, isEditable, blockID):
      BlockLabel.__init__(self,parent,  initLabelText, '', '', labelType, isEditable, blockID, False, QtGui.QColor(190, 250, 125));
      self.socket = socket;

   '''
   * Returns true if the socket label should not be added to this.  Conditions for ignoring socket labels are:
   * 1.  the specified socket is a bottom socket
   * 2.  the specified socket has an empty label
   * @param socket the BlockConnector to test
   * @return true if the specified socket should have a corresponding Blocklabel instance added to this.
    '''
   def ignoreSocket(socket):
      return (socket.getPositionType() == BlockConnector.PositionType.BOTTOM) or socket.getLabel() == ("");

   def update(self,socketPoint):
      if(SocketLabel.ignoreSocket(self.socket)):return;
      #abstarct location so we need to tranform it
      x = 0;
      y = 0;
      if (BlockConnectorShape.isCommandConnector(self.socket)):
         #command socket
         x = -8  - BlockConnectorShape.COMMAND_INPUT_BAR_WIDTH + socketPoint.x();
         y = -4 + socketPoint.y();
      else:
         # data socket
         x = -4  - BlockConnectorShape.getConnectorDimensions(self.socket).width() + socketPoint.x();
         y = -8 + socketPoint.y();

      x = - self.getAbstractWidth() + x;
      self.setPixelLocation(self.rescale(x), self.rescale(y));


	#@Override
   def textChanged(self,text):
      # Prevents running this when sockets are null.
      # Sockets can be null during loading.
      return
      if (self.socket != None):
         socket.setLabel(text);
         BlockLabel.textChanged(self,text);
blocks/CollapseLabel.py
class CollapseLabel(BlockControlLabel):
   def __init__(self,blockID):
      BlockControlLabel.__init__(self,blockID)
blocks/NameLabel.py
class NameLabel(BlockLabel):

    def __init__(self, parent, initLabelText, prefix, suffix, labelType, isEditable, blockID):
        BlockLabel.__init__(self,parent, initLabelText,prefix, suffix, labelType, isEditable, blockID, True, QtGui.QColor(255,255,225))
        self.blockID = blockID;


    def update(self):
        from blocks.RenderableBlock import RenderableBlock
        rb = RenderableBlock.getRenderableBlock(self.blockID);
        if (rb != None):
            x = 0;
            y = 0;
            if(rb.getBlock().isCommandBlock()): x+=5;
            #if(rb.getBlock().isDeclaration()): x+=12;
            if(rb.getBlock().hasPlug()):
                x+=4+BlockConnectorShape.getConnectorDimensions(rb.getBlock().getPlug()).width();
            
            if(rb.getBlock().isInfix() and rb.getBlock().getSocketAt(0) != None):
                if(not rb.getBlock().getSocketAt(0).hasBlock()):
                    x+=30;
                else:
                   if(rb.getSocketSpaceDimension(rb.getBlock().getSocketAt(0)) != None):
                      x+= rb.getSocketSpaceDimension(rb.getBlock().getSocketAt(0)).width();

            if(rb.getBlockWidget()==None):
                y+=rb.getAbstractBlockArea().controlPointRect().height()/2;
            else:
                y+=8;

            if(rb.getBlock().isCommandBlock()):
                y-=2;

            if(rb.getBlock().hasPageLabel() and rb.getBlock().hasAfterConnector()):
                y-=BlockConnectorShape.CONTROL_PLUG_HEIGHT;

            if(not rb.getBlock().hasPageLabel()):
                y-=self.getAbstractHeight()/2;

            # Comment Label and Collapse Label take up some additional amount of space
            x += rb.getControlLabelsWidth();

            # if block is collapsed keep the name label from moving
            y += BlockConnectorShape.CONTROL_PLUG_HEIGHT/2 if rb.isCollapsed() else 0;
            #y+=3
            x=self.rescale(x);
            y=self.rescale(y);

            self.setPixelLocation(x, y);


Question: from PyQt4 import QtCore,QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from blocks.Block import Block
from blocks.BlockShape import BlockShape
from blocks.InfixBlockShape import InfixBlockShape
from blocks.CollapseLabel import CollapseLabel
from blocks.ConnectorTag import ConnectorTag
from blocks.NameLabel import NameLabel
from blocks.BlockLabel import BlockLabel
from blocks.SocketLabel import SocketLabel
from blocks.BlockConnectorShape import BlockConnectorShape
from blocks.GraphicsManager import GraphicsManager
from blocks.BlockShapeUtil import BlockShapeUtil
from blocks.BlockLinkChecker import BlockLinkChecker
from blocks.BlockImageIcon import BlockImageIcon
        from blocks.WorkspaceController import WorkspaceController
        from blocks.FactoryManager import FactoryManager
        from blocks.BlockConnector import BlockConnector
        from blocks.BlockLink import BlockLink
import math, os, sys





def printPath(path):
    for i in range(0, path.elementCount()):
      cur = path.elementAt(i);

      if(cur.type == QtGui.QPainterPath.MoveToElement):
        print("M %d %d"%(cur.x,cur.y))
      elif(cur.type == QtGui.QPainterPath.LineToElement):
        print("L %d %d"%(cur.x,cur.y))
      elif(cur.type == QtGui.QPainterPath.CurveToElement):
        c1 = path.elementAt(i + 1);
        c2 = path.elementAt(i + 2);

        assert(c1.type == QtGui.QPainterPath.CurveToDataElement);
        assert(c2.type == QtGui.QPainterPath.CurveToDataElement);


        print("C (%d %d) (%d %d) (%d %d)"%(cur.x,cur.y,c1.x,c1.y,c2.x,c2.y));

        i += 2;
      elif(cur.type == QtGui.QPainterPath.CurveToDataElement):
          pass

class RenderableBlock(QtGui.QWidget):

    ALL_RENDERABLE_BLOCKS = {} 
        
    def __init__(self, _workspaceWidget):         
        QtGui.QWidget.__init__(self)
        self._workspaceWidget = _workspaceWidget
        self.setMouseTracking(True);
        self.factory_block = None
        self.mouse_enter = False
        
    def __del__(self):
        pass

    @classmethod
    def from_block(cls, _workspaceWidget, block, isLoading=False,back_color=QtGui.QColor(225,225,225,255)):
        
        obj = cls(_workspaceWidget)
        
        obj.workspace = WorkspaceController.workspace
        obj.back_color = back_color
        obj.isLoading= isLoading
        
        obj._blockID = block.blockID;

        if(obj._blockID != -1):
          RenderableBlock.ALL_RENDERABLE_BLOCKS[obj.blockID] = obj
        else:
          RenderableBlock.tmpRB = obj
          
        #self.setAttribute(74, True);
        obj.mouse_enter = False
        obj.pickedUp = False
        obj.linkedDefArgsBefore = False
        obj.commentLabelChanged = False
        obj.dragging = False
        obj.overTrash = False

        obj.blockLabel = None
        obj.blockWidget = None
        obj.comment = None
        obj.buffImg = None
        obj.collapseLabel = None
        obj.last_link = None
        obj.blockShape = None
        
        obj.zoom = 1.0;
        obj.socketTags = []
        obj.imageList = []
        
        # initialize block image map
        # note: must do this before updateBuffImg();
        for  img in obj.getBlock().getInitBlockImages():
            new_img = BlockImageIcon(
                img.url, 
                img.location, 
                img.icon, 
                img.width(), 
                img.height(), 
                img.isEditable,
                img.wrapText)
            obj.imageList.append(new_img)
            new_img.setParent(obj)

        # initialize tags, labels, and sockets:

Answer:         obj.plugTag = ConnectorTag(block.getPlug());