diff options
Diffstat (limited to 'grc')
40 files changed, 477 insertions, 348 deletions
diff --git a/grc/Makefile.inc b/grc/Makefile.inc index 96ee11b67..c45d1ce1f 100644 --- a/grc/Makefile.inc +++ b/grc/Makefile.inc @@ -1,5 +1,5 @@ # -# Copyright 2008 Free Software Foundation, Inc. +# Copyright 2008, 2009 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -20,5 +20,5 @@ # include $(top_srcdir)/Makefile.common -grc_src_prefix = $(pythondir)/gnuradio/grc +grc_src_prefix = $(pkgpythondir)/grc grc_blocksdir = $(pkgdatadir)/grc/blocks diff --git a/grc/base/Block.py b/grc/base/Block.py index d5e104785..fc501205f 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -47,7 +47,9 @@ class TemplateArg(UserDict): return self._param.get_evaluated() def _get_keys(lst): return [elem.get_key() for elem in lst] -def _get_elem(lst, key): return lst[_get_keys(lst).index(key)] +def _get_elem(lst, key): + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Block(Element): @@ -72,16 +74,16 @@ class Block(Element): self._params = list() #add the id param self.get_params().append(self.get_parent().get_parent().Param( - self, - odict({ + block=self, + n=odict({ 'name': 'ID', 'key': 'id', 'type': 'id', }) )) self.get_params().append(self.get_parent().get_parent().Param( - self, - odict({ + block=self, + n=odict({ 'name': 'Enabled', 'key': '_enabled', 'type': 'raw', @@ -89,7 +91,7 @@ class Block(Element): 'hide': 'all', }) )) - for param in map(lambda n: self.get_parent().get_parent().Param(self, n), params): + for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): key = param.get_key() #test against repeated keys try: assert key not in self.get_param_keys() @@ -98,7 +100,7 @@ class Block(Element): self.get_params().append(param) #create the source objects self._sources = list() - for source in map(lambda n: self.get_parent().get_parent().Source(self, n), sources): + for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): key = source.get_key() #test against repeated keys try: assert key not in self.get_source_keys() @@ -107,21 +109,13 @@ class Block(Element): self.get_sources().append(source) #create the sink objects self._sinks = list() - for sink in map(lambda n: self.get_parent().get_parent().Sink(self, n), sinks): + for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): key = sink.get_key() #test against repeated keys try: assert key not in self.get_sink_keys() except AssertionError: raise Exception, 'Key "%s" already exists in sinks'%key #store the port self.get_sinks().append(sink) - #begin the testing - self.test() - - def test(self): - """ - Call test on all children. - """ - map(lambda c: c.test(), self.get_params() + self.get_sinks() + self.get_sources()) def get_enabled(self): """ @@ -138,18 +132,25 @@ class Block(Element): """ self.get_param('_enabled').set_value(str(enabled)) + def rewrite(self): + """ + Rewrite critical structures. + Call rewrite on all sub elements. + """ + Element.rewrite(self) + for elem in self.get_ports() + self.get_params(): elem.rewrite() + def validate(self): """ Validate the block. All ports and params must be valid. All checks must evaluate to true. + Validate the params, ports, and the connections to this block. """ Element.validate(self) for c in self.get_params() + self.get_ports() + self.get_connections(): - try: - c.validate() - assert c.is_valid() - except AssertionError: + c.validate() + if not c.is_valid(): for msg in c.get_error_messages(): self.add_error_message('>>> %s:\n\t%s'%(c, msg)) diff --git a/grc/base/Element.py b/grc/base/Element.py index 16000c46c..43cee886c 100644 --- a/grc/base/Element.py +++ b/grc/base/Element.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 @@ -21,14 +21,6 @@ class Element(object): def __init__(self, parent=None): self._parent = parent - self.flag() - - def test(self): - """ - Test the element against failures. - Overload this method in sub-classes. - """ - pass ################################################## # Element Validation API @@ -38,22 +30,13 @@ class Element(object): def add_error_message(self, msg): self._error_messages.append(msg) def get_error_messages(self): return self._error_messages + def rewrite(self): pass + def get_enabled(self): return True def get_parent(self): return self._parent ############################################## - ## Update flagging - ############################################## - def is_flagged(self): return self._flag - def flag(self): - self._flag = True - if self.get_parent(): self.get_parent().flag() - def deflag(self): - self._flag = False - if self.get_parent(): self.get_parent().deflag() - - ############################################## ## Type testing methods ############################################## def is_element(self): return True diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index ea489e948..b24f13b09 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -102,7 +102,6 @@ class FlowGraph(Element): @param key the block key @return the new block or None if not found """ - self.flag() if key not in self.get_parent().get_block_keys(): return None block = self.get_parent().get_new_block(self, key) self.get_elements().append(block) @@ -116,8 +115,7 @@ class FlowGraph(Element): @throw Exception bad connection @return the new connection """ - self.flag() - connection = self.get_parent().Connection(self, porta, portb) + connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) self.get_elements().append(connection) return connection @@ -128,7 +126,6 @@ class FlowGraph(Element): If the element is a block, remove its connections. If the element is a connection, just remove the connection. """ - self.flag() if element not in self.get_elements(): return #found a port, set to parent signal block if element.is_port(): @@ -147,17 +144,25 @@ class FlowGraph(Element): """ raise NotImplementedError + def rewrite(self): + """ + Rewrite critical structures. + Call rewrite on all sub elements. + """ + Element.rewrite(self) + for elem in self.get_elements(): elem.rewrite() + def validate(self): """ Validate the flow graph. - All connections and blocks must be valid. + Validate only the blocks. + Connections will be validated within the blocks. """ Element.validate(self) - for c in self.get_elements(): - try: - c.validate() - assert c.is_valid() - except AssertionError: self.add_error_message('Element "%s" is not valid.'%c) + for c in self.get_blocks(): + c.validate() + if not c.is_valid(): + self.add_error_message('Element "%s" is not valid.'%c) ############################################## ## Import/Export Methods @@ -198,7 +203,7 @@ class FlowGraph(Element): #only load the block when the block key was valid if block: block.import_data(block_n) else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent())) - self.validate() #validate all blocks before connections are made (in case of nports) + self.rewrite() #rewrite all blocks before connections are made (ex: nports) #build the connections for connection_n in connections_n: #try to make the connection @@ -225,3 +230,4 @@ class FlowGraph(Element): #build the connection self.connect(source, sink) except AssertionError: Messages.send_error_load('Connection between %s(%s) and %s(%s) could not be made.'%(source_block_id, source_key, sink_block_id, sink_key)) + self.rewrite() #global rewrite diff --git a/grc/base/Param.py b/grc/base/Param.py index 93c1c52bd..e56eac36e 100644 --- a/grc/base/Param.py +++ b/grc/base/Param.py @@ -19,74 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from . import odict from Element import Element -import pygtk -pygtk.require('2.0') -import gtk -class InputParam(gtk.HBox): - """The base class for an input parameter inside the input parameters dialog.""" - - def __init__(self, param, _handle_changed): - gtk.HBox.__init__(self) - self.param = param - self._handle_changed = _handle_changed - self.label = gtk.Label('') #no label, markup is added by set_markup - self.label.set_size_request(150, -1) - self.pack_start(self.label, False) - self.set_markup = lambda m: self.label.set_markup(m) - self.tp = None - def set_color(self, color): pass - -class EntryParam(InputParam): - """Provide an entry box for strings and numbers.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self.entry = input = gtk.Entry() - input.set_text(self.param.get_value()) - input.connect('changed', self._handle_changed) - self.pack_start(input, True) - self.get_text = input.get_text - #tool tip - self.tp = gtk.Tooltips() - self.tp.set_tip(self.entry, '') - self.tp.enable() - def set_color(self, color): self.entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - -class EnumParam(InputParam): - """Provide an entry box for Enum types with a drop down menu.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - self._input.connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): return self.param.get_option_keys()[self._input.get_active()] - -class EnumEntryParam(InputParam): - """Provide an entry box and drop down menu for Raw Enum types.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_entry_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - except: - self._input.set_active(-1) - self._input.get_child().set_text(self.param.get_value()) - self._input.connect('changed', self._handle_changed) - self._input.get_child().connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): - if self._input.get_active() == -1: return self._input.get_child().get_text() - return self.param.get_option_keys()[self._input.get_active()] - def set_color(self, color): - if self._input.get_active() == -1: #custom entry, use color - self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - else: #from enum, make white background - self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse('#ffffff')) +def _get_keys(lst): return [elem.get_key() for elem in lst] +def _get_elem(lst, key): + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Option(Element): @@ -123,15 +60,11 @@ class Option(Element): class Param(Element): - ##possible param types - TYPES = ['enum', 'raw'] - def __init__(self, block, n): """ Make a new param from nested data. @param block the parent element @param n the nested odict - @return a new param """ #grab the data self._name = n.find('name') @@ -142,22 +75,22 @@ class Param(Element): #build the param Element.__init__(self, block) #create the Option objects from the n data - self._options = odict() - for option in map(lambda o: Option(self, o), n.findall('option')): + self._options = list() + for option in map(lambda o: Option(param=self, n=o), n.findall('option')): key = option.get_key() #test against repeated keys try: assert key not in self.get_option_keys() except AssertionError: raise Exception, 'Key "%s" already exists in options'%key #store the option - self._options[key] = option + self.get_options().append(option) #test the enum options if self.is_enum(): #test against options with identical keys - try: assert len(set(self.get_option_keys())) == len(self._options) + try: assert len(set(self.get_option_keys())) == len(self.get_options()) except AssertionError: raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() #test against inconsistent keys in options - opt_keys = self._options.values()[0].get_opt_keys() - for option in self._options.values(): + opt_keys = self.get_options()[0].get_opt_keys() + for option in self.get_options(): try: assert set(opt_keys) == set(option.get_opt_keys()) except AssertionError: raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys #if a value is specified, it must be in the options keys @@ -165,14 +98,6 @@ class Param(Element): try: assert self.get_value() in self.get_option_keys() except AssertionError: raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) else: self._value = value or '' - #begin the testing - self.test() - - def test(self): - """ - call test on all children - """ - map(lambda c: c.test(), self.get_options()) def validate(self): """ @@ -180,7 +105,7 @@ class Param(Element): The value must be evaluated and type must a possible type. """ Element.validate(self) - try: assert self.get_type() in self.TYPES + try: assert self.get_type() in self.get_types() except AssertionError: self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) def get_evaluated(self): raise NotImplementedError @@ -192,12 +117,19 @@ class Param(Element): """ raise NotImplementedError + def get_types(self): + """ + Get a list of all possible param types. + @throw NotImplementedError + """ + raise NotImplementedError + def get_color(self): return '#FFFFFF' def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) def is_param(self): return True def get_name(self): return self._name def get_key(self): return self._key - def get_hide(self): return self.get_parent().resolve_dependencies(self._hide) + def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() def get_value(self): value = self._value @@ -206,9 +138,7 @@ class Param(Element): self.set_value(value) return value - def set_value(self, value): - self.flag() - self._value = str(value) #must be a string + def set_value(self, value): self._value = str(value) #must be a string def get_type(self): return self.get_parent().resolve_dependencies(self._type) def is_enum(self): return self._type == 'enum' @@ -223,31 +153,19 @@ class Param(Element): if self.is_enum(): return self.get_option(self.get_value()).get_name() return self.get_value() - def get_input_class(self): - """ - Get the graphical gtk class to represent this parameter. - An enum requires and combo parameter. - A non-enum with options gets a combined entry/combo parameter. - All others get a standard entry parameter. - @return gtk input class - """ - if self.is_enum(): return EnumParam - if self.get_options(): return EnumEntryParam - return EntryParam - ############################################## # Access Options ############################################## - def get_option_keys(self): return self._options.keys() - def get_option(self, key): return self._options[key] - def get_options(self): return self._options.values() + def get_option_keys(self): return _get_keys(self.get_options()) + def get_option(self, key): return _get_elem(self.get_options(), key) + def get_options(self): return self._options ############################################## # Access Opts ############################################## - def get_opt_keys(self): return self._options[self.get_value()].get_opt_keys() - def get_opt(self, key): return self._options[self.get_value()].get_opt(key) - def get_opts(self): return self._options[self.get_value()].get_opts() + def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() + def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) + def get_opts(self): return self.get_option(self.get_value()).get_opts() ############################################## ## Import/Export Methods diff --git a/grc/base/Platform.py b/grc/base/Platform.py index 02d6d2319..51a3b2f87 100644 --- a/grc/base/Platform.py +++ b/grc/base/Platform.py @@ -146,7 +146,7 @@ class Platform(_Element): def is_platform(self): return True - def get_new_flow_graph(self): return self.FlowGraph(self) + def get_new_flow_graph(self): return self.FlowGraph(platform=self) def get_generator(self): return self._generator @@ -171,6 +171,5 @@ class Platform(_Element): FlowGraph = _FlowGraph Connection = _Connection Block = _Block - Source = _Port - Sink = _Port + Port = _Port Param = _Param diff --git a/grc/base/Port.py b/grc/base/Port.py index f4e8e5e1f..494ea894f 100644 --- a/grc/base/Port.py +++ b/grc/base/Port.py @@ -21,25 +21,20 @@ from Element import Element class Port(Element): - ##possible port types - TYPES = [] - - def __init__(self, block, n): + def __init__(self, block, n, dir): """ Make a new port from nested data. @param block the parent element @param n the nested odict - @return a new port + @param dir the direction source or sink """ - #grab the data - name = n['name'] - key = n['key'] - type = n['type'] #build the port Element.__init__(self, block) - self._name = name - self._key = key - self._type = type + #grab the data + self._name = n['name'] + self._key = n['key'] + self._type = n['type'] + self._dir = dir def validate(self): """ @@ -47,7 +42,7 @@ class Port(Element): The port must be non-empty and type must a possible type. """ Element.validate(self) - try: assert self.get_type() in self.TYPES + try: assert self.get_type() in self.get_types() except AssertionError: self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) def __str__(self): @@ -56,12 +51,19 @@ class Port(Element): if self.is_sink(): return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) + def get_types(self): + """ + Get a list of all possible port types. + @throw NotImplementedError + """ + raise NotImplementedError + def is_port(self): return True def get_color(self): return '#FFFFFF' def get_name(self): return self._name def get_key(self): return self._key - def is_sink(self): return self in self.get_parent().get_sinks() - def is_source(self): return self in self.get_parent().get_sources() + def is_sink(self): return self._dir == 'sink' + def is_source(self): return self._dir == 'source' def get_type(self): return self.get_parent().resolve_dependencies(self._type) def get_connections(self): diff --git a/grc/blocks/Makefile.am b/grc/blocks/Makefile.am index caae6ce75..32ddc6567 100644 --- a/grc/blocks/Makefile.am +++ b/grc/blocks/Makefile.am @@ -218,6 +218,8 @@ dist_ourdata_DATA = \ variable_slider.xml \ variable_static_text.xml \ variable_text_box.xml \ + virtual_sink.xml \ + virtual_source.xml \ wxgui_constellationsink2.xml \ wxgui_fftsink2.xml \ wxgui_histosink2.xml \ diff --git a/grc/blocks/band_pass_filter.xml b/grc/blocks/band_pass_filter.xml index e2e9acf4e..af083473d 100644 --- a/grc/blocks/band_pass_filter.xml +++ b/grc/blocks/band_pass_filter.xml @@ -10,8 +10,8 @@ <import>from gnuradio import gr</import> <import>from gnuradio.gr import firdes</import> <make>gr.$(type)(#if str($type).startswith('interp') then $interp else $decim#, firdes.$(type.fcn)( - $gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, firdes.$window, $beta))</make> - <callback>set_taps(firdes.$(type.fcn)($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, firdes.$window, $beta))</callback> + $gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta))</make> + <callback>set_taps(firdes.$(type.fcn)($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta))</callback> <param> <name>FIR Type</name> <key>type</key> @@ -118,27 +118,28 @@ </param> <param> <name>Window</name> - <key>window</key> - <type>enum</type> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> <option> <name>Hamming</name> - <key>WIN_HAMMING</key> + <key>firdes.WIN_HAMMING</key> </option> <option> <name>Hann</name> - <key>WIN_HANN</key> + <key>firdes.WIN_HANN</key> </option> <option> <name>Blackman</name> - <key>WIN_BLACKMAN</key> + <key>firdes.WIN_BLACKMAN</key> </option> <option> <name>Rectangular</name> - <key>WIN_RECTANGULAR</key> + <key>firdes.WIN_RECTANGULAR</key> </option> <option> <name>Kaiser</name> - <key>WIN_KAISER</key> + <key>firdes.WIN_KAISER</key> </option> </param> <param> diff --git a/grc/blocks/band_reject_filter.xml b/grc/blocks/band_reject_filter.xml index 3b58f0b51..dd5e7a9d7 100644 --- a/grc/blocks/band_reject_filter.xml +++ b/grc/blocks/band_reject_filter.xml @@ -10,8 +10,8 @@ <import>from gnuradio import gr</import> <import>from gnuradio.gr import firdes</import> <make>gr.$(type)(#if str($type).startswith('interp') then $interp else $decim#, firdes.band_reject( - $gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, firdes.$window, $beta))</make> - <callback>set_taps(firdes.band_reject($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, firdes.$window, $beta))</callback> + $gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta))</make> + <callback>set_taps(firdes.band_reject($gain, $samp_rate, $low_cutoff_freq, $high_cutoff_freq, $width, $win, $beta))</callback> <param> <name>FIR Type</name> <key>type</key> @@ -84,27 +84,28 @@ </param> <param> <name>Window</name> - <key>window</key> - <type>enum</type> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> <option> <name>Hamming</name> - <key>WIN_HAMMING</key> + <key>firdes.WIN_HAMMING</key> </option> <option> <name>Hann</name> - <key>WIN_HANN</key> + <key>firdes.WIN_HANN</key> </option> <option> <name>Blackman</name> - <key>WIN_BLACKMAN</key> + <key>firdes.WIN_BLACKMAN</key> </option> <option> <name>Rectangular</name> - <key>WIN_RECTANGULAR</key> + <key>firdes.WIN_RECTANGULAR</key> </option> <option> <name>Kaiser</name> - <key>WIN_KAISER</key> + <key>firdes.WIN_KAISER</key> </option> </param> <param> diff --git a/grc/blocks/block_tree.xml b/grc/blocks/block_tree.xml index 5b45466f5..5a989cc01 100644 --- a/grc/blocks/block_tree.xml +++ b/grc/blocks/block_tree.xml @@ -22,6 +22,7 @@ <block>gr_wavfile_source</block> <block>gr_message_source</block> <block>pad_source</block> + <block>virtual_source</block> </cat> <cat> <name>Sinks</name> @@ -35,6 +36,7 @@ <block>gr_wavfile_sink</block> <block>gr_message_sink</block> <block>pad_sink</block> + <block>virtual_sink</block> </cat> <cat> <name>Graphical Sinks</name> diff --git a/grc/blocks/gr_noise_source_x.xml b/grc/blocks/gr_noise_source_x.xml index 4fcef5148..4789b4400 100644 --- a/grc/blocks/gr_noise_source_x.xml +++ b/grc/blocks/gr_noise_source_x.xml @@ -40,7 +40,7 @@ <name>Noise Type</name> <key>noise_type</key> <value>gr.GR_GAUSSIAN</value> - <type>raw</type> + <type>int</type> <option> <name>Uniform</name> <key>gr.GR_UNIFORM</key> diff --git a/grc/blocks/gr_sig_source_x.xml b/grc/blocks/gr_sig_source_x.xml index c329dba67..644cf52d0 100644 --- a/grc/blocks/gr_sig_source_x.xml +++ b/grc/blocks/gr_sig_source_x.xml @@ -53,7 +53,7 @@ <name>Waveform</name> <key>waveform</key> <value>gr.GR_COS_WAVE</value> - <type>raw</type> + <type>int</type> <option> <name>Constant</name> <key>gr.GR_CONST_WAVE</key> diff --git a/grc/blocks/high_pass_filter.xml b/grc/blocks/high_pass_filter.xml index 5be916fa9..0e29cbb36 100644 --- a/grc/blocks/high_pass_filter.xml +++ b/grc/blocks/high_pass_filter.xml @@ -10,8 +10,8 @@ <import>from gnuradio import gr</import> <import>from gnuradio.gr import firdes</import> <make>gr.$(type)(#if str($type).startswith('interp') then $interp else $decim#, firdes.high_pass( - $gain, $samp_rate, $cutoff_freq, $width, firdes.$window, $beta))</make> - <callback>set_taps(firdes.high_pass($gain, $samp_rate, $cutoff_freq, $width, firdes.$window, $beta))</callback> + $gain, $samp_rate, $cutoff_freq, $width, $win, $beta))</make> + <callback>set_taps(firdes.high_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta))</callback> <param> <name>FIR Type</name> <key>type</key> @@ -79,27 +79,28 @@ </param> <param> <name>Window</name> - <key>window</key> - <type>enum</type> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> <option> <name>Hamming</name> - <key>WIN_HAMMING</key> + <key>firdes.WIN_HAMMING</key> </option> <option> <name>Hann</name> - <key>WIN_HANN</key> + <key>firdes.WIN_HANN</key> </option> <option> <name>Blackman</name> - <key>WIN_BLACKMAN</key> + <key>firdes.WIN_BLACKMAN</key> </option> <option> <name>Rectangular</name> - <key>WIN_RECTANGULAR</key> + <key>firdes.WIN_RECTANGULAR</key> </option> <option> <name>Kaiser</name> - <key>WIN_KAISER</key> + <key>firdes.WIN_KAISER</key> </option> </param> <param> diff --git a/grc/blocks/low_pass_filter.xml b/grc/blocks/low_pass_filter.xml index 27120c047..26435fd4d 100644 --- a/grc/blocks/low_pass_filter.xml +++ b/grc/blocks/low_pass_filter.xml @@ -10,8 +10,8 @@ <import>from gnuradio import gr</import> <import>from gnuradio.gr import firdes</import> <make>gr.$(type)(#if str($type).startswith('interp') then $interp else $decim#, firdes.low_pass( - $gain, $samp_rate, $cutoff_freq, $width, firdes.$window, $beta))</make> - <callback>set_taps(firdes.low_pass($gain, $samp_rate, $cutoff_freq, $width, firdes.$window, $beta))</callback> + $gain, $samp_rate, $cutoff_freq, $width, $win, $beta))</make> + <callback>set_taps(firdes.low_pass($gain, $samp_rate, $cutoff_freq, $width, $win, $beta))</callback> <param> <name>FIR Type</name> <key>type</key> @@ -79,27 +79,28 @@ </param> <param> <name>Window</name> - <key>window</key> - <type>enum</type> + <key>win</key> + <value>firdes.WIN_HAMMING</value> + <type>int</type> <option> <name>Hamming</name> - <key>WIN_HAMMING</key> + <key>firdes.WIN_HAMMING</key> </option> <option> <name>Hann</name> - <key>WIN_HANN</key> + <key>firdes.WIN_HANN</key> </option> <option> <name>Blackman</name> - <key>WIN_BLACKMAN</key> + <key>firdes.WIN_BLACKMAN</key> </option> <option> <name>Rectangular</name> - <key>WIN_RECTANGULAR</key> + <key>firdes.WIN_RECTANGULAR</key> </option> <option> <name>Kaiser</name> - <key>WIN_KAISER</key> + <key>firdes.WIN_KAISER</key> </option> </param> <param> diff --git a/grc/blocks/options.xml b/grc/blocks/options.xml index 18d6e2f0c..1798a69f8 100644 --- a/grc/blocks/options.xml +++ b/grc/blocks/options.xml @@ -9,16 +9,17 @@ <block> <name>Options</name> <key>options</key> - <import>from gnuradio import gr -#if $generate_options() == 'wx_gui' + <import>from gnuradio import gr</import> + <import>from gnuradio.gr import firdes</import> + <import>#if $generate_options() == 'wx_gui' from grc_gnuradio import wxgui as grc_wxgui import wx #end if #if $generate_options() != 'hb' from optparse import OptionParser from gnuradio.eng_option import eng_option -#end if -</import> +from gnuradio import eng_notation +#end if</import> <make></make> <callback>if $run: self.start() else: self.stop(); self.wait()</callback> diff --git a/grc/blocks/pad_sink.xml b/grc/blocks/pad_sink.xml index 477f2ad13..734526793 100644 --- a/grc/blocks/pad_sink.xml +++ b/grc/blocks/pad_sink.xml @@ -59,10 +59,7 @@ <nports>$nports</nports> </sink> <doc> -This is a sink pad block for creating hierarchical flow graphs. \ The inputs of this block will become the outputs to this flow graph when it is instantiated as a hierarchical block. \ Limit one sink pad block per flow graph. - -Remember to set the generate options to hier block. </doc> </block> diff --git a/grc/blocks/pad_source.xml b/grc/blocks/pad_source.xml index b6ef2c55d..f44d96238 100644 --- a/grc/blocks/pad_source.xml +++ b/grc/blocks/pad_source.xml @@ -59,10 +59,8 @@ <nports>$nports</nports> </source> <doc> -This is a source pad block for creating hierarchical flow graphs. \ The outputs of this block will become the inputs to this flow graph when it is instantiated as a hierarchical block. \ -Limit one source pad block per flow graph. - -Remember to set the generate options to hier block. +Limit one source pad block per flow graph. \ +The "pad sink id" will be ignored in this mode. </doc> </block> diff --git a/grc/blocks/parameter.xml b/grc/blocks/parameter.xml index 5d08c4b39..e35b8f4d1 100644 --- a/grc/blocks/parameter.xml +++ b/grc/blocks/parameter.xml @@ -45,7 +45,7 @@ </option> <option> <name>Int</name> - <key>int</key> + <key>intx</key> <opt>type:int</opt> </option> <option> @@ -58,6 +58,13 @@ <key>string</key> <opt>type:string</opt> </option> + <!-- not supported yet in tmpl + <option> + <name>Boolean</name> + <key>bool</key> + <opt>type:bool</opt> + </option> + --> </param> <param> <name>Short ID</name> diff --git a/grc/blocks/virtual_sink.xml b/grc/blocks/virtual_sink.xml new file mode 100644 index 000000000..35fb27e67 --- /dev/null +++ b/grc/blocks/virtual_sink.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Virtual Sink +################################################### + --> +<block> + <name>Virtual Sink</name> + <key>virtual_sink</key> + <make></make> + <param> + <name>Stream ID</name> + <key>stream_id</key> + <value></value> + <type>stream_id</type> + </param> + <sink> + <name>in</name> + <type></type> + </sink> +</block> diff --git a/grc/blocks/virtual_source.xml b/grc/blocks/virtual_source.xml new file mode 100644 index 000000000..e0c775449 --- /dev/null +++ b/grc/blocks/virtual_source.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Virtual Source +################################################### + --> +<block> + <name>Virtual Source</name> + <key>virtual_source</key> + <make></make> + <param> + <name>Stream ID</name> + <key>stream_id</key> + <value></value> + <type>stream_id</type> + </param> + <source> + <name>out</name> + <type></type> + </source> +</block> diff --git a/grc/blocks/wxgui_fftsink2.xml b/grc/blocks/wxgui_fftsink2.xml index faeca37e3..6f19f1aa4 100644 --- a/grc/blocks/wxgui_fftsink2.xml +++ b/grc/blocks/wxgui_fftsink2.xml @@ -15,6 +15,7 @@ fftsink2.$(type.fcn)( y_per_div=$y_per_div, y_divs=$y_divs, ref_level=$ref_level, + ref_scale=$ref_scale, sample_rate=$samp_rate, fft_size=$fft_size, fft_rate=$fft_rate, @@ -103,6 +104,12 @@ $(parent).GridAdd(self.$(id).win, $(', '.join(map(str, $grid_pos())))) <type>real</type> </param> <param> + <name>Ref Scale (p2p)</name> + <key>ref_scale</key> + <value>2.0</value> + <type>real</type> + </param> + <param> <name>FFT Size</name> <key>fft_size</key> <value>1024</value> diff --git a/grc/blocks/wxgui_waterfallsink2.xml b/grc/blocks/wxgui_waterfallsink2.xml index 79ca356f7..35790f820 100644 --- a/grc/blocks/wxgui_waterfallsink2.xml +++ b/grc/blocks/wxgui_waterfallsink2.xml @@ -14,6 +14,7 @@ waterfallsink2.$(type.fcn)( baseband_freq=$baseband_freq, dynamic_range=$dynamic_range, ref_level=$ref_level, + ref_scale=$ref_scale, sample_rate=$samp_rate, fft_size=$fft_size, fft_rate=$fft_rate, @@ -75,6 +76,12 @@ $(parent).GridAdd(self.$(id).win, $(', '.join(map(str, $grid_pos())))) <type>real</type> </param> <param> + <name>Ref Scale (p2p)</name> + <key>ref_scale</key> + <value>2.0</value> + <type>real</type> + </param> + <param> <name>FFT Size</name> <key>fft_size</key> <value>512</value> diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index ff137f669..8f317d6a8 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -30,7 +30,6 @@ from threading import Thread import Messages from .. base import ParseXML import random -from Platform import Platform from MainWindow import MainWindow from ParamsDialog import ParamsDialog import Dialogs @@ -53,7 +52,6 @@ class ActionHandler: @param platform platform module """ self.clipboard = None - platform = Platform(platform) for action in Actions.get_all_actions(): action.connect('activate', self._handle_actions) #setup the main window self.main_window = MainWindow(self.handle_states, platform) @@ -133,7 +131,7 @@ class ActionHandler: Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS, Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, - Actions.COLORS_WINDOW_DISPLAY, + Actions.TYPES_WINDOW_DISPLAY, ): Actions.get_action_from_name(action).set_sensitive(True) if not self.init_file_paths: self.init_file_paths = Preferences.files_open() @@ -221,13 +219,11 @@ class ActionHandler: elif state == Actions.PORT_CONTROLLER_INC: if self.get_flow_graph().port_controller_modify_selected(1): self.get_flow_graph().update() - self.get_flow_graph().update() #2 times self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) elif state == Actions.PORT_CONTROLLER_DEC: if self.get_flow_graph().port_controller_modify_selected(-1): self.get_flow_graph().update() - self.get_flow_graph().update() #2 times self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) ################################################## @@ -237,8 +233,8 @@ class ActionHandler: Dialogs.AboutDialog(self.get_flow_graph().get_parent()) elif state == Actions.HELP_WINDOW_DISPLAY: Dialogs.HelpDialog() - elif state == Actions.COLORS_WINDOW_DISPLAY: - Dialogs.ColorsDialog(self.get_flow_graph().get_parent()) + elif state == Actions.TYPES_WINDOW_DISPLAY: + Dialogs.TypesDialog(self.get_flow_graph().get_parent()) ################################################## # Param Modifications ################################################## diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 3695e09ef..c3ef2711a 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -57,7 +57,7 @@ FLOW_GRAPH_KILL = 'flow graph kill' FLOW_GRAPH_SCREEN_CAPTURE = 'flow graph screen capture' ABOUT_WINDOW_DISPLAY = 'about window display' HELP_WINDOW_DISPLAY = 'help window display' -COLORS_WINDOW_DISPLAY = 'colors window display' +TYPES_WINDOW_DISPLAY = 'types window display' ###################################################################################################### # Action Key Map @@ -132,7 +132,7 @@ _actions_list = ( gtk.Action(BLOCK_PASTE, '_Paste', 'Paste', gtk.STOCK_PASTE), gtk.Action(ABOUT_WINDOW_DISPLAY, '_About', 'About this program', gtk.STOCK_ABOUT), gtk.Action(HELP_WINDOW_DISPLAY, '_Help', 'Usage Tips', gtk.STOCK_HELP), - gtk.Action(COLORS_WINDOW_DISPLAY, '_Colors', 'Color Mapping', gtk.STOCK_DIALOG_INFO), + gtk.Action(TYPES_WINDOW_DISPLAY, '_Types', 'Types Color Mapping', gtk.STOCK_DIALOG_INFO), gtk.Action(FLOW_GRAPH_GEN, '_Generate', 'Generate the flow graph', gtk.STOCK_CONVERT), gtk.Action(FLOW_GRAPH_EXEC, '_Execute', 'Execute the flow graph', gtk.STOCK_EXECUTE), gtk.Action(FLOW_GRAPH_KILL, '_Kill', 'Kill the flow graph', gtk.STOCK_STOP), diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index e0c547eba..697d48a3c 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -88,7 +88,7 @@ MENU_BAR_LIST = ( ]), (gtk.Action('Help', '_Help', None, None), [ Actions.HELP_WINDOW_DISPLAY, - Actions.COLORS_WINDOW_DISPLAY, + Actions.TYPES_WINDOW_DISPLAY, None, Actions.ABOUT_WINDOW_DISPLAY, ]), diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 4add3aa19..0f3e511d8 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -44,8 +44,8 @@ class Block(Element): """ #add the position param self.get_params().append(self.get_parent().get_parent().Param( - self, - odict({ + block=self, + n=odict({ 'name': 'GUI Coordinate', 'key': '_coordinate', 'type': 'raw', @@ -54,8 +54,8 @@ class Block(Element): }) )) self.get_params().append(self.get_parent().get_parent().Param( - self, - odict({ + block=self, + n=odict({ 'name': 'GUI Rotation', 'key': '_rotation', 'type': 'raw', diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py index 8d764e28e..3cf617b92 100644 --- a/grc/gui/Dialogs.py +++ b/grc/gui/Dialogs.py @@ -98,8 +98,8 @@ COLORS_DIALOG_MARKUP_TMPL = """\ #end if """ -def ColorsDialog(platform): MessageDialogHelper( +def TypesDialog(platform): MessageDialogHelper( type=gtk.MESSAGE_INFO, buttons=gtk.BUTTONS_CLOSE, - title='Colors', + title='Types', markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors())) diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index f8028f199..5e645be72 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -291,8 +291,10 @@ class FlowGraph(Element): def update(self): """ + Do a global rewrite and validate. Call update on all elements. """ + self.rewrite() self.validate() for element in self.get_elements(): element.update() diff --git a/grc/gui/Param.py b/grc/gui/Param.py index a11fd9065..4955d3336 100644 --- a/grc/gui/Param.py +++ b/grc/gui/Param.py @@ -23,6 +23,71 @@ import pygtk pygtk.require('2.0') import gtk +class InputParam(gtk.HBox): + """The base class for an input parameter inside the input parameters dialog.""" + + def __init__(self, param, _handle_changed): + gtk.HBox.__init__(self) + self.param = param + self._handle_changed = _handle_changed + self.label = gtk.Label('') #no label, markup is added by set_markup + self.label.set_size_request(150, -1) + self.pack_start(self.label, False) + self.set_markup = lambda m: self.label.set_markup(m) + self.tp = None + def set_color(self, color): pass + +class EntryParam(InputParam): + """Provide an entry box for strings and numbers.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self.entry = input = gtk.Entry() + input.set_text(self.param.get_value()) + input.connect('changed', self._handle_changed) + self.pack_start(input, True) + self.get_text = input.get_text + #tool tip + self.tp = gtk.Tooltips() + self.tp.set_tip(self.entry, '') + self.tp.enable() + def set_color(self, color): self.entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) + +class EnumParam(InputParam): + """Provide an entry box for Enum types with a drop down menu.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + self._input.connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): return self.param.get_option_keys()[self._input.get_active()] + +class EnumEntryParam(InputParam): + """Provide an entry box and drop down menu for Raw Enum types.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_entry_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + except: + self._input.set_active(-1) + self._input.get_child().set_text(self.param.get_value()) + self._input.connect('changed', self._handle_changed) + self._input.get_child().connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): + if self._input.get_active() == -1: return self._input.get_child().get_text() + return self.param.get_option_keys()[self._input.get_active()] + def set_color(self, color): + if self._input.get_active() == -1: #custom entry, use color + self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) + else: #from enum, make white background + self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse('#ffffff')) + PARAM_MARKUP_TMPL="""\ #set $foreground = $param.is_valid() and 'black' or 'red' <span foreground="$foreground" font_desc="Sans 7.5"><b>$encode($param.get_name()): </b>$encode(repr($param))</span>""" @@ -49,6 +114,18 @@ Error: class Param(Element): """The graphical parameter.""" + def get_input_class(self): + """ + Get the graphical gtk class to represent this parameter. + An enum requires and combo parameter. + A non-enum with options gets a combined entry/combo parameter. + All others get a standard entry parameter. + @return gtk input class + """ + if self.is_enum(): return EnumParam + if self.get_options(): return EnumEntryParam + return EntryParam + def update(self): """ Called when an external change occurs. diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index a32b0209f..8f0aa533d 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.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 @@ -17,32 +17,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ -from FlowGraph import FlowGraph -from Connection import Connection -from Block import Block -from Port import Port -from Param import Param +from Element import Element -def conjoin_classes(name, c1, c2): - exec(""" -class %s(c1, c2): - def __init__(self, *args, **kwargs): - c1.__init__(self, *args, **kwargs) - c2.__init__(self, *args, **kwargs) -"""%name, locals()) - return locals()[name] - -def Platform(platform): - #combine with gui class - for attr, value in ( - ('FlowGraph', FlowGraph), - ('Connection', Connection), - ('Block', Block), - ('Source', Port), - ('Sink', Port), - ('Param', Param), - ): - old_value = getattr(platform, attr) - c = conjoin_classes(attr, old_value, value) - setattr(platform, attr, c) - return platform +class Platform(Element): pass diff --git a/grc/gui/Port.py b/grc/gui/Port.py index d1f36f8b9..6fc2c4b15 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -1,5 +1,5 @@ """ -Copyright 2007 Free Software Foundation, Inc. +Copyright 2007, 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/Block.py b/grc/python/Block.py index 47fe13a3c..dd39b095d 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -18,10 +18,14 @@ 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): +class Block(_Block, _GUIBlock): + + def is_virtual_sink(self): return self.get_key() == 'virtual_sink' + def is_virtual_source(self): return self.get_key() == 'virtual_source' ##for make source to keep track of indexes _source_count = 0 @@ -48,13 +52,13 @@ class Block(_Block): flow_graph=flow_graph, n=n, ) + _GUIBlock.__init__(self) 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 @@ -65,6 +69,12 @@ class Block(_Block): 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) + + def rewrite(self): + """ + Add and remove ports to adjust for the nports. + """ + _Block.rewrite(self) #adjust nports for get_ports, get_port in ( (self.get_sources, self.get_source), diff --git a/grc/python/Connection.py b/grc/python/Connection.py index 5eba9f24d..edc18841a 100644 --- a/grc/python/Connection.py +++ b/grc/python/Connection.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 @@ -18,8 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from .. base.Connection import Connection as _Connection +from .. gui.Connection import Connection as _GUIConnection -class Connection(_Connection): +class Connection(_Connection, _GUIConnection): + + def __init__(self, **kwargs): + _Connection.__init__(self, **kwargs) + _GUIConnection.__init__(self) def is_msg(self): return self.get_source().get_type() == self.get_sink().get_type() == 'msg' diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 8cad8be49..6b2936c75 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import expr_utils from .. base.FlowGraph import FlowGraph as _FlowGraph +from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph from Block import Block from Connection import Connection import re @@ -26,9 +27,13 @@ import re _variable_matcher = re.compile('^(variable\w*)$') _parameter_matcher = re.compile('^(parameter)$') -class FlowGraph(_FlowGraph): +class FlowGraph(_FlowGraph, _GUIFlowGraph): + + def __init__(self, **kwargs): + _FlowGraph.__init__(self, **kwargs) + _GUIFlowGraph.__init__(self) + self._eval_cache = dict() - _eval_cache = dict() def _eval(self, code, namespace, namespace_hash): """ Evaluate the code with the given namespace. @@ -109,6 +114,13 @@ class FlowGraph(_FlowGraph): parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) return parameters + def rewrite(self): + """ + Flag the namespace to be renewed. + """ + self._renew_eval_ns = True + _FlowGraph.rewrite(self) + def evaluate(self, expr): """ Evaluate the expression. @@ -116,8 +128,8 @@ class FlowGraph(_FlowGraph): @throw Exception bad expression @return the evaluated data """ - if self.is_flagged(): - self.deflag() + if self._renew_eval_ns: + self._renew_eval_ns = False #reload namespace n = dict() #load imports diff --git a/grc/python/Param.py b/grc/python/Param.py index f971d0c3f..e61779136 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -18,7 +18,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ import expr_utils -from .. base.Param import Param as _Param, EntryParam +from .. base.Param import Param as _Param +from .. gui.Param import Param as _GUIParam +from .. gui.Param import EntryParam import Constants import numpy import os @@ -83,21 +85,24 @@ COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES) REAL_TYPES = tuple(REAL_TYPES + INT_TYPES) INT_TYPES = tuple(INT_TYPES) -class Param(_Param): +class Param(_Param, _GUIParam): - _init = False - _hostage_cells = list() + def __init__(self, **kwargs): + _Param.__init__(self, **kwargs) + _GUIParam.__init__(self) + self._init = False + self._hostage_cells = list() - ##possible param types - TYPES = _Param.TYPES + [ + def get_types(self): return ( + 'raw', 'enum', 'complex', 'real', 'int', 'complex_vector', 'real_vector', 'int_vector', 'hex', 'string', 'bool', 'file_open', 'file_save', - 'id', + 'id', 'stream_id', 'grid_pos', 'notebook', 'import', - ] + ) def __repr__(self): """ @@ -150,7 +155,7 @@ class Param(_Param): def get_input_class(self): if self.get_type() in ('file_open', 'file_save'): return FileParam - return _Param.get_input_class(self) + return _GUIParam.get_input_class(self) def get_color(self): """ @@ -172,6 +177,7 @@ class Param(_Param): 'hex': Constants.INT_COLOR_SPEC, 'string': Constants.BYTE_VECTOR_COLOR_SPEC, 'id': Constants.ID_COLOR_SPEC, + 'stream_id': Constants.ID_COLOR_SPEC, 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, 'notebook': Constants.INT_VECTOR_COLOR_SPEC, 'raw': Constants.WILDCARD_COLOR_SPEC, @@ -310,14 +316,31 @@ class Param(_Param): #can python use this as a variable? try: assert _check_id_matcher.match(v) except AssertionError: raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v - params = self.get_all_params('id') - keys = [param.get_value() for param in params] - try: assert keys.count(v) <= 1 #id should only appear once, or zero times if block is disabled + ids = [param.get_value() for param in self.get_all_params(t)] + try: assert ids.count(v) <= 1 #id should only appear once, or zero times if block is disabled except: raise Exception, 'ID "%s" is not unique.'%v try: assert v not in ID_BLACKLIST except: raise Exception, 'ID "%s" is blacklisted.'%v return v ######################### + # Stream ID Type + ######################### + elif t == 'stream_id': + #get a list of all stream ids used in the virtual sinks + ids = [param.get_value() for param in filter( + lambda p: p.get_parent().is_virtual_sink(), + self.get_all_params(t), + )] + #check that the virtual sink's stream id is unique + if self.get_parent().is_virtual_sink(): + try: assert ids.count(v) <= 1 #id should only appear once, or zero times if block is disabled + except: raise Exception, 'Stream ID "%s" is not unique.'%v + #check that the virtual source's steam id is found + if self.get_parent().is_virtual_source(): + try: assert v in ids + except: raise Exception, 'Stream ID "%s" is not found.'%v + return v + ######################### # Grid Position Type ######################### elif t == 'grid_pos': @@ -380,17 +403,18 @@ class Param(_Param): def to_code(self): """ Convert the value to code. + For string and list types, check the init flag, call evaluate(). + This ensures that evaluate() was called to set the xxxify_flags. @return a string representing the code """ - #run init tasks in evaluate - #such as setting flags - if not self._init: self.evaluate() v = self.get_value() t = self.get_type() if t in ('string', 'file_open', 'file_save'): #string types + if not self._init: self.evaluate() if self._stringify_flag: return '"%s"'%v.replace('"', '\"') else: return v elif t in ('complex_vector', 'real_vector', 'int_vector'): #vector types + if not self._init: self.evaluate() if self._lisitify_flag: return '(%s, )'%v else: return '(%s)'%v else: return v diff --git a/grc/python/Platform.py b/grc/python/Platform.py index d55dbf4ce..bb56d361b 100644 --- a/grc/python/Platform.py +++ b/grc/python/Platform.py @@ -20,10 +20,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os from gnuradio import gr from .. base.Platform import Platform as _Platform +from .. gui.Platform import Platform as _GUIPlatform from FlowGraph import FlowGraph as _FlowGraph from Connection import Connection as _Connection from Block import Block as _Block -from Port import Source,Sink +from Port import Port as _Port from Param import Param as _Param from Generator import Generator from Constants import \ @@ -46,7 +47,7 @@ COLORS = (#title, #color spec ('Message', Constants.MSG_COLOR_SPEC), ) -class Platform(_Platform): +class Platform(_Platform, _GUIPlatform): def __init__(self): """ @@ -70,6 +71,7 @@ class Platform(_Platform): generator=Generator, colors=COLORS, ) + _GUIPlatform.__init__(self) ############################################## # Constructors @@ -77,6 +79,5 @@ class Platform(_Platform): FlowGraph = _FlowGraph Connection = _Connection Block = _Block - Source = Source - Sink = Sink + Port = _Port Param = _Param diff --git a/grc/python/Port.py b/grc/python/Port.py index daf8f9ca3..33426d905 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -18,41 +18,100 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from .. base.Port import Port as _Port +from .. gui.Port import Port as _GUIPort import Constants -class Port(_Port): - - ##possible port types - TYPES = ['complex', 'float', 'int', 'short', 'byte', 'msg'] - - def __init__(self, block, n): +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. + """ + try: return _get_source_from_virtual_source_port( + vsp.get_enabled_connections()[0].get_source()) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + +def _get_source_from_virtual_source_port(vsp, traversed=[]): + """ + Recursively resolve source ports over the virtual connections. + Keep track of traversed sources to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_source(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp + try: return _get_source_from_virtual_source_port( + _get_source_from_virtual_sink_port( + filter(#get all virtual sinks 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_sink(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sink(vsp.get_key()) + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + +class Port(_Port, _GUIPort): + + def __init__(self, block, n, dir): """ Make a new port from nested data. @param block the parent element @param n the nested odict + @param dir the direction """ + self._n = n + if n['type'] == 'msg': n['key'] = 'msg' + if dir == 'source' and not n.find('key'): + n['key'] = str(block._source_count) + block._source_count += 1 + if dir == 'sink' and not n.find('key'): + n['key'] = str(block._sink_count) + block._sink_count += 1 #build the port _Port.__init__( self, block=block, n=n, + dir=dir, ) + _GUIPort.__init__(self) self._nports = n.find('nports') or '' self._vlen = n.find('vlen') or '' self._optional = bool(n.find('optional')) + def get_types(self): return ('complex', 'float', 'int', 'short', 'byte', 'msg', '') + 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.') + #message port logic if self.get_type() == 'msg': try: assert not self.get_nports() except AssertionError: self.add_error_message('A port of type "msg" cannot have "nports" set.') try: assert self.get_vlen() == 1 except AssertionError: self.add_error_message('A port of type "msg" must have a "vlen" of 1.') + def rewrite(self): + """ + Handle the port cloning for virtual blocks. + """ + _Port.rewrite(self) + if self.get_parent().is_virtual_sink() or self.get_parent().is_virtual_source(): + try: #clone type and vlen + source = self.resolve_virtual_source() + 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 get_vlen(self): """ Get the vector length. @@ -109,24 +168,4 @@ class Port(_Port): def copy(self, new_key=None): n = self._n.copy() if new_key: n['key'] = new_key - return self.__class__(self.get_parent(), n) - -class Source(Port): - - def __init__(self, block, n): - self._n = n #save n - if n['type'] == 'msg': n['key'] = 'msg' - if not n.find('key'): - n['key'] = str(block._source_count) - block._source_count = block._source_count + 1 - Port.__init__(self, block, n) - -class Sink(Port): - - def __init__(self, block, n): - self._n = n #save n - if n['type'] == 'msg': n['key'] = 'msg' - if not n.find('key'): - n['key'] = str(block._sink_count) - block._sink_count = block._sink_count + 1 - Port.__init__(self, block, n) + return self.__class__(self.get_parent(), n, self._dir) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index df346dd16..dce4037d5 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -154,6 +154,13 @@ class $(class_name)(gr.hier_block2): ## The port name should be the id of the parent block. ## However, port names for IO pads should be self. ######################################################## +#def make_port_name($port) + #if $port.get_parent().get_key().startswith('pad_') +self#slurp + #else +self.$port.get_parent().get_id()#slurp + #end if +#end def #if $connections $DIVIDER @@ -163,17 +170,14 @@ class $(class_name)(gr.hier_block2): #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() + ##resolve virtual sources to the actual sources + #if $source.get_parent().is_virtual_source() + #set $source = $source.resolve_virtual_source() #end if - #if $sink.get_parent().get_key() == 'pad_sink' - #set $sink_name = 'self' - #else - #set $sink_name = 'self.' + $sink.get_parent().get_id() + ##do not generate connections with virtual sinks + #if not $sink.get_parent().is_virtual_sink() + self.connect(($make_port_name($source), $source.get_key()), ($make_port_name($sink), $sink.get_key())) #end if - self.connect(($source_name, $source.get_key()), ($sink_name, $sink.get_key())) #end for ######################################################## @@ -194,6 +198,20 @@ class $(class_name)(gr.hier_block2): ## For top block code, generate a main routine. ## Instantiate the top block and run as gui or cli. ######################################################## +#def make_default($type, $param) + #if $type == 'eng_float' +eng_notation.num_to_str($param.get_make())#slurp + #else +$param.get_make()#slurp + #end if +#end def +#def make_short_id($param) + #set $short_id = $param.get_param('short_id').get_evaluated() + #if $short_id + #set $short_id = '-' + $short_id + #end if +$short_id#slurp +#end def #if $generate_options != 'hb' if __name__ == '__main__': parser = OptionParser(option_class=eng_option, usage="%prog: [options]") @@ -202,12 +220,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())) - #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]") + parser.add_option("$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") #end if #end for (options, args) = parser.parse_args() diff --git a/grc/todo.txt b/grc/todo.txt index bb40e1f16..ffc9d64db 100644 --- a/grc/todo.txt +++ b/grc/todo.txt @@ -25,8 +25,7 @@ * size params for the graphical sinks * callbacks for set average on fft, waterfall, number sinks * add units to params: Sps, Hz, dB... -* command line options should replace _ with - for the --option - * add bool type to command line option store_true or store_false +* add bool type to command line option store_true or store_false ################################################## # Features @@ -59,6 +58,8 @@ ################################################## # Problems ################################################## +* msg ports dont work with virtual connections + * dont fix this until pmts are used? * hier block generation * auto generate hier library on changes * auto clean hier library when block removed @@ -66,7 +67,6 @@ * dont generate py files in saved flowgraph dir * save/restore cwd * threads dont die on exit in probe and variable sink -* overloaded gui classes for each platform, move param input objects into overloaded * align param titles in paramsdialog * better error for blank string params * weird grid params misbehaving @@ -74,7 +74,6 @@ * will not update for non-enum params * needs to account for added or removed params * example with grid params need update after notebook change -* use .strip() on the hide property so we can do away with #slurp(s) in the templates ################################################## # Future |