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
20 from utils import expr_utils
21 from .. base.Param import Param as _Param, EntryParam
28 from gnuradio import eng_notation
30 class FileParam(EntryParam):
31 """Provide an entry box for filename and a button to browse for a file."""
33 def __init__(self, *args, **kwargs):
34 EntryParam.__init__(self, *args, **kwargs)
35 input = gtk.Button('...')
36 input.connect('clicked', self._handle_clicked)
37 self.pack_start(input, False)
39 def _handle_clicked(self, widget=None):
41 If the button was clicked, open a file dialog in open/save format.
42 Replace the text in the entry with the new filename from the file dialog.
45 file_path = self.param.is_valid() and self.param.evaluate() or ''
46 (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '')
47 if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths
49 if self.param.get_type() == 'file_open':
50 file_dialog = gtk.FileChooserDialog('Open a Data File...', None,
51 gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK))
52 elif self.param.get_type() == 'file_save':
53 file_dialog = gtk.FileChooserDialog('Save a Data File...', None,
54 gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK))
55 file_dialog.set_do_overwrite_confirmation(True)
56 file_dialog.set_current_name(basename) #show the current filename
57 file_dialog.set_current_folder(dirname) #current directory
58 file_dialog.set_select_multiple(False)
59 file_dialog.set_local_only(True)
60 if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog
61 file_path = file_dialog.get_filename() #get the file path
62 self.entry.set_text(file_path)
63 self._handle_changed()
64 file_dialog.destroy() #destroy the dialog
66 #blacklist certain ids, its not complete, but should help
68 ID_BLACKLIST = ['options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + dir(__builtin__)
69 #define types, native python + numpy
70 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
71 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
72 REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
73 INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
74 numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
75 #cast to tuple for isinstance, concat subtypes
76 COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
77 REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
78 INT_TYPES = tuple(INT_TYPES)
83 _hostage_cells = list()
85 ##possible param types
86 TYPES = _Param.TYPES + [
87 'complex', 'real', 'int',
88 'complex_vector', 'real_vector', 'int_vector',
90 'file_open', 'file_save',
97 Get the repr (nice string format) for this param.
98 @return the string representation
100 if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
101 ##################################################
102 # display logic for numbers
103 ##################################################
105 if isinstance(num, COMPLEX_TYPES):
106 num = complex(num) #cast to python complex
107 if num == 0: return '0' #value is zero
108 elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
109 elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
110 elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
111 else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
112 else: return str(num)
113 ##################################################
114 # split up formatting by type
115 ##################################################
116 truncate = 0 #default center truncate
117 max_len = max(27 - len(self.get_name()), 3)
120 if isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e)
121 elif isinstance(e, VECTOR_TYPES): #vector types
123 dt_str = self.get_value() #large vectors use code
125 else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval
126 elif t in ('file_open', 'file_save'):
127 dt_str = self.get_value()
129 else: dt_str = str(e) #other types
130 ##################################################
132 ##################################################
133 if len(dt_str) > max_len:
134 if truncate < 0: #front truncate
135 dt_str = '...' + dt_str[3-max_len:]
136 elif truncate == 0: #center truncate
137 dt_str = dt_str[:max_len/2 -3] + '...' + dt_str[-max_len/2:]
138 elif truncate > 0: #rear truncate
139 dt_str = dt_str[:max_len-3] + '...'
142 def get_input_class(self):
143 if self.get_type() in ('file_open', 'file_save'): return FileParam
144 return _Param.get_input_class(self)
148 Get the color that represents this param's type.
149 @return a hex color code.
154 'complex': Constants.COMPLEX_COLOR_SPEC,
155 'real': Constants.FLOAT_COLOR_SPEC,
156 'int': Constants.INT_COLOR_SPEC,
158 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
159 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
160 'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
162 'hex': Constants.INT_COLOR_SPEC,
163 'string': Constants.BYTE_VECTOR_COLOR_SPEC,
164 'id': Constants.ID_COLOR_SPEC,
165 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
167 except: return _Param.get_color(self)
171 Get the hide value from the base class.
172 Hide the ID parameter for most blocks. Exceptions below.
173 If the parameter controls a port type, vlen, or nports, return part.
174 If the parameter is an empty grid position, return part.
175 These parameters are redundant to display in the flow graph view.
176 @return hide the hide property string
178 hide = _Param.get_hide(self)
180 #hide ID in non variable blocks
181 if self.get_key() == 'id' and self.get_parent().get_key() not in (
182 'variable', 'variable_slider', 'variable_chooser', 'variable_text_box', 'parameter', 'options'
184 #hide port controllers for type and nports
185 if self.get_key() in ' '.join(map(
186 lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports())
188 #hide port controllers for vlen, when == 1
189 if self.get_key() in ' '.join(map(
190 lambda p: p._vlen, self.get_parent().get_ports())
193 assert int(self.evaluate()) == 1
196 #hide empty grid positions
197 if self.get_key() == 'grid_pos' and not self.get_value(): return 'part'
203 @return evaluated type
205 self._lisitify_flag = False
206 self._stringify_flag = False
207 self._hostage_cells = list()
210 e = self.get_parent().get_parent().evaluate(v)
211 assert isinstance(e, str)
214 self._stringify_flag = True
218 #########################
220 #########################
221 if self.is_enum(): return v
222 #########################
224 #########################
225 elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex'):
226 #raise exception if python cannot evaluate this value
227 try: e = self.get_parent().get_parent().evaluate(v)
229 self._add_error_message('Value "%s" cannot be evaluated: %s'%(v, e))
231 #raise an exception if the data is invalid
232 if t == 'raw': return e
234 try: assert(isinstance(e, COMPLEX_TYPES))
235 except AssertionError:
236 self._add_error_message('Expression "%s" is invalid for type complex.'%str(e))
240 try: assert(isinstance(e, REAL_TYPES))
241 except AssertionError:
242 self._add_error_message('Expression "%s" is invalid for type real.'%str(e))
246 try: assert(isinstance(e, INT_TYPES))
247 except AssertionError:
248 self._add_error_message('Expression "%s" is invalid for type integer.'%str(e))
251 #########################
252 # Numeric Vector Types
253 #########################
254 elif t == 'complex_vector':
255 if not isinstance(e, VECTOR_TYPES):
256 self._lisitify_flag = True
260 assert(isinstance(ei, COMPLEX_TYPES))
261 except AssertionError:
262 self._add_error_message('Expression "%s" is invalid for type complex vector.'%str(e))
265 elif t == 'real_vector':
266 if not isinstance(e, VECTOR_TYPES):
267 self._lisitify_flag = True
271 assert(isinstance(ei, REAL_TYPES))
272 except AssertionError:
273 self._add_error_message('Expression "%s" is invalid for type real vector.'%str(e))
276 elif t == 'int_vector':
277 if not isinstance(e, VECTOR_TYPES):
278 self._lisitify_flag = True
282 assert(isinstance(ei, INT_TYPES))
283 except AssertionError:
284 self._add_error_message('Expression "%s" is invalid for type integer vector.'%str(e))
289 else: raise TypeError, 'Type "%s" not handled'%t
290 #########################
292 #########################
293 elif t in ('string', 'file_open', 'file_save'):
294 #do not check if file/directory exists, that is a runtime issue
297 #########################
299 #########################
301 #can python use this as a variable?
304 assert(v[0].isalpha())
305 for c in v: assert(c.isalnum() or c in ('_',))
306 except AssertionError:
307 self._add_error_message('ID "%s" must be alpha-numeric or underscored, and begin with a letter.'%v)
309 params = self.get_all_params('id')
310 keys = [param.get_value() for param in params]
311 try: assert keys.count(v) <= 1 #id should only appear once, or zero times if block is disabled
313 self._add_error_message('ID "%s" is not unique.'%v)
315 try: assert v not in ID_BLACKLIST
317 self._add_error_message('ID "%s" is blacklisted.'%v)
320 #########################
322 #########################
323 elif t == 'grid_pos':
324 if not v: return '' #allow for empty grid pos
325 e = self.get_parent().get_parent().evaluate(v)
327 assert(isinstance(e, (list, tuple)) and len(e) == 4)
328 for ei in e: assert(isinstance(ei, int))
329 except AssertionError:
330 self._add_error_message('A grid position must be a list of 4 integers.')
332 row, col, row_span, col_span = e
334 try: assert(row >= 0 and col >= 0)
335 except AssertionError:
336 self._add_error_message('Row and column must be non-negative.')
338 #check row span, col span
339 try: assert(row_span > 0 and col_span > 0)
340 except AssertionError:
341 self._add_error_message('Row and column span must be greater than zero.')
343 #calculate hostage cells
344 for r in range(row_span):
345 for c in range(col_span):
346 self._hostage_cells.append((row+r, col+c))
348 params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
350 for cell in param._hostage_cells:
351 if cell in self._hostage_cells:
352 self._add_error_message('Another graphical element is using cell "%s".'%str(cell))
355 #########################
357 #########################
359 n = dict() #new namespace
362 self._add_error_message('Import "%s" failed.'%v)
365 self._add_error_message('Bad import syntax: "%s".'%v)
367 return filter(lambda k: str(k) != '__builtins__', n.keys())
368 #########################
369 else: raise TypeError, 'Type "%s" not handled'%t
373 Convert the value to code.
374 @return a string representing the code
376 #run init tasks in evaluate
377 #such as setting flags
383 if t in ('string', 'file_open', 'file_save'): #string types
384 if self._stringify_flag:
385 return '"%s"'%v.replace('"', '\"')
388 elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
389 if self._lisitify_flag:
396 def get_all_params(self, type):
398 Get all the params from the flowgraph that have the given type.
399 @param type the specified type
400 @return a list of params
402 return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])