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

Annotation of /trunk/pygtk/canvas/blockitem.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2686 - (hide annotations) (download) (as text)
Wed Feb 27 02:08:53 2013 UTC (9 years, 7 months ago) by saheb
File MIME type: text/x-python
File size: 17342 byte(s)
Merging saheb-canvas work from Saheb in GSOC2012 into trunk (from saheb-merge branch at changeset 4445). Some minor changes needed. -- jpye.
1 saheb 2686 from gaphas.constraint import LineConstraint, LessThanConstraint, EqualsConstraint, Constraint, _update, BalanceConstraint,LineAlignConstraint, EquationConstraint
2     from gaphas.item import Line, SW, NE, NW, SE, Item, Handle, Element
3 jpye 1945 from gaphas.util import *
4 saheb 2686 from gaphas.connector import Position
5     from gaphas.solver import solvable, WEAK, NORMAL, STRONG, VERY_STRONG, Variable, REQUIRED
6 grivan 2200 from gaphas.state import observed, reversible_method, reversible_pair, reversible_property
7 jpye 1970 from gaphas.geometry import distance_rectangle_point
8 saheb 2686 from gaphas.examples import Circle
9     from gaphas.canvas import Canvas
10     from gaphas.matrix import Matrix
11     from numpy import *
12     import math
13     import cairo
14 jpye 1945
15 jpye 1979 from blockport import BlockPort
16 jpye 2394 from blockinstance import PORT_IN, PORT_OUT, PORT_INOUT
17 jpye 1979
18 saheb 2686 class ElementNoPorts(Element):
19     """
20     This is a copy of the Element class, but without the declaration
21 jpye 1970 of the LinePorts in the __init__ method. It will be proposed to the
22     Gaphor team that the Element class be modified like this, because
23     there is quite a lot of useful code aside from the LinePort definition.
24 saheb 2686 """
25 jpye 1945
26 saheb 2686 min_width = solvable(strength=REQUIRED, varname='_min_width')
27     min_height = solvable(strength=REQUIRED, varname='_min_height')
28 jpye 1970
29 saheb 2686 def __init__(self, width=10, height=10):
30     super(Element, self).__init__()
31     self._handles = [ h(strength=VERY_STRONG) for h in [Handle]*4 ]
32 jpye 1970
33 saheb 2686 handles = self._handles
34     h_nw = handles[NW]
35     h_ne = handles[NE]
36     h_sw = handles[SW]
37     h_se = handles[SE]
38 jpye 1970
39 saheb 2686 # Share variables
40     h_sw.pos.set_x(h_nw.pos.x)
41     h_se.pos.set_x(h_ne.pos.x)
42     h_ne.pos.set_y(h_nw.pos.y)
43     h_se.pos.set_y(h_sw.pos.y)
44 jpye 1970
45 saheb 2686 # No ports by default
46     self._ports = []
47 jpye 1970
48 saheb 2686 # initialize min_x variables
49     self.min_width, self.min_height = 10, 10
50     factor = 7
51     delta_w = len(self.blockinstance.name)*factor
52     delta_h = 10
53     self.min_width = delta_w
54     self.min_height = delta_h
55 jpye 1970
56 saheb 2686 # create minimal size constraints
57     self.constraint(left_of=(h_nw.pos, h_se.pos), delta=self._min_width)
58     self.constraint(above=(h_nw.pos, h_se.pos), delta=self._min_height)
59 jpye 1970
60 saheb 2686 self.width = width
61     self.height = height
62 jpye 1970
63 saheb 2686 # TODO: constraints that calculate width and height based on handle pos
64     #self.constraints.append(EqualsConstraint(p1[1], p2[1], delta))
65 jpye 1970
66    
67 saheb 2686 class BlockItem(ElementNoPorts):
68     """
69     This is an ASCEND 'block' in the canvas-based modeller. The block will have
70     sets of input and output ports to which connector lines can be 'glued'.
71     The block will also have a corresponding ASCEND MODEL type, and a name
72     which will be used in ASCEND to refer to this block. Each of the ports will
73     be special visual elements, but note that these are not 'handles', because
74     they can not be used to resize/modify the element.
75     """
76 jpye 1970
77 saheb 2686 def __init__(self,blockinstance, width=64, height=64):
78     self.blockinstance = blockinstance
79     super(BlockItem, self).__init__(width, height)
80     self.port_in = blockinstance.blocktype.port_in
81     self.port_out = blockinstance.blocktype.port_out
82     self.h = self._handles
83     self.wide = self.h[SE].pos.x - self.h[NW].pos.x
84     self.height = self.h[SE].pos.y - self.h[NW].pos.y
85     self.normx = self.wide*0.1
86     self.normy = self.height*0.1
87 jpye 1970
88 saheb 2686 def up1(self):
89     ''' this is a update method used to update variables before drawing them again'''
90 jpye 1970
91 saheb 2686 self.wide = self.h[SE].pos.x - self.h[NW].pos.x
92     self.height = self.h[SE].pos.y - self.h[NW].pos.y
93     self.normx = self.wide*0.1
94     self.normy = self.height*0.1
95     if not (len(self.port_in)==0 and len(self.port_out)==0):
96     for w in self._ports:
97     if(w.get_portinstance().io == PORT_IN):
98     w.point._set_pos(Position(((float(self.port_in[w.portinstance.name][0]) * self.normx),(float(self.port_in[w.portinstance.name][1]) * self.normy))))
99     else:
100     w.point._set_pos(Position(((float(self.port_out[w.portinstance.name][0]) * self.normx),(float(self.port_out[w.portinstance.name][1]) * self.normy))))
101 jpye 1970
102 saheb 2686 #Here combination of translate(x,y),rotate(angle),translate(-x,-y) is used to perform
103     #rotation and flip about centre(x,y)
104 jpye 1970
105 saheb 2686 def rotate_clock(self):
106     self.matrix.translate(self.h[SE].pos.x/2,self.h[SE].pos.y/2)
107     self.matrix.rotate(math.pi/2)
108     self.matrix.translate(-1*self.h[SE].pos.x/2,-1*self.h[SE].pos.y/2)
109     self.request_update()
110 jpye 1970
111 saheb 2686 def rotate_anti(self):
112     self.matrix.translate(self.h[SE].pos.x/2,self.h[SE].pos.y/2)
113     self.matrix.rotate(-1*math.pi/2)
114     self.matrix.translate(-1*self.h[SE].pos.x/2,-1*self.h[SE].pos.y/2)
115     self.request_update()
116 jpye 1970
117 saheb 2686 def flip(self):
118     self.matrix.translate(self.h[SE].pos.x/2,self.h[SE].pos.y/2)
119     self.matrix.rotate(math.pi)
120     self.matrix.translate(-1*self.h[SE].pos.x/2,-1*self.h[SE].pos.y/2)
121     self.request_update()
122 jpye 1970
123 saheb 2686 def draw(self, context):
124     """
125     We want all ports within ASCEND to have a common appearance, so we
126     implement the drawing of ports here, and allow sub-classes of BlockItem
127     to implement the drawing of the other parts of the block, including the
128     outline etc.
129 jpye 1970
130 saheb 2686 Connected ports will be coloured red, other ports will be pale blue.
131     """
132     from blockconnecttool import SET_CONNECTION_FLAG
133 jpye 1970
134 saheb 2686 self.up1()
135     c = context.cairo
136     phalfsize = 3
137     if SET_CONNECTION_FLAG[0]:
138     for p in self._ports:
139     if hasattr(p,"point") and checkportscanconnect(p.portinstance, SET_CONNECTION_FLAG[1]):
140     c.rectangle(p.point.x - phalfsize, p.point.y - phalfsize, 2*phalfsize, 2*phalfsize)
141     c.set_source_rgba(0.9,0.9,0.9, 0.8)
142     c.fill_preserve()
143     c.set_source_rgb(0,0,1) # blue when connect able
144     c.stroke()
145 jpye 1970
146 saheb 2686 elif hasattr(p,"point") and not checkportscanconnect(p.portinstance, SET_CONNECTION_FLAG[1]):
147     c.rectangle(p.point.x - phalfsize, p.point.y - phalfsize, 2*phalfsize, 2*phalfsize)
148     c.set_source_rgba(0,0,0, 0.8)
149     c.fill_preserve()
150     c.set_source_rgb(0,0,0) # black when not connect able
151     c.stroke()
152     else:
153     for p in self._ports:
154     if hasattr(p,"point"):
155     c.rectangle(p.point.x - phalfsize, p.point.y - phalfsize, 2*phalfsize, 2*phalfsize)
156     c.set_source_rgba(0,0,0, 0.8)
157     c.fill_preserve()
158     c.set_source_rgb(0,0,0)
159     c.stroke()
160 jpye 1970
161 saheb 2686 #port-labels will be displayed when mouse is hovered over the item's context
162     #port-labels are numbers initially, but will be name of variables concerned with it later
163     #when it will be linked with the solvers and parameter window.
164 jpye 1970
165 saheb 2686 if context.focused:
166     for w in self._ports:
167     if(w.portinstance.io is PORT_IN):
168     if(w.point.y/self.normy==0):
169     text_align(c,w.point.x,w.point.y-10,str(w.get_portname()))
170     elif (w.point.y/self.normy==10):
171     text_align(c,w.point.x,w.point.y+10,str(w.get_portname()))
172     else:
173     text_align(c,w.point.x-3.5*len(str(w.get_portname())),w.point.y,str(w.get_portname()))
174     else:
175     if(w.point.y/self.normy==0):
176     text_align(c,w.point.x,w.point.y-10,str(w.get_portname()))
177     elif (w.point.y/self.normy==10):
178     text_align(c,w.point.x,w.point.y+10,str(w.get_portname()))
179     else:
180     text_align(c,w.point.x+3.5*len(str(w.get_portname())),w.point.y,str(w.get_portname()))
181 jpye 1945
182 saheb 2686 c.set_source_rgb(0,0,0)
183     text_center(c,self.h[SE].pos.x/2,self.h[SE].y/0.9,self.blockinstance.name)
184 jpye 1945
185 saheb 2686 def pre_update(self,context):
186     #print "PRE-UPDATE BLOCK"
187     pass
188 jpye 1970
189 saheb 2686 class GraphicalBlockItem(BlockItem):
190     """This class draws the custom blocks using graphic string from the notes section of model
191     file. The notes section will be like this:
192     MODEL tee REFINES equipment;
193     NOTES
194     'block' SELF {Tee piece}
195     'icon' SELF {tee.svg}
196     'graphic' SELF {0,0-0,10
197     0,0-10,5
198     10,5-0,10}
199     'port_in' SELF {0,5}
200     'port_out' SELF {10,4 10,6}
201     END NOTES;
202     side "out:" IS_A stream;
203     inlet.mdot = outlet.mdot + side.mdot;
204     END tee;
205     So it will draw the custom icon for a model using the co-ordinates present in the graphic
206     string.
207     """
208    
209     def __init__(self, blockinstance):
210    
211     self.blockinstance = blockinstance
212     super(GraphicalBlockItem, self).__init__(blockinstance)
213    
214     eq = EqualsConstraint
215     bal = BalanceConstraint
216     handles = self._handles
217     self.rad =1
218     self.graphical_properties = blockinstance.blocktype.gr
219     self.port_in = blockinstance.blocktype.port_in
220     self.port_out = blockinstance.blocktype.port_out
221     self.h_nw = handles[NW]
222     self.h_ne = handles[NE]
223     self.h_sw = handles[SW]
224     self.h_se = handles[SE]
225     self.wide = self.h_se.pos.x - self.h_nw.pos.x
226     self.height = self.h_se.pos.y - self.h_nw.pos.y
227     self.normx = self.wide*0.1 #normx is normalization factor for x-co-ordinate
228     self.normy = self.height*0.1 #normy is normalization factor for y-co-ordinate
229     ninputs = len(blockinstance.blocktype.inputs)
230     noutputs = len(blockinstance.blocktype.outputs)
231     ii, oi = (0,0) # input and output index counters
232     _ports = []
233     try:
234     if not (len(self.port_in)==0 and len(self.port_out)==0):
235     for i in self.blockinstance.ports:
236     if self.blockinstance.ports[i].io is PORT_IN:
237     p = BlockPort(blockinstance, i,self.port_in[i],ii)
238     ii += 1
239     elif self.blockinstance.ports[i].io is PORT_OUT:
240     p = BlockPort(blockinstance, i,self.port_out[i],oi)
241     oi += 1
242     else:
243     raise RuntimeError("Unknown port type")
244     _ports.append(p)
245     else:
246     for i in self.blockinstance.ports:
247     if self.blockinstance.ports[i].io is PORT_IN:
248     p = BlockPort(blockinstance, i,[0,0],ii)
249     self._constraints.append(eq(p.point.x, self.h_nw.x))
250     self._constraints.append(bal(band=(self.h_nw.y, self.h_sw.y),v=p.point.y, balance=(0.5 + ii)/ninputs))
251     ii += 1
252     elif self.blockinstance.ports[i].io is PORT_OUT:
253     p = BlockPort(blockinstance, i,[0,0],oi)
254     self._constraints.append(eq(p.point.x, self.h_ne.x))
255     self._constraints.append(bal(band=(self.h_ne.y,self.h_se.y),v=p.point.y, balance=(0.5 + oi)/noutputs))
256     oi += 1
257     else:
258     raise RuntimeError("Unknown port type")
259     _ports.append(p)
260     except KeyError:
261     print "Error Reporter to be called as it is a user error"
262     print "Syntax error while defining ports or some ports-location are left to be added"
263     self._ports = _ports
264    
265     def up(self):
266     self.wide = self.h[SE].pos.x - self.h[NW].pos.x
267     self.height = self.h[SE].pos.y - self.h[NW].pos.y
268     #self.height = 1 * self.wide
269     self.normx = self.wide*0.1
270     self.normy = self.height*0.1
271    
272     def draw(self, context):
273     # drawing the custom icons using the co-ordinates from model file(Graphical Representation)
274 jpye 2394 c = context.cairo
275 saheb 2686 self.up()
276     for m in self.graphical_properties:
277     c.move_to(float(m[0][0])*self.normx,float(m[0][1])*self.normy) #normalization
278     for mm in m:
279     if(len(mm)==2):
280     c.line_to(float(mm[0])*self.normx,float(mm[1])*self.normy)
281     else:
282     c.move_to(float(mm[0])*self.normx + float(mm[2])*self.normx,float(mm[1])*self.normy)
283     self.min_height = self.wide ## minimum ratio for custom icons having arcs......
284     c.arc_negative(float(mm[0])*self.normx,float(mm[1])*self.normy,float(mm[2])*self.normx,math.pi*float(mm[3]),float(mm[4]))
285     c.stroke()
286     c.fill_preserve()
287     super(GraphicalBlockItem,self).draw(context)
288 jpye 1945
289     class DefaultBlockItem(BlockItem):
290     """
291 saheb 2686 This is a 'default block' with a certain number of input and output ports
292     shown depending on the values sent to __init__. It is drawn as a simple
293     box with the input ports on the left and the output ports on the right.
294 jpye 1970
295 saheb 2686 @TODO Not clear yet whether blocks with 'custom' representations should have
296     as their parent class: this class or BlockItem.
297     """
298 jpye 1945
299     def __init__(self, blockinstance):
300    
301     self.blockinstance = blockinstance
302 saheb 2686 super(DefaultBlockItem, self).__init__(blockinstance)
303 jpye 1945
304     eq = EqualsConstraint
305     bal = BalanceConstraint
306     handles = self._handles
307 saheb 2686 self.graphical_properties = blockinstance.blocktype.gr
308     self.port_in = blockinstance.blocktype.port_in
309     self.port_out = blockinstance.blocktype.port_out
310     self.h_nw = handles[NW]
311     self.h_ne = handles[NE]
312     self.h_sw = handles[SW]
313     self.h_se = handles[SE]
314     self.wide = self.h_se.pos.x - self.h_nw.pos.x
315     self.height = self.h_se.pos.y - self.h_nw.pos.y
316     self.normx = self.wide*0.1
317     self.normy = self.height*0.1
318 jpye 1979 ninputs = len(blockinstance.blocktype.inputs)
319     noutputs = len(blockinstance.blocktype.outputs)
320     ii, oi = (0,0) # input and output index counters
321 jpye 1970 _ports = []
322 saheb 2686
323     try:
324     # check to ensure if there is no port_in and port_out string
325     # it will draw ports at default location in that case
326     if not (len(self.port_in)==0 and len(self.port_out)==0):
327     for i in self.blockinstance.ports:
328     if self.blockinstance.ports[i].io is PORT_IN:
329     p = BlockPort(blockinstance, i,self.port_in[i],ii)
330     #print self.port_in[i],i
331     ii += 1
332     elif self.blockinstance.ports[i].io is PORT_OUT:
333     p = BlockPort(blockinstance, i,self.port_out[i],oi)
334     oi += 1
335     else:
336     raise RuntimeError("Unknown port type")
337     _ports.append(p)
338 jpye 1979 else:
339 saheb 2686 for i in self.blockinstance.ports:
340     if self.blockinstance.ports[i].io is PORT_IN:
341     p = BlockPort(blockinstance, i,[0,0],ii)
342     self._constraints.append(eq(p.point.x, self.h_nw.x))
343     self._constraints.append(bal(band=(self.h_nw.y, self.h_sw.y),v=p.point.y, balance=(0.5 + ii)/ninputs))
344     ii += 1
345     elif self.blockinstance.ports[i].io is PORT_OUT:
346     p = BlockPort(blockinstance, i,[0,0],oi)
347     self._constraints.append(eq(p.point.x, self.h_ne.x))
348     self._constraints.append(bal(band=(self.h_ne.y,self.h_se.y),v=p.point.y, balance=(0.5 + oi)/noutputs))
349     oi += 1
350     else:
351     raise RuntimeError("Unknown port type")
352     _ports.append(p)
353     except KeyError:
354     print "Error Reporter to be called as it is a user error"
355     print "Syntax error while defining ports or some ports-location are left to be added"
356     self._ports = _ports
357 jpye 1945
358 saheb 2686 def draw(self, context):
359     # draw the default box itself
360 jpye 1970
361 jpye 1945 c = context.cairo
362     nw = self._handles[NW]
363     c.rectangle(nw.x, nw.y, self.width, self.height)
364 arijit 2074 c.set_source_rgb(0,0,0)
365 jpye 1945 c.stroke()
366    
367 saheb 2686 c.fill_preserve()
368     super(DefaultBlockItem,self).draw(context)
369 jpye 1945
370 saheb 2686 class CustomBlockItem_pump(BlockItem):
371     ''' This is a hard-coded class for custom block item which will
372     resemble the pump icon displayed in the icon palette '''
373     ''' It was the first ever custom block on canvas and will not be
374     used later- it was just meant to test cairo context and gaphas'''
375 jpye 1954
376 saheb 2686 def __init__(self, blockinstance):
377     self.blockinstance = blockinstance
378     super(CustomBlockItem_pump, self).__init__(self.blockinstance,64, 64)
379     self.f = 1.05 # h = f * w
380     self.h = self._handles
381     self.wid = self.h[SE].pos.x - self.h[NW].pos.x
382     self.r = self.wid/2
383     self.xc = self.h[NW].pos.x + self.r
384     self.yc = self.h[NW].pos.y + self.r
385     self.th = math.atan2(self.f*self.wid - self.r, self.r)
386     self.min_height = self.f*self.wid
387     self._ports = [] # no ports yet... need to add them though
388    
389     def update(self):
390     self.wid = self.h[SE].pos.x - self.h[NW].pos.x
391     self.r = self.wid/2
392     self.xc = self.h[NW].pos.x + self.r
393     self.yc = self.h[NW].pos.y + self.r
394     self.th = math.atan2(self.f*self.wid - self.r, self.r)
395     self.min_height = self.f*self.wid
396    
397     def draw(self, context):
398     self.update()
399     cc = context.cairo
400     cc.set_source_rgb(0,0,0)
401     cc.new_sub_path()
402     cc.arc_negative(self.xc,self.yc,self.r,2*math.pi,0)
403     cc.move_to(self.xc + self.r*math.sin(self.th),self.yc + self.r*math.cos(self.th))
404     cc.line_to(self.h[SE].pos.x, self.h[SE].pos.y)
405     cc.line_to(self.h[SW].pos.x, self.h[SW].pos.y)
406     cc.line_to(self.xc - self.r*math.sin(self.th),self.yc + self.r*math.cos(self.th))
407     cc.stroke()
408     text_center(cc,self.wid/2,self.f*self.wid/2,self.blockinstance.name)
409    
410     class CustomBlockItem_turbine(BlockItem):
411     ''' This is a also a hard-coded class for custom block item which will
412     resemble the turbine displayed in the icon palette '''
413     ''' It will be replaced by graphical representation later'''
414    
415     def __init__(self,blockinstance):
416     self.blockinstance = blockinstance
417     super(CustomBlockItem_turbine,self).__init__(self.blockinstance,width=64,height=64)
418     self._ports=[]
419     self.graphical_properties = blockinstance.blocktype.gr
420     self.theta = math.pi/4
421     self.min_inlet = 20
422     self.h = self._handles
423     self.wide = self.h[SE].pos.x - self.h[NW].pos.x
424     self.height = self.h[SE].pos.y - self.h[NW].pos.y
425     self.min_height = self.wide*2 + self.min_inlet
426     self.normx = self.wide*0.1
427     self.normy = self.height*0.1
428    
429     def update(self):
430     self.wide = self.h[SE].pos.x - self.h[NW].pos.x
431     self.height = self.h[SE].pos.y - self.h[NW].pos.y
432     self.min_height = self.wide*2 + self.min_inlet
433     self.normx = self.wide*0.1
434     self.normy = self.height*0.1
435    
436     def draw(self, context):
437     self.update()
438     cc = context.cairo
439     cc.move_to(self.h[SE].pos.x,self.h[SE].pos.y)
440     cc.line_to(self.h[NE].pos.x,self.h[NE].pos.y)
441     cc.line_to(self.h[NW].pos.x,self.h[NW].pos.y+self.wide/math.tan(self.theta))
442     cc.line_to(self.h[SW].pos.x,self.h[SW].pos.y-self.wide/math.tan(self.theta))
443     cc.line_to(self.h[SE].pos.x,self.h[SE].pos.y)
444     cc.stroke()
445    
446     #super(CustomBlockItem_turbine,self).draw(context)
447    
448 jpye 2394 def checkportscanconnect(port1,port2):
449     '''Checks if the two portinstances can connect
450 saheb 2686 Returns: True, if Connectable
451     False, if not Connectable'''
452    
453 jpye 2394 #TODO: Right now port Type checking is a simple str(Type) checking, have a complex procedure to check for
454     #connectable ports!!
455 saheb 2686
456 jpye 2394 if str(port1.type) == str(port2.type):
457     if port1.io == PORT_IN or port1.io == PORT_INOUT:
458     if port2.io == PORT_OUT or port2.io == PORT_INOUT:
459     return True
460     elif port2.io == PORT_IN or port2.io == PORT_INOUT:
461     if port1.io == PORT_OUT or port1.io == PORT_INOUT:
462     return True
463 saheb 2686 return False

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