diff options
-rw-r--r-- | gr-audio/grc/audio_sink.xml | 2 | ||||
-rw-r--r-- | gr-audio/grc/audio_source.xml | 2 | ||||
-rw-r--r-- | gr-blocks/grc/blocks_throttle.xml | 2 | ||||
-rw-r--r-- | gr-blocks/grc/blocks_udp_source.xml | 2 | ||||
-rw-r--r-- | gr-fcd/grc/fcd_source_c.xml | 2 | ||||
-rw-r--r-- | gr-uhd/grc/gen_uhd_usrp_blocks.py | 2 | ||||
-rw-r--r-- | grc/base/Block.py | 96 | ||||
-rw-r--r-- | grc/base/Constants.py | 8 | ||||
-rw-r--r-- | grc/base/FlowGraph.py | 50 | ||||
-rw-r--r-- | grc/gui/ActionHandler.py | 42 | ||||
-rw-r--r-- | grc/gui/Actions.py | 6 | ||||
-rw-r--r-- | grc/gui/Bars.py | 3 | ||||
-rw-r--r-- | grc/gui/Block.py | 2 | ||||
-rw-r--r-- | grc/gui/Colors.py | 1 | ||||
-rw-r--r-- | grc/gui/FlowGraph.py | 18 | ||||
-rw-r--r-- | grc/python/Block.py | 3 | ||||
-rw-r--r-- | grc/python/Generator.py | 2 | ||||
-rw-r--r-- | grc/python/block.dtd | 5 |
18 files changed, 210 insertions, 38 deletions
diff --git a/gr-audio/grc/audio_sink.xml b/gr-audio/grc/audio_sink.xml index 9a70006e72..727767b72e 100644 --- a/gr-audio/grc/audio_sink.xml +++ b/gr-audio/grc/audio_sink.xml @@ -8,7 +8,7 @@ <name>Audio Sink</name> <key>audio_sink</key> <category>Audio</category> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import audio</import> <make>audio.sink($samp_rate, $device_name, $ok_to_block)</make> <param> diff --git a/gr-audio/grc/audio_source.xml b/gr-audio/grc/audio_source.xml index 0cb73a3ae9..aaa3225e8b 100644 --- a/gr-audio/grc/audio_source.xml +++ b/gr-audio/grc/audio_source.xml @@ -8,7 +8,7 @@ <name>Audio Source</name> <key>audio_source</key> <category>Audio</category> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import audio</import> <make>audio.source($samp_rate, $device_name, $ok_to_block)</make> <param> diff --git a/gr-blocks/grc/blocks_throttle.xml b/gr-blocks/grc/blocks_throttle.xml index 790e195750..26f4aa768b 100644 --- a/gr-blocks/grc/blocks_throttle.xml +++ b/gr-blocks/grc/blocks_throttle.xml @@ -7,7 +7,7 @@ <block> <name>Throttle</name> <key>blocks_throttle</key> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import blocks</import> <make>blocks.throttle($type.size*$vlen, $samples_per_second,$ignoretag)</make> <callback>set_sample_rate($samples_per_second)</callback> diff --git a/gr-blocks/grc/blocks_udp_source.xml b/gr-blocks/grc/blocks_udp_source.xml index 3b434c107e..03dbb5781b 100644 --- a/gr-blocks/grc/blocks_udp_source.xml +++ b/gr-blocks/grc/blocks_udp_source.xml @@ -7,7 +7,7 @@ <block> <name>UDP Source</name> <key>blocks_udp_source</key> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import blocks</import> <make>blocks.udp_source($type.size*$vlen, $ipaddr, $port, $psize, $eof)</make> <callback>set_mtu($mtu)</callback> diff --git a/gr-fcd/grc/fcd_source_c.xml b/gr-fcd/grc/fcd_source_c.xml index b514c6126c..01ea26d81c 100644 --- a/gr-fcd/grc/fcd_source_c.xml +++ b/gr-fcd/grc/fcd_source_c.xml @@ -3,7 +3,7 @@ <name>Funcube Dongle Source</name> <key>fcd_source_c</key> <category>FCD</category> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import fcd</import> <make>fcd.source_c($device_name) #if $lna() != 20.0 diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py index fda1b9dfd7..72f1b50135 100644 --- a/gr-uhd/grc/gen_uhd_usrp_blocks.py +++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py @@ -23,7 +23,7 @@ MAIN_TMPL = """\ <block> <name>UHD: USRP $sourk.title()</name> <key>uhd_usrp_$(sourk)</key> - <throttle>1</throttle> + <flags>throttle</flags> <import>from gnuradio import uhd</import> <import>import time</import> <make>uhd.usrp_$(sourk)( diff --git a/grc/base/Block.py b/grc/base/Block.py index b367e60e69..5e8a7179ac 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -19,12 +19,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from . import odict from . Constants import ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB +from . Constants import BLOCK_FLAG_THROTTLE, BLOCK_FLAG_DISABLE_BYPASS +from . Constants import BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED from Element import Element from Cheetah.Template import Template from UserDict import UserDict from itertools import imap +from .. gui import Messages + + class TemplateArg(UserDict): """ A cheetah template argument created from a param. @@ -46,11 +51,16 @@ class TemplateArg(UserDict): def __call__(self): return self._param.get_evaluated() -def _get_keys(lst): return [elem.get_key() for elem in lst] + +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 Block(Element): def __init__(self, flow_graph, n): @@ -73,6 +83,10 @@ class Block(Element): self._name = n.find('name') self._key = n.find('key') self._category = n.find('category') or '' + self._flags = n.find('flags') or '' + # Backwards compatibility + if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: + self._flags += BLOCK_FLAG_THROTTLE self._grc_source = n.find('grc_source') or '' self._block_wrapper_path = n.find('block_wrapper_path') self._bussify_sink = n.find('bus_sink') @@ -143,6 +157,10 @@ class Block(Element): and (self._key != "pad_sink")) is_variable = self._key.startswith('variable') + # Disable blocks that are virtual/pads or variables + if not is_not_virtual_or_pad or is_variable: + self._flags += BLOCK_FLAG_DISABLE_BYPASS + if is_not_virtual_or_pad and not is_variable: self.get_params().append(self.get_parent().get_parent().Param( block=self, @@ -197,7 +215,6 @@ class Block(Element): }) )) - def back_ofthe_bus(self, portlist): portlist.sort(key=lambda p: p._type == 'bus') @@ -205,6 +222,35 @@ class Block(Element): buslist = [p for p in ports if p._type == 'bus'] return buslist or ports + # Main functions to get and set the block state + # Also kept get_enabled and set_enabled to keep compatibility + def get_state(self): + """ + Gets the block's current state. + + Returns: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + try: return int(eval(self.get_param('_enabled').get_value())) + except: return BLOCK_ENABLED + + def set_state(self, state): + """ + Sets the state for the block. + + Args: + ENABLED - 0 + BYPASSED - 1 + DISABLED - 2 + """ + if state in [BLOCK_ENABLED, BLOCK_BYPASSED, BLOCK_DISABLED]: + self.get_param('_enabled').set_value(str(state)) + else: + self.get_param('_enabled').set_value(str(BLOCK_ENABLED)) + + # Enable/Disable Aliases def get_enabled(self): """ Get the enabled state of the block. @@ -212,8 +258,7 @@ class Block(Element): Returns: true for enabled """ - try: return eval(self.get_param('_enabled').get_value()) - except: return True + return not (self.get_state() == BLOCK_DISABLED) def set_enabled(self, enabled): """ @@ -221,8 +266,45 @@ class Block(Element): Args: enabled: true for enabled + + Returns: + True if block changed state + """ + old_state = self.get_state() + new_state = BLOCK_ENABLED if enabled else BLOCK_DISABLED + self.set_state(new_state) + return old_state != new_state + + # Block bypassing + def get_bypassed(self): + """ + Check if the block is bypassed """ - self.get_param('_enabled').set_value(str(enabled)) + return self.get_state() == BLOCK_BYPASSED + + def set_bypassed(self): + """ + Bypass the block + + Returns: + True if block chagnes state + """ + if self.get_state() != BLOCK_BYPASSED and self.can_bypass(): + self.set_state(BLOCK_BYPASSED) + return True + return False + + def can_bypass(self): + """ Check the number of sinks and sources and see if this block can be bypassed """ + # Check to make sure this is a single path block + # Could possibly support 1 to many blocks + if len(self.get_sources()) != 1 or len(self.get_sinks()) != 1: + return False + if not (self.get_sources()[0].get_type() == self.get_sinks()[0].get_type()): + return False + if self.bypass_disabled(): + return False + return True def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) @@ -240,6 +322,10 @@ class Block(Element): def get_block_wrapper_path(self): return self._block_wrapper_path def get_comment(self): return self.get_param('comment').get_value() + def get_flags(self): return self._flags + def throtteling(self): return BLOCK_FLAG_THROTTLE in self._flags + def bypass_disabled(self): return BLOCK_FLAG_DISABLE_BYPASS in self._flags + ############################################## # Access Params ############################################## diff --git a/grc/base/Constants.py b/grc/base/Constants.py index efae0ecbb5..0c5116c604 100644 --- a/grc/base/Constants.py +++ b/grc/base/Constants.py @@ -38,3 +38,11 @@ DOMAIN_DTD = os.path.join(DATA_DIR, 'domain.dtd') GR_STREAM_DOMAIN = "gr_stream" GR_MESSAGE_DOMAIN = "gr_message" DEFAULT_DOMAIN = GR_STREAM_DOMAIN + +BLOCK_FLAG_THROTTLE = 'throttle' +BLOCK_FLAG_DISABLE_BYPASS = 'disable_bypass' + +# Block States +BLOCK_DISABLED = 0 +BLOCK_ENABLED = 1 +BLOCK_BYPASSED = 2 diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index 790aed07f6..7fd8df5f64 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -78,9 +78,6 @@ class FlowGraph(Element): bus_structure = block.form_bus_structure('sink'); if 'bus' in map(lambda a: a.get_type(), get_p_gui()): - - - if len(get_p_gui()) > len(bus_structure): times = range(len(bus_structure), len(get_p_gui())); for i in times: @@ -100,8 +97,6 @@ class FlowGraph(Element): port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); get_p().append(port); - - for child in self.get_children(): child.rewrite() refactor_bus_structure(); @@ -159,6 +154,15 @@ class FlowGraph(Element): """ return filter(lambda b: b.get_enabled(), self.get_blocks()) + def get_bypassed_blocks(self): + """ + Get a list of all blocks that are bypassed. + + Returns: + a list of blocks + """ + return filter(lambda b: b.get_bypassed(), self.get_blocks()) + def get_enabled_connections(self): """ Get a list of all connections that are enabled. @@ -166,7 +170,41 @@ class FlowGraph(Element): Returns: a list of connections """ - return filter(lambda c: c.get_enabled(), self.get_connections()) + # First get all the enabled connections, then get the bypassed blocks. + connections = filter(lambda c: c.get_enabled(), self.get_connections()) + bypassed_blocks = self.get_bypassed_blocks() + + # Bypassing blocks: Need to find all the enabled connections for the block using + # the *connections* object rather than get_connections(). Create new connections + # that bypass the selected block and remove the existing ones. This allows adjacent + # bypassed blocks to see the newly created connections to downstream blocks, + # allowing them to correctly construct bypass connections. + + for block in bypassed_blocks: + # Get the upstream connection (off of the sink ports) + # Use *connections* not get_connections() + get_source_connection = lambda c: c.get_sink() == block.get_sinks()[0] + source_connection = filter(get_source_connection, connections) + # The source connection should never have more than one element. + assert (len(source_connection) == 1) + + # Get the source of the connection. + source_port = source_connection[0].get_source() + + # Loop through all the downstream connections + get_sink_connections = lambda c: c.get_source() == block.get_sources()[0] + for sink in filter(get_sink_connections, connections): + if not sink.get_enabled(): + # Ignore disabled connections + continue + sink_port = sink.get_sink() + connection = self.get_parent().Connection(flow_graph=self, porta=source_port, portb=sink_port) + connections.append(connection) + # Remove this sink connection + connections.remove(sink) + # Remove the source connection + connections.remove(source_connection[0]) + return connections def get_new_block(self, key): """ diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index fee96624bb..1ce4aeda2d 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -29,7 +29,7 @@ import subprocess import Preferences from threading import Thread import Messages -from .. base import ParseXML +from .. base import ParseXML, Constants from MainWindow import MainWindow from PropsDialog import PropsDialog from ParserErrorsDialog import ParserErrorsDialog @@ -171,6 +171,11 @@ class ActionHandler: self.get_flow_graph().update() self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) self.get_page().set_saved(False) + elif action == Actions.BLOCK_BYPASS: + if self.get_flow_graph().bypass_selected(): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) ################################################## # Cut/Copy/Paste ################################################## @@ -553,23 +558,34 @@ class ActionHandler: ################################################## # Global Actions for all States ################################################## + selected_block = self.get_flow_graph().get_selected_block() + selected_blocks = self.get_flow_graph().get_selected_blocks() + #update general buttons Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) - Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(selected_block)) + Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(selected_blocks)) + Actions.BLOCK_ROTATE_CW.set_sensitive(bool(selected_blocks)) #update cut/copy/paste - Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_CUT.set_sensitive(bool(selected_blocks)) + Actions.BLOCK_COPY.set_sensitive(bool(selected_blocks)) Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) - #update enable/disable - Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SOURCES.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BUSSIFY_SINKS.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + #update enable/disable/bypass + can_enable = any(block.get_state() != Constants.BLOCK_ENABLED + for block in selected_blocks) + can_disable = any(block.get_state() != Constants.BLOCK_DISABLED + for block in selected_blocks) + can_bypass_all = all(block.can_bypass() for block in selected_blocks) \ + and any (not block.get_bypassed() for block in selected_blocks) + Actions.BLOCK_ENABLE.set_sensitive(can_enable) + Actions.BLOCK_DISABLE.set_sensitive(can_disable) + Actions.BLOCK_BYPASS.set_sensitive(can_bypass_all) + + Actions.BLOCK_CREATE_HIER.set_sensitive(bool(selected_blocks)) + Actions.OPEN_HIER.set_sensitive(bool(selected_blocks)) + Actions.BUSSIFY_SOURCES.set_sensitive(bool(selected_blocks)) + Actions.BUSSIFY_SINKS.set_sensitive(bool(selected_blocks)) Actions.RELOAD_BLOCKS.set_sensitive(True) Actions.FIND_BLOCKS.set_sensitive(True) #set the exec and stop buttons diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index b2b3a76386..a028a33a11 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -247,6 +247,12 @@ BLOCK_DISABLE = Action( stock_id=gtk.STOCK_DISCONNECT, keypresses=(gtk.keysyms.d, NO_MODS_MASK), ) +BLOCK_BYPASS = Action( + label='_Bypass', + tooltip='Bypass the selected block', + stock_id=gtk.STOCK_MEDIA_FORWARD, + keypresses=(gtk.keysyms.b, NO_MODS_MASK), +) TOGGLE_SNAP_TO_GRID = ToggleAction( label='_Snap to grid', tooltip='Snap blocks to a grid for an easier connection alignment', diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index 40ce20536c..abcc3c6434 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -49,6 +49,7 @@ TOOLBAR_LIST = ( None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, Actions.TOGGLE_HIDE_DISABLED_BLOCKS, None, Actions.FIND_BLOCKS, @@ -85,6 +86,7 @@ MENU_BAR_LIST = ( None, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, None, Actions.BLOCK_PARAM_MODIFY, ]), @@ -134,6 +136,7 @@ CONTEXT_MENU_LIST = [ Actions.BLOCK_ROTATE_CW, Actions.BLOCK_ENABLE, Actions.BLOCK_DISABLE, + Actions.BLOCK_BYPASS, None, (gtk.Action('More', '_More', None, None), [ Actions.BLOCK_CREATE_HIER, diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 60f19fc1a4..83706ed1aa 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -145,7 +145,9 @@ class Block(Element): """Create the labels for the signal block.""" Element.create_labels(self) self._bg_color = self.is_dummy_block() and Colors.MISSING_BLOCK_BACKGROUND_COLOR or \ + self.get_bypassed() and Colors.BLOCK_BYPASSED_COLOR or \ self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR + layouts = list() #create the main layout layout = gtk.DrawingArea().create_pango_layout('') diff --git a/grc/gui/Colors.py b/grc/gui/Colors.py index f64106b03f..52c95e8edf 100644 --- a/grc/gui/Colors.py +++ b/grc/gui/Colors.py @@ -38,6 +38,7 @@ try: #block color constants BLOCK_ENABLED_COLOR = get_color('#F1ECFF') BLOCK_DISABLED_COLOR = get_color('#CCCCCC') + BLOCK_BYPASSED_COLOR = get_color('#FFFFE6') #connection color constants CONNECTION_ENABLED_COLOR = get_color('black') CONNECTION_DISABLED_COLOR = get_color('#BBBBBB') diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 97f814f1bf..bf6e1eed78 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -211,9 +211,21 @@ class FlowGraph(Element): """ changed = False for selected_block in self.get_selected_blocks(): - if selected_block.get_enabled() != enable: - selected_block.set_enabled(enable) - changed = True + if selected_block.set_enabled(enable): changed = True + return changed + + def bypass_selected(self): + """ + Bypass the selected blocks. + + Args: + None + Returns: + true if changed + """ + changed = False + for selected_block in self.get_selected_blocks(): + if selected_block.set_bypassed(): changed = True return changed def move_selected(self, delta_coordinate): diff --git a/grc/python/Block.py b/grc/python/Block.py index 191b03b452..5d52e2bf3c 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -52,7 +52,6 @@ class Block(_Block, _GUIBlock): self._var_make = n.find('var_make') self._checks = n.findall('check') self._callbacks = n.findall('callback') - self._throttle = n.find('throttle') or '' self._bus_structure_source = n.find('bus_structure_source') or '' self._bus_structure_sink = n.find('bus_structure_sink') or '' #build the block @@ -78,8 +77,6 @@ class Block(_Block, _GUIBlock): except: return '' - def throttle(self): return bool(self._throttle) - def validate(self): """ Validate this block. diff --git a/grc/python/Generator.py b/grc/python/Generator.py index fc1dd56f50..33807c5b95 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -86,7 +86,7 @@ class TopBlockGenerator(object): def write(self): """generate output and write it to files""" # do throttle warning - throttling_blocks = filter(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks()) + throttling_blocks = filter(lambda b: b.throtteling(), self._flow_graph.get_enabled_blocks()) if not throttling_blocks and self._generate_options != 'hb': Messages.send_warning("This flow graph may not have flow control: " "no audio or RF hardware blocks found. " diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 8cfd3dd392..145f4d8610 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -25,7 +25,9 @@ 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. --> -<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, var_value?, make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> +<!ELEMENT block (name, key, category?, throttle?, flags?, import*, var_make?, var_value?, + make, callback*, param_tab_order?, param*, bus_sink?, bus_source?, check*, + sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> <!-- Sub level elements. --> @@ -64,3 +66,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <!ELEMENT callback (#PCDATA)> <!ELEMENT optional (#PCDATA)> <!ELEMENT throttle (#PCDATA)> +<!ELEMENT flags (#PCDATA)> |