Switched the python classes to inherit from the base and gui classes.
[debian/gnuradio] / grc / base / FlowGraph.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 Block import Block
23 from Connection import Connection
24 from .. gui import Messages
25
26 class FlowGraph(Element):
27
28         def __init__(self, platform):
29                 """
30                 Make a flow graph from the arguments.
31                 @param platform a platforms with blocks and contrcutors
32                 @return the flow graph object
33                 """
34                 #initialize
35                 Element.__init__(self, platform)
36                 #inital blank import
37                 self.import_data()
38
39         def _get_unique_id(self, base_id=''):
40                 """
41                 Get a unique id starting with the base id.
42                 @param base_id the id starts with this and appends a count
43                 @return a unique id
44                 """
45                 index = 0
46                 while True:
47                         id = '%s_%d'%(base_id, index)
48                         index = index + 1
49                         #make sure that the id is not used by another block
50                         if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id
51
52         def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id'))
53
54         def get_option(self, key):
55                 """
56                 Get the option for a given key.
57                 The option comes from the special options block.
58                 @param key the param key for the options block
59                 @return the value held by that param
60                 """
61                 return self._options_block.get_param(key).get_evaluated()
62
63         def is_flow_graph(self): return True
64
65         ##############################################
66         ## Access Elements
67         ##############################################
68         def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0]
69         def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements())
70         def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements())
71         def get_elements(self):
72                 """
73                 Get a list of all the elements.
74                 Always ensure that the options block is in the list (only once).
75                 @return the element list
76                 """
77                 options_block_count = self._elements.count(self._options_block)
78                 if not options_block_count:
79                         self._elements.append(self._options_block)
80                 for i in range(options_block_count-1):
81                         self._elements.remove(self._options_block)
82                 return self._elements
83
84         def get_enabled_blocks(self):
85                 """
86                 Get a list of all blocks that are enabled.
87                 @return a list of blocks
88                 """
89                 return filter(lambda b: b.get_enabled(), self.get_blocks())
90
91         def get_enabled_connections(self):
92                 """
93                 Get a list of all connections that are enabled.
94                 @return a list of connections
95                 """
96                 return filter(lambda c: c.get_enabled(), self.get_connections())
97
98         def get_new_block(self, key):
99                 """
100                 Get a new block of the specified key.
101                 Add the block to the list of elements.
102                 @param key the block key
103                 @return the new block or None if not found
104                 """
105                 self.flag()
106                 if key not in self.get_parent().get_block_keys(): return None
107                 block = self.get_parent().get_new_block(self, key)
108                 self.get_elements().append(block)
109                 return block
110
111         def connect(self, porta, portb):
112                 """
113                 Create a connection between porta and portb.
114                 @param porta a port
115                 @param portb another port
116                 @throw Exception bad connection
117                 @return the new connection
118                 """
119                 self.flag()
120                 connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb)
121                 self.get_elements().append(connection)
122                 return connection
123
124         def remove_element(self, element):
125                 """
126                 Remove the element from the list of elements.
127                 If the element is a port, remove the whole block.
128                 If the element is a block, remove its connections.
129                 If the element is a connection, just remove the connection.
130                 """
131                 self.flag()
132                 if element not in self.get_elements(): return
133                 #found a port, set to parent signal block
134                 if element.is_port():
135                         element = element.get_parent()
136                 #remove block, remove all involved connections
137                 if element.is_block():
138                         for port in element.get_ports():
139                                 map(self.remove_element, port.get_connections())
140                 self.get_elements().remove(element)
141
142         def evaluate(self, expr):
143                 """
144                 Evaluate the expression.
145                 @param expr the string expression
146                 @throw NotImplementedError
147                 """
148                 raise NotImplementedError
149
150         def rewrite(self):
151                 """
152                 Rewrite critical structures.
153                 Call rewrite on all sub elements.
154                 """
155                 Element.rewrite(self)
156                 for elem in self.get_elements(): elem.rewrite()
157
158         def validate(self):
159                 """
160                 Validate the flow graph.
161                 Validate only the blocks.
162                 Connections will be validated within the blocks.
163                 """
164                 Element.validate(self)
165                 for c in self.get_blocks():
166                         c.validate()
167                         if not c.is_valid():
168                                 self.add_error_message('Element "%s" is not valid.'%c)
169
170         ##############################################
171         ## Import/Export Methods
172         ##############################################
173         def export_data(self):
174                 """
175                 Export this flow graph to nested data.
176                 Export all block and connection data.
177                 @return a nested data odict
178                 """
179                 import time
180                 n = odict()
181                 n['timestamp'] = time.ctime()
182                 n['block'] = [block.export_data() for block in self.get_blocks()]
183                 n['connection'] = [connection.export_data() for connection in self.get_connections()]
184                 return odict({'flow_graph': n})
185
186         def import_data(self, n=None):
187                 """
188                 Import blocks and connections into this flow graph.
189                 Clear this flowgraph of all previous blocks and connections.
190                 Any blocks or connections in error will be ignored.
191                 @param n the nested data odict
192                 """
193                 #remove previous elements
194                 self._elements = list()
195                 #use blank data if none provided
196                 fg_n = n and n.find('flow_graph') or odict()
197                 blocks_n = fg_n.findall('block')
198                 connections_n = fg_n.findall('connection')
199                 #create option block
200                 self._options_block = self.get_parent().get_new_block(self, 'options')
201                 #build the blocks
202                 for block_n in blocks_n:
203                         key = block_n.find('key')
204                         if key == 'options': block = self._options_block
205                         else: block = self.get_new_block(key)
206                         #only load the block when the block key was valid
207                         if block: block.import_data(block_n)
208                         else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent()))
209                 self.rewrite() #rewrite all blocks before connections are made (ex: nports)
210                 #build the connections
211                 for connection_n in connections_n:
212                         #try to make the connection
213                         try:
214                                 #get the block ids
215                                 source_block_id = connection_n.find('source_block_id')
216                                 sink_block_id = connection_n.find('sink_block_id')
217                                 #get the port keys
218                                 source_key = connection_n.find('source_key')
219                                 sink_key = connection_n.find('sink_key')
220                                 #verify the blocks
221                                 block_ids = map(lambda b: b.get_id(), self.get_blocks())
222                                 assert(source_block_id in block_ids)
223                                 assert(sink_block_id in block_ids)
224                                 #get the blocks
225                                 source_block = self.get_block(source_block_id)
226                                 sink_block = self.get_block(sink_block_id)
227                                 #verify the ports
228                                 assert(source_key in source_block.get_source_keys())
229                                 assert(sink_key in sink_block.get_sink_keys())
230                                 #get the ports
231                                 source = source_block.get_source(source_key)
232                                 sink = sink_block.get_sink(sink_key)
233                                 #build the connection
234                                 self.connect(source, sink)
235                         except AssertionError: Messages.send_error_load('Connection between %s(%s) and %s(%s) could not be made.'%(source_block_id, source_key, sink_block_id, sink_key))
236                 self.rewrite() #global rewrite