grc bug fix from Dimitris Symeonidis
[debian/gnuradio] / grc / python / Param.py
index 8b5efc97f2e8ccf5a5901cf5b99ea3898cef963e..e04bc8fcb587589effbc2bcfef2fc61085bfba69 100644 (file)
@@ -17,8 +17,9 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 """
 
-import expr_utils
-from .. base.Param import Param as _Param, EntryParam
+from .. base.Param import Param as _Param
+from .. gui.Param import Param as _GUIParam
+from .. gui.Param import EntryParam
 import Constants
 import numpy
 import os
@@ -30,7 +31,7 @@ import re
 from gnuradio import gr
 
 _check_id_matcher = re.compile('^[a-z|A-Z]\w*$')
-_show_id_matcher = re.compile('^(variable\w*|parameter|options)$')
+_show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$')
 
 class FileParam(EntryParam):
        """Provide an entry box for filename and a button to browse for a file."""
@@ -64,7 +65,7 @@ class FileParam(EntryParam):
                file_dialog.set_local_only(True)
                if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog
                        file_path = file_dialog.get_filename() #get the file path
-                       self.entry.set_text(file_path)
+                       self._input.set_text(file_path)
                        self._handle_changed()
                file_dialog.destroy() #destroy the dialog
 
@@ -83,26 +84,31 @@ COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES)
 REAL_TYPES = tuple(REAL_TYPES + INT_TYPES)
 INT_TYPES = tuple(INT_TYPES)
 
-class Param(_Param):
+class Param(_Param, _GUIParam):
 
-       _init = False
-       _hostage_cells = list()
+       def __init__(self, **kwargs):
+               _Param.__init__(self, **kwargs)
+               _GUIParam.__init__(self)
+               self._init = False
+               self._hostage_cells = list()
 
-       ##possible param types
-       TYPES = _Param.TYPES + [
+       def get_types(self): return (
+               'raw', 'enum',
                'complex', 'real', 'int',
                'complex_vector', 'real_vector', 'int_vector',
                'hex', 'string', 'bool',
                'file_open', 'file_save',
-               'id',
-               'grid_pos', 'import',
-       ]
+               'id', 'stream_id',
+               'grid_pos', 'notebook',
+               'import',
+       )
 
        def __repr__(self):
                """
                Get the repr (nice string format) for this param.
                @return the string representation
                """
+               if not self.is_valid(): return self.get_value()
                if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name()
                ##################################################
                # display logic for numbers
@@ -146,9 +152,9 @@ class Param(_Param):
                                dt_str = dt_str[:max_len-3] + '...'
                return dt_str
 
-       def get_input_class(self):
-               if self.get_type() in ('file_open', 'file_save'): return FileParam
-               return _Param.get_input_class(self)
+       def get_input(self, *args, **kwargs):
+               if self.get_type() in ('file_open', 'file_save'): return FileParam(self, *args, **kwargs)
+               return _GUIParam.get_input(self, *args, **kwargs)
 
        def get_color(self):
                """
@@ -170,7 +176,9 @@ class Param(_Param):
                                'hex': Constants.INT_COLOR_SPEC,
                                'string': Constants.BYTE_VECTOR_COLOR_SPEC,
                                'id': Constants.ID_COLOR_SPEC,
+                               'stream_id': Constants.ID_COLOR_SPEC,
                                'grid_pos': Constants.INT_VECTOR_COLOR_SPEC,
+                               'notebook': Constants.INT_VECTOR_COLOR_SPEC,
                                'raw': Constants.WILDCARD_COLOR_SPEC,
                        }[self.get_type()]
                except: return _Param.get_color(self)
@@ -201,7 +209,7 @@ class Param(_Param):
                                return 'part'
                        except: pass
                #hide empty grid positions
-               if self.get_key() == 'grid_pos' and not self.get_value(): return 'part'
+               if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part'
                return hide
 
        def validate(self):
@@ -242,10 +250,10 @@ class Param(_Param):
                #########################
                # Numeric Types
                #########################
-               elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex', 'bool'):
+               elif t in ('raw', 'complex', 'real', 'int', 'hex', 'bool'):
                        #raise exception if python cannot evaluate this value
                        try: e = self.get_parent().get_parent().evaluate(v)
-                       except Exception, e: raise Exception, 'Value "%s" cannot be evaluated: %s'%(v, e)
+                       except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e)
                        #raise an exception if the data is invalid
                        if t == 'raw': return e
                        elif t == 'complex':
@@ -260,10 +268,22 @@ class Param(_Param):
                                try: assert isinstance(e, INT_TYPES)
                                except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer.'%str(e)
                                return e
-                       #########################
-                       # Numeric Vector Types
-                       #########################
-                       elif t == 'complex_vector':
+                       elif t == 'hex': return hex(e)
+                       elif t == 'bool':
+                               try: assert isinstance(e, bool)
+                               except AssertionError: raise Exception, 'Expression "%s" is invalid for type bool.'%str(e)
+                               return e
+                       else: raise TypeError, 'Type "%s" not handled'%t
+               #########################
+               # Numeric Vector Types
+               #########################
+               elif t in ('complex_vector', 'real_vector', 'int_vector'):
+                       if not v: v = '()' #turn a blank string into an empty list, so it will eval
+                       #raise exception if python cannot evaluate this value
+                       try: e = self.get_parent().get_parent().evaluate(v)
+                       except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e)
+                       #raise an exception if the data is invalid
+                       if t == 'complex_vector':
                                if not isinstance(e, VECTOR_TYPES):
                                        self._lisitify_flag = True
                                        e = [e]
@@ -287,12 +307,6 @@ class Param(_Param):
                                        for ei in e: assert isinstance(ei, INT_TYPES)
                                except AssertionError: raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e)
                                return e
-                       elif t == 'hex': return hex(e)
-                       elif t == 'bool':
-                               try: assert isinstance(e, bool)
-                               except AssertionError: raise Exception, 'Expression "%s" is invalid for type bool.'%str(e)
-                               return e
-                       else: raise TypeError, 'Type "%s" not handled'%t
                #########################
                # String Types
                #########################
@@ -307,14 +321,31 @@ class Param(_Param):
                        #can python use this as a variable?
                        try: assert _check_id_matcher.match(v)
                        except AssertionError: raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v
-                       params = self.get_all_params('id')
-                       keys = [param.get_value() for param in params]
-                       try: assert keys.count(v) <= 1 #id should only appear once, or zero times if block is disabled
+                       ids = [param.get_value() for param in self.get_all_params(t)]
+                       try: assert ids.count(v) <= 1 #id should only appear once, or zero times if block is disabled
                        except: raise Exception, 'ID "%s" is not unique.'%v
                        try: assert v not in ID_BLACKLIST
                        except: raise Exception, 'ID "%s" is blacklisted.'%v
                        return v
                #########################
+               # Stream ID Type
+               #########################
+               elif t == 'stream_id':
+                       #get a list of all stream ids used in the virtual sinks 
+                       ids = [param.get_value() for param in filter(
+                               lambda p: p.get_parent().is_virtual_sink(),
+                               self.get_all_params(t),
+                       )]
+                       #check that the virtual sink's stream id is unique
+                       if self.get_parent().is_virtual_sink():
+                               try: assert ids.count(v) <= 1 #id should only appear once, or zero times if block is disabled
+                               except: raise Exception, 'Stream ID "%s" is not unique.'%v
+                       #check that the virtual source's steam id is found
+                       if self.get_parent().is_virtual_source():
+                               try: assert v in ids
+                               except: raise Exception, 'Stream ID "%s" is not found.'%v
+                       return v
+               #########################
                # Grid Position Type
                #########################
                elif t == 'grid_pos':
@@ -331,17 +362,38 @@ class Param(_Param):
                        #check row span, col span
                        try: assert row_span > 0 and col_span > 0
                        except AssertionError: raise Exception, 'Row and column span must be greater than zero.'
+                       #get hostage cell parent
+                       try: my_parent = self.get_parent().get_param('notebook').evaluate()
+                       except: my_parent = ''
                        #calculate hostage cells
                        for r in range(row_span):
                                for c in range(col_span):
-                                       self._hostage_cells.append((row+r, col+c))
+                                       self._hostage_cells.append((my_parent, (row+r, col+c)))
                        #avoid collisions
                        params = filter(lambda p: p is not self, self.get_all_params('grid_pos'))
                        for param in params:
-                               for cell in param._hostage_cells:
-                                       if cell in self._hostage_cells: raise Exception, 'Another graphical element is using cell "%s".'%str(cell)
+                               for parent, cell in param._hostage_cells:
+                                       if (parent, cell) in self._hostage_cells:
+                                               raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell))
                        return e
                #########################
+               # Notebook Page Type
+               #########################
+               elif t == 'notebook':
+                       if not v: return '' #allow for empty notebook
+                       #get a list of all notebooks
+                       notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks())
+                       #check for notebook param syntax
+                       try: notebook_id, page_index = map(str.strip, v.split(','))
+                       except: raise Exception, 'Bad notebook page format.'
+                       #check that the notebook id is valid
+                       try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0]
+                       except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id
+                       #check that page index exists
+                       try: assert int(page_index) in range(len(notebook_block.get_param('labels').evaluate()))
+                       except: raise Exception, 'Page index "%s" is not a valid index number.'%page_index
+                       return notebook_id, page_index
+               #########################
                # Import Type
                #########################
                elif t == 'import':
@@ -356,17 +408,18 @@ class Param(_Param):
        def to_code(self):
                """
                Convert the value to code.
+               For string and list types, check the init flag, call evaluate().
+               This ensures that evaluate() was called to set the xxxify_flags.
                @return a string representing the code
                """
-               #run init tasks in evaluate
-               #such as setting flags
-               if not self._init: self.evaluate()
                v = self.get_value()
                t = self.get_type()
                if t in ('string', 'file_open', 'file_save'): #string types
+                       if not self._init: self.evaluate()
                        if self._stringify_flag: return '"%s"'%v.replace('"', '\"')
                        else: return v
                elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types
+                       if not self._init: self.evaluate()
                        if self._lisitify_flag: return '(%s, )'%v
                        else: return '(%s)'%v
                else: return v