/[ascend]/trunk/pygtk/canvas/port.py
ViewVC logotype

Diff of /trunk/pygtk/canvas/port.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1944 by jpye, Tue Nov 4 09:16:21 2008 UTC revision 1945 by jpye, Thu Nov 6 08:54:02 2008 UTC
# Line 10  from gaphas.state import observed, rever Line 10  from gaphas.state import observed, rever
10  from gaphas.tool import HandleTool  from gaphas.tool import HandleTool
11    
12  from gaphas import GtkView, View  from gaphas import GtkView, View
13  from gaphas.item import Line, SW, NE, NW, SE, Element, Handle  
14  from gaphas.tool import Tool, HoverTool, PlacementTool, HandleTool, ToolChain  from gaphas.solver import Variable, solvable, WEAK, NORMAL, STRONG, VERY_STRONG
 from gaphas.tool import ItemTool, RubberbandTool  
 from gaphas.geometry import point_on_rectangle, distance_rectangle_point  
15  from gaphas.constraint import LineConstraint, LessThanConstraint, EqualsConstraint, Constraint, _update, BalanceConstraint  from gaphas.constraint import LineConstraint, LessThanConstraint, EqualsConstraint, Constraint, _update, BalanceConstraint
16  from gaphas.canvas import CanvasProjection  from gaphas.canvas import CanvasProjection
 from gaphas.solver import Variable, solvable, WEAK, NORMAL, STRONG, VERY_STRONG  
   
 from gaphas.painter import ItemPainter  
 from gaphas import state  
 from gaphas.util import text_extents  
   
 from gaphas import painter  
   
 from blockcanvas import *  
 from panzoom import *  
 #painter.DEBUG_DRAW_BOUNDING_BOX = True  
17    
18  #------------------------------------------------------------------------------  #------------------------------------------------------------------------------
19    
# Line 35  class Port(object): Line 22  class Port(object):
22      Ports are special places onto which Handles can be connected, specifically      Ports are special places onto which Handles can be connected, specifically
23      when users are drawing 'connector' lines between modelling 'blocks'.      when users are drawing 'connector' lines between modelling 'blocks'.
24    
25      A subclass of Item called 'Block' will be given the ability to store      A subclass of Item called 'BlockItem' will be given the ability to store
26      an array of these Ports, in such a way that gluing methods will be able to      an array of these Ports, in such a way that gluing methods will be able to
27      select appropriate ports for a given connector line. The Ports also give      select appropriate ports for a given connector line. The Ports also give
28      data about where these available connected are located graphically, in the      data about where these available connected are located graphically, in the
29      Block's local coordinate system.      BlockItem's local coordinate system.
30    
31      It is not intended that the number or location of ports would be editable      It is not intended that the number or location of ports would be editable
32      once a port is instantiated. Only the connection of a port to a handle      once a port is instantiated. Only the connection of a port to a handle
# Line 50  class Port(object): Line 37  class Port(object):
37      connect to any given port.      connect to any given port.
38    
39      Attributes:      Attributes:
40      - block: Block to which Port belongs      - block: BlockItem to which Port belongs
41      - connected_to: Handle to which port is connected, if any      - connected_to: Handle to which port is connected, if any
42      - x: port x-location in item's local coordinate system      - x: port x-location in item's local coordinate system
43      - y: port y-location in item's local coordinate system      - y: port y-location in item's local coordinate system
# Line 131  class PointConstraint(Constraint): Line 118  class PointConstraint(Constraint):
118      """      """
119    
120      def __init__(self, A, B):      def __init__(self, A, B):
121          print "A =",A          #print "A =",A
122          print "A[0] =",A[0].variable()          #print "A[0] =",A[0].variable()
123          print "A[0].strength =",A[0].variable().strength          #print "A[0].strength =",A[0].variable().strength
124    
125          print "B =",B          #print "B =",B
126          print "B[0] =",B[0].variable()          #print "B[0] =",B[0].variable()
127          print "B[0].strength =",B[0].variable().strength          #print "B[0].strength =",B[0].variable().strength
128    
129  #        assert isinstance(p1[0],Variable)  #        assert isinstance(p1[0],Variable)
130  #        assert isinstance(p1[1],Variable)  #        assert isinstance(p1[1],Variable)
# Line 149  class PointConstraint(Constraint): Line 136  class PointConstraint(Constraint):
136          self._B = B          self._B = B
137    
138      def solve_for(self, var=None):      def solve_for(self, var=None):
139          print "Solving PointConstraint",self,"for var",var          #print "Solving PointConstraint",self,"for var",var
140    
141          _update(self._B[0], self._A[0].value)          _update(self._B[0], self._A[0].value)
142          _update(self._B[1], self._A[1].value)          _update(self._B[1], self._A[1].value)
143    
 class Block(Element):  
     """  
     This is an ASCEND 'block' in the canvas-based modeller. The block will have  
     sets of input and output ports to which connector lines can be 'glued'.  
     The block will also have a corresponding ASCEND MODEL type, and a name  
     which will be used in ASCEND to refer to this block. Each of the ports will  
     be special visual elements, but note that these are not 'handles', because  
     they can not be used to resize/modify the element.  
     """  
   
     def __init__(self, label="unnamed", width=64, height=64):  
   
         self.ports = []  
         self.label = label          
         super(Block, self).__init__(width, height)  
   
     def draw(self, context):  
         #print 'Box.draw', self  
         c = context.cairo  
         nw = self._handles[NW]  
         c.rectangle(nw.x, nw.y, self.width, self.height)  
         if context.hovered:  
             c.set_source_rgba(.8,.8,1, .8)  
         else:  
             c.set_source_rgba(1,1,1, .8)  
         c.fill_preserve()  
         c.set_source_rgb(0,0,0.8)  
         c.stroke()  
       
         phalfsize = 3  
         for p in self.ports:  
             c.rectangle(p.x - phalfsize, p.y - phalfsize, 2*phalfsize, 2*phalfsize)  
             if p.connected_to is None:  
                 c.set_source_rgba(0.8,0.8,1, 0.8)  
             else:  
                 c.set_source_rgba(1,0,0,1)  
             c.fill_preserve()  
             c.set_source_rgb(0.8,0.8,0)  
             c.stroke()  
   
     def glue(self,item, handle, ix, iy):  
         gluerange = 10  
         mindist = -1;  
         minport = None  
         for p in self.ports:  
             dist = math.sqrt((ix-p.x)**2 + (iy-p.y)**2)  
             if dist < gluerange:  
                 if not minport or dist<mindist:  
                     mindist = dist  
                     minport = p  
         return mindist, minport  
   
     def pre_update(self,context):  
         print "PRE-UPDATE BLOCK"  
   
 class DefaultBlock(Block):  
     """  
     This is a 'default block' with a certain number of input and output ports  
     shown depending on the values sent to __init__. It is drawn as a simple  
     box with the input ports on the left and the output ports on the right.  
     """  
   
     def __init__(self, label="unnamed", width=64, height=64, inputs=2, outputs=3):  
   
         super(DefaultBlock, self).__init__(label, width, height)  
   
         eq = EqualsConstraint  
         bal = BalanceConstraint  
         handles = self._handles  
         h_nw = handles[NW]  
         h_ne = handles[NE]  
         h_sw = handles[SW]  
         h_se = handles[SE]  
   
         for i in range(inputs):  
             p = Port(self)  
             self.ports.append(p)  
             self._constraints.append(eq(p.x, h_nw.x))  
             self._constraints.append(bal(band=(h_nw.y, h_sw.y),v=p.y, balance=(0.5 + i)/inputs))  
   
         for i in range(outputs):  
             p = Port(self)  
             self.ports.append(p)  
             self._constraints.append(eq(p.x, h_ne.x))  
             self._constraints.append(bal(band=(h_ne.y,h_se.y),v=p.y, balance=(0.5 + i)/outputs))  
   
     def draw(self, context):  
         # draw the box itself  
         c = context.cairo  
         nw = self._handles[NW]  
         c.rectangle(nw.x, nw.y, self.width, self.height)  
         if context.hovered:  
             c.set_source_rgba(.8,.8,1, .8)  
         else:  
             c.set_source_rgba(1,1,1, .8)  
         c.fill_preserve()  
         c.set_source_rgb(0,0,0.8)  
         c.stroke()  
   
         # now the draw the ports using the base class  
         super(DefaultBlock, self).draw(context)  
   
 class PortConnectingHandleTool(HandleTool):  
     """  
     This is a HandleTool which supports the connection of lines to the Ports  
     of Blocks, for the purpose of building up process flow diagrams, control  
     diagrams, etc, for the proposed canvas-based modeller of ASCEND.  
     """  
   
     def glue(self, view, item, handle, wx, wy):  
         """  
         This allows the tool to glue a handle to any connectable Port of a Block.  
         The distance from the item to the handle is determined in canvas  
         coordinates, using a 10 pixel glue distance.  
   
         Returns the closest Port that is within the glue distance.  
         """  
         if handle and not handle.connectable:  
             return  
   
         # Make glue distance depend on the zoom ratio (should be about 10 pixels)  
         inverse = cairo.Matrix(*view.matrix)  
         inverse.invert()  
         #glue_distance, dummy = inverse.transform_distance(10, 0)  
         glue_distance = 10  
         glue_port = None  
         glue_point = None  
         #print "Gluing..."    
         for i in view.canvas.get_all_items():  
             if not hasattr(i,'ports'):  
                 continue  
             if not i is item:  
                 #print "Trying glue to",i  
                 v2i = view.get_matrix_v2i(i).transform_point  
                 ix, iy = v2i(wx, wy)  
                 distance, port = i.glue(item, handle, ix, iy)  
                 # Transform distance to world coordinates  
                 #distance, dumy = matrix_i2w(i).transform_distance(distance, 0)  
                 if not port is None and distance <= glue_distance:  
                     glue_distance = distance  
                     i2v = view.get_matrix_i2v(i).transform_point  
                     glue_point = i2v(port.x, port.y)  
                     glue_port = port  
             else:  
                 print "i is item"  
         if glue_point and handle and item:  
             v2i = view.get_matrix_v2i(item).transform_point  
             handle.x, handle.y = v2i(*glue_point)  
             #print "Found glue point ",handle.x,handle.y  
         return glue_port  
   
     def connect(self, view, item, handle, wx, wy):  
         """  
         Connect a handle to a port. 'item' is the line to which the handle  
         belongs; wx and wy are the location of the cursor, so we run the 'glue'  
         routine to find the desired gluing point, then make the connection to  
         the object which 'glue' returns, which will be a Port object (in the  
         context of this tool).  
   
         In this "method" the following assumptios are made:  
          1. Only ``Port``s of ``Block``s will accept connections from handles.  
          2. The only items with connectable handles are ``Line``s  
           
         """  
   
         # create a special local handle_disconnect function  
         def handle_disconnect():  
             try:  
                 view.canvas.solver.remove_constraint(handle.connection_data)  
             except KeyError:  
                 print 'constraint was already removed for', item, handle  
                 pass # constraint was alreasy removed  
             else:  
                 print 'constraint removed for', item, handle  
             handle.connected_port.connected_to = None  
             handle.connection_data = None  
             handle.connection_port = None  
             handle.connected_to = None  
               
             # Remove disconnect handler:  
             handle.disconnect = lambda: 0  
   
         #print 'Handle.connect', view, item, handle, wx, wy  
         glue_port = self.glue(view, item, handle, wx, wy)  
   
         if glue_port and hasattr(handle,'connected_port') and handle.connected_port is glue_port:  
             try:  
                 view.canvas.solver.remove_constraint(handle.connection_data)  
             except KeyError:  
                 pass  
         else:  
             # ie no glue_port found, or the handle connected to something else  
             if handle.connected_to:  
                 handle.disconnect()  
   
         if glue_port:  
             if isinstance(glue_port, Port):  
                 print "Gluing to port",glue_port  
   
                 print "handle.pos =",handle.pos  
                 print "glue_port =",glue_port  
                 print "glue_port.pos = ",glue_port.pos  
                 print "glue_port.block =",glue_port.block  
   
                 handle.connection_data = PointConstraint(  
                     B=CanvasProjection(handle.pos,item)  
                     ,A=CanvasProjection(glue_port.pos, glue_port.block)  
                 )  
                 view.canvas.solver.add_constraint(handle.connection_data)  
                 #glue_port.block._constraints.append(handle.connection_data)  
   
                 handle.connected_to = glue_port.block  
                 handle.connected_port = glue_port  
                 handle.disconnect = handle_disconnect  
                 glue_port.connected_to = handle  
   
     def disconnect(self, view, item, handle):  
         if handle.connected_to:  
             print 'Handle.disconnect', view, item, handle  
             view.canvas.solver.remove_constraint(handle.connection_data)  
   
   
   
144  # vim: sw=4:et:ai  # vim: sw=4:et:ai

Legend:
Removed from v.1944  
changed lines
  Added in v.1945

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22