Merge branch 'maint'
[debian/gnuradio] / grc / python / Block.py
1 """
2 Copyright 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
4
5 GNU Radio Companion is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 GNU Radio Companion is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18 """
19
20 from .. base.Block import Block as _Block
21 from .. gui.Block import Block as _GUIBlock
22 import extract_docs
23 import extract_category
24
25 class Block(_Block, _GUIBlock):
26
27         def is_virtual_sink(self): return self.get_key() == 'virtual_sink'
28         def is_virtual_source(self): return self.get_key() == 'virtual_source'
29
30         ##for make source to keep track of indexes
31         _source_count = 0
32         ##for make sink to keep track of indexes
33         _sink_count = 0
34
35         def __init__(self, flow_graph, n):
36                 """
37                 Make a new block from nested data.
38                 @param flow graph the parent element
39                 @param n the nested odict
40                 @return block a new block
41                 """
42                 #grab the data
43                 self._doc = n.find('doc') or ''
44                 self._imports = map(lambda i: i.strip(), n.findall('import'))
45                 self._make = n.find('make')
46                 self._var_make = n.find('var_make')
47                 self._checks = n.findall('check')
48                 self._callbacks = n.findall('callback')
49                 #build the block
50                 _Block.__init__(
51                         self,
52                         flow_graph=flow_graph,
53                         n=n,
54                 )
55                 _GUIBlock.__init__(self)
56
57         def validate(self):
58                 """
59                 Validate this block.
60                 Call the base class validate.
61                 Evaluate the checks: each check must evaluate to True.
62                 """
63                 _Block.validate(self)
64                 #evaluate the checks
65                 for check in self._checks:
66                         check_res = self.resolve_dependencies(check)
67                         try:
68                                 check_eval = self.get_parent().evaluate(check_res)
69                                 try: assert check_eval
70                                 except AssertionError: self.add_error_message('Check "%s" failed.'%check)
71                         except: self.add_error_message('Check "%s" did not evaluate.'%check)
72
73         def rewrite(self):
74                 """
75                 Add and remove ports to adjust for the nports.
76                 """
77                 _Block.rewrite(self)
78
79                 def insert_port(get_ports, get_port, key):
80                         prev_port = get_port(str(int(key)-1))
81                         get_ports().insert(
82                                 get_ports().index(prev_port)+1,
83                                 prev_port.copy(new_key=key),
84                         )
85                         #restore integer contiguity after insertion
86                         for i, port in enumerate(get_ports()): port._key = str(i)
87
88                 def remove_port(get_ports, get_port, key):
89                         port = get_port(key)
90                         for connection in port.get_connections():
91                                 self.get_parent().remove_element(connection)
92                         get_ports().remove(port)
93                         #restore integer contiguity after insertion
94                         for i, port in enumerate(get_ports()): port._key = str(i)
95
96                 #adjust nports
97                 for get_ports, get_port in (
98                         (self.get_sources, self.get_source),
99                         (self.get_sinks, self.get_sink),
100                 ):
101                         master_ports = filter(lambda p: p.get_nports(), get_ports())
102                         for i, master_port in enumerate(master_ports):
103                                 nports = master_port.get_nports()
104                                 index_first = get_ports().index(master_port)
105                                 try: index_last = get_ports().index(master_ports[i+1])
106                                 except IndexError: index_last = len(get_ports())
107                                 num_ports = index_last - index_first
108                                 #do nothing if nports is already num ports
109                                 if nports == num_ports: continue
110                                 #remove excess ports and connections
111                                 if nports < num_ports:
112                                         for key in map(str, range(index_first+nports, index_first+num_ports)):
113                                                 remove_port(get_ports, get_port, key)
114                                         continue
115                                 #add more ports
116                                 if nports > num_ports:
117                                         for key in map(str, range(index_first+num_ports, index_first+nports)):
118                                                 insert_port(get_ports, get_port, key)
119                                         continue
120
121         def port_controller_modify(self, direction):
122                 """
123                 Change the port controller.
124                 @param direction +1 or -1
125                 @return true for change
126                 """
127                 changed = False
128                 #concat the nports string from the private nports settings of all ports
129                 nports_str = ' '.join([port._nports for port in self.get_ports()])
130                 #modify all params whose keys appear in the nports string
131                 for param in self.get_params():
132                         if param.is_enum() or param.get_key() not in nports_str: continue
133                         #try to increment the port controller by direction
134                         try:
135                                 value = param.get_evaluated()
136                                 value = value + direction
137                                 assert 0 < value
138                                 param.set_value(value)
139                                 changed = True
140                         except: pass
141                 return changed
142
143         def get_doc(self):
144                 doc = self._doc.strip('\n').replace('\\\n', '')
145                 #merge custom doc with doxygen docs
146                 return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n')
147
148         def get_category(self):
149                 category = extract_category.extract(self.get_key())
150                 #if category: return category
151                 return _Block.get_category(self)
152
153         def get_imports(self):
154                 """
155                 Resolve all import statements.
156                 Split each import statement at newlines.
157                 Combine all import statments into a list.
158                 Filter empty imports.
159                 @return a list of import statements
160                 """
161                 return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), []))
162
163         def get_make(self): return self.resolve_dependencies(self._make)
164         def get_var_make(self): return self.resolve_dependencies(self._var_make)
165
166         def get_callbacks(self):
167                 """
168                 Get a list of function callbacks for this block.
169                 @return a list of strings
170                 """
171                 def make_callback(callback):
172                         callback = self.resolve_dependencies(callback)
173                         if 'self.' in callback: return callback
174                         return 'self.%s.%s'%(self.get_id(), callback)
175                 return map(make_callback, self._callbacks)