dont hide vlen controller when vlen > 1
[debian/gnuradio] / grc / src / platforms / python / Param.py
1 """
2 Copyright 2008 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 utils import expr_utils
21 from .. base.Param import Param as _Param, EntryParam
22 import Constants
23 import numpy
24 import os
25 import pygtk
26 pygtk.require('2.0')
27 import gtk
28
29 class FileParam(EntryParam):
30         """Provide an entry box for filename and a button to browse for a file."""
31
32         def __init__(self, *args, **kwargs):
33                 EntryParam.__init__(self, *args, **kwargs)
34                 input = gtk.Button('...')
35                 input.connect('clicked', self._handle_clicked)
36                 self.pack_start(input, False)
37
38         def _handle_clicked(self, widget=None):
39                 """
40                 If the button was clicked, open a file dialog in open/save format.
41                 Replace the text in the entry with the new filename from the file dialog.
42                 """
43                 #get the paths
44                 file_path = self.param.is_valid() and self.param.evaluate() or ''
45                 (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '')
46                 if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths
47                 #build the dialog
48                 if self.param.get_type() == 'file_open':
49                         file_dialog = gtk.FileChooserDialog('Open a Data File...', None,
50                                 gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK))
51                 elif self.param.get_type() == 'file_save':
52                         file_dialog = gtk.FileChooserDialog('Save a Data File...', None,
53                                 gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK))
54                         file_dialog.set_do_overwrite_confirmation(True)
55                         file_dialog.set_current_name(basename) #show the current filename
56                 file_dialog.set_current_folder(dirname) #current directory
57                 file_dialog.set_select_multiple(False)
58                 file_dialog.set_local_only(True)
59                 if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog
60                         file_path = file_dialog.get_filename() #get the file path
61                         self.entry.set_text(file_path)
62                         self._handle_changed()
63                 file_dialog.destroy() #destroy the dialog
64
65 #define types, native python + numpy
66 VECTOR_TYPES = (tuple, list, set, numpy.ndarray)
67 COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128]
68 REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64]
69 INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64,
70         numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64]
71 #cast to tuple for isinstance, concat subtypes
72 COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
73 REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
74 INT_TYPES = tuple(INT_TYPES)
75
76 class Param(_Param):
77
78         _init = False
79         _hostage_cells = list()
80
81         ##possible param types
82         TYPES = _Param.TYPES + [
83                 'complex', 'real', 'int',
84                 'complex_vector', 'real_vector', 'int_vector',
85                 'hex', 'string',
86                 'file_open', 'file_save',
87                 'id',
88                 'grid_pos', 'import',
89         ]
90
91         def get_input_class(self):
92                 if self.get_type() in ('file_open', 'file_save'): return FileParam
93                 return _Param.get_input_class(self)
94
95         def get_color(self):
96                 """
97                 Get the color that represents this param's type.
98                 @return a hex color code.
99                 """
100                 try:
101                         return {
102                                 #number types
103                                 'complex': Constants.COMPLEX_COLOR_SPEC,
104                                 'real': Constants.FLOAT_COLOR_SPEC,
105                                 'int': Constants.INT_COLOR_SPEC,
106                                 #vector types
107                                 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC,
108                                 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC,
109                                 'int_vector': Constants.INT_VECTOR_COLOR_SPEC,
110                                 #special
111                                 'hex': Constants.INT_COLOR_SPEC,
112                                 'string': Constants.BYTE_VECTOR_COLOR_SPEC,
113                                 'id': '#DDDDDD',
114                                 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
115                         }[self.get_type()]
116                 except: return _Param.get_color(self)
117
118         def get_hide(self):
119                 """
120                 Get the hide value from the base class.
121                 Hide the ID parameter for most blocks. Exceptions below.
122                 If the parameter controls a port type, vlen, or nports, return part.
123                 If the parameter is an empty grid position, return part.
124                 These parameters are redundant to display in the flow graph view.
125                 @return hide the hide property string
126                 """
127                 hide = _Param.get_hide(self)
128                 if hide: return hide
129                 #hide ID in non variable blocks
130                 if self.get_key() == 'id' and self.get_parent().get_key() not in (
131                         'variable', 'variable_slider', 'variable_chooser', 'variable_text_box', 'parameter', 'options'
132                 ): return 'part'
133                 #hide port controllers for type and nports
134                 if self.get_key() in ' '.join(map(
135                         lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports())
136                 ): return 'part'
137                 #hide port controllers for vlen, when == 1
138                 if self.get_key() in ' '.join(map(
139                         lambda p: p._vlen, self.get_parent().get_ports())
140                 ):
141                         try:
142                                 assert int(self.evaluate()) == 1
143                                 return 'part'
144                         except: pass
145                 #hide empty grid positions
146                 if self.get_key() == 'grid_pos' and not self.get_value(): return 'part'
147                 return hide
148
149         def evaluate(self):
150                 """
151                 Evaluate the value.
152                 @return evaluated type
153                 """
154                 self._lisitify_flag = False
155                 self._stringify_flag = False
156                 self._hostage_cells = list()
157                 def eval_string(v):
158                         try:
159                                 e = self.get_parent().get_parent().evaluate(v)
160                                 assert isinstance(e, str)
161                                 return e
162                         except:
163                                 self._stringify_flag = True
164                                 return v
165                 t = self.get_type()
166                 v = self.get_value()
167                 #########################
168                 # Enum Type
169                 #########################
170                 if self.is_enum(): return self.get_value()
171                 #########################
172                 # Numeric Types
173                 #########################
174                 elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex'):
175                         #raise exception if python cannot evaluate this value
176                         try: e = self.get_parent().get_parent().evaluate(v)
177                         except:
178                                 self._add_error_message('Value "%s" cannot be evaluated.'%v)
179                                 raise Exception
180                         #raise an exception if the data is invalid
181                         if t == 'raw': return e
182                         elif t == 'complex':
183                                 try: assert(isinstance(e, COMPLEX_TYPES))
184                                 except AssertionError:
185                                         self._add_error_message('Expression "%s" is invalid for type complex.'%str(e))
186                                         raise Exception
187                                 return e
188                         elif t == 'real':
189                                 try: assert(isinstance(e, REAL_TYPES))
190                                 except AssertionError:
191                                         self._add_error_message('Expression "%s" is invalid for type real.'%str(e))
192                                         raise Exception
193                                 return e
194                         elif t == 'int':
195                                 try: assert(isinstance(e, INT_TYPES))
196                                 except AssertionError:
197                                         self._add_error_message('Expression "%s" is invalid for type integer.'%str(e))
198                                         raise Exception
199                                 return e
200                         #########################
201                         # Numeric Vector Types
202                         #########################
203                         elif t == 'complex_vector':
204                                 if not isinstance(e, VECTOR_TYPES):
205                                         self._lisitify_flag = True
206                                         e = [e]
207                                 try:
208                                         for ei in e:
209                                                 assert(isinstance(ei, COMPLEX_TYPES))
210                                 except AssertionError:
211                                         self._add_error_message('Expression "%s" is invalid for type complex vector.'%str(e))
212                                         raise Exception
213                                 return e
214                         elif t == 'real_vector':
215                                 if not isinstance(e, VECTOR_TYPES):
216                                         self._lisitify_flag = True
217                                         e = [e]
218                                 try:
219                                         for ei in e:
220                                                 assert(isinstance(ei, REAL_TYPES))
221                                 except AssertionError:
222                                         self._add_error_message('Expression "%s" is invalid for type real vector.'%str(e))
223                                         raise Exception
224                                 return e
225                         elif t == 'int_vector':
226                                 if not isinstance(e, VECTOR_TYPES):
227                                         self._lisitify_flag = True
228                                         e = [e]
229                                 try:
230                                         for ei in e:
231                                                 assert(isinstance(ei, INT_TYPES))
232                                 except AssertionError:
233                                         self._add_error_message('Expression "%s" is invalid for type integer vector.'%str(e))
234                                         raise Exception
235                                 return e
236                         elif t == 'hex':
237                                 return hex(e)
238                         else: raise TypeError, 'Type "%s" not handled'%t
239                 #########################
240                 # String Types
241                 #########################
242                 elif t in ('string', 'file_open', 'file_save'):
243                         #do not check if file/directory exists, that is a runtime issue
244                         e = eval_string(v)
245                         return str(e)
246                 #########################
247                 # Unique ID Type
248                 #########################
249                 elif t == 'id':
250                         #can python use this as a variable?
251                         try:
252                                 assert(len(v) > 0)
253                                 assert(v[0].isalpha())
254                                 for c in v: assert(c.isalnum() or c in ('_',))
255                         except AssertionError:
256                                 self._add_error_message('ID "%s" must be alpha-numeric or underscored, and begin with a letter.'%v)
257                                 raise Exception
258                         params = self.get_all_params('id')
259                         keys = [param.get_value() for param in params]
260                         try: assert(len(keys) == len(set(keys)))
261                         except:
262                                 self._add_error_message('ID "%s" is not unique.'%v)
263                                 raise Exception
264                         return v
265                 #########################
266                 # Grid Position Type
267                 #########################
268                 elif t == 'grid_pos':
269                         if not v: return '' #allow for empty grid pos
270                         e = self.get_parent().get_parent().evaluate(v)
271                         try:
272                                 assert(isinstance(e, (list, tuple)) and len(e) == 4)
273                                 for ei in e: assert(isinstance(ei, int))
274                         except AssertionError:
275                                 self._add_error_message('A grid position must be a list of 4 integers.')
276                                 raise Exception
277                         row, col, row_span, col_span = e
278                         #check row, col
279                         try: assert(row >= 0 and col >= 0)
280                         except AssertionError:
281                                 self._add_error_message('Row and column must be non-negative.')
282                                 raise Exception
283                         #check row span, col span
284                         try: assert(row_span > 0 and col_span > 0)
285                         except AssertionError:
286                                 self._add_error_message('Row and column span must be greater than zero.')
287                                 raise Exception
288                         #calculate hostage cells
289                         for r in range(row_span):
290                                 for c in range(col_span):
291                                         self._hostage_cells.append((row+r, col+c))
292                         #avoid collisions
293                         params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
294                         for param in params:
295                                 for cell in param._hostage_cells:
296                                         if cell in self._hostage_cells:
297                                                 self._add_error_message('Another graphical element is using cell "%s".'%str(cell))
298                                                 raise Exception
299                         return e
300                 #########################
301                 # Import Type
302                 #########################
303                 elif t == 'import':
304                         n = dict() #new namespace
305                         try: exec v in n
306                         except ImportError:
307                                 self._add_error_message('Import "%s" failed.'%v)
308                                 raise Exception
309                         except Exception:
310                                 self._add_error_message('Bad import syntax: "%s".'%v)
311                                 raise Exception
312                         return filter(lambda k: str(k) != '__builtins__', n.keys())
313                 #########################
314                 else: raise TypeError, 'Type "%s" not handled'%t
315
316         def to_code(self):
317                 """
318                 Convert the value to code.
319                 @return a string representing the code
320                 """
321                 #run init tasks in evaluate
322                 #such as setting flags
323                 if not self._init:
324                         self.evaluate()
325                         self._init = True
326                 v = self.get_value()
327                 t = self.get_type()
328                 if t in ('string', 'file_open', 'file_save'): #string types
329                         if self._stringify_flag:
330                                 return '"%s"'%v.replace('"', '\"')
331                         else:
332                                 return v
333                 elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
334                         if self._lisitify_flag:
335                                 return '(%s, )'%v
336                         else:
337                                 return '(%s)'%v
338                 else:
339                         return v
340
341         def get_all_params(self, type):
342                 """
343                 Get all the params from the flowgraph that have the given type.
344                 @param type the specified type
345                 @return a list of params
346                 """
347                 return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_blocks()], [])