diff options
-rw-r--r-- | grc/base/Block.py | 86 | ||||
-rw-r--r-- | grc/base/Constants.py | 6 | ||||
-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/Generator.py | 2 |
10 files changed, 186 insertions, 30 deletions
diff --git a/grc/base/Block.py b/grc/base/Block.py index d2d7ebf426..5e8a7179ac 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -18,7 +18,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from . import odict -from . Constants import ADVANCED_PARAM_TAB, DEFAULT_PARAM_TAB, BLOCK_FLAG_THROTTLE +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 @@ -82,7 +84,8 @@ class Block(Element): self._key = n.find('key') self._category = n.find('category') or '' self._flags = n.find('flags') or '' - if n.find('throttle') and BLOCK_FLAG_THROTTLE not in self._flags: # backwards-compatibility + # 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') @@ -154,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, @@ -208,7 +215,6 @@ class Block(Element): }) )) - def back_ofthe_bus(self, portlist): portlist.sort(key=lambda p: p._type == 'bus') @@ -216,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. @@ -223,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): """ @@ -232,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 + """ + return self.get_state() == BLOCK_BYPASSED + + def set_bypassed(self): + """ + Bypass the block + + Returns: + True if block chagnes state """ - self.get_param('_enabled').set_value(str(enabled)) + 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()) @@ -252,7 +323,8 @@ class Block(Element): def get_comment(self): return self.get_param('comment').get_value() def get_flags(self): return self._flags - def throttle(self): return "throttle" in 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 7949708d5b..0c5116c604 100644 --- a/grc/base/Constants.py +++ b/grc/base/Constants.py @@ -40,3 +40,9 @@ 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/Generator.py b/grc/python/Generator.py index a3f9f10fc1..d9e92cd31f 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. " |