2 Copyright 2008, 2009 Free Software Foundation, Inc.
3 This file is part of GNU Radio
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.
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.
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
21 from .. base.Param import Param as _Param, EntryParam
28 from gnuradio import eng_notation
30 from gnuradio import gr
32 _check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
33 _show_id_matcher = re.compile('^(variable\w*|parameter|options)$')
35 class FileParam(EntryParam):
36 """Provide an entry box for filename and a button to browse for a file."""
38 def __init__(self, *args, **kwargs):
39 EntryParam.__init__(self, *args, **kwargs)
40 input = gtk.Button('...')
41 input.connect('clicked', self._handle_clicked)
42 self.pack_start(input, False)
44 def _handle_clicked(self, widget=None):
46 If the button was clicked, open a file dialog in open/save format.
47 Replace the text in the entry with the new filename from the file dialog.
50 file_path = self.param.is_valid() and self.param.get_evaluated() or ''
51 (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '')
52 if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths
54 if self.param.get_type() == 'file_open':
55 file_dialog = gtk.FileChooserDialog('Open a Data File...', None,
56 gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK))
57 elif self.param.get_type() == 'file_save':
58 file_dialog = gtk.FileChooserDialog('Save a Data File...', None,
59 gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK))
60 file_dialog.set_do_overwrite_confirmation(True)
61 file_dialog.set_current_name(basename) #show the current filename
62 file_dialog.set_current_folder(dirname) #current directory
63 file_dialog.set_select_multiple(False)
64 file_dialog.set_local_only(True)
65 if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog
66 file_path = file_dialog.get_filename() #get the file path
67 self.entry.set_text(file_path)
68 self._handle_changed()
69 file_dialog.destroy() #destroy the dialog
71 #blacklist certain ids, its not complete, but should help
73 ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \
74 filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__)
75 #define types, native python + numpy
76 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
77 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
78 REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
79 INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
80 numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
81 #cast to tuple for isinstance, concat subtypes
82 COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
83 REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
84 INT_TYPES = tuple(INT_TYPES)
89 _hostage_cells = list()
91 ##possible param types
92 TYPES = _Param.TYPES + [
93 'complex', 'real', 'int',
94 'complex_vector', 'real_vector', 'int_vector',
95 'hex', 'string', 'bool',
96 'file_open', 'file_save',
103 Get the repr (nice string format) for this param.
104 @return the string representation
106 if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
107 ##################################################
108 # display logic for numbers
109 ##################################################
111 if isinstance(num, COMPLEX_TYPES):
112 num = complex(num) #cast to python complex
113 if num == 0: return '0' #value is zero
114 elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
115 elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
116 elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
117 else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
118 else: return str(num)
119 ##################################################
120 # split up formatting by type
121 ##################################################
122 truncate = 0 #default center truncate
123 max_len = max(27 - len(self.get_name()), 3)
124 e = self.get_evaluated()
126 if isinstance(e, bool): return str(e)
127 elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e)
128 elif isinstance(e, VECTOR_TYPES): #vector types
130 dt_str = self.get_value() #large vectors use code
132 else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval
133 elif t in ('file_open', 'file_save'):
134 dt_str = self.get_value()
136 else: dt_str = str(e) #other types
137 ##################################################
139 ##################################################
140 if len(dt_str) > max_len:
141 if truncate < 0: #front truncate
142 dt_str = '...' + dt_str[3-max_len:]
143 elif truncate == 0: #center truncate
144 dt_str = dt_str[:max_len/2 -3] + '...' + dt_str[-max_len/2:]
145 elif truncate > 0: #rear truncate
146 dt_str = dt_str[:max_len-3] + '...'
149 def get_input_class(self):
150 if self.get_type() in ('file_open', 'file_save'): return FileParam
151 return _Param.get_input_class(self)
155 Get the color that represents this param's type.
156 @return a hex color code.
161 'complex': Constants.COMPLEX_COLOR_SPEC,
162 'real': Constants.FLOAT_COLOR_SPEC,
163 'int': Constants.INT_COLOR_SPEC,
165 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
166 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
167 'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
169 'bool': Constants.INT_COLOR_SPEC,
170 'hex': Constants.INT_COLOR_SPEC,
171 'string': Constants.BYTE_VECTOR_COLOR_SPEC,
172 'id': Constants.ID_COLOR_SPEC,
173 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
174 'raw': Constants.WILDCARD_COLOR_SPEC,
176 except: return _Param.get_color(self)
180 Get the hide value from the base class.
181 Hide the ID parameter for most blocks. Exceptions below.
182 If the parameter controls a port type, vlen, or nports, return part.
183 If the parameter is an empty grid position, return part.
184 These parameters are redundant to display in the flow graph view.
185 @return hide the hide property string
187 hide = _Param.get_hide(self)
189 #hide ID in non variable blocks
190 if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part'
191 #hide port controllers for type and nports
192 if self.get_key() in ' '.join(map(
193 lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports())
195 #hide port controllers for vlen, when == 1
196 if self.get_key() in ' '.join(map(
197 lambda p: p._vlen, self.get_parent().get_ports())
200 assert int(self.get_evaluated()) == 1
203 #hide empty grid positions
204 if self.get_key() == 'grid_pos' and not self.get_value(): return 'part'
210 A test evaluation is performed
212 _Param.validate(self) #checks type
213 self._evaluated = None
214 try: self._evaluated = self.evaluate()
215 except Exception, e: self.add_error_message(str(e))
217 def get_evaluated(self): return self._evaluated
222 @return evaluated type
225 self._lisitify_flag = False
226 self._stringify_flag = False
227 self._hostage_cells = list()
230 e = self.get_parent().get_parent().evaluate(v)
231 assert isinstance(e, str)
234 self._stringify_flag = True
238 #########################
240 #########################
241 if self.is_enum(): return v
242 #########################
244 #########################
245 elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex', 'bool'):
246 #raise exception if python cannot evaluate this value
247 try: e = self.get_parent().get_parent().evaluate(v)
248 except Exception, e: raise Exception, 'Value "%s" cannot be evaluated: %s'%(v, e)
249 #raise an exception if the data is invalid
250 if t == 'raw': return e
252 try: assert isinstance(e, COMPLEX_TYPES)
253 except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex.'%str(e)
256 try: assert isinstance(e, REAL_TYPES)
257 except AssertionError: raise Exception, 'Expression "%s" is invalid for type real.'%str(e)
260 try: assert isinstance(e, INT_TYPES)
261 except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer.'%str(e)
263 #########################
264 # Numeric Vector Types
265 #########################
266 elif t == 'complex_vector':
267 if not isinstance(e, VECTOR_TYPES):
268 self._lisitify_flag = True
271 for ei in e: assert isinstance(ei, COMPLEX_TYPES)
272 except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e)
274 elif t == 'real_vector':
275 if not isinstance(e, VECTOR_TYPES):
276 self._lisitify_flag = True
279 for ei in e: assert isinstance(ei, REAL_TYPES)
280 except AssertionError: raise Exception, 'Expression "%s" is invalid for type real vector.'%str(e)
282 elif t == 'int_vector':
283 if not isinstance(e, VECTOR_TYPES):
284 self._lisitify_flag = True
287 for ei in e: assert isinstance(ei, INT_TYPES)
288 except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e)
290 elif t == 'hex': return hex(e)
292 try: assert isinstance(e, bool)
293 except AssertionError: raise Exception, 'Expression "%s" is invalid for type bool.'%str(e)
295 else: raise TypeError, 'Type "%s" not handled'%t
296 #########################
298 #########################
299 elif t in ('string', 'file_open', 'file_save'):
300 #do not check if file/directory exists, that is a runtime issue
303 #########################
305 #########################
307 #can python use this as a variable?
308 try: assert _check_id_matcher.match(v)
309 except AssertionError: raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v
310 params = self.get_all_params('id')
311 keys = [param.get_value() for param in params]
312 try: assert keys.count(v) <= 1 #id should only appear once, or zero times if block is disabled
313 except: raise Exception, 'ID "%s" is not unique.'%v
314 try: assert v not in ID_BLACKLIST
315 except: raise Exception, 'ID "%s" is blacklisted.'%v
317 #########################
319 #########################
320 elif t == 'grid_pos':
321 if not v: return '' #allow for empty grid pos
322 e = self.get_parent().get_parent().evaluate(v)
324 assert isinstance(e, (list, tuple)) and len(e) == 4
325 for ei in e: assert isinstance(ei, int)
326 except AssertionError: raise Exception, 'A grid position must be a list of 4 integers.'
327 row, col, row_span, col_span = e
329 try: assert row >= 0 and col >= 0
330 except AssertionError: raise Exception, 'Row and column must be non-negative.'
331 #check row span, col span
332 try: assert row_span > 0 and col_span > 0
333 except AssertionError: raise Exception, 'Row and column span must be greater than zero.'
334 #calculate hostage cells
335 for r in range(row_span):
336 for c in range(col_span):
337 self._hostage_cells.append((row+r, col+c))
339 params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
341 for cell in param._hostage_cells:
342 if cell in self._hostage_cells: raise Exception, 'Another graphical element is using cell "%s".'%str(cell)
344 #########################
346 #########################
348 n = dict() #new namespace
350 except ImportError: raise Exception, 'Import "%s" failed.'%v
351 except Exception: raise Exception, 'Bad import syntax: "%s".'%v
352 return filter(lambda k: str(k) != '__builtins__', n.keys())
353 #########################
354 else: raise TypeError, 'Type "%s" not handled'%t
358 Convert the value to code.
359 @return a string representing the code
361 #run init tasks in evaluate
362 #such as setting flags
363 if not self._init: self.evaluate()
366 if t in ('string', 'file_open', 'file_save'): #string types
367 if self._stringify_flag: return '"%s"'%v.replace('"', '\"')
369 elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
370 if self._lisitify_flag: return '(%s, )'%v
371 else: return '(%s)'%v
374 def get_all_params(self, type):
376 Get all the params from the flowgraph that have the given type.
377 @param type the specified type
378 @return a list of params
380 return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])