diff options
author | Nicholas McCarthy <namccart@gmail.com> | 2013-07-07 18:08:20 -0400 |
---|---|---|
committer | Johnathan Corgan <johnathan@corganlabs.com> | 2013-07-08 07:19:33 -0700 |
commit | cd99526b5d1c8b1130ac1ba87e21f96c621b1bdc (patch) | |
tree | 12836648bf75868b17b04a2c3ead9c1b6fa2d4c5 | |
parent | 2dc1b6f2ed0cabd2bccbcc58487e93be4295df84 (diff) |
grc: add bus ports
Bus ports allow ganging together of block input or output ports into
a single display item for connection to other bus ports.
-rw-r--r-- | grc/base/Block.py | 123 | ||||
-rw-r--r-- | grc/base/Connection.py | 22 | ||||
-rw-r--r-- | grc/base/FlowGraph.py | 73 | ||||
-rw-r--r-- | grc/base/ParseXML.py | 1 | ||||
-rw-r--r-- | grc/base/Port.py | 30 | ||||
-rw-r--r-- | grc/base/flow_graph.dtd | 4 | ||||
-rw-r--r-- | grc/blocks/bus_sink.xml | 27 | ||||
-rw-r--r-- | grc/blocks/bus_source.xml | 27 | ||||
-rw-r--r-- | grc/blocks/bus_structure_sink.xml | 18 | ||||
-rw-r--r-- | grc/blocks/bus_structure_source.xml | 18 | ||||
-rw-r--r-- | grc/blocks/pad_sink.xml | 9 | ||||
-rw-r--r-- | grc/blocks/pad_source.xml | 10 | ||||
-rw-r--r-- | grc/gui/ActionHandler.py | 19 | ||||
-rw-r--r-- | grc/gui/Actions.py | 10 | ||||
-rw-r--r-- | grc/gui/Bars.py | 1 | ||||
-rw-r--r-- | grc/gui/Block.py | 13 | ||||
-rw-r--r-- | grc/gui/Connection.py | 18 | ||||
-rw-r--r-- | grc/gui/Element.py | 3 | ||||
-rw-r--r-- | grc/gui/FlowGraph.py | 14 | ||||
-rw-r--r-- | grc/gui/Port.py | 25 | ||||
-rw-r--r-- | grc/gui/Utils.py | 6 | ||||
-rw-r--r-- | grc/python/Block.py | 31 | ||||
-rw-r--r-- | grc/python/Connection.py | 6 | ||||
-rw-r--r-- | grc/python/Constants.py | 1 | ||||
-rw-r--r-- | grc/python/FlowGraph.py | 80 | ||||
-rw-r--r-- | grc/python/Generator.py | 2 | ||||
-rw-r--r-- | grc/python/Port.py | 8 | ||||
-rw-r--r-- | grc/python/block.dtd | 6 | ||||
-rw-r--r-- | grc/python/convert_hier.py | 12 |
29 files changed, 589 insertions, 28 deletions
diff --git a/grc/base/Block.py b/grc/base/Block.py index 223f3183b7..1fb0db9ad8 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -22,6 +22,7 @@ from Element import Element from Cheetah.Template import Template from UserDict import UserDict +from .. gui import Actions class TemplateArg(UserDict): """ @@ -63,6 +64,7 @@ class Block(Element): block a new block """ #build the block + Element.__init__(self, flow_graph) #grab the data params = n.findall('param') @@ -73,6 +75,10 @@ class Block(Element): self._category = n.find('category') or '' self._grc_source = n.find('grc_source') or '' self._block_wrapper_path = n.find('block_wrapper_path') + self._bussify_sink = n.find('bus_sink') + self._bussify_source = n.find('bus_source') + + #create the param objects self._params = list() #add the id param @@ -110,6 +116,7 @@ class Block(Element): raise Exception, 'Key "%s" already exists in sources'%key #store the port self.get_sources().append(source) + self.back_ofthe_bus(self.get_sources()) #create the sink objects self._sinks = list() for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): @@ -119,7 +126,21 @@ class Block(Element): raise Exception, 'Key "%s" already exists in sinks'%key #store the port self.get_sinks().append(sink) + self.back_ofthe_bus(self.get_sinks()) + self.current_bus_structure = {'source':'','sink':''}; + + def back_ofthe_bus(self, portlist): + portlist.sort(key=lambda a: a.get_type() == 'bus'); + + + def filter_bus_port(self, ports): + buslist = [i for i in ports if i.get_type() == 'bus']; + if len(buslist) == 0: + return ports; + else: + return buslist; + def get_enabled(self): """ Get the enabled state of the block. @@ -148,7 +169,12 @@ class Block(Element): def get_category(self): return self._category def get_doc(self): return '' def get_ports(self): return self.get_sources() + self.get_sinks() + def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()); + + + def get_children(self): return self.get_ports() + self.get_params() + def get_children_gui(self): return self.get_ports_gui() + self.get_params() def get_block_wrapper_path(self): return self._block_wrapper_path ############################################## @@ -164,6 +190,7 @@ class Block(Element): def get_sink_keys(self): return _get_keys(self._sinks) def get_sink(self, key): return _get_elem(self._sinks, key) def get_sinks(self): return self._sinks + def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks()) ############################################## # Access Sources @@ -171,6 +198,9 @@ class Block(Element): def get_source_keys(self): return _get_keys(self._sources) def get_source(self, key): return _get_elem(self._sources, key) def get_sources(self): return self._sources + def get_sources_gui(self): return self.filter_bus_port(self.get_sources()); + + def get_connections(self): return sum([port.get_connections() for port in self.get_ports()], []) @@ -188,6 +218,7 @@ class Block(Element): tmpl = str(tmpl) if '$' not in tmpl: return tmpl n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) + try: return str(Template(tmpl, n)) except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl) @@ -235,6 +266,77 @@ class Block(Element): """ return False + + def form_bus_structure(self, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + struct = [range(len(get_p()))]; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + + + structlet = []; + last = 0; + for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: + structlet.extend(map(lambda a: a+last, range(j))); + last = structlet[-1] + 1; + struct = [structlet]; + if bus_structure: + + struct = bus_structure + + self.current_bus_structure[direc] = struct; + return struct + + def bussify(self, n, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + + for elt in get_p(): + for connect in elt.get_connections(): + self.get_parent().remove_element(connect); + + + + + + + if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: + + struct = self.form_bus_structure(direc); + self.current_bus_structure[direc] = struct; + if get_p()[0].get_nports(): + n['nports'] = str(1); + + for i in range(len(struct)): + n['key'] = str(len(get_p())); + n = odict(n); + port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc); + get_p().append(port); + + + + + elif 'bus' in map(lambda a: a.get_type(), get_p()): + for elt in get_p_gui(): + get_p().remove(elt); + self.current_bus_structure[direc] = '' + + + ############################################## ## Import/Export Methods ############################################## @@ -246,8 +348,14 @@ class Block(Element): a nested data odict """ n = odict() + + n['key'] = self.get_key() n['param'] = map(lambda p: p.export_data(), self.get_params()) + if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): + n['bus_sink'] = str(1); + if 'bus' in map(lambda a: a.get_type(), self.get_sources()): + n['bus_source'] = str(1); return n def import_data(self, n): @@ -262,6 +370,7 @@ class Block(Element): Args: n: the nested data odict """ + get_hash = lambda: hash(tuple(map(hash, self.get_params()))) my_hash = 0 while get_hash() != my_hash: @@ -275,3 +384,17 @@ class Block(Element): #store hash and call rewrite my_hash = get_hash() self.rewrite() + bussinks = n.findall('bus_sink'); + if len(bussinks) > 0 and not self._bussify_sink: + self.bussify({'name':'bus','type':'bus'}, 'sink') + elif len(bussinks) > 0: + self.bussify({'name':'bus','type':'bus'}, 'sink') + self.bussify({'name':'bus','type':'bus'}, 'sink') + + bussrcs = n.findall('bus_source'); + if len(bussrcs) > 0 and not self._bussify_source: + self.bussify({'name':'bus','type':'bus'}, 'source') + elif len(bussrcs) > 0: + self.bussify({'name':'bus','type':'bus'}, 'source') + self.bussify({'name':'bus','type':'bus'}, 'source') + diff --git a/grc/base/Connection.py b/grc/base/Connection.py index b9afe1434d..654eedb357 100644 --- a/grc/base/Connection.py +++ b/grc/base/Connection.py @@ -43,13 +43,31 @@ class Connection(Element): if port.is_sink(): sink = port if not source: raise ValueError('Connection could not isolate source') if not sink: raise ValueError('Connection could not isolate sink') + + busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2 + if not busses == 0: raise ValueError('busses must get with busses') + + if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): + raise ValueError('port connections must have same cardinality'); #ensure that this connection (source -> sink) is unique for connection in self.get_parent().get_connections(): if connection.get_source() is source and connection.get_sink() is sink: raise Exception('This connection between source and sink is not unique.') self._source = source self._sink = sink - + + if source.get_type() == 'bus': + + sources = source.get_associated_ports(); + sinks = sink.get_associated_ports(); + + for i in range(len(sources)): + try: + flow_graph.connect(sources[i], sinks[i]); + except: + pass + + def __str__(self): return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( self.get_source().get_parent(), @@ -71,6 +89,8 @@ class Connection(Element): if source_type != sink_type: self.add_error_message('Source type "%s" does not match sink type "%s".'%(source_type, sink_type)) + + def get_enabled(self): """ Get the enabled state of this connection. diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index e8c49466d0..a35afa363b 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -56,6 +56,48 @@ class FlowGraph(Element): if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id')) + def rewrite(self): + def refactor_bus_structure(): + + for block in self.get_blocks(): + for direc in ['source', 'sink']: + if direc == 'source': + get_p = block.get_sources; + get_p_gui = block.get_sources_gui; + bus_structure = block.form_bus_structure('source'); + else: + get_p = block.get_sinks; + get_p_gui = block.get_sinks_gui + 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: + for connect in get_p_gui()[-1].get_connections(): + block.get_parent().remove_element(connect); + get_p().remove(get_p_gui()[-1]); + elif len(get_p_gui()) < len(bus_structure): + n = {'name':'bus','type':'bus'}; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + n['nports'] = str(1); + + times = range(len(get_p_gui()), len(bus_structure)); + + for i in times: + n['key'] = str(len(get_p())); + n = odict(n); + 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(); + def get_option(self, key): """ @@ -76,7 +118,15 @@ class FlowGraph(Element): ## Access Elements ############################################## def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] - def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements()) + def get_blocks_unordered(self): return filter(lambda e: e.is_block(), self.get_elements()) + def get_blocks(self): + blocks = self.get_blocks_unordered(); + for i in range(len(blocks)): + if blocks[i].get_key() == 'variable': + blk = blocks[i]; + blocks.remove(blk); + blocks.insert(1, blk); + return blocks; def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) def get_children(self): return self.get_elements() def get_elements(self): @@ -123,10 +173,15 @@ class FlowGraph(Element): Returns: the new block or None if not found """ + 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) - return block + self.get_elements().append(block); + if block._bussify_sink: + block.bussify({'name':'bus','type':'bus'}, 'sink') + if block._bussify_source: + block.bussify({'name':'bus','type':'bus'}, 'source') + return block; def connect(self, porta, portb): """ @@ -140,7 +195,10 @@ class FlowGraph(Element): Returns: the new connection """ + connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) + + self.get_elements().append(connection) return connection @@ -151,6 +209,7 @@ class FlowGraph(Element): If the element is a block, remove its connections. If the element is a connection, just remove the connection. """ + if element not in self.get_elements(): return #found a port, set to parent signal block if element.is_port(): @@ -159,7 +218,14 @@ class FlowGraph(Element): if element.is_block(): for port in element.get_ports(): map(self.remove_element, port.get_connections()) + if element.is_connection(): + if element.is_bus(): + cons_list = [] + for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): + cons_list.extend(i); + map(self.remove_element, cons_list); self.get_elements().remove(element) + def evaluate(self, expr): """ @@ -214,6 +280,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())) + #build the connections for connection_n in connections_n: #try to make the connection diff --git a/grc/base/ParseXML.py b/grc/base/ParseXML.py index 0d19f6b212..e8f9b0583e 100644 --- a/grc/base/ParseXML.py +++ b/grc/base/ParseXML.py @@ -78,6 +78,7 @@ def _from_file(xml): #delistify if the length of values is 1 for key, values in nested_data.iteritems(): if len(values) == 1: nested_data[key] = values[0] + return odict({tag: nested_data}) def to_file(nested_data, xml_file): diff --git a/grc/base/Port.py b/grc/base/Port.py index 0e58f583c3..1b2acb0582 100644 --- a/grc/base/Port.py +++ b/grc/base/Port.py @@ -38,6 +38,7 @@ class Port(Element): self._type = n['type'] self._dir = dir + def validate(self): """ Validate the port. @@ -62,7 +63,14 @@ class Port(Element): def is_port(self): return True def get_color(self): return '#FFFFFF' - def get_name(self): return self._name + def get_name(self): + number = '' + if self.get_type() == 'bus': + busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()); + + number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())); + return self._name + number + def get_key(self): return self._key def is_sink(self): return self._dir == 'sink' def is_source(self): return self._dir == 'source' @@ -87,3 +95,23 @@ class Port(Element): a list of connection objects """ return filter(lambda c: c.get_enabled(), self.get_connections()) + + def get_associated_ports(self): + if not self.get_type() == 'bus': + return [self]; + else: + if self.is_source(): + get_p = self.get_parent().get_sources; + bus_structure = self.get_parent().current_bus_structure['source']; + direc = 'source' + else: + get_p = self.get_parent().get_sinks; + bus_structure = self.get_parent().current_bus_structure['sink']; + direc = 'sink' + + ports = [i for i in get_p() if not i.get_type() == 'bus']; + if bus_structure: + busses = [i for i in get_p() if i.get_type() == 'bus']; + bus_index = busses.index(self); + ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports); + return ports; diff --git a/grc/base/flow_graph.dtd b/grc/base/flow_graph.dtd index 74f48f10ab..becfc21e9b 100644 --- a/grc/base/flow_graph.dtd +++ b/grc/base/flow_graph.dtd @@ -24,10 +24,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <!ELEMENT flow_graph (timestamp?, block*, connection*)> <!-- optional timestamp --> <!ELEMENT timestamp (#PCDATA)> <!-- Block --> -<!ELEMENT block (key, param*)> +<!ELEMENT block (key, param*, bus_sink?, bus_source?)> <!ELEMENT param (key, value)> <!ELEMENT key (#PCDATA)> <!ELEMENT value (#PCDATA)> +<!ELEMENT bus_sink (#PCDATA)> +<!ELEMENT bus_source (#PCDATA)> <!-- Connection --> <!ELEMENT connection (source_block_id, sink_block_id, source_key, sink_key)> <!ELEMENT source_block_id (#PCDATA)> diff --git a/grc/blocks/bus_sink.xml b/grc/blocks/bus_sink.xml new file mode 100644 index 0000000000..273b4c517a --- /dev/null +++ b/grc/blocks/bus_sink.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Sink</name> + <key>bus_sink</key> + <make>$yesno.yesno</make> + + <param> + <name>On/Off</name> + <key>yesno</key> + <type>enum</type> + <option> + <name>On</name> + <key>on</key> + <opt>yesno:True</opt> + </option> + <option> + <name>Off</name> + <key>off</key> + <opt>yesno:False</opt> + </option> + </param> +</block> diff --git a/grc/blocks/bus_source.xml b/grc/blocks/bus_source.xml new file mode 100644 index 0000000000..15e4a9f31b --- /dev/null +++ b/grc/blocks/bus_source.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Source</name> + <key>bus_source</key> + <make>$yesno.yesno</make> + + <param> + <name>On/Off</name> + <key>yesno</key> + <type>enum</type> + <option> + <name>On</name> + <key>on</key> + <opt>yesno:True</opt> + </option> + <option> + <name>Off</name> + <key>off</key> + <opt>yesno:False</opt> + </option> + </param> +</block> diff --git a/grc/blocks/bus_structure_sink.xml b/grc/blocks/bus_structure_sink.xml new file mode 100644 index 0000000000..df16657282 --- /dev/null +++ b/grc/blocks/bus_structure_sink.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Sink Structure</name> + <key>bus_structure_sink</key> + <make>None</make> + + <param> + <name>Structure</name> + <key>struct</key> + <value></value> + <type>raw</type> + </param> +</block> diff --git a/grc/blocks/bus_structure_source.xml b/grc/blocks/bus_structure_source.xml new file mode 100644 index 0000000000..27652ca3b3 --- /dev/null +++ b/grc/blocks/bus_structure_source.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Source Structure</name> + <key>bus_structure_source</key> + <make>None</make> + + <param> + <name>Structure</name> + <key>struct</key> + <value></value> + <type>raw</type> + </param> +</block> diff --git a/grc/blocks/pad_sink.xml b/grc/blocks/pad_sink.xml index f0e10a3391..37e132c34c 100644 --- a/grc/blocks/pad_sink.xml +++ b/grc/blocks/pad_sink.xml @@ -62,6 +62,13 @@ None;self.message_port_register_hier_in($label) <value>1</value> <type>int</type> </param> + + <param> + <name>Num Streams</name> + <key>num_streams</key> + <value>1</value> + <type>int</type> + </param> <param> <name>Optional</name> <key>optional</key> @@ -78,10 +85,12 @@ None;self.message_port_register_hier_in($label) </option> </param> <check>$vlen > 0</check> + <check>$num_streams > 0</check> <sink> <name>in</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_streams</nports> </sink> <doc> The inputs of this block will become the outputs to this flow graph when it is instantiated as a hierarchical block. diff --git a/grc/blocks/pad_source.xml b/grc/blocks/pad_source.xml index a56a65dcc3..b6faebfc68 100644 --- a/grc/blocks/pad_source.xml +++ b/grc/blocks/pad_source.xml @@ -62,6 +62,14 @@ None;self.message_port_register_hier_out($label) <value>1</value> <type>int</type> </param> + + <param> + <name>Num Streams</name> + <key>num_streams</key> + <value>1</value> + <type>int</type> + </param> + <param> <name>Optional</name> <key>optional</key> @@ -78,10 +86,12 @@ None;self.message_port_register_hier_out($label) </option> </param> <check>$vlen > 0</check> + <check>$num_streams > 0</check> <source> <name>out</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_streams</nports> </source> <doc> The outputs of this block will become the inputs to this flow graph when it is instantiated as a hierarchical block. diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index add32dbdac..fa3e960d26 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -447,6 +447,23 @@ class ActionHandler: for b in self.get_flow_graph().get_selected_blocks(): if b._grc_source: self.main_window.new_page(b._grc_source, show=True); + elif action == Actions.BUSSIFY_SOURCES: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'source'); + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + + elif action == Actions.BUSSIFY_SINKS: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'sink') + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + + else: print '!!! Action "%s" not handled !!!'%action ################################################## # Global Actions for all States @@ -466,6 +483,8 @@ class ActionHandler: 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())) Actions.RELOAD_BLOCKS.set_sensitive(True) #set the exec and stop buttons self.update_exec_stop() diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 9225b0bd52..5832e08bf0 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -295,3 +295,13 @@ OPEN_HIER = Action( tooltip='Open the source of the selected hierarchical block', stock_id=gtk.STOCK_JUMP_TO, ) +BUSSIFY_SOURCES = Action( + label='Toggle So_urce Bus', + tooltip='Gang source ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, +) +BUSSIFY_SINKS = Action( + label='Toggle S_ink Bus', + tooltip='Gang sink ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, +) diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index d95d23f1fe..770e705ffc 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -52,6 +52,7 @@ TOOLBAR_LIST = ( None, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER, + Actions.BUSSIFY_SOURCES, ) ##The list of actions and categories for the menu bar. diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 11e66bff85..e69fe1a418 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -124,6 +124,7 @@ class Block(Element): def create_shapes(self): """Update the block, parameters, and ports when a change occurs.""" + Element.create_shapes(self) if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H)) elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W)) @@ -173,7 +174,10 @@ class Block(Element): self.H = max(*( [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \ sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION - for ports in (self.get_sources(), self.get_sinks())] + for ports in (self.get_sources_gui(), self.get_sinks_gui())] + + [4*PORT_BORDER_SEPARATION + \ + sum([(port.H) + PORT_SEPARATION for port in ports]) - PORT_SEPARATION + for ports in ([i for i in self.get_sources_gui() if i.get_type() == 'bus'], [i for i in self.get_sinks_gui() if i.get_type() == 'bus'])] )) def draw(self, gc, window): @@ -196,7 +200,10 @@ class Block(Element): elif self.is_vertical(): window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) #draw ports - for port in self.get_ports(): port.draw(gc, window) + + + for port in self.get_ports_gui(): + port.draw(gc, window) def what_is_selected(self, coor, coor_m=None): """ @@ -209,7 +216,7 @@ class Block(Element): Returns: this block, a port, or None """ - for port in self.get_ports(): + for port in self.get_ports_gui(): port_selected = port.what_is_selected(coor, coor_m) if port_selected: return port_selected return Element.what_is_selected(self, coor, coor_m) diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py index 5d24fefded..4f46e73ea9 100644 --- a/grc/gui/Connection.py +++ b/grc/gui/Connection.py @@ -62,7 +62,10 @@ class Connection(Element): self._sink_coor = None self._source_coor = None #get the source coordinate - connector_length = self.get_source().get_connector_length() + try: + connector_length = self.get_source().get_connector_length() + except: + return self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.get_source().get_rotation()) #get the sink coordinate connector_length = self.get_sink().get_connector_length() + CONNECTOR_ARROW_HEIGHT @@ -133,7 +136,11 @@ class Connection(Element): source = self.get_source() #check for changes if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes() - elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): self._update_after_move() + elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): + try: + self._update_after_move() + except: + return #cache values self._sink_rot = sink.get_rotation() self._source_rot = source.get_rotation() @@ -145,5 +152,8 @@ class Connection(Element): else: border_color = Colors.CONNECTION_DISABLED_COLOR Element.draw(self, gc, window, bg_color=None, border_color=border_color) #draw arrow on sink port - gc.set_foreground(self._arrow_color) - window.draw_polygon(gc, True, self._arrow) + try: + gc.set_foreground(self._arrow_color) + window.draw_polygon(gc, True, self._arrow) + except: + return diff --git a/grc/gui/Element.py b/grc/gui/Element.py index eac59d88eb..cd97a3fb21 100644 --- a/grc/gui/Element.py +++ b/grc/gui/Element.py @@ -69,7 +69,7 @@ class Element(object): Create labels (if applicable) and call on all children. Call this base method before creating labels in the element. """ - for child in self.get_children(): child.create_labels() + for child in self.get_children():child.create_labels() def create_shapes(self): """ @@ -89,6 +89,7 @@ class Element(object): border_color: the color for lines and rectangle borders bg_color: the color for the inside of the rectangle """ + X,Y = self.get_coordinate() for (rX,rY),(W,H) in self._areas_list: aX = X + rX diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 3f17c47bc8..a238ed166a 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -65,6 +65,8 @@ class FlowGraph(Element): Actions.BLOCK_PARAM_MODIFY, Actions.BLOCK_CREATE_HIER, Actions.OPEN_HIER, + Actions.BUSSIFY_SOURCES, + Actions.BUSSIFY_SINKS, ]: self._context_menu.append(action.create_menu_item()) ########################################################################### @@ -141,6 +143,7 @@ class FlowGraph(Element): Args: clipboard: the nested data of blocks, connections """ + selected = set() (x_min, y_min), blocks_n, connections_n = clipboard old_id2block = dict() @@ -158,18 +161,23 @@ class FlowGraph(Element): #set params params_n = block_n.findall('param') for param_n in params_n: + param_key = param_n.find('key') param_value = param_n.find('value') #setup id parameter if param_key == 'id': old_id2block[param_value] = block #if the block id is not unique, get a new block id - if param_value in [block.get_id() for block in self.get_blocks()]: + if param_value in [bluck.get_id() for bluck in self.get_blocks()]: param_value = self._get_unique_id(param_value) + + #set value to key + block.get_param(param_key).set_value(param_value) #move block to offset coordinate block.move((x_off, y_off)) + #update before creating connections self.update() #create connections @@ -333,9 +341,13 @@ class FlowGraph(Element): Call the top level rewrite and validate. Call the top level create labels and shapes. """ + self.rewrite() + self.validate() + self.create_labels() + self.create_shapes() ########################################################################## diff --git a/grc/gui/Port.py b/grc/gui/Port.py index 7b4c27dd5f..2ccc394971 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -45,15 +45,22 @@ class Port(Element): def create_shapes(self): """Create new areas and labels for the port.""" Element.create_shapes(self) + #get current rotation rotation = self.get_rotation() #get all sibling ports - if self.is_source(): ports = self.get_parent().get_sources() - elif self.is_sink(): ports = self.get_parent().get_sinks() + if self.is_source(): ports = self.get_parent().get_sources_gui() + elif self.is_sink(): ports = self.get_parent().get_sinks_gui() #get the max width self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) #get a numeric index for this port relative to its sibling ports - index = ports.index(self) + try: + index = ports.index(self) + except: + if hasattr(self, '_connector_length'): + del self._connector_length; + return + length = len(ports) #reverse the order of ports for these rotations if rotation in (180, 270): index = length-index-1 @@ -81,7 +88,15 @@ class Port(Element): self._connector_coordinate = (x+self.H/2, y+1+self.W) #the connector length self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index - + + def modify_height(self, start_height): + type_dict = {'bus':(lambda a: a * 3)}; + + if self.get_type() in type_dict: + return type_dict[self.get_type()](start_height); + else: + return start_height; + def create_labels(self): """Create the labels for the socket.""" Element.create_labels(self) @@ -91,6 +106,7 @@ class Port(Element): layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self)) self.w, self.h = layout.get_pixel_size() self.W, self.H = 2*PORT_LABEL_PADDING+self.w, 2*PORT_LABEL_PADDING+self.h + self.H = self.modify_height(self.H); #create the pixmap pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) gc = pixmap.new_gc() @@ -111,6 +127,7 @@ class Port(Element): gc: the graphics context window: the gtk window to draw on """ + Element.draw( self, gc, window, bg_color=self._bg_color, border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index b68c19c4e1..407c875a87 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -101,4 +101,10 @@ def parse_template(tmpl_str, **kwargs): a string of the parsed template """ kwargs['encode'] = gobject.markup_escape_text + #try: + # cat = str(Template(tmpl_str, kwargs)) + #except TypeError: + # print 'guppy' + # print tmpl_str + # print str(kwargs['param'].get_error_messages()) return str(Template(tmpl_str, kwargs)) diff --git a/grc/python/Block.py b/grc/python/Block.py index 806de46724..d365c43319 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -31,6 +31,8 @@ class Block(_Block, _GUIBlock): ##for make sink to keep track of indexes _sink_count = 0 + + def __init__(self, flow_graph, n): """ Make a new block from nested data. @@ -50,6 +52,8 @@ class Block(_Block, _GUIBlock): 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 _Block.__init__( self, @@ -58,6 +62,21 @@ class Block(_Block, _GUIBlock): ) _GUIBlock.__init__(self) + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self._bus_structure_source; + else: + bus_structure = self._bus_structure_sink; + + bus_structure = self.resolve_dependencies(bus_structure); + + if not bus_structure: return '' + try: + clean_bus_structure = self.get_parent().evaluate(bus_structure) + return clean_bus_structure + + except: return '' + def throttle(self): return bool(self._throttle) def validate(self): @@ -84,10 +103,11 @@ class Block(_Block, _GUIBlock): def rectify(ports): #restore integer contiguity after insertion #rectify the port names with the index + self.back_ofthe_bus(ports); for i, port in enumerate(ports): port._key = str(i) port._name = port._n['name'] - if len(ports) > 1: port._name += str(i) + if len(ports) > 1 and not port._type == 'bus': port._name += str(i) def insert_port(get_ports, get_port, key): prev_port = get_port(str(int(key)-1)) @@ -121,14 +141,19 @@ class Block(_Block, _GUIBlock): #remove excess ports and connections if nports < num_ports: for key in reversed(map(str, range(index_first+nports, index_first+num_ports))): - remove_port(get_ports, get_port, key) + remove_port(get_ports, get_port, key); + + continue #add more ports if nports > num_ports: for key in map(str, range(index_first+num_ports, index_first+nports)): insert_port(get_ports, get_port, key) + + continue - + + def port_controller_modify(self, direction): """ Change the port controller. diff --git a/grc/python/Connection.py b/grc/python/Connection.py index 341dd2d821..97bf61d590 100644 --- a/grc/python/Connection.py +++ b/grc/python/Connection.py @@ -33,7 +33,9 @@ class Connection(_Connection, _GUIConnection): def is_message(self): return self.get_source().get_type() == self.get_sink().get_type() == 'message' - + + def is_bus(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'bus' def validate(self): """ Validate the connections. @@ -44,3 +46,5 @@ class Connection(_Connection, _GUIConnection): sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() if source_size != sink_size: self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) + + diff --git a/grc/python/Constants.py b/grc/python/Constants.py index b8dc9a96a1..5666a2378b 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -59,6 +59,7 @@ CORE_TYPES = ( #name, key, sizeof, color ('Integer 8', 's8', 1, '#FF66FF'), ('Message Queue', 'msg', 0, '#777777'), ('Async Message', 'message', 0, '#C0C0C0'), + ('Bus Connection', 'bus', 0, '#FFFFFF'), ('Wildcard', '', 0, '#FFFFFF'), ) diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 1080006cf5..89acfd89ef 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -20,11 +20,17 @@ 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 .. base.odict import odict import re _variable_matcher = re.compile('^(variable\w*)$') _parameter_matcher = re.compile('^(parameter)$') _monitors_searcher = re.compile('(ctrlport_monitor)') +_bussink_searcher = re.compile('^(bus_sink)$') +_bussrc_searcher = re.compile('^(bus_source)$') +_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') +_bus_struct_src_searcher = re.compile('^(bus_structure_source)$') + class FlowGraph(_FlowGraph, _GUIFlowGraph): @@ -48,6 +54,7 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): if not code: raise Exception, 'Cannot evaluate empty statement.' my_hash = hash(code) ^ namespace_hash #cache if does not exist + if not self._eval_cache.has_key(my_hash): self._eval_cache[my_hash] = eval(code, namespace, namespace) #return from cache @@ -69,6 +76,10 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): }[direction] # we only want stream ports sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); + expanded_pads = []; + for i in sorted_pads: + for j in range(i.get_param('num_streams').get_evaluated()): + expanded_pads.append(i); #load io signature return [{ 'label': str(pad.get_param('label').get_evaluated()), @@ -76,7 +87,7 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): 'vlen': str(pad.get_param('vlen').get_evaluated()), 'size': pad.get_param('type').get_opt('size'), 'optional': bool(pad.get_param('optional').get_evaluated()), - } for pad in sorted_pads] + } for pad in expanded_pads] def get_pad_sources(self): """ @@ -145,12 +156,73 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks()) return monitors + def get_bussink(self): + bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) + + for i in bussink: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True; + + return False + + + + def get_bussrc(self): + bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) + + for i in bussrc: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True; + + return False + + def get_bus_structure_sink(self): + bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) + + return bussink + + def get_bus_structure_src(self): + bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) + + return bussrc + + def rewrite(self): """ Flag the namespace to be renewed. """ + + + + + def reconnect_bus_blocks(): + for block in self.get_blocks(): + + if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): + + + for i in range(len(block.get_sources_gui())): + if len(block.get_sources_gui()[i].get_connections()) > 0: + source = block.get_sources_gui()[i] + sink = [] + + for j in range(len(source.get_connections())): + sink.append(source.get_connections()[j].get_sink()); + + + for elt in source.get_connections(): + self.remove_element(elt); + for j in sink: + self.connect(source, j); self._renew_eval_ns = True - _FlowGraph.rewrite(self) + _FlowGraph.rewrite(self); + reconnect_bus_blocks(); + + + + def evaluate(self, expr): """ @@ -163,6 +235,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): Returns: the evaluated data """ + + if self._renew_eval_ns: self._renew_eval_ns = False #reload namespace @@ -188,6 +262,8 @@ class FlowGraph(_FlowGraph, _GUIFlowGraph): #make namespace public self.n = n self.n_hash = hash(str(n)) + #evaluate e = self._eval(expr, self.n, self.n_hash) + return e diff --git a/grc/python/Generator.py b/grc/python/Generator.py index 77abc45281..47fe1d98fd 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -123,7 +123,7 @@ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') #list of regular blocks (all blocks minus the special ones) blocks = filter(lambda b: b not in (imports + parameters), blocks) #list of connections where each endpoint is enabled - connections = filter(lambda c: not (c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) + connections = filter(lambda c: not (c.is_bus() or c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) #list of variable names diff --git a/grc/python/Port.py b/grc/python/Port.py index d4afa6cf77..8ebf5c7b05 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -110,7 +110,8 @@ class Port(_Port, _GUIPort): self._nports = n.find('nports') or '' self._vlen = n.find('vlen') or '' self._optional = bool(n.find('optional')) - + + def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() def is_type_empty(self): return not self._n['type'] @@ -174,6 +175,9 @@ class Port(_Port, _GUIPort): try: return int(self.get_parent().get_parent().evaluate(vlen)) except: return 1 + + + def get_nports(self): """ Get the number of ports. @@ -191,6 +195,8 @@ class Port(_Port, _GUIPort): if 0 < nports: return nports except: return 1 + + def get_optional(self): return bool(self._optional) def get_color(self): diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 292ea06cb6..99d38a0d46 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -25,7 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Top level element. A block contains a name, ...parameters list, and list of IO ports. --> -<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, check*, sink*, source*, doc?, grc_source?)> +<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> <!-- Sub level elements. --> @@ -44,11 +44,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <!ELEMENT name (#PCDATA)> <!ELEMENT key (#PCDATA)> <!ELEMENT check (#PCDATA)> +<!ELEMENT bus_sink (#PCDATA)> +<!ELEMENT bus_source (#PCDATA)> <!ELEMENT opt (#PCDATA)> <!ELEMENT type (#PCDATA)> <!ELEMENT hide (#PCDATA)> <!ELEMENT vlen (#PCDATA)> <!ELEMENT nports (#PCDATA)> +<!ELEMENT bus_structure_sink (#PCDATA)> +<!ELEMENT bus_structure_source (#PCDATA)> <!ELEMENT var_make (#PCDATA)> <!ELEMENT make (#PCDATA)> <!ELEMENT value (#PCDATA)> diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index 508ec63b2b..de76827541 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -28,6 +28,10 @@ def convert_hier(flow_graph, python_file): input_msgp = flow_graph.get_msg_pad_sources(); output_msgp = flow_graph.get_msg_pad_sinks(); parameters = flow_graph.get_parameters() + bussink = flow_graph.get_bussink() + bussrc = flow_graph.get_bussrc() + bus_struct_sink = flow_graph.get_bus_structure_sink() + bus_struct_src = flow_graph.get_bus_structure_src() block_key = flow_graph.get_option('id') block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() block_category = flow_graph.get_option('category') @@ -58,6 +62,10 @@ def convert_hier(flow_graph, python_file): params_n.append(param_n) block_n['param'] = params_n #sink data stream ports + if bussink: + block_n['bus_sink'] = '1'; + if bussrc: + block_n['bus_source'] = '1'; block_n['sink'] = list() for input_sig in input_sigs: sink_n = odict() @@ -75,6 +83,10 @@ def convert_hier(flow_graph, python_file): block_n['sink'].append(sink_n) #source data stream ports block_n['source'] = list() + if bus_struct_sink: + block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value(); + if bus_struct_src: + block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value(); for output_sig in output_sigs: source_n = odict() source_n['name'] = output_sig['label'] |