Switched the python classes to inherit from the base and gui classes.
[debian/gnuradio] / grc / base / 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 . import odict
21 from Element import Element
22 from Param import Param
23 from Port import Port
24
25 from Cheetah.Template import Template
26 from UserDict import UserDict
27
28 class TemplateArg(UserDict):
29         """
30         A cheetah template argument created from a param.
31         The str of this class evaluates to the param's to code method.
32         The use of this class as a dictionary (enum only) will reveal the enum opts.
33         The __call__ or () method can return the param evaluated to a raw python data type.
34         """
35
36         def __init__(self, param):
37                 UserDict.__init__(self)
38                 self._param = param
39                 if param.is_enum():
40                         for key in param.get_opt_keys():
41                                 self[key] = str(param.get_opt(key))
42
43         def __str__(self):
44                 return str(self._param.to_code())
45
46         def __call__(self):
47                 return self._param.get_evaluated()
48
49 def _get_keys(lst): return [elem.get_key() for elem in lst]
50 def _get_elem(lst, key):
51         try: return lst[_get_keys(lst).index(key)]
52         except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst))
53
54 class Block(Element):
55
56         def __init__(self, flow_graph, n):
57                 """
58                 Make a new block from nested data.
59                 @param flow graph the parent element
60                 @param n the nested odict
61                 @return block a new block
62                 """
63                 #build the block
64                 Element.__init__(self, flow_graph)
65                 #grab the data
66                 params = n.findall('param')
67                 sources = n.findall('source')
68                 sinks = n.findall('sink')
69                 self._name = n.find('name')
70                 self._key = n.find('key')
71                 self._category = n.find('category') or ''
72                 self._block_wrapper_path = n.find('block_wrapper_path')
73                 #create the param objects
74                 self._params = list()
75                 #add the id param
76                 self.get_params().append(self.get_parent().get_parent().Param(
77                         block=self,
78                         n=odict({
79                                 'name': 'ID',
80                                 'key': 'id',
81                                 'type': 'id',
82                         })
83                 ))
84                 self.get_params().append(self.get_parent().get_parent().Param(
85                         block=self,
86                         n=odict({
87                                 'name': 'Enabled',
88                                 'key': '_enabled',
89                                 'type': 'raw',
90                                 'value': 'True',
91                                 'hide': 'all',
92                         })
93                 ))
94                 for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params):
95                         key = param.get_key()
96                         #test against repeated keys
97                         try: assert key not in self.get_param_keys()
98                         except AssertionError: raise Exception, 'Key "%s" already exists in params'%key
99                         #store the param
100                         self.get_params().append(param)
101                 #create the source objects
102                 self._sources = list()
103                 for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources):
104                         key = source.get_key()
105                         #test against repeated keys
106                         try: assert key not in self.get_source_keys()
107                         except AssertionError: raise Exception, 'Key "%s" already exists in sources'%key
108                         #store the port
109                         self.get_sources().append(source)
110                 #create the sink objects
111                 self._sinks = list()
112                 for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks):
113                         key = sink.get_key()
114                         #test against repeated keys
115                         try: assert key not in self.get_sink_keys()
116                         except AssertionError: raise Exception, 'Key "%s" already exists in sinks'%key
117                         #store the port
118                         self.get_sinks().append(sink)
119                 #begin the testing
120                 self.test()
121
122         def test(self):
123                 """
124                 Call test on all children.
125                 """
126                 map(lambda c: c.test(), self.get_params() + self.get_sinks() + self.get_sources())
127
128         def get_enabled(self):
129                 """
130                 Get the enabled state of the block.
131                 @return true for enabled
132                 """
133                 try: return eval(self.get_param('_enabled').get_value())
134                 except: return True
135
136         def set_enabled(self, enabled):
137                 """
138                 Set the enabled state of the block.
139                 @param enabled true for enabled
140                 """
141                 self.get_param('_enabled').set_value(str(enabled))
142
143         def rewrite(self):
144                 """
145                 Rewrite critical structures.
146                 Call rewrite on all sub elements.
147                 """
148                 Element.rewrite(self)
149                 for elem in self.get_ports() + self.get_params(): elem.rewrite()
150
151         def validate(self):
152                 """
153                 Validate the block.
154                 All ports and params must be valid.
155                 All checks must evaluate to true.
156                 Validate the params, ports, and the connections to this block.
157                 """
158                 Element.validate(self)
159                 for c in self.get_params() + self.get_ports() + self.get_connections():
160                         c.validate()
161                         if not c.is_valid():
162                                 for msg in c.get_error_messages():
163                                         self.add_error_message('>>> %s:\n\t%s'%(c, msg))
164
165         def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key())
166
167         def get_id(self): return self.get_param('id').get_value()
168         def is_block(self): return True
169         def get_name(self): return self._name
170         def get_key(self): return self._key
171         def get_category(self): return self._category
172         def get_doc(self): return ''
173         def get_ports(self): return self.get_sources() + self.get_sinks()
174         def get_block_wrapper_path(self): return self._block_wrapper_path
175
176         ##############################################
177         # Access Params
178         ##############################################
179         def get_param_keys(self): return _get_keys(self._params)
180         def get_param(self, key): return _get_elem(self._params, key)
181         def get_params(self): return self._params
182
183         ##############################################
184         # Access Sinks
185         ##############################################
186         def get_sink_keys(self): return _get_keys(self._sinks)
187         def get_sink(self, key): return _get_elem(self._sinks, key)
188         def get_sinks(self): return self._sinks
189
190         ##############################################
191         # Access Sources
192         ##############################################
193         def get_source_keys(self): return _get_keys(self._sources)
194         def get_source(self, key): return _get_elem(self._sources, key)
195         def get_sources(self): return self._sources
196
197         def get_connections(self):
198                 return sum([port.get_connections() for port in self.get_ports()], [])
199
200         def resolve_dependencies(self, tmpl):
201                 """
202                 Resolve a paramater dependency with cheetah templates.
203                 @param tmpl the string with dependencies
204                 @return the resolved value
205                 """
206                 tmpl = str(tmpl)
207                 if '$' not in tmpl: return tmpl
208                 n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params())
209                 try: return str(Template(tmpl, n))
210                 except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl)
211
212         ##############################################
213         # Controller Modify
214         ##############################################
215         def type_controller_modify(self, direction):
216                 """
217                 Change the type controller.
218                 @param direction +1 or -1
219                 @return true for change
220                 """
221                 changed = False
222                 type_param = None
223                 for param in filter(lambda p: p.is_enum(), self.get_params()):
224                         children = self.get_ports() + self.get_params()
225                         #priority to the type controller
226                         if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param
227                         #use param if type param is unset
228                         if not type_param: type_param = param
229                 if type_param:
230                         #try to increment the enum by direction
231                         try:
232                                 keys = type_param.get_option_keys()
233                                 old_index = keys.index(type_param.get_value())
234                                 new_index = (old_index + direction + len(keys))%len(keys)
235                                 type_param.set_value(keys[new_index])
236                                 changed = True
237                         except: pass
238                 return changed
239
240         def port_controller_modify(self, direction):
241                 """
242                 Change the port controller.
243                 @param direction +1 or -1
244                 @return true for change
245                 """
246                 return False
247
248         ##############################################
249         ## Import/Export Methods
250         ##############################################
251         def export_data(self):
252                 """
253                 Export this block's params to nested data.
254                 @return a nested data odict
255                 """
256                 n = odict()
257                 n['key'] = self.get_key()
258                 n['param'] = map(lambda p: p.export_data(), self.get_params())
259                 return n
260
261         def import_data(self, n):
262                 """
263                 Import this block's params from nested data.
264                 Any param keys that do not exist will be ignored.
265                 @param n the nested data odict
266                 """
267                 params_n = n.findall('param')
268                 for param_n in params_n:
269                         key = param_n.find('key')
270                         value = param_n.find('value')
271                         #the key must exist in this block's params
272                         if key in self.get_param_keys():
273                                 self.get_param(key).set_value(value)