From 9988664127b367fa8fee4409f8460673d6f265e1 Mon Sep 17 00:00:00 2001 From: jblum Date: Tue, 23 Jun 2009 20:38:18 +0000 Subject: Merging r11186:11273 from grc branch. Fixes, features, and reorganization for grc. Minor fixes and features for wxgui forms. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11274 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Block.py | 153 +++++++++++++++ grc/python/Connection.py | 34 ++++ grc/python/Constants.py | 63 +++++++ grc/python/FlowGraph.py | 155 ++++++++++++++++ grc/python/Generator.py | 125 +++++++++++++ grc/python/Makefile.am | 43 +++++ grc/python/Param.py | 380 ++++++++++++++++++++++++++++++++++++++ grc/python/Platform.py | 81 ++++++++ grc/python/Port.py | 129 +++++++++++++ grc/python/__init__.py | 1 + grc/python/block.dtd | 55 ++++++ grc/python/convert_hier.py | 79 ++++++++ grc/python/default_flow_graph.grc | 43 +++++ grc/python/expr_utils.py | 137 ++++++++++++++ grc/python/extract_docs.py | 90 +++++++++ grc/python/flow_graph.tmpl | 205 ++++++++++++++++++++ 16 files changed, 1773 insertions(+) create mode 100644 grc/python/Block.py create mode 100644 grc/python/Connection.py create mode 100644 grc/python/Constants.py create mode 100644 grc/python/FlowGraph.py create mode 100644 grc/python/Generator.py create mode 100644 grc/python/Makefile.am create mode 100644 grc/python/Param.py create mode 100644 grc/python/Platform.py create mode 100644 grc/python/Port.py create mode 100644 grc/python/__init__.py create mode 100644 grc/python/block.dtd create mode 100644 grc/python/convert_hier.py create mode 100644 grc/python/default_flow_graph.grc create mode 100644 grc/python/expr_utils.py create mode 100644 grc/python/extract_docs.py create mode 100644 grc/python/flow_graph.tmpl (limited to 'grc/python') diff --git a/grc/python/Block.py b/grc/python/Block.py new file mode 100644 index 000000000..a9e999491 --- /dev/null +++ b/grc/python/Block.py @@ -0,0 +1,153 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from .. base.Block import Block as _Block +import extract_docs + +class Block(_Block): + + ##for make source to keep track of indexes + _source_count = 0 + ##for make sink to keep track of indexes + _sink_count = 0 + + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + @param flow graph the parent element + @param n the nested odict + @return block a new block + """ + #grab the data + self._doc = n.find('doc') or '' + self._imports = map(lambda i: i.strip(), n.findall('import')) + self._make = n.find('make') + self._var_make = n.find('var_make') + self._checks = n.findall('check') + self._callbacks = n.findall('callback') + #build the block + _Block.__init__( + self, + flow_graph=flow_graph, + n=n, + ) + + def validate(self): + """ + Validate this block. + Call the base class validate. + Evaluate the checks: each check must evaluate to True. + Adjust the nports. + """ + _Block.validate(self) + #evaluate the checks + for check in self._checks: + check_res = self.resolve_dependencies(check) + try: + check_eval = self.get_parent().evaluate(check_res) + try: assert check_eval + except AssertionError: self.add_error_message('Check "%s" failed.'%check) + except: self.add_error_message('Check "%s" did not evaluate.'%check) + #adjust nports + for ports, Port in ( + (self._sources, self.get_parent().get_parent().Source), + (self._sinks, self.get_parent().get_parent().Sink), + ): + #how many ports? + num_ports = len(ports) + #do nothing for 0 ports + if not num_ports: continue + #get the nports setting + port0 = ports[str(0)] + nports = port0.get_nports() + #do nothing for no nports + if not nports: continue + #do nothing if nports is already num ports + if nports == num_ports: continue + #remove excess ports and connections + if nports < num_ports: + #remove the connections + for key in map(str, range(nports, num_ports)): + port = ports[key] + for connection in port.get_connections(): + self.get_parent().remove_element(connection) + #remove the ports + for key in map(str, range(nports, num_ports)): ports.pop(key) + continue + #add more ports + if nports > num_ports: + for key in map(str, range(num_ports, nports)): + n = port0._n + n['key'] = key + port = Port(self, n) + ports[key] = port + continue + + def port_controller_modify(self, direction): + """ + Change the port controller. + @param direction +1 or -1 + @return true for change + """ + changed = False + #concat the nports string from the private nports settings of both port0 + nports_str = \ + (self.get_sinks() and self.get_sinks()[0]._nports or '') + \ + (self.get_sources() and self.get_sources()[0]._nports or '') + #modify all params whose keys appear in the nports string + for param in self.get_params(): + if param.is_enum() or param.get_key() not in nports_str: continue + #try to increment the port controller by direction + try: + value = param.get_evaluated() + value = value + direction + assert 0 < value + param.set_value(value) + changed = True + except: pass + return changed + + def get_doc(self): + doc = self._doc.strip('\n').replace('\\\n', '') + #merge custom doc with doxygen docs + return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') + + def get_imports(self): + """ + Resolve all import statements. + Split each import statement at newlines. + Combine all import statments into a list. + Filter empty imports. + @return a list of import statements + """ + return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) + + def get_make(self): return self.resolve_dependencies(self._make) + def get_var_make(self): return self.resolve_dependencies(self._var_make) + + def get_callbacks(self): + """ + Get a list of function callbacks for this block. + @return a list of strings + """ + def make_callback(callback): + callback = self.resolve_dependencies(callback) + if callback.startswith('self.'): return callback + return 'self.%s.%s'%(self.get_id(), callback) + return map(make_callback, self._callbacks) diff --git a/grc/python/Connection.py b/grc/python/Connection.py new file mode 100644 index 000000000..d8a894bb1 --- /dev/null +++ b/grc/python/Connection.py @@ -0,0 +1,34 @@ +""" +Copyright 2008 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from .. base.Connection import Connection as _Connection + +class Connection(_Connection): + + def validate(self): + """ + Validate the connections. + The ports must match in type and vector length. + """ + _Connection.validate(self) #checks type + #check vector length + source_vlen = self.get_source().get_vlen() + sink_vlen = self.get_sink().get_vlen() + try: assert source_vlen == sink_vlen + except AssertionError: self.add_error_message('Source vector length "%s" does not match sink vector length "%s".'%(source_vlen, sink_vlen)) diff --git a/grc/python/Constants.py b/grc/python/Constants.py new file mode 100644 index 000000000..5f203237f --- /dev/null +++ b/grc/python/Constants.py @@ -0,0 +1,63 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +import os +import sys +import stat +from gnuradio import gr + +_gr_prefs = gr.prefs() + +PYEXEC = os.environ.get('PYTHONW', _gr_prefs.get_string('grc', 'pythonw', '')) + +#setup paths +PATH_SEP = ':' +DOCS_DIR = os.environ.get('GR_DOC_DIR', _gr_prefs.get_string('grc', 'doc_dir', '')) +HIER_BLOCKS_LIB_DIR = os.path.join(os.path.expanduser('~'), '.grc_gnuradio') +BLOCKS_DIRS = filter( #filter blank strings + lambda x: x, PATH_SEP.join([ + os.environ.get('GRC_BLOCKS_PATH', ''), + _gr_prefs.get_string('grc', 'local_blocks_path', ''), + _gr_prefs.get_string('grc', 'global_blocks_path', ''), + ]).split(PATH_SEP), +) + [HIER_BLOCKS_LIB_DIR] + +#file creation modes +TOP_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH +HIER_BLOCK_FILE_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH + +#data files +DATA_DIR = os.path.dirname(__file__) +FLOW_GRAPH_TEMPLATE = os.path.join(DATA_DIR, 'flow_graph.tmpl') +BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') +DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') + +#coloring +COMPLEX_COLOR_SPEC = '#3399FF' +FLOAT_COLOR_SPEC = '#FF8C69' +INT_COLOR_SPEC = '#00FF99' +SHORT_COLOR_SPEC = '#FFFF66' +BYTE_COLOR_SPEC = '#FF66FF' +COMPLEX_VECTOR_COLOR_SPEC = '#3399AA' +FLOAT_VECTOR_COLOR_SPEC = '#CC8C69' +INT_VECTOR_COLOR_SPEC = '#00CC99' +SHORT_VECTOR_COLOR_SPEC = '#CCCC33' +BYTE_VECTOR_COLOR_SPEC = '#CC66CC' +ID_COLOR_SPEC = '#DDDDDD' +WILDCARD_COLOR_SPEC = '#FFFFFF' diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py new file mode 100644 index 000000000..47089a305 --- /dev/null +++ b/grc/python/FlowGraph.py @@ -0,0 +1,155 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +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.FlowGraph import FlowGraph as _FlowGraph +from Block import Block +from Connection import Connection +import re + +_variable_matcher = re.compile('^(variable\w*)$') +_parameter_matcher = re.compile('^(parameter)$') + +class FlowGraph(_FlowGraph): + + _eval_cache = dict() + def _eval(self, code, namespace, namespace_hash): + """ + Evaluate the code with the given namespace. + @param code a string with python code + @param namespace a dict representing the namespace + @param namespace_hash a unique hash for the namespace + @return the resultant object + """ + my_hash = hash(code) ^ namespace_hash + #cache if does not exist + if not self._eval_cache.has_key(my_hash): + self._eval_cache[my_hash] = eval(code, namespace, namespace) + #return from cache + return self._eval_cache[my_hash] + + def _get_io_signature(self, pad_key): + """ + Get an io signature for this flow graph. + The pad key determines the directionality of the io signature. + @param pad_key a string of pad_source or pad_sink + @return a dict with: type, nports, vlen, size + """ + pads = filter(lambda b: b.get_key() == pad_key, self.get_enabled_blocks()) + if not pads: return { + 'nports': '0', + 'type': '', + 'vlen': '0', + 'size': '0', + } + pad = pads[0] #take only the first, user should not have more than 1 + #load io signature + return { + 'nports': str(pad.get_param('nports').get_evaluated()), + 'type': str(pad.get_param('type').get_evaluated()), + 'vlen': str(pad.get_param('vlen').get_evaluated()), + 'size': pad.get_param('type').get_opt('size'), + } + + def get_input_signature(self): + """ + Get the io signature for the input side of this flow graph. + The io signature with be "0", "0" if no pad source is present. + @return a string tuple of type, num_ports, port_size + """ + return self._get_io_signature('pad_source') + + def get_output_signature(self): + """ + Get the io signature for the output side of this flow graph. + The io signature with be "0", "0" if no pad sink is present. + @return a string tuple of type, num_ports, port_size + """ + return self._get_io_signature('pad_sink') + + def get_imports(self): + """ + Get a set of all import statments in this flow graph namespace. + @return a set of import statements + """ + imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) + imports = sorted(set(imports)) + return imports + + def get_variables(self): + """ + Get a list of all variables in this flow graph namespace. + Exclude paramterized variables. + @return a sorted list of variable blocks in order of dependency (indep -> dep) + """ + variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) + #map var id to variable block + id2var = dict([(var.get_id(), var) for var in variables]) + #map var id to variable code + #variable code is a concatenation of all param code (without the id param) + id2expr = dict([(var.get_id(), var.get_var_make()) for var in variables]) + #sort according to dependency + sorted_ids = expr_utils.sort_variables(id2expr) + #create list of sorted variable blocks + variables = [id2var[id] for id in sorted_ids] + return variables + + def get_parameters(self): + """ + Get a list of all paramterized variables in this flow graph namespace. + @return a list of paramterized variables + """ + parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) + return parameters + + def evaluate(self, expr): + """ + Evaluate the expression. + @param expr the string expression + @throw Exception bad expression + @return the evaluated data + """ + if self.is_flagged(): + self.deflag() + #reload namespace + n = dict() + #load imports + for imp in self.get_imports(): + try: exec imp in n + except: pass + #load parameters + np = dict() + for parameter in self.get_parameters(): + try: + e = eval(parameter.get_param('value').to_code(), n, n) + np[parameter.get_id()] = e + except: pass + n.update(np) #merge param namespace + #load variables + for variable in self.get_variables(): + try: + e = eval(variable.get_param('value').to_code(), n, n) + n[variable.get_id()] = e + except: pass + #make namespace public + self.n = n + self.n_hash = hash(str(n)) + #evaluate + e = self._eval(expr, self.n, self.n_hash) + return e diff --git a/grc/python/Generator.py b/grc/python/Generator.py new file mode 100644 index 000000000..cde7dc3d4 --- /dev/null +++ b/grc/python/Generator.py @@ -0,0 +1,125 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +import os +import subprocess +from Cheetah.Template import Template +import expr_utils +from Constants import \ + TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, \ + HIER_BLOCKS_LIB_DIR, PYEXEC, \ + FLOW_GRAPH_TEMPLATE +import convert_hier +from .. gui import Messages + +class Generator(object): + + def __init__(self, flow_graph, file_path): + """ + Initialize the generator object. + Determine the file to generate. + @param flow_graph the flow graph object + @param file_path the path to write the file to + """ + self._flow_graph = flow_graph + self._generate_options = self._flow_graph.get_option('generate_options') + if self._generate_options == 'hb': + self._mode = HIER_BLOCK_FILE_MODE + dirname = HIER_BLOCKS_LIB_DIR + else: + self._mode = TOP_BLOCK_FILE_MODE + dirname = os.path.dirname(file_path) + filename = self._flow_graph.get_option('id') + '.py' + self._file_path = os.path.join(dirname, filename) + + def get_file_path(self): return self._file_path + + def write(self): + #do throttle warning + all_keys = ' '.join(map(lambda b: b.get_key(), self._flow_graph.get_enabled_blocks())) + if ('usrp' not in all_keys) and ('audio' not in all_keys) and ('throttle' not in all_keys) and self._generate_options != 'hb': + Messages.send_warning('''\ +This flow graph may not have flow control: no audio or usrp blocks found. \ +Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') + #generate + open(self.get_file_path(), 'w').write(str(self)) + if self._generate_options == 'hb': + #convert hier block to xml wrapper + convert_hier.convert_hier(self._flow_graph, self.get_file_path()) + os.chmod(self.get_file_path(), self._mode) + + def get_popen(self): + """ + Execute this python flow graph. + @return a popen object + """ + #execute + cmds = [PYEXEC, '-u', self.get_file_path()] #-u is unbuffered stdio + if self._generate_options == 'no_gui': + cmds = ['xterm', '-e'] + cmds + p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True) + return p + + def __str__(self): + """ + Convert the flow graph to python code. + @return a string of python code + """ + title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() + imports = self._flow_graph.get_imports() + variables = self._flow_graph.get_variables() + parameters = self._flow_graph.get_parameters() + #list of variables with controls + controls = filter(lambda v: v.get_make(), variables) + #list of blocks not including variables and imports and parameters and disabled + blocks = sorted(self._flow_graph.get_enabled_blocks(), lambda x, y: cmp(x.get_id(), y.get_id())) + probes = filter(lambda b: b.get_key().startswith('probe_'), blocks) #ensure probes are last in the block list + blocks = filter(lambda b: b not in (imports + parameters + variables + probes), blocks) + probes + #list of connections where each endpoint is enabled + connections = self._flow_graph.get_enabled_connections() + #list of variable names + var_ids = [var.get_id() for var in parameters + variables] + #prepend self. + replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids]) + #list of callbacks + callbacks = [ + expr_utils.expr_replace(cb, replace_dict) + for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) + ] + #map var id to callbacks + var_id2cbs = dict( + [(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) + for var_id in var_ids] + ) + #load the namespace + namespace = { + 'title': title, + 'imports': imports, + 'flow_graph': self._flow_graph, + 'variables': variables, + 'controls': controls, + 'parameters': parameters, + 'blocks': blocks, + 'connections': connections, + 'generate_options': self._generate_options, + 'var_id2cbs': var_id2cbs, + } + #build the template + t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) + return str(t) diff --git a/grc/python/Makefile.am b/grc/python/Makefile.am new file mode 100644 index 000000000..e6d253f5c --- /dev/null +++ b/grc/python/Makefile.am @@ -0,0 +1,43 @@ +# +# Copyright 2008, 2009 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +include $(top_srcdir)/grc/Makefile.inc + +ourpythondir = $(grc_src_prefix)/python +ourpython_PYTHON = \ + convert_hier.py \ + expr_utils.py \ + extract_docs.py \ + Block.py \ + Connection.py \ + Constants.py \ + FlowGraph.py \ + Generator.py \ + Param.py \ + Platform.py \ + Port.py \ + __init__.py + +ourdatadir = $(grc_src_prefix)/python +dist_ourdata_DATA = \ + block.dtd \ + default_flow_graph.grc \ + flow_graph.tmpl diff --git a/grc/python/Param.py b/grc/python/Param.py new file mode 100644 index 000000000..8b5efc97f --- /dev/null +++ b/grc/python/Param.py @@ -0,0 +1,380 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +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 +import Constants +import numpy +import os +import pygtk +pygtk.require('2.0') +import gtk +from gnuradio import eng_notation +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)$') + +class FileParam(EntryParam): + """Provide an entry box for filename and a button to browse for a file.""" + + def __init__(self, *args, **kwargs): + EntryParam.__init__(self, *args, **kwargs) + input = gtk.Button('...') + input.connect('clicked', self._handle_clicked) + self.pack_start(input, False) + + def _handle_clicked(self, widget=None): + """ + If the button was clicked, open a file dialog in open/save format. + Replace the text in the entry with the new filename from the file dialog. + """ + #get the paths + file_path = self.param.is_valid() and self.param.get_evaluated() or '' + (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '') + if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths + #build the dialog + if self.param.get_type() == 'file_open': + file_dialog = gtk.FileChooserDialog('Open a Data File...', None, + gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK)) + elif self.param.get_type() == 'file_save': + file_dialog = gtk.FileChooserDialog('Save a Data File...', None, + gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK)) + file_dialog.set_do_overwrite_confirmation(True) + file_dialog.set_current_name(basename) #show the current filename + file_dialog.set_current_folder(dirname) #current directory + file_dialog.set_select_multiple(False) + 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._handle_changed() + file_dialog.destroy() #destroy the dialog + +#blacklist certain ids, its not complete, but should help +import __builtin__ +ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \ + filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__) +#define types, native python + numpy +VECTOR_TYPES = (tuple, list, set, numpy.ndarray) +COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128] +REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64] +INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64, + numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] +#cast to tuple for isinstance, concat subtypes +COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES) +REAL_TYPES = tuple(REAL_TYPES + INT_TYPES) +INT_TYPES = tuple(INT_TYPES) + +class Param(_Param): + + _init = False + _hostage_cells = list() + + ##possible param types + TYPES = _Param.TYPES + [ + 'complex', 'real', 'int', + 'complex_vector', 'real_vector', 'int_vector', + 'hex', 'string', 'bool', + 'file_open', 'file_save', + 'id', + 'grid_pos', 'import', + ] + + def __repr__(self): + """ + Get the repr (nice string format) for this param. + @return the string representation + """ + if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name() + ################################################## + # display logic for numbers + ################################################## + def num_to_str(num): + if isinstance(num, COMPLEX_TYPES): + num = complex(num) #cast to python complex + if num == 0: return '0' #value is zero + elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real + elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary + elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag))) + else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag)) + else: return str(num) + ################################################## + # split up formatting by type + ################################################## + truncate = 0 #default center truncate + max_len = max(27 - len(self.get_name()), 3) + e = self.get_evaluated() + t = self.get_type() + if isinstance(e, bool): return str(e) + elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) + elif isinstance(e, VECTOR_TYPES): #vector types + if len(e) > 8: + dt_str = self.get_value() #large vectors use code + truncate = 1 + else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval + elif t in ('file_open', 'file_save'): + dt_str = self.get_value() + truncate = -1 + else: dt_str = str(e) #other types + ################################################## + # truncate + ################################################## + if len(dt_str) > max_len: + if truncate < 0: #front truncate + dt_str = '...' + dt_str[3-max_len:] + elif truncate == 0: #center truncate + dt_str = dt_str[:max_len/2 -3] + '...' + dt_str[-max_len/2:] + elif truncate > 0: #rear truncate + 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_color(self): + """ + Get the color that represents this param's type. + @return a hex color code. + """ + try: + return { + #number types + 'complex': Constants.COMPLEX_COLOR_SPEC, + 'real': Constants.FLOAT_COLOR_SPEC, + 'int': Constants.INT_COLOR_SPEC, + #vector types + 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, + 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, + 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, + #special + 'bool': Constants.INT_COLOR_SPEC, + 'hex': Constants.INT_COLOR_SPEC, + 'string': Constants.BYTE_VECTOR_COLOR_SPEC, + 'id': Constants.ID_COLOR_SPEC, + 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, + 'raw': Constants.WILDCARD_COLOR_SPEC, + }[self.get_type()] + except: return _Param.get_color(self) + + def get_hide(self): + """ + Get the hide value from the base class. + Hide the ID parameter for most blocks. Exceptions below. + If the parameter controls a port type, vlen, or nports, return part. + If the parameter is an empty grid position, return part. + These parameters are redundant to display in the flow graph view. + @return hide the hide property string + """ + hide = _Param.get_hide(self) + if hide: return hide + #hide ID in non variable blocks + if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' + #hide port controllers for type and nports + if self.get_key() in ' '.join(map( + lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) + ): return 'part' + #hide port controllers for vlen, when == 1 + if self.get_key() in ' '.join(map( + lambda p: p._vlen, self.get_parent().get_ports()) + ): + try: + assert int(self.get_evaluated()) == 1 + return 'part' + except: pass + #hide empty grid positions + if self.get_key() == 'grid_pos' and not self.get_value(): return 'part' + return hide + + def validate(self): + """ + Validate the param. + A test evaluation is performed + """ + _Param.validate(self) #checks type + self._evaluated = None + try: self._evaluated = self.evaluate() + except Exception, e: self.add_error_message(str(e)) + + def get_evaluated(self): return self._evaluated + + def evaluate(self): + """ + Evaluate the value. + @return evaluated type + """ + self._init = True + self._lisitify_flag = False + self._stringify_flag = False + self._hostage_cells = list() + def eval_string(v): + try: + e = self.get_parent().get_parent().evaluate(v) + assert isinstance(e, str) + return e + except: + self._stringify_flag = True + return v + t = self.get_type() + v = self.get_value() + ######################### + # Enum Type + ######################### + if self.is_enum(): return v + ######################### + # Numeric Types + ######################### + elif t in ('raw', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', '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) + #raise an exception if the data is invalid + if t == 'raw': return e + elif t == 'complex': + try: assert isinstance(e, COMPLEX_TYPES) + except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) + return e + elif t == 'real': + try: assert isinstance(e, REAL_TYPES) + except AssertionError: raise Exception, 'Expression "%s" is invalid for type real.'%str(e) + return e + elif t == 'int': + 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': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + try: + for ei in e: assert isinstance(ei, COMPLEX_TYPES) + except AssertionError: raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) + return e + elif t == 'real_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + try: + for ei in e: assert isinstance(ei, REAL_TYPES) + except AssertionError: raise Exception, 'Expression "%s" is invalid for type real vector.'%str(e) + return e + elif t == 'int_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + try: + 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 + ######################### + elif t in ('string', 'file_open', 'file_save'): + #do not check if file/directory exists, that is a runtime issue + e = eval_string(v) + return str(e) + ######################### + # Unique ID Type + ######################### + elif t == 'id': + #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 + 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 + ######################### + # Grid Position Type + ######################### + elif t == 'grid_pos': + if not v: return '' #allow for empty grid pos + e = self.get_parent().get_parent().evaluate(v) + try: + assert isinstance(e, (list, tuple)) and len(e) == 4 + for ei in e: assert isinstance(ei, int) + except AssertionError: raise Exception, 'A grid position must be a list of 4 integers.' + row, col, row_span, col_span = e + #check row, col + try: assert row >= 0 and col >= 0 + except AssertionError: raise Exception, 'Row and column must be non-negative.' + #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.' + #calculate hostage cells + for r in range(row_span): + for c in range(col_span): + self._hostage_cells.append((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) + return e + ######################### + # Import Type + ######################### + elif t == 'import': + n = dict() #new namespace + try: exec v in n + except ImportError: raise Exception, 'Import "%s" failed.'%v + except Exception: raise Exception, 'Bad import syntax: "%s".'%v + return filter(lambda k: str(k) != '__builtins__', n.keys()) + ######################### + else: raise TypeError, 'Type "%s" not handled'%t + + def to_code(self): + """ + Convert the value to code. + @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 self._stringify_flag: return '"%s"'%v.replace('"', '\"') + else: return v + elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types + if self._lisitify_flag: return '(%s, )'%v + else: return '(%s)'%v + else: return v + + def get_all_params(self, type): + """ + Get all the params from the flowgraph that have the given type. + @param type the specified type + @return a list of params + """ + return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) diff --git a/grc/python/Platform.py b/grc/python/Platform.py new file mode 100644 index 000000000..8718fe955 --- /dev/null +++ b/grc/python/Platform.py @@ -0,0 +1,81 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +import os +from .. import VERSION #TEMP: until gnuradio has __version__ +from .. base.Platform import Platform as _Platform +from FlowGraph import FlowGraph as _FlowGraph +from Connection import Connection as _Connection +from Block import Block as _Block +from Port import Source,Sink +from Param import Param as _Param +from Generator import Generator +from Constants import \ + HIER_BLOCKS_LIB_DIR, BLOCK_DTD, \ + DEFAULT_FLOW_GRAPH, BLOCKS_DIRS +import Constants + +COLORS = (#title, #color spec + ('Complex', Constants.COMPLEX_COLOR_SPEC), + ('Float', Constants.FLOAT_COLOR_SPEC), + ('Integer', Constants.INT_COLOR_SPEC), + ('Short', Constants.SHORT_COLOR_SPEC), + ('Byte', Constants.BYTE_COLOR_SPEC), + ('Complex Vector', Constants.COMPLEX_VECTOR_COLOR_SPEC), + ('Float Vector', Constants.FLOAT_VECTOR_COLOR_SPEC), + ('Integer Vector', Constants.INT_VECTOR_COLOR_SPEC), + ('Short Vector', Constants.SHORT_VECTOR_COLOR_SPEC), + ('Byte Vector', Constants.BYTE_VECTOR_COLOR_SPEC), + ('Wildcard Type', Constants.WILDCARD_COLOR_SPEC), +) + +class Platform(_Platform): + + def __init__(self): + """ + Make a platform for gnuradio. + """ + #ensure hier dir + if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR) + #convert block paths to absolute paths + block_paths = set(map(os.path.abspath, BLOCKS_DIRS)) + #init + _Platform.__init__( + self, + name='GNU Radio Companion', + version=VERSION, + key='grc', + license=__doc__.strip(), + website='http://gnuradio.org/trac/wiki/GNURadioCompanion', + block_paths=block_paths, + block_dtd=BLOCK_DTD, + default_flow_graph=DEFAULT_FLOW_GRAPH, + generator=Generator, + colors=COLORS, + ) + + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Source = Source + Sink = Sink + Param = _Param diff --git a/grc/python/Port.py b/grc/python/Port.py new file mode 100644 index 000000000..5a2b047f0 --- /dev/null +++ b/grc/python/Port.py @@ -0,0 +1,129 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from .. base.Port import Port as _Port +import Constants + +class Port(_Port): + + ##possible port types + TYPES = ['complex', 'float', 'int', 'short', 'byte'] + + def __init__(self, block, n): + """ + Make a new port from nested data. + @param block the parent element + @param n the nested odict + @return a new port + """ + vlen = n.find('vlen') or '1' + nports = n.find('nports') or '' + optional = n.find('optional') or '' + #build the port + _Port.__init__( + self, + block=block, + n=n, + ) + self._nports = nports + self._vlen = vlen + self._optional = bool(optional) + + def validate(self): + _Port.validate(self) + try: assert self.get_enabled_connections() or self.get_optional() + except AssertionError: self.add_error_message('Port is not connected.') + try: assert self.is_source() or len(self.get_enabled_connections()) <= 1 + except AssertionError: self.add_error_message('Port has too many connections.') + + def get_vlen(self): + """ + Get the vector length. + If the evaluation of vlen cannot be cast to an integer, return 1. + @return the vector length or 1 + """ + vlen = self.get_parent().resolve_dependencies(self._vlen) + try: return int(self.get_parent().get_parent().evaluate(vlen)) + except: return 1 + + def get_nports(self): + """ + Get the number of ports. + If already blank, return a blank + If the evaluation of nports cannot be cast to an integer, return 1. + @return the number of ports or 1 + """ + nports = self.get_parent().resolve_dependencies(self._nports) + #return blank if nports is blank + if not nports: return '' + try: + nports = int(self.get_parent().get_parent().evaluate(nports)) + assert 0 < nports + return nports + except: return 1 + + def get_optional(self): return bool(self._optional) + + def get_color(self): + """ + Get the color that represents this port's type. + Codes differ for ports where the vec length is 1 or greater than 1. + @return a hex color code. + """ + try: + if self.get_vlen() == 1: + return {#vlen is 1 + 'complex': Constants.COMPLEX_COLOR_SPEC, + 'float': Constants.FLOAT_COLOR_SPEC, + 'int': Constants.INT_COLOR_SPEC, + 'short': Constants.SHORT_COLOR_SPEC, + 'byte': Constants.BYTE_COLOR_SPEC, + }[self.get_type()] + return {#vlen is non 1 + 'complex': Constants.COMPLEX_VECTOR_COLOR_SPEC, + 'float': Constants.FLOAT_VECTOR_COLOR_SPEC, + 'int': Constants.INT_VECTOR_COLOR_SPEC, + 'short': Constants.SHORT_VECTOR_COLOR_SPEC, + 'byte': Constants.BYTE_VECTOR_COLOR_SPEC, + }[self.get_type()] + except: return _Port.get_color(self) + +class Source(Port): + + def __init__(self, block, n): + self._n = n #save n + #key is port index + n['key'] = str(block._source_count) + block._source_count = block._source_count + 1 + Port.__init__(self, block, n) + + def __del__(self): + self.get_parent()._source_count = self.get_parent()._source_count - 1 + +class Sink(Port): + + def __init__(self, block, n): + self._n = n #save n + #key is port index + n['key'] = str(block._sink_count) + block._sink_count = block._sink_count + 1 + Port.__init__(self, block, n) + + def __del__(self): + self.get_parent()._sink_count = self.get_parent()._sink_count - 1 diff --git a/grc/python/__init__.py b/grc/python/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/grc/python/__init__.py @@ -0,0 +1 @@ + diff --git a/grc/python/block.dtd b/grc/python/block.dtd new file mode 100644 index 000000000..7c6c39811 --- /dev/null +++ b/grc/python/block.dtd @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py new file mode 100644 index 000000000..bdafbcbc1 --- /dev/null +++ b/grc/python/convert_hier.py @@ -0,0 +1,79 @@ +""" +Copyright 2008 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from Constants import BLOCK_DTD +from .. base import ParseXML +from .. base import odict + +def convert_hier(flow_graph, python_file): + #extract info from the flow graph + input_sig = flow_graph.get_input_signature() + output_sig = flow_graph.get_output_signature() + parameters = flow_graph.get_parameters() + block_key = flow_graph.get_option('id') + block_name = flow_graph.get_option('title') + block_category = flow_graph.get_option('category') + block_desc = flow_graph.get_option('description') + block_author = flow_graph.get_option('author') + #build the nested data + block_n = odict() + block_n['name'] = block_name + block_n['key'] = block_key + block_n['category'] = block_category + block_n['import'] = 'execfile("%s")'%python_file + #make data + if parameters: block_n['make'] = '%s(\n\t%s,\n)'%( + block_key, + ',\n\t'.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]), + ) + else: block_n['make'] = '%s()'%block_key + #callback data + block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters] + #param data + params_n = list() + for param in parameters: + param_n = odict() + param_n['name'] = param.get_param('label').get_value() or param.get_id() + param_n['key'] = param.get_id() + param_n['value'] = param.get_param('value').get_value() + param_n['type'] = 'raw' + params_n.append(param_n) + block_n['param'] = params_n + #sink data + if int(input_sig['nports']): + sink_n = odict() + sink_n['name'] = 'in' + sink_n['type'] = input_sig['type'] + sink_n['vlen'] = input_sig['vlen'] + sink_n['nports'] = input_sig['nports'] + block_n['sink'] = sink_n + #source data + if int(output_sig['nports']): + source_n = odict() + source_n['name'] = 'out' + source_n['type'] = output_sig['type'] + source_n['vlen'] = output_sig['vlen'] + source_n['nports'] = output_sig['nports'] + block_n['source'] = source_n + #doc data + block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) + #write the block_n to file + xml_file = python_file + '.xml' + ParseXML.to_file({'block': block_n}, xml_file) + ParseXML.validate_dtd(xml_file, BLOCK_DTD) diff --git a/grc/python/default_flow_graph.grc b/grc/python/default_flow_graph.grc new file mode 100644 index 000000000..dea26f3a5 --- /dev/null +++ b/grc/python/default_flow_graph.grc @@ -0,0 +1,43 @@ + + + + + options + + id + top_block + + + _coordinate + (10, 10) + + + _rotation + 0 + + + + variable + + id + samp_rate + + + value + 32000 + + + _coordinate + (10, 170) + + + _rotation + 0 + + + diff --git a/grc/python/expr_utils.py b/grc/python/expr_utils.py new file mode 100644 index 000000000..1880c8f9c --- /dev/null +++ b/grc/python/expr_utils.py @@ -0,0 +1,137 @@ +""" +Copyright 2008 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +import string +VAR_CHARS = string.letters + string.digits + '_' + +class graph(object): + """ + Simple graph structure held in a dictionary. + """ + + def __init__(self): self._graph = dict() + + def __str__(self): return str(self._graph) + + def add_node(self, node_key): + if self._graph.has_key(node_key): return + self._graph[node_key] = set() + + def remove_node(self, node_key): + if not self._graph.has_key(node_key): return + for edges in self._graph.values(): + if node_key in edges: edges.remove(node_key) + self._graph.pop(node_key) + + def add_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].add(dest_node_key) + + def remove_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].remove(dest_node_key) + + def get_nodes(self): return self._graph.keys() + + def get_edges(self, node_key): return self._graph[node_key] + +def expr_split(expr): + """ + Split up an expression by non alphanumeric characters, including underscore. + Leave strings in-tact. + #TODO ignore escaped quotes, use raw strings. + @param expr an expression string + @return a list of string tokens that form expr + """ + toks = list() + tok = '' + quote = '' + for char in expr: + if quote or char in VAR_CHARS: + if char == quote: quote = '' + tok += char + elif char in ("'", '"'): + toks.append(tok) + tok = char + quote = char + else: + toks.append(tok) + toks.append(char) + tok = '' + toks.append(tok) + return filter(lambda t: t, toks) + +def expr_replace(expr, replace_dict): + """ + Search for vars in the expression and add the prepend. + @param expr an expression string + @param replace_dict a dict of find:replace + @return a new expression with the prepend + """ + expr_splits = expr_split(expr) + for i, es in enumerate(expr_splits): + if es in replace_dict.keys(): + expr_splits[i] = replace_dict[es] + return ''.join(expr_splits) + +def get_variable_dependencies(expr, vars): + """ + Return a set of variables used in this expression. + @param expr an expression string + @param vars a list of variable names + @return a subset of vars used in the expression + """ + expr_toks = expr_split(expr) + return set(filter(lambda v: v in expr_toks, vars)) + +def get_graph(exprs): + """ + Get a graph representing the variable dependencies + @param exprs a mapping of variable name to expression + @return a graph of variable deps + """ + vars = exprs.keys() + #get dependencies for each expression, load into graph + var_graph = graph() + for var in vars: var_graph.add_node(var) + for var, expr in exprs.iteritems(): + for dep in get_variable_dependencies(expr, vars): + if dep != var: var_graph.add_edge(dep, var) + return var_graph + +def sort_variables(exprs): + """ + Get a list of variables in order of dependencies. + @param exprs a mapping of variable name to expression + @return a list of variable names + @throws AssertionError circular dependencies + """ + var_graph = get_graph(exprs) + sorted_vars = list() + #determine dependency order + while var_graph.get_nodes(): + #get a list of nodes with no edges + indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) + assert indep_vars + #add the indep vars to the end of the list + sorted_vars.extend(sorted(indep_vars)) + #remove each edge-less node from the graph + for var in indep_vars: var_graph.remove_node(var) + return reversed(sorted_vars) + +if __name__ == '__main__': + for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py new file mode 100644 index 000000000..fa9140bdf --- /dev/null +++ b/grc/python/extract_docs.py @@ -0,0 +1,90 @@ +""" +Copyright 2008, 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from Constants import DOCS_DIR +from lxml import etree +import os +import re + +DOXYGEN_NAME_XPATH = '/doxygen/compounddef/compoundname' +DOXYGEN_BRIEFDESC_GR_XPATH = '/doxygen/compounddef/briefdescription' +DOXYGEN_DETAILDESC_GR_XPATH = '/doxygen/compounddef/detaileddescription' + +def extract_txt(xml): + """ + Recursivly pull the text out of an xml tree. + @param xml the xml tree + @return a string + """ + text = (xml.text or '').replace('\n', '') + tail = (xml.tail or '').replace('\n', '') + if xml.tag == 'para': tail += '\n\n' + if xml.tag == 'linebreak': text += '\n' + if xml.tag == 'parametername': text += ': ' + return text + ''.join( + map(lambda x: extract_txt(x), xml) + ) + tail + +def _extract(key): + """ + Extract the documentation from the doxygen generated xml files. + If multiple files match, combine the docs. + @param key the block key + @return a string with documentation + """ + docs_dir = os.path.join(DOCS_DIR, 'xml') + if not os.path.exists(docs_dir): return '' + #extract matches + pattern = key.replace('_', '_*').replace('x', '\w') + prog = re.compile('^class%s\..*$'%pattern) + matches = filter(lambda f: prog.match(f), os.listdir(docs_dir)) + #combine all matches + doc_strs = list() + for match in matches: + try: + xml_file = os.path.join(docs_dir, match) + xml = etree.parse(xml_file) + #extract descriptions + comp_name = extract_txt(xml.xpath(DOXYGEN_NAME_XPATH)[0]).strip() + comp_name = ' --- ' + comp_name + ' --- ' + if re.match('(gr|usrp2|trellis)_.*', key): + brief_desc = extract_txt(xml.xpath(DOXYGEN_BRIEFDESC_GR_XPATH)[0]).strip() + detailed_desc = extract_txt(xml.xpath(DOXYGEN_DETAILDESC_GR_XPATH)[0]).strip() + else: + brief_desc = '' + detailed_desc = '' + #combine + doc_strs.append('\n\n'.join([comp_name, brief_desc, detailed_desc]).strip()) + except IndexError: pass #bad format + return '\n\n'.join(doc_strs) + +_docs_cache = dict() +def extract(key): + """ + Call the private extract and cache the result. + @param key the block key + @return a string with documentation + """ + try: assert _docs_cache.has_key(key) + except: _docs_cache[key] = _extract(key) + return _docs_cache[key] + +if __name__ == '__main__': + import sys + print extract(sys.argv[1]) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl new file mode 100644 index 000000000..a45a918d0 --- /dev/null +++ b/grc/python/flow_graph.tmpl @@ -0,0 +1,205 @@ +#!/usr/bin/env python +######################################################## +##Cheetah template - gnuradio_python +## +##@param imports the import statements +##@param flow_graph the flow_graph +##@param variables the variable blocks +##@param controls the variables with gui controls +##@param parameters the paramater blocks +##@param blocks the signal blocks +##@param connections the connections +##@param generate_options the type of flow graph +##@param var_id2cbs variable id map to callback strings +######################################################## +#def indent($code) +#set $code = '\n\t\t'.join(str($code).splitlines()) +$code#slurp +#end def +#import time +#set $DIVIDER = '#'*50 +$DIVIDER +# Gnuradio Python Flow Graph +# Title: $title +#if $flow_graph.get_option('author') +# Author: $flow_graph.get_option('author') +#end if +#if $flow_graph.get_option('description') +# Description: $flow_graph.get_option('description') +#end if +# Generated: $time.ctime() +$DIVIDER + +######################################################## +##Create Imports +######################################################## +#for $imp in $imports +$imp +#end for + +######################################################## +##Create Class +## Write the class declaration for a top or hier block. +## The parameter names are the arguments to __init__. +## Determine the absolute icon path (wx gui only). +## Setup the IO signature (hier block only). +######################################################## +#set $class_name = $flow_graph.get_option('id') +#set $param_str = ', '.join(['self'] + ['%s=%s'%(param.get_id(), param.get_make()) for param in $parameters]) +#if $generate_options == 'wx_gui' + #import gtk + #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) +class $(class_name)(grc_wxgui.top_block_gui): + + def __init__($param_str): + grc_wxgui.top_block_gui.__init__( + self, + title="$title", + #if $icon + icon="$icon.get_filename()", + #end if + ) +#elif $generate_options == 'no_gui' +class $(class_name)(gr.top_block): + + def __init__($param_str): + gr.top_block.__init__(self, "$title") +#elif $generate_options == 'hb' + #set $in_sig = $flow_graph.get_input_signature() + #set $out_sig = $flow_graph.get_output_signature() +class $(class_name)(gr.hier_block2): + + def __init__($param_str): + gr.hier_block2.__init__( + self, + "$title", + gr.io_signature($in_sig.nports, $in_sig.nports, $in_sig.size*$in_sig.vlen), + gr.io_signature($out_sig.nports, $out_sig.nports, $out_sig.size*$out_sig.vlen), + ) +#end if +######################################################## +##Create Parameters +## Set the parameter to a property of self. +######################################################## +#if $parameters + + $DIVIDER + # Parameters + $DIVIDER +#end if +#for $param in $parameters + $indent($param.get_var_make()) +#end for +######################################################## +##Create Variables +## Set the variable to a property of self. +## Write the first line of the variable make. +######################################################## +#if $variables + + $DIVIDER + # Variables + $DIVIDER +#end if +#for $var in $variables + $indent($var.get_var_make()) +#end for +######################################################## +##Create Controls +## Write the variable make (excluding first line). +## Indent each line with 2 tabs. +######################################################## +#if $controls + + $DIVIDER + # Controls + $DIVIDER +#end if +#for $ctrl in $controls + $indent($ctrl.get_make()) +#end for +######################################################## +##Create Blocks +## Write the block make, and indent with 2 tabs. +######################################################## +#if $blocks + + $DIVIDER + # Blocks + $DIVIDER +#end if +#for $blk in filter(lambda b: b.get_make(), $blocks) + self.$blk.get_id() = $indent($blk.get_make()) +#end for +######################################################## +##Create Connections +## The port name should be the id of the parent block. +## However, port names for IO pads should be self. +######################################################## +#if $connections + + $DIVIDER + # Connections + $DIVIDER +#end if +#for $con in $connections + #set $source = $con.get_source() + #set $sink = $con.get_sink() + #if $source.get_parent().get_key() == 'pad_source' + #set $source_name = 'self' + #else + #set $source_name = 'self.' + $source.get_parent().get_id() + #end if + #if $sink.get_parent().get_key() == 'pad_sink' + #set $sink_name = 'self' + #else + #set $sink_name = 'self.' + $sink.get_parent().get_id() + #end if + self.connect(($source_name, $source.get_key()), ($sink_name, $sink.get_key())) +#end for + +######################################################## +##Create Callbacks +## Write a set method for this variable that calls the callbacks +## and sets the direct variable dependencies. +######################################################## +#for $var in $parameters + $variables + #set $id = $var.get_id() + def set_$(id)(self, $id): + self.$id = $id + #for $callback in $var_id2cbs[$id] + $indent($callback) + #end for + +#end for +######################################################## +##Create Main +## For top block code, generate a main routine. +## Instantiate the top block and run as gui or cli. +######################################################## +#if $generate_options != 'hb' +if __name__ == '__main__': + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + #set $params_eq_list = list() + #for $param in $parameters + #set $type = $param.get_param('type').get_value() + #if $type + #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) + parser.add_option("--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make()) + #end if + #end for + (options, args) = parser.parse_args() + #if $flow_graph.get_option('realtime_scheduling') + if gr.enable_realtime_scheduling() != gr.RT_OK: + print "Error: failed to enable realtime scheduling." + #end if + tb = $(class_name)($(', '.join($params_eq_list))) + #if $generate_options == 'wx_gui' + tb.Run($flow_graph.get_option('autostart')) + #elif $generate_options == 'no_gui' + tb.start() + raw_input('Press Enter to quit: ') + tb.stop() + #end if +#end if + -- cgit From 0dc667ed68b90e0aceca193324f58106077072f8 Mon Sep 17 00:00:00 2001 From: jblum Date: Thu, 25 Jun 2009 04:27:04 +0000 Subject: added to todo, added help message to optparse option w/ param git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11281 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/flow_graph.tmpl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index a45a918d0..742ceb944 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -185,7 +185,8 @@ if __name__ == '__main__': #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option("--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make()) + parser.add_option("--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make(), + help="Set $($param.get_param('label').evaluate() or $param.get_id()) [default=%default]") #end if #end for (options, args) = parser.parse_args() -- cgit From 5762d01a3d45b8d500c5ad560c272692d7d46db2 Mon Sep 17 00:00:00 2001 From: jblum Date: Mon, 29 Jun 2009 04:29:36 +0000 Subject: Added ability to extract category names from doxygen. For now, this is commented out until the current block tree is overhauled. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11304 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Block.py | 6 +++++ grc/python/Makefile.am | 1 + grc/python/expr_utils.py | 2 +- grc/python/extract_category.py | 61 ++++++++++++++++++++++++++++++++++++++++++ grc/python/extract_docs.py | 4 +-- 5 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 grc/python/extract_category.py (limited to 'grc/python') diff --git a/grc/python/Block.py b/grc/python/Block.py index a9e999491..6d7595777 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from .. base.Block import Block as _Block import extract_docs +import extract_category class Block(_Block): @@ -128,6 +129,11 @@ class Block(_Block): #merge custom doc with doxygen docs return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') + def get_category(self): + #category = extract_category.extract(self.get_key()) + #if category: return category + return _Block.get_category(self) + def get_imports(self): """ Resolve all import statements. diff --git a/grc/python/Makefile.am b/grc/python/Makefile.am index e6d253f5c..0a62c0825 100644 --- a/grc/python/Makefile.am +++ b/grc/python/Makefile.am @@ -25,6 +25,7 @@ ourpythondir = $(grc_src_prefix)/python ourpython_PYTHON = \ convert_hier.py \ expr_utils.py \ + extract_category.py \ extract_docs.py \ Block.py \ Connection.py \ diff --git a/grc/python/expr_utils.py b/grc/python/expr_utils.py index 1880c8f9c..1bee22497 100644 --- a/grc/python/expr_utils.py +++ b/grc/python/expr_utils.py @@ -1,5 +1,5 @@ """ -Copyright 2008 Free Software Foundation, Inc. +Copyright 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or diff --git a/grc/python/extract_category.py b/grc/python/extract_category.py new file mode 100644 index 000000000..f9358f616 --- /dev/null +++ b/grc/python/extract_category.py @@ -0,0 +1,61 @@ +""" +Copyright 2009 Free Software Foundation, Inc. +This file is part of GNU Radio + +GNU Radio Companion is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +GNU Radio Companion is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +""" + +from Constants import DOCS_DIR +from lxml import etree +import os +import re + +DOXYGEN_TITLE_XPATH = '/doxygen/compounddef/title' +DOXYGEN_CLASS_XPATH = '/doxygen/compounddef/innerclass' + +#map a group/category to a list of blocks +_category_map = dict() + +#extract the group/category information +docs_dir = os.path.join(DOCS_DIR, 'xml') +if os.path.exists(docs_dir): + group_file_matcher = re.compile('^group__\w*\..*$') #xml or xml.gz + matches = filter(lambda f: group_file_matcher.match(f), os.listdir(docs_dir)) + for match in matches: + try: + xml_file = os.path.join(docs_dir, match) + xml = etree.parse(xml_file) + category = xml.xpath(DOXYGEN_TITLE_XPATH)[0].text + blocks = map(lambda x: x.text, xml.xpath(DOXYGEN_CLASS_XPATH)) + _category_map[category] = blocks + except: pass + +def extract(key): + """ + Match the given key to a key in an existing category. + If no match can be made, return an empty string. + @param key the block key + @return the category or empty string + """ + pattern = key.replace('_', '_*').replace('x', '\w') + class_name_matcher = re.compile('^%s$'%pattern) + for category, blocks in _category_map.iteritems(): + for block in blocks: + if class_name_matcher.match(block): return category + return '' + +if __name__ == '__main__': + import sys + print extract(sys.argv[1]) diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py index fa9140bdf..f0c1e749c 100644 --- a/grc/python/extract_docs.py +++ b/grc/python/extract_docs.py @@ -52,8 +52,8 @@ def _extract(key): if not os.path.exists(docs_dir): return '' #extract matches pattern = key.replace('_', '_*').replace('x', '\w') - prog = re.compile('^class%s\..*$'%pattern) - matches = filter(lambda f: prog.match(f), os.listdir(docs_dir)) + class_file_matcher = re.compile('^class%s\..*$'%pattern) #xml or xml.gz + matches = filter(lambda f: class_file_matcher.match(f), os.listdir(docs_dir)) #combine all matches doc_strs = list() for match in matches: -- cgit From 25c5d91fb7c4b54f1e7d77fd9af213a3675a8339 Mon Sep 17 00:00:00 2001 From: jblum Date: Mon, 6 Jul 2009 02:28:52 +0000 Subject: Merged r11309:11357 from grc branch. Adds notebook cabability to grc and its wxgui windows/controls. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11358 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Block.py | 4 ++-- grc/python/FlowGraph.py | 11 +---------- grc/python/Generator.py | 9 ++++++++- grc/python/Param.py | 36 ++++++++++++++++++++++++++++++------ grc/python/expr_utils.py | 16 ++++++++++++++++ grc/python/flow_graph.tmpl | 32 ++++++++++++++++++-------------- 6 files changed, 75 insertions(+), 33 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Block.py b/grc/python/Block.py index 6d7595777..957fee18e 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -130,7 +130,7 @@ class Block(_Block): return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') def get_category(self): - #category = extract_category.extract(self.get_key()) + category = extract_category.extract(self.get_key()) #if category: return category return _Block.get_category(self) @@ -154,6 +154,6 @@ class Block(_Block): """ def make_callback(callback): callback = self.resolve_dependencies(callback) - if callback.startswith('self.'): return callback + if 'self.' in callback: return callback return 'self.%s.%s'%(self.get_id(), callback) return map(make_callback, self._callbacks) diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 47089a305..8cad8be49 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -99,16 +99,7 @@ class FlowGraph(_FlowGraph): @return a sorted list of variable blocks in order of dependency (indep -> dep) """ variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) - #map var id to variable block - id2var = dict([(var.get_id(), var) for var in variables]) - #map var id to variable code - #variable code is a concatenation of all param code (without the id param) - id2expr = dict([(var.get_id(), var.get_var_make()) for var in variables]) - #sort according to dependency - sorted_ids = expr_utils.sort_variables(id2expr) - #create list of sorted variable blocks - variables = [id2var[id] for id in sorted_ids] - return variables + return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) def get_parameters(self): """ diff --git a/grc/python/Generator.py b/grc/python/Generator.py index cde7dc3d4..33be4a726 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -90,7 +90,13 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') #list of blocks not including variables and imports and parameters and disabled blocks = sorted(self._flow_graph.get_enabled_blocks(), lambda x, y: cmp(x.get_id(), y.get_id())) probes = filter(lambda b: b.get_key().startswith('probe_'), blocks) #ensure probes are last in the block list - blocks = filter(lambda b: b not in (imports + parameters + variables + probes), blocks) + probes + #get a list of notebooks and sort them according dependencies + notebooks = expr_utils.sort_objects( + filter(lambda b: b.get_key() == 'notebook', blocks), + lambda n: n.get_id(), lambda n: n.get_param('notebook').get_value(), + ) + #list of regular blocks (all blocks minus the special ones) + blocks = filter(lambda b: b not in (imports + parameters + variables + probes + notebooks), blocks) + probes #list of connections where each endpoint is enabled connections = self._flow_graph.get_enabled_connections() #list of variable names @@ -113,6 +119,7 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') 'imports': imports, 'flow_graph': self._flow_graph, 'variables': variables, + 'notebooks': notebooks, 'controls': controls, 'parameters': parameters, 'blocks': blocks, diff --git a/grc/python/Param.py b/grc/python/Param.py index 8b5efc97f..f971d0c3f 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -30,7 +30,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.""" @@ -95,7 +95,8 @@ class Param(_Param): 'hex', 'string', 'bool', 'file_open', 'file_save', 'id', - 'grid_pos', 'import', + 'grid_pos', 'notebook', + 'import', ] def __repr__(self): @@ -103,6 +104,7 @@ class Param(_Param): 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 @@ -171,6 +173,7 @@ class Param(_Param): 'string': Constants.BYTE_VECTOR_COLOR_SPEC, '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 +204,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): @@ -331,17 +334,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').get_evaluated())) + except: raise Exception, 'Page index "%s" is not a valid index number.'%page_index + return notebook_id, page_index + ######################### # Import Type ######################### elif t == 'import': diff --git a/grc/python/expr_utils.py b/grc/python/expr_utils.py index 1bee22497..3c39f5d89 100644 --- a/grc/python/expr_utils.py +++ b/grc/python/expr_utils.py @@ -133,5 +133,21 @@ def sort_variables(exprs): for var in indep_vars: var_graph.remove_node(var) return reversed(sorted_vars) +def sort_objects(objects, get_id, get_expr): + """ + Sort a list of objects according to their expressions. + @param objects the list of objects to sort + @param get_id the function to extract an id from the object + @param get_expr the function to extract an expression from the object + @return a list of sorted objects + """ + id2obj = dict([(get_id(obj), obj) for obj in objects]) + #map obj id to expression code + id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) + #sort according to dependency + sorted_ids = sort_variables(id2expr) + #return list of sorted objects + return [id2obj[id] for id in sorted_ids] + if __name__ == '__main__': for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 742ceb944..a94e45e8e 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -5,6 +5,7 @@ ##@param imports the import statements ##@param flow_graph the flow_graph ##@param variables the variable blocks +##@param notebooks a list of notebook blocks ##@param controls the variables with gui controls ##@param parameters the paramater blocks ##@param blocks the signal blocks @@ -52,13 +53,11 @@ $imp class $(class_name)(grc_wxgui.top_block_gui): def __init__($param_str): - grc_wxgui.top_block_gui.__init__( - self, - title="$title", + grc_wxgui.top_block_gui.__init__(self, title="$title") #if $icon - icon="$icon.get_filename()", + _icon_path = "$icon.get_filename()" + self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) #end if - ) #elif $generate_options == 'no_gui' class $(class_name)(gr.top_block): @@ -71,8 +70,7 @@ class $(class_name)(gr.hier_block2): def __init__($param_str): gr.hier_block2.__init__( - self, - "$title", + self, "$title", gr.io_signature($in_sig.nports, $in_sig.nports, $in_sig.size*$in_sig.vlen), gr.io_signature($out_sig.nports, $out_sig.nports, $out_sig.size*$out_sig.vlen), ) @@ -92,8 +90,6 @@ class $(class_name)(gr.hier_block2): #end for ######################################################## ##Create Variables -## Set the variable to a property of self. -## Write the first line of the variable make. ######################################################## #if $variables @@ -105,9 +101,19 @@ class $(class_name)(gr.hier_block2): $indent($var.get_var_make()) #end for ######################################################## +##Create Notebooks +######################################################## +#if $notebooks + + $DIVIDER + # Notebooks + $DIVIDER +#end if +#for $notebook in $notebooks + $indent($notebook.get_make()) +#end for +######################################################## ##Create Controls -## Write the variable make (excluding first line). -## Indent each line with 2 tabs. ######################################################## #if $controls @@ -120,7 +126,6 @@ class $(class_name)(gr.hier_block2): #end for ######################################################## ##Create Blocks -## Write the block make, and indent with 2 tabs. ######################################################## #if $blocks @@ -161,7 +166,6 @@ class $(class_name)(gr.hier_block2): ######################################################## ##Create Callbacks ## Write a set method for this variable that calls the callbacks -## and sets the direct variable dependencies. ######################################################## #for $var in $parameters + $variables #set $id = $var.get_id() @@ -196,7 +200,7 @@ if __name__ == '__main__': #end if tb = $(class_name)($(', '.join($params_eq_list))) #if $generate_options == 'wx_gui' - tb.Run($flow_graph.get_option('autostart')) + tb.Run($flow_graph.get_option('run')) #elif $generate_options == 'no_gui' tb.start() raw_input('Press Enter to quit: ') -- cgit From 54e02f64a15b3a2c679399575a53b69f65bc6514 Mon Sep 17 00:00:00 2001 From: jcorgan Date: Wed, 8 Jul 2009 06:32:08 +0000 Subject: Adds short commandline parameter handling in GRC. git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11376 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Param.py | 7 +++++++ grc/python/flow_graph.tmpl | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/Param.py b/grc/python/Param.py index f971d0c3f..15ccd323c 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -302,6 +302,13 @@ class Param(_Param): elif t in ('string', 'file_open', 'file_save'): #do not check if file/directory exists, that is a runtime issue e = eval_string(v) + str_e = str(e) + if t == 'string' and self.get_name() == 'Short ID' and len(str_e) > 0: + try: + assert len(str_e) == 1 + ord_e = ord(str_e) + assert ord_e >= ord('a') and ord_e <= ord('z') or ord_e >= ord('A') and ord_e <= ord('Z') + except AssertionError: raise Exception, 'Short ID "%s" must be a single letter'%v return str(e) ######################### # Unique ID Type diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index a94e45e8e..bc89fb1b5 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -189,7 +189,7 @@ if __name__ == '__main__': #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option("--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make(), + parser.add_option("-$param.get_param('short_id').get_value()", "--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make(), help="Set $($param.get_param('label').evaluate() or $param.get_id()) [default=%default]") #end if #end for -- cgit From eefb51c0c0fac68d16544e492aebd883528607e1 Mon Sep 17 00:00:00 2001 From: jblum Date: Wed, 8 Jul 2009 19:50:21 +0000 Subject: short id fix git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11380 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Param.py | 7 ------- grc/python/flow_graph.tmpl | 6 +++++- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Param.py b/grc/python/Param.py index 15ccd323c..f971d0c3f 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -302,13 +302,6 @@ class Param(_Param): elif t in ('string', 'file_open', 'file_save'): #do not check if file/directory exists, that is a runtime issue e = eval_string(v) - str_e = str(e) - if t == 'string' and self.get_name() == 'Short ID' and len(str_e) > 0: - try: - assert len(str_e) == 1 - ord_e = ord(str_e) - assert ord_e >= ord('a') and ord_e <= ord('z') or ord_e >= ord('A') and ord_e <= ord('Z') - except AssertionError: raise Exception, 'Short ID "%s" must be a single letter'%v return str(e) ######################### # Unique ID Type diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index bc89fb1b5..b537c43e2 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -189,7 +189,11 @@ if __name__ == '__main__': #set $type = $param.get_param('type').get_value() #if $type #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option("-$param.get_param('short_id').get_value()", "--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make(), + #set $short_id = $param.get_param('short_id').get_evaluated() + #if $short_id + #set $short_id = '-' + $short_id + #end if + parser.add_option("$short_id", "--$param.get_id()", dest="$param.get_id()", type="$type", default=$param.get_make(), help="Set $($param.get_param('label').evaluate() or $param.get_id()) [default=%default]") #end if #end for -- cgit From c51227a80b5e03e2d18f02f8693de97610fe8f00 Mon Sep 17 00:00:00 2001 From: jblum Date: Sat, 11 Jul 2009 22:10:25 +0000 Subject: make use of gr.version() git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@11420 221aa14e-8319-0410-a670-987f0aec2ac5 --- grc/python/Platform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Platform.py b/grc/python/Platform.py index 8718fe955..f56e3fb2d 100644 --- a/grc/python/Platform.py +++ b/grc/python/Platform.py @@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import os -from .. import VERSION #TEMP: until gnuradio has __version__ +from gnuradio import gr from .. base.Platform import Platform as _Platform from FlowGraph import FlowGraph as _FlowGraph from Connection import Connection as _Connection @@ -59,7 +59,7 @@ class Platform(_Platform): _Platform.__init__( self, name='GNU Radio Companion', - version=VERSION, + version=gr.version(), key='grc', license=__doc__.strip(), website='http://gnuradio.org/trac/wiki/GNURadioCompanion', -- cgit From 9cdea550a868695d3b75dc79ccde4a4cc3b78851 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Mon, 10 Aug 2009 22:29:55 -0700 Subject: apply diff from previous commits --- grc/python/Constants.py | 1 + grc/python/Platform.py | 3 ++- grc/python/Port.py | 33 +++++++++++++++++---------------- grc/python/block.dtd | 4 ++-- 4 files changed, 22 insertions(+), 19 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 5f203237f..2f629d1bf 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -61,3 +61,4 @@ SHORT_VECTOR_COLOR_SPEC = '#CCCC33' BYTE_VECTOR_COLOR_SPEC = '#CC66CC' ID_COLOR_SPEC = '#DDDDDD' WILDCARD_COLOR_SPEC = '#FFFFFF' +MSG_COLOR_SPEC = '#FF6600' diff --git a/grc/python/Platform.py b/grc/python/Platform.py index f56e3fb2d..d55dbf4ce 100644 --- a/grc/python/Platform.py +++ b/grc/python/Platform.py @@ -42,7 +42,8 @@ COLORS = (#title, #color spec ('Integer Vector', Constants.INT_VECTOR_COLOR_SPEC), ('Short Vector', Constants.SHORT_VECTOR_COLOR_SPEC), ('Byte Vector', Constants.BYTE_VECTOR_COLOR_SPEC), - ('Wildcard Type', Constants.WILDCARD_COLOR_SPEC), + ('Wildcard', Constants.WILDCARD_COLOR_SPEC), + ('Message', Constants.MSG_COLOR_SPEC), ) class Platform(_Platform): diff --git a/grc/python/Port.py b/grc/python/Port.py index 5a2b047f0..c01884cc3 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -23,27 +23,23 @@ import Constants class Port(_Port): ##possible port types - TYPES = ['complex', 'float', 'int', 'short', 'byte'] + TYPES = ['complex', 'float', 'int', 'short', 'byte', 'msg'] def __init__(self, block, n): """ Make a new port from nested data. @param block the parent element @param n the nested odict - @return a new port """ - vlen = n.find('vlen') or '1' - nports = n.find('nports') or '' - optional = n.find('optional') or '' #build the port _Port.__init__( self, block=block, n=n, ) - self._nports = nports - self._vlen = vlen - self._optional = bool(optional) + self._nports = n.find('nports') or '' + self._vlen = n.find('vlen') or '1' + self._optional = bool(n.find('optional')) def validate(self): _Port.validate(self) @@ -94,6 +90,7 @@ class Port(_Port): 'int': Constants.INT_COLOR_SPEC, 'short': Constants.SHORT_COLOR_SPEC, 'byte': Constants.BYTE_COLOR_SPEC, + 'msg': Constants.MSG_COLOR_SPEC, }[self.get_type()] return {#vlen is non 1 'complex': Constants.COMPLEX_VECTOR_COLOR_SPEC, @@ -108,10 +105,10 @@ class Source(Port): def __init__(self, block, n): self._n = n #save n - #key is port index - n['key'] = str(block._source_count) - block._source_count = block._source_count + 1 - Port.__init__(self, block, n) + if n['type'] != 'msg': #key is port index + n['key'] = str(block._source_count) + block._source_count = block._source_count + 1 + Port.__init__(self, block, n) def __del__(self): self.get_parent()._source_count = self.get_parent()._source_count - 1 @@ -120,10 +117,14 @@ class Sink(Port): def __init__(self, block, n): self._n = n #save n - #key is port index - n['key'] = str(block._sink_count) - block._sink_count = block._sink_count + 1 - Port.__init__(self, block, n) + if n['type'] != 'msg': #key is port index + n['key'] = str(block._sink_count) + block._sink_count = block._sink_count + 1 + Port.__init__(self, block, n) def __del__(self): self.get_parent()._sink_count = self.get_parent()._sink_count - 1 + +#TODO merge source and sink classes into port class +#TODO check that key is only defined if and only if type is message +#TODO check that nports is undefined when type is message diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 7c6c39811..8681d1255 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -31,8 +31,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> - - + + - - + + - + @@ -53,3 +53,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + -- cgit From 8be1f3f82e0f17583cecab9c8a4763dda4ebc1e7 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 18 Oct 2011 17:31:48 -0700 Subject: grc: tweaks for colors, added 64 int, vector darken --- grc/python/Constants.py | 12 +++++++----- grc/python/Port.py | 10 ++++++---- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 4a234f080..ab547ea27 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -46,13 +46,15 @@ BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') CORE_TYPES = ( #name, key, sizeof, color - ('Complex Float 64', 'fc64', 16, '#72f313'), + ('Complex Float 64', 'fc64', 16, '#CC8C69'), ('Complex Float 32', 'fc32', 8, '#3399FF'), - ('Complex Integer 32', 'sc32', 8, '#00b789'), - ('Complex Integer 16', 'sc16', 4, '#f37913'), - ('Complex Integer 8', 'sc8', 2, '#ff0e7f'), - ('Float 64', 'f64', 8, '#86a8fa'), + ('Complex Integer 64', 'sc64', 16, '#66CC00'), + ('Complex Integer 32', 'sc32', 8, '#33cc66'), + ('Complex Integer 16', 'sc16', 4, '#cccc00'), + ('Complex Integer 8', 'sc8', 2, '#cc00cc'), + ('Float 64', 'f64', 8, '#66CCCC'), ('Float 32', 'f32', 4, '#FF8C69'), + ('Integer 64', 's64', 8, '#99FF33'), ('Integer 32', 's32', 4, '#00FF99'), ('Integer 16', 's16', 2, '#FFFF66'), ('Integer 8', 's8', 1, '#FF66FF'), diff --git a/grc/python/Port.py b/grc/python/Port.py index 9baa81110..c2bfd9ccc 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -147,14 +147,16 @@ class Port(_Port, _GUIPort): """ try: color = Constants.TYPE_TO_COLOR[self.get_type()] - if self.get_vlen() == 1: return color + vlen = self.get_vlen() + if vlen == 1: return color color_val = int(color[1:], 16) r = (color_val >> 16) & 0xff g = (color_val >> 8) & 0xff b = (color_val >> 0) & 0xff - r = max(r-50, 0) - g = max(g-50, 0) - b = max(b-50, 0) + dark = (0, 0, 30, 50, 70)[min(4, vlen)] + r = max(r-dark, 0) + g = max(g-dark, 0) + b = max(b-dark, 0) return '#%.2x%.2x%.2x'%(r, g, b) except: return _Port.get_color(self) -- cgit From 037c5f60492f9201e23671fc87dd1ed0dd5cc5c9 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 29 Oct 2011 16:46:19 -0700 Subject: grc: fix bug when displaying string params If the param contents evald successfully but not to a string, it would display None. This patch forces an exception, so the string will get auto-quoted and interpreted correctly. --- grc/python/Param.py | 1 + 1 file changed, 1 insertion(+) (limited to 'grc/python') diff --git a/grc/python/Param.py b/grc/python/Param.py index 5536138c1..2a7258dc7 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -244,6 +244,7 @@ class Param(_Param, _GUIParam): try: e = self.get_parent().get_parent().evaluate(v) if isinstance(e, str): return e + raise Exception #want to stringify except: self._stringify_flag = True return v -- cgit From 7b57dae20bb182561fcda78e5eeec44e44f0a9fb Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 17 Nov 2011 07:59:50 -0800 Subject: grc: replaced doxygen extracted docs with python docstrings --- grc/python/Block.py | 3 --- grc/python/CMakeLists.txt | 1 - grc/python/Constants.py | 1 - grc/python/Makefile.am | 1 - grc/python/extract_category.py | 61 ------------------------------------------ grc/python/extract_docs.py | 58 +++++++++------------------------------ 6 files changed, 13 insertions(+), 112 deletions(-) delete mode 100644 grc/python/extract_category.py (limited to 'grc/python') diff --git a/grc/python/Block.py b/grc/python/Block.py index 967a27ce9..2c334dfc2 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from .. base.Block import Block as _Block from .. gui.Block import Block as _GUIBlock import extract_docs -import extract_category class Block(_Block, _GUIBlock): @@ -154,8 +153,6 @@ class Block(_Block, _GUIBlock): return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') def get_category(self): - category = extract_category.extract(self.get_key()) - #if category: return category return _Block.get_category(self) def get_imports(self): diff --git a/grc/python/CMakeLists.txt b/grc/python/CMakeLists.txt index 2075d126d..0871c7bd1 100644 --- a/grc/python/CMakeLists.txt +++ b/grc/python/CMakeLists.txt @@ -21,7 +21,6 @@ GR_PYTHON_INSTALL(FILES convert_hier.py expr_utils.py - extract_category.py extract_docs.py Block.py Connection.py diff --git a/grc/python/Constants.py b/grc/python/Constants.py index ab547ea27..1a65caf1c 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -25,7 +25,6 @@ _gr_prefs = gr.prefs() #setup paths PATH_SEP = {'/':':', '\\':';'}[os.path.sep] -DOCS_DIR = os.environ.get('GR_DOC_DIR', _gr_prefs.get_string('grc', 'doc_dir', '')) HIER_BLOCKS_LIB_DIR = os.path.join(os.path.expanduser('~'), '.grc_gnuradio') BLOCKS_DIRS = filter( #filter blank strings lambda x: x, PATH_SEP.join([ diff --git a/grc/python/Makefile.am b/grc/python/Makefile.am index 67b71bc47..eb35b6fc1 100644 --- a/grc/python/Makefile.am +++ b/grc/python/Makefile.am @@ -25,7 +25,6 @@ ourpythondir = $(pkgpythondir)/grc/python ourpython_PYTHON = \ convert_hier.py \ expr_utils.py \ - extract_category.py \ extract_docs.py \ Block.py \ Connection.py \ diff --git a/grc/python/extract_category.py b/grc/python/extract_category.py deleted file mode 100644 index f9358f616..000000000 --- a/grc/python/extract_category.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Copyright 2009 Free Software Foundation, Inc. -This file is part of GNU Radio - -GNU Radio Companion is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -GNU Radio Companion is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -""" - -from Constants import DOCS_DIR -from lxml import etree -import os -import re - -DOXYGEN_TITLE_XPATH = '/doxygen/compounddef/title' -DOXYGEN_CLASS_XPATH = '/doxygen/compounddef/innerclass' - -#map a group/category to a list of blocks -_category_map = dict() - -#extract the group/category information -docs_dir = os.path.join(DOCS_DIR, 'xml') -if os.path.exists(docs_dir): - group_file_matcher = re.compile('^group__\w*\..*$') #xml or xml.gz - matches = filter(lambda f: group_file_matcher.match(f), os.listdir(docs_dir)) - for match in matches: - try: - xml_file = os.path.join(docs_dir, match) - xml = etree.parse(xml_file) - category = xml.xpath(DOXYGEN_TITLE_XPATH)[0].text - blocks = map(lambda x: x.text, xml.xpath(DOXYGEN_CLASS_XPATH)) - _category_map[category] = blocks - except: pass - -def extract(key): - """ - Match the given key to a key in an existing category. - If no match can be made, return an empty string. - @param key the block key - @return the category or empty string - """ - pattern = key.replace('_', '_*').replace('x', '\w') - class_name_matcher = re.compile('^%s$'%pattern) - for category, blocks in _category_map.iteritems(): - for block in blocks: - if class_name_matcher.match(block): return category - return '' - -if __name__ == '__main__': - import sys - print extract(sys.argv[1]) diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py index fe157a221..a7e945c37 100644 --- a/grc/python/extract_docs.py +++ b/grc/python/extract_docs.py @@ -17,63 +17,31 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from Constants import DOCS_DIR -from lxml import etree -import os import re -DOXYGEN_NAME_XPATH = '/doxygen/compounddef/compoundname' -DOXYGEN_BRIEFDESC_GR_XPATH = '/doxygen/compounddef/briefdescription' -DOXYGEN_DETAILDESC_GR_XPATH = '/doxygen/compounddef/detaileddescription' - -GROUP_KEYS = "gr|trellis|noaa|vocoder|digital" - -def extract_txt(xml): - """ - Recursivly pull the text out of an xml tree. - @param xml the xml tree - @return a string - """ - text = (xml.text or '').replace('\n', '') - tail = (xml.tail or '').replace('\n', '') - if xml.tag == 'para': tail += '\n\n' - if xml.tag == 'linebreak': text += '\n' - if xml.tag == 'parametername': text += ': ' - return text + ''.join( - map(lambda x: extract_txt(x), xml) - ) + tail - def _extract(key): """ - Extract the documentation from the doxygen generated xml files. - If multiple files match, combine the docs. + Extract the documentation from the python __doc__ strings. + If multiple modules match, combine the docs. @param key the block key @return a string with documentation """ - docs_dir = os.path.join(DOCS_DIR, 'xml') - if not os.path.exists(docs_dir): return '' #extract matches - pattern = key.replace('_', '_*').replace('x', '\w') - class_file_matcher = re.compile('^class%s\..*$'%pattern) #xml or xml.gz - matches = filter(lambda f: class_file_matcher.match(f), os.listdir(docs_dir)) + try: + module_name, constructor_name = key.split('_', 1) + module = __import__('gnuradio.'+module_name) + module = getattr(module, module_name) + except: return '' + pattern = constructor_name.replace('_', '_*').replace('x', '\w') + pattern_matcher = re.compile('^%s\w*$'%pattern) + matches = filter(lambda x: pattern_matcher.match(x), dir(module)) #combine all matches doc_strs = list() for match in matches: try: - xml_file = os.path.join(docs_dir, match) - xml = etree.parse(xml_file) - #extract descriptions - comp_name = extract_txt(xml.xpath(DOXYGEN_NAME_XPATH)[0]).strip() - comp_name = ' --- ' + comp_name + ' --- ' - if re.match(('(%s)_.*' % GROUP_KEYS), key): - brief_desc = extract_txt(xml.xpath(DOXYGEN_BRIEFDESC_GR_XPATH)[0]).strip() - detailed_desc = extract_txt(xml.xpath(DOXYGEN_DETAILDESC_GR_XPATH)[0]).strip() - else: - brief_desc = '' - detailed_desc = '' - #combine - doc_strs.append('\n\n'.join([comp_name, brief_desc, detailed_desc]).strip()) - except IndexError: pass #bad format + title = ' --- ' + match + ' --- ' + doc_strs.append('\n\n'.join([title, getattr(module, match).__doc__]).strip()) + except: pass return '\n\n'.join(doc_strs) _docs_cache = dict() -- cgit From 00420d32081d8252bb37142b2be19a8a7c4dc4c4 Mon Sep 17 00:00:00 2001 From: Johnathan Corgan Date: Thu, 8 Dec 2011 13:48:48 -0800 Subject: Removed autotools, gr-waveform, some cleanup Nick Foster owes Nick Corgan a six-pack of beer! --- grc/python/.gitignore | 2 -- grc/python/Makefile.am | 43 ------------------------------------------- 2 files changed, 45 deletions(-) delete mode 100644 grc/python/.gitignore delete mode 100644 grc/python/Makefile.am (limited to 'grc/python') diff --git a/grc/python/.gitignore b/grc/python/.gitignore deleted file mode 100644 index b336cc7ce..000000000 --- a/grc/python/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Makefile -/Makefile.in diff --git a/grc/python/Makefile.am b/grc/python/Makefile.am deleted file mode 100644 index eb35b6fc1..000000000 --- a/grc/python/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright 2008, 2009 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 51 Franklin Street, -# Boston, MA 02110-1301, USA. -# - -include $(top_srcdir)/Makefile.common - -ourpythondir = $(pkgpythondir)/grc/python -ourpython_PYTHON = \ - convert_hier.py \ - expr_utils.py \ - extract_docs.py \ - Block.py \ - Connection.py \ - Constants.py \ - FlowGraph.py \ - Generator.py \ - Param.py \ - Platform.py \ - Port.py \ - __init__.py - -ourdatadir = $(pkgpythondir)/grc/python -dist_ourdata_DATA = \ - block.dtd \ - default_flow_graph.grc \ - flow_graph.tmpl -- cgit From 25b29495b71e8d3276a9a2f27044524c833772cb Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 25 Mar 2012 17:52:42 -0700 Subject: grc: resolve down and up stream for empty types --- grc/python/Port.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Port.py b/grc/python/Port.py index c2bfd9ccc..d0ef51b48 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -1,5 +1,5 @@ """ -Copyright 2008-2011 Free Software Foundation, Inc. +Copyright 2008-2012 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or @@ -24,7 +24,7 @@ import Constants def _get_source_from_virtual_sink_port(vsp): """ Resolve the source port that is connected to the given virtual sink port. - Use the get source from virtual source to recursively resolve subsequent ports. + Use the get source from virtual source to recursively resolve subsequent ports. """ try: return _get_source_from_virtual_source_port( vsp.get_enabled_connections()[0].get_source()) @@ -50,6 +50,35 @@ def _get_source_from_virtual_source_port(vsp, traversed=[]): ) except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp +def _get_sink_from_virtual_source_port(vsp): + """ + Resolve the sink port that is connected to the given virtual source port. + Use the get sink from virtual sink to recursively resolve subsequent ports. + """ + try: return _get_sink_from_virtual_sink_port( + vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + +def _get_sink_from_virtual_sink_port(vsp, traversed=[]): + """ + Recursively resolve sink ports over the virtual connections. + Keep track of traversed sinks to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_sink(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp + try: return _get_sink_from_virtual_sink_port( + _get_sink_from_virtual_source_port( + filter(#get all virtual source with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter(#get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_source(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sources()[0] + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + class Port(_Port, _GUIPort): def __init__(self, block, n, dir): @@ -81,6 +110,8 @@ class Port(_Port, _GUIPort): def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() + def is_type_empty(self): return not self._n['type'] + def validate(self): _Port.validate(self) if not self.get_enabled_connections() and not self.get_optional(): @@ -99,18 +130,30 @@ class Port(_Port, _GUIPort): Handle the port cloning for virtual blocks. """ _Port.rewrite(self) - if self.get_parent().is_virtual_sink() or self.get_parent().is_virtual_source(): + if self.is_type_empty(): try: #clone type and vlen - source = self.resolve_virtual_source() + source = self.resolve_empty_type() self._type = str(source.get_type()) self._vlen = str(source.get_vlen()) except: #reset type and vlen self._type = '' self._vlen = '' - def resolve_virtual_source(self): - if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) - if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) + def resolve_empty_type(self): + if self.is_sink(): + try: + src = _get_source_from_virtual_sink_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_sink_port(self) + if not sink.is_type_empty(): return sink + if self.is_source(): + try: + src = _get_source_from_virtual_source_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_source_port(self) + if not sink.is_type_empty(): return sink def get_vlen(self): """ -- cgit From f919f9dcbb54a08e6e26d6c229ce92fb784fa1b2 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Fri, 13 Apr 2012 18:36:53 -0400 Subject: Removed whitespace and added dtools/bin/remove-whitespace as a tool to do this in the future. The sed script was provided by Moritz Fischer. --- grc/python/CMakeLists.txt | 8 ++++---- grc/python/Param.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'grc/python') diff --git a/grc/python/CMakeLists.txt b/grc/python/CMakeLists.txt index 0871c7bd1..4832dd897 100644 --- a/grc/python/CMakeLists.txt +++ b/grc/python/CMakeLists.txt @@ -1,17 +1,17 @@ # Copyright 2011 Free Software Foundation, Inc. -# +# # This file is part of GNU Radio -# +# # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. -# +# # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, diff --git a/grc/python/Param.py b/grc/python/Param.py index 2a7258dc7..2caca4802 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -335,7 +335,7 @@ class Param(_Param, _GUIParam): # Stream ID Type ######################### elif t == 'stream_id': - #get a list of all stream ids used in the virtual sinks + #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), -- cgit From f4da804ea3dde5e376e520769a93f80545670208 Mon Sep 17 00:00:00 2001 From: Jose Quaresma Date: Thu, 3 May 2012 12:50:39 -0400 Subject: GRC: patch to allow users to specify max_noutput_items in GRC options block. --- grc/python/flow_graph.tmpl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'grc/python') diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 0878be4ba..17feb01f6 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -244,12 +244,20 @@ if __name__ == '__main__': #end if #if $generate_options == 'wx_gui' tb = $(class_name)($(', '.join($params_eq_list))) + #if $flow_graph.get_option('max_nouts') + tb.Run($flow_graph.get_option('run'), $flow_graph.get_option('max_nouts')) + #else tb.Run($flow_graph.get_option('run')) + #end if #elif $generate_options == 'qt_gui' qapp = Qt.QApplication(sys.argv) tb = $(class_name)($(', '.join($params_eq_list))) #if $flow_graph.get_option('run') + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else tb.start() + #end if #end if tb.show() qapp.exec_() @@ -258,11 +266,19 @@ if __name__ == '__main__': tb = $(class_name)($(', '.join($params_eq_list))) #set $run_options = $flow_graph.get_option('run_options') #if $run_options == 'prompt' + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else tb.start() + #end if raw_input('Press Enter to quit: ') tb.stop() #elif $run_options == 'run' + #if $flow_graph.get_option('max_nouts') + tb.run($flow_graph.get_option('max_nouts')) + #else tb.run() + #end if #end if #end if #end if -- cgit From 53dabcddefa202facfd0825fd2171f840a1a47c3 Mon Sep 17 00:00:00 2001 From: Justin R. Cutler Date: Thu, 31 May 2012 17:02:05 -0700 Subject: grc: restore virtual ports in grc --- grc/python/Port.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'grc/python') diff --git a/grc/python/Port.py b/grc/python/Port.py index d0ef51b48..9f8b50d05 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -139,6 +139,10 @@ class Port(_Port, _GUIPort): self._type = '' self._vlen = '' + def resolve_virtual_source(self): + if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) + if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) + def resolve_empty_type(self): if self.is_sink(): try: -- cgit From 515d1b6f91f5dd28997525b1e88006bbfc0f170a Mon Sep 17 00:00:00 2001 From: Tim O'Shea Date: Wed, 26 Sep 2012 14:18:30 -0400 Subject: added the ability to open custom GRC hier block definitions from graphs using an instance of them --- grc/python/block.dtd | 3 ++- grc/python/convert_hier.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 41a744d07..292ea06cb 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Top level element. A block contains a name, ...parameters list, and list of IO ports. --> - + @@ -40,6 +40,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index c6ca5b769..f4d082d59 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -73,6 +73,7 @@ def convert_hier(flow_graph, python_file): block_n['source'].append(source_n) #doc data block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) + block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) #write the block_n to file xml_file = python_file + '.xml' ParseXML.to_file({'block': block_n}, xml_file) -- cgit From 6fb32f05d01c23e1953fe874a33f37bc44758d9e Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 4 Oct 2012 18:49:34 -0700 Subject: grc: added optional flag to pad source and sink --- grc/python/FlowGraph.py | 1 + grc/python/convert_hier.py | 2 ++ grc/python/flow_graph.tmpl | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'grc/python') diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 89a169355..efe362760 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -64,6 +64,7 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): 'type': str(pad.get_param('type').get_evaluated()), 'vlen': str(pad.get_param('vlen').get_evaluated()), 'size': pad.get_param('type').get_opt('size'), + 'optional': bool(pad.get_param('optional').get_evaluated()), } for pad in sorted_pads] def get_pad_sources(self): diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index f4d082d59..b609af24a 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -62,6 +62,7 @@ def convert_hier(flow_graph, python_file): sink_n['name'] = input_sig['label'] sink_n['type'] = input_sig['type'] sink_n['vlen'] = input_sig['vlen'] + if input_sig['optional']: sink_n['optional'] = '1' block_n['sink'].append(sink_n) #source data block_n['source'] = list() @@ -70,6 +71,7 @@ def convert_hier(flow_graph, python_file): source_n['name'] = output_sig['label'] source_n['type'] = output_sig['type'] source_n['vlen'] = output_sig['vlen'] + if output_sig['optional']: source_n['optional'] = '1' block_n['source'].append(source_n) #doc data block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 17feb01f6..57f2a4a45 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -91,9 +91,9 @@ class $(class_name)(gr.hier_block2): #if len($io_sigs) == 0 gr.io_signature(0, 0, 0)#slurp #elif len($io_sigs) == 1 -gr.io_signature(1, 1, $size_strs[0])#slurp +gr.io_signature(0, 1, $size_strs[0])#slurp #else -gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])#slurp +gr.io_signaturev(0, $(len($io_sigs)), [$(', '.join($size_strs))])#slurp #end if #end def -- cgit From 5285039a04436e55c7c90ab01e9dc2d388924312 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 5 Oct 2012 11:37:35 -0700 Subject: grc: revert changes to flow_graph.tmpl, runtime enforces this --- grc/python/flow_graph.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'grc/python') diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 57f2a4a45..17feb01f6 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -91,9 +91,9 @@ class $(class_name)(gr.hier_block2): #if len($io_sigs) == 0 gr.io_signature(0, 0, 0)#slurp #elif len($io_sigs) == 1 -gr.io_signature(0, 1, $size_strs[0])#slurp +gr.io_signature(1, 1, $size_strs[0])#slurp #else -gr.io_signaturev(0, $(len($io_sigs)), [$(', '.join($size_strs))])#slurp +gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])#slurp #end if #end def -- cgit From 92cfb0240005675f4e7a55a81552f4c7a5128cd8 Mon Sep 17 00:00:00 2001 From: Tim O'Shea Date: Wed, 28 Nov 2012 15:15:58 -0800 Subject: core: adding msg_connect, updating msg interface, adding symbolic block names --- grc/python/Connection.py | 3 +++ grc/python/Constants.py | 1 + grc/python/Generator.py | 4 +++- grc/python/flow_graph.tmpl | 11 +++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/Connection.py b/grc/python/Connection.py index 218baf074..341dd2d82 100644 --- a/grc/python/Connection.py +++ b/grc/python/Connection.py @@ -31,6 +31,9 @@ class Connection(_Connection, _GUIConnection): def is_msg(self): return self.get_source().get_type() == self.get_sink().get_type() == 'msg' + def is_message(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'message' + def validate(self): """ Validate the connections. diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 1a65caf1c..09c308196 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -58,6 +58,7 @@ CORE_TYPES = ( #name, key, sizeof, color ('Integer 16', 's16', 2, '#FFFF66'), ('Integer 8', 's8', 1, '#FF66FF'), ('Message Queue', 'msg', 0, '#777777'), + ('Async Message', 'message', 0, '#777777'), ('Wildcard', '', 0, '#FFFFFF'), ) diff --git a/grc/python/Generator.py b/grc/python/Generator.py index 2a6fe51d5..616ea00fc 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -116,8 +116,9 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') #list of regular blocks (all blocks minus the special ones) blocks = filter(lambda b: b not in (imports + parameters), blocks) #list of connections where each endpoint is enabled - connections = filter(lambda c: not c.is_msg(), self._flow_graph.get_enabled_connections()) + connections = filter(lambda c: not (c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) + messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) #list of variable names var_ids = [var.get_id() for var in parameters + variables] #prepend self. @@ -142,6 +143,7 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') 'blocks': blocks, 'connections': connections, 'messages': messages, + 'messages2': messages2, 'generate_options': self._generate_options, 'var_id2cbs': var_id2cbs, } diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index 17feb01f6..af55ad641 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -189,6 +189,17 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) self.connect($make_port_sig($source), $make_port_sig($sink)) #end if #end for +######################################################## +##Create Asynch Message Connections +######################################################## +#if $messages2 + $DIVIDER + # Asynch Message Connections + $DIVIDER +#end if +#for $msg in $messages2 + self.msg_connect(self.$msg.get_source().get_parent().get_id(), "$msg.get_source().get_name()", self.$msg.get_sink().get_parent().get_id(), "$msg.get_sink().get_name()") +#end for ######################################################## ##Create Callbacks -- cgit From 6cc818260128df57c51a41e4e6aa459de5faf4fe Mon Sep 17 00:00:00 2001 From: Tim O'Shea Date: Fri, 30 Nov 2012 22:31:43 -0800 Subject: core: gr_blocks can now have only message ports with no general_work() * msg only blocks now get thread context * added blocking msg queue delete call * added gr_message_strobe block * added grc definitions for message_debug, message_strobe, pdu_to_tagged_stream, tagged_stream_to_pdu. * allow message fan-in connections in GRC --- grc/python/Constants.py | 2 +- grc/python/Port.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'grc/python') diff --git a/grc/python/Constants.py b/grc/python/Constants.py index 09c308196..b8dc9a96a 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -58,7 +58,7 @@ CORE_TYPES = ( #name, key, sizeof, color ('Integer 16', 's16', 2, '#FFFF66'), ('Integer 8', 's8', 1, '#FF66FF'), ('Message Queue', 'msg', 0, '#777777'), - ('Async Message', 'message', 0, '#777777'), + ('Async Message', 'message', 0, '#C0C0C0'), ('Wildcard', '', 0, '#FFFFFF'), ) diff --git a/grc/python/Port.py b/grc/python/Port.py index 9f8b50d05..738a33ba7 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -116,7 +116,7 @@ class Port(_Port, _GUIPort): _Port.validate(self) if not self.get_enabled_connections() and not self.get_optional(): self.add_error_message('Port is not connected.') - if not self.is_source() and len(self.get_enabled_connections()) > 1: + if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: self.add_error_message('Port has too many connections.') #message port logic if self.get_type() == 'msg': -- cgit From 52ca5e2765b7a4532d26502b5b76b7c85c5019d7 Mon Sep 17 00:00:00 2001 From: Tim O'Shea Date: Fri, 7 Dec 2012 09:28:41 -0800 Subject: core: added gr_tuntap_pdu, gr_socket_pdu, and msg passing enhancements --- grc/python/FlowGraph.py | 10 ++++++++++ grc/python/convert_hier.py | 20 ++++++++++++++++++-- grc/python/flow_graph.tmpl | 17 ++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) (limited to 'grc/python') diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index efe362760..376c2e337 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -58,6 +58,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): 'in': self.get_pad_sources(), 'out': self.get_pad_sinks(), }[direction] + # we only want stream ports + sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); #load io signature return [{ 'label': str(pad.get_param('label').get_evaluated()), @@ -83,6 +85,14 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + def get_msg_pad_sources(self): + ps = self.get_pad_sources(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + + def get_msg_pad_sinks(self): + ps = self.get_pad_sinks(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + def get_imports(self): """ Get a set of all import statments in this flow graph namespace. diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index b609af24a..508ec63b2 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -25,6 +25,8 @@ def convert_hier(flow_graph, python_file): #extract info from the flow graph input_sigs = flow_graph.get_io_signaturev('in') output_sigs = flow_graph.get_io_signaturev('out') + input_msgp = flow_graph.get_msg_pad_sources(); + output_msgp = flow_graph.get_msg_pad_sinks(); parameters = flow_graph.get_parameters() block_key = flow_graph.get_option('id') block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() @@ -55,7 +57,7 @@ def convert_hier(flow_graph, python_file): param_n['type'] = 'raw' params_n.append(param_n) block_n['param'] = params_n - #sink data + #sink data stream ports block_n['sink'] = list() for input_sig in input_sigs: sink_n = odict() @@ -64,7 +66,14 @@ def convert_hier(flow_graph, python_file): sink_n['vlen'] = input_sig['vlen'] if input_sig['optional']: sink_n['optional'] = '1' block_n['sink'].append(sink_n) - #source data + #sink data msg ports + for input_sig in input_msgp: + sink_n = odict() + sink_n['name'] = input_sig.get_param("label").get_value(); + sink_n['type'] = "message" + sink_n['optional'] = input_sig.get_param("optional").get_value(); + block_n['sink'].append(sink_n) + #source data stream ports block_n['source'] = list() for output_sig in output_sigs: source_n = odict() @@ -73,6 +82,13 @@ def convert_hier(flow_graph, python_file): source_n['vlen'] = output_sig['vlen'] if output_sig['optional']: source_n['optional'] = '1' block_n['source'].append(source_n) + #source data msg ports + for output_sig in output_msgp: + source_n = odict() + source_n['name'] = output_sig.get_param("label").get_value(); + source_n['type'] = "message" + source_n['optional'] = output_sig.get_param("optional").get_value(); + block_n['source'].append(source_n) #doc data block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index af55ad641..163e7f76a 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -189,6 +189,7 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) self.connect($make_port_sig($source), $make_port_sig($sink)) #end if #end for + ######################################################## ##Create Asynch Message Connections ######################################################## @@ -198,7 +199,21 @@ gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))]) $DIVIDER #end if #for $msg in $messages2 - self.msg_connect(self.$msg.get_source().get_parent().get_id(), "$msg.get_source().get_name()", self.$msg.get_sink().get_parent().get_id(), "$msg.get_sink().get_name()") + #set $sr = $msg.get_source() + #set $source = "self.%s"%($sr.get_parent().get_id()) + #set $source_port = $sr.get_name(); + #if $sr.get_parent().get_key() == "pad_source" + #set $source = "self" + #set $source_port = $sr.get_parent().get_param("label").get_value(); + #end if + #set $sk = $msg.get_sink() + #set $sink = "self.%s"%($sk.get_parent().get_id()) + #set $sink_port = $sk.get_name(); + #if $sk.get_parent().get_key() == "pad_sink" + #set $sink = "self" + #set $sink_port = $sk.get_parent().get_param("label").get_value(); + #end if + self.msg_connect($source, "$source_port", $sink, "$sink_port") #end for ######################################################## -- cgit From e826097e09fdfb04d14bf87861646b88229db881 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sun, 13 Jan 2013 13:51:46 -0800 Subject: gras: support changeset for 3.6.4 used volk from next branch cf5c930d89ac89ba5a0da4a616c88d3c37e018ae for grextras support (it uses the dispatcher) empty stubs for the gr_basic_block msg passing. This is going to be difficult to figure out. The alias stuff may or may not be related most qa pass, there seems to be some additional issues, will be working through them on futher commits Conflicts: gnuradio-core/CMakeLists.txt gnuradio-core/src/lib/runtime/CMakeLists.txt gnuradio-core/src/lib/runtime/gr_block.cc gnuradio-core/src/lib/runtime/gr_block.h gnuradio-core/src/lib/runtime/gr_hier_block2.h gnuradio-core/src/lib/runtime/gr_top_block.h gnuradio-core/src/python/gnuradio/gr/__init__.py gr-audio/examples/c++/CMakeLists.txt gr-fcd/examples/c++/CMakeLists.txt grc/python/Port.py --- grc/python/Port.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/Port.py b/grc/python/Port.py index 738a33ba7..4c0175f90 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -116,7 +116,8 @@ class Port(_Port, _GUIPort): _Port.validate(self) if not self.get_enabled_connections() and not self.get_optional(): self.add_error_message('Port is not connected.') - if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: + is_msg = (not self.get_type()) or (self.get_type() == "message") + if not self.is_source() and (not is_msg) and len(self.get_enabled_connections()) > 1: self.add_error_message('Port has too many connections.') #message port logic if self.get_type() == 'msg': -- cgit From d2e8ec96084a26127907fe1cee2f15871d67b60d Mon Sep 17 00:00:00 2001 From: Julien Olivain Date: Mon, 18 Feb 2013 16:01:46 -0500 Subject: GRC: adds ability for grc to pull in documentation for blocks from other GR modules. --- grc/python/extract_docs.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'grc/python') diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py index a7e945c37..33c404362 100644 --- a/grc/python/extract_docs.py +++ b/grc/python/extract_docs.py @@ -31,7 +31,13 @@ def _extract(key): module_name, constructor_name = key.split('_', 1) module = __import__('gnuradio.'+module_name) module = getattr(module, module_name) - except: return '' + except ImportError: + try: + module_name, constructor_name = key.split('_', 1) + module = __import__(module_name) + except: return '' + except: + return '' pattern = constructor_name.replace('_', '_*').replace('x', '\w') pattern_matcher = re.compile('^%s\w*$'%pattern) matches = filter(lambda x: pattern_matcher.match(x), dir(module)) -- cgit From ce52bd370c0b13ed4de11df08168768dec327497 Mon Sep 17 00:00:00 2001 From: Tom Rondeau Date: Tue, 26 Mar 2013 13:27:29 -0400 Subject: grc: fixes problem with order of defining message ports vs. data ports. --- grc/python/Port.py | 1 + 1 file changed, 1 insertion(+) (limited to 'grc/python') diff --git a/grc/python/Port.py b/grc/python/Port.py index 738a33ba7..9e9f5676e 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -90,6 +90,7 @@ class Port(_Port, _GUIPort): """ self._n = n if n['type'] == 'msg': n['key'] = 'msg' + if n['type'] == 'message': n['key'] = n['name'] if dir == 'source' and not n.find('key'): n['key'] = str(block._source_count) block._source_count += 1 -- cgit