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|notebook)$')
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',
98
'grid_pos', 'notebook',
104
Get the repr (nice string format) for this param.
105
@return the string representation
107
if not self.is_valid(): return self.get_value()
108
if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
109
##################################################
110
# display logic for numbers
111
##################################################
113
if isinstance(num, COMPLEX_TYPES):
114
num = complex(num) #cast to python complex
115
if num == 0: return '0' #value is zero
116
elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real
117
elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary
118
elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag)))
119
else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag))
120
else: return str(num)
121
##################################################
122
# split up formatting by type
123
##################################################
124
truncate = 0 #default center truncate
125
max_len = max(27 - len(self.get_name()), 3)
126
e = self.get_evaluated()
128
if isinstance(e, bool): return str(e)
129
elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e)
130
elif isinstance(e, VECTOR_TYPES): #vector types
132
dt_str = self.get_value() #large vectors use code
134
else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval
135
elif t in ('file_open', 'file_save'):
136
dt_str = self.get_value()
138
else: dt_str = str(e) #other types
139
##################################################
141
##################################################
142
if len(dt_str) > max_len:
143
if truncate < 0: #front truncate
144
dt_str = '...' + dt_str[3-max_len:]
145
elif truncate == 0: #center truncate
146
dt_str = dt_str[:max_len/2 -3] + '...' + dt_str[-max_len/2:]
147
elif truncate > 0: #rear truncate
148
dt_str = dt_str[:max_len-3] + '...'
151
def get_input_class(self):
152
if self.get_type() in ('file_open', 'file_save'): return FileParam
153
return _Param.get_input_class(self)
157
Get the color that represents this param's type.
158
@return a hex color code.
163
'complex': Constants.COMPLEX_COLOR_SPEC,
164
'real': Constants.FLOAT_COLOR_SPEC,
165
'int': Constants.INT_COLOR_SPEC,
167
'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
168
'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
169
'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
171
'bool': Constants.INT_COLOR_SPEC,
172
'hex': Constants.INT_COLOR_SPEC,
173
'string': Constants.BYTE_VECTOR_COLOR_SPEC,
174
'id': Constants.ID_COLOR_SPEC,
175
'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
176
'notebook': Constants.INT_VECTOR_COLOR_SPEC,
177
'raw': Constants.WILDCARD_COLOR_SPEC,
179
except: return _Param.get_color(self)
183
Get the hide value from the base class.
184
Hide the ID parameter for most blocks. Exceptions below.
185
If the parameter controls a port type, vlen, or nports, return part.
186
If the parameter is an empty grid position, return part.
187
These parameters are redundant to display in the flow graph view.
188
@return hide the hide property string
190
hide = _Param.get_hide(self)
192
#hide ID in non variable blocks
193
if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part'
194
#hide port controllers for type and nports
195
if self.get_key() in ' '.join(map(
196
lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports())
198
#hide port controllers for vlen, when == 1
199
if self.get_key() in ' '.join(map(
200
lambda p: p._vlen, self.get_parent().get_ports())
203
assert int(self.get_evaluated()) == 1
206
#hide empty grid positions
207
if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part'
213
A test evaluation is performed
215
_Param.validate(self) #checks type
216
self._evaluated = None
217
try: self._evaluated = self.evaluate()
218
except Exception, e: self.add_error_message(str(e))
220
def get_evaluated(self): return self._evaluated
225
@return evaluated type
228
self._lisitify_flag = False
229
self._stringify_flag = False
230
self._hostage_cells = list()
233
e = self.get_parent().get_parent().evaluate(v)
234
assert isinstance(e, str)
237
self._stringify_flag = True
241
#########################
243
#########################
244
if self.is_enum(): return v
245
#########################
247
#########################
248
elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex', 'bool'):
249
#raise exception if python cannot evaluate this value
250
try: e = self.get_parent().get_parent().evaluate(v)
251
except Exception, e: raise Exception, 'Value "%s" cannot be evaluated: %s'%(v, e)
252
#raise an exception if the data is invalid
253
if t == 'raw': return e
255
try: assert isinstance(e, COMPLEX_TYPES)
256
except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex.'%str(e)
259
try: assert isinstance(e, REAL_TYPES)
260
except AssertionError: raise Exception, 'Expression "%s" is invalid for type real.'%str(e)
263
try: assert isinstance(e, INT_TYPES)
264
except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer.'%str(e)
266
#########################
267
# Numeric Vector Types
268
#########################
269
elif t == 'complex_vector':
270
if not isinstance(e, VECTOR_TYPES):
271
self._lisitify_flag = True
274
for ei in e: assert isinstance(ei, COMPLEX_TYPES)
275
except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e)
277
elif t == 'real_vector':
278
if not isinstance(e, VECTOR_TYPES):
279
self._lisitify_flag = True
282
for ei in e: assert isinstance(ei, REAL_TYPES)
283
except AssertionError: raise Exception, 'Expression "%s" is invalid for type real vector.'%str(e)
285
elif t == 'int_vector':
286
if not isinstance(e, VECTOR_TYPES):
287
self._lisitify_flag = True
290
for ei in e: assert isinstance(ei, INT_TYPES)
291
except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e)
293
elif t == 'hex': return hex(e)
295
try: assert isinstance(e, bool)
296
except AssertionError: raise Exception, 'Expression "%s" is invalid for type bool.'%str(e)
298
else: raise TypeError, 'Type "%s" not handled'%t
299
#########################
301
#########################
302
elif t in ('string', 'file_open', 'file_save'):
303
#do not check if file/directory exists, that is a runtime issue
306
#########################
308
#########################
310
#can python use this as a variable?
311
try: assert _check_id_matcher.match(v)
312
except AssertionError: raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v
313
params = self.get_all_params('id')
314
keys = [param.get_value() for param in params]
315
try: assert keys.count(v) <= 1 #id should only appear once, or zero times if block is disabled
316
except: raise Exception, 'ID "%s" is not unique.'%v
317
try: assert v not in ID_BLACKLIST
318
except: raise Exception, '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: raise Exception, 'A grid position must be a list of 4 integers.'
330
row, col, row_span, col_span = e
332
try: assert row >= 0 and col >= 0
333
except AssertionError: raise Exception, 'Row and column must be non-negative.'
334
#check row span, col span
335
try: assert row_span > 0 and col_span > 0
336
except AssertionError: raise Exception, 'Row and column span must be greater than zero.'
337
#get hostage cell parent
338
try: my_parent = self.get_parent().get_param('notebook').evaluate()
339
except: my_parent = ''
340
#calculate hostage cells
341
for r in range(row_span):
342
for c in range(col_span):
343
self._hostage_cells.append((my_parent, (row+r, col+c)))
345
params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
347
for parent, cell in param._hostage_cells:
348
if (parent, cell) in self._hostage_cells:
349
raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell))
351
#########################
353
#########################
354
elif t == 'notebook':
355
if not v: return '' #allow for empty notebook
356
#get a list of all notebooks
357
notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
358
#check for notebook param syntax
359
try: notebook_id, page_index = map(str.strip, v.split(','))
360
except: raise Exception, 'Bad notebook page format.'
361
#check that the notebook id is valid
362
try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
363
except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id
364
#check that page index exists
365
try: assert int(page_index) in range(len(notebook_block.get_param('labels').get_evaluated()))
366
except: raise Exception, 'Page index "%s" is not a valid index number.'%page_index
367
return notebook_id, page_index
368
#########################
370
#########################
372
n = dict() #new namespace
374
except ImportError: raise Exception, 'Import "%s" failed.'%v
375
except Exception: raise Exception, 'Bad import syntax: "%s".'%v
376
return filter(lambda k: str(k) != '__builtins__', n.keys())
377
#########################
378
else: raise TypeError, 'Type "%s" not handled'%t
382
Convert the value to code.
383
@return a string representing the code
385
#run init tasks in evaluate
386
#such as setting flags
387
if not self._init: self.evaluate()
390
if t in ('string', 'file_open', 'file_save'): #string types
391
if self._stringify_flag: return '"%s"'%v.replace('"', '\"')
393
elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
394
if self._lisitify_flag: return '(%s, )'%v
395
else: return '(%s)'%v
398
def get_all_params(self, type):
400
Get all the params from the flowgraph that have the given type.
401
@param type the specified type
402
@return a list of params
404
return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], [])