diff options
Diffstat (limited to 'grc')
-rw-r--r-- | grc/blocks/grc.tree.yml | 4 | ||||
-rw-r--r-- | grc/core/FlowGraph.py | 2 | ||||
-rw-r--r-- | grc/core/blocks/block.py | 223 | ||||
-rw-r--r-- | grc/core/generator/cpp_top_block.py | 29 | ||||
-rw-r--r-- | grc/core/generator/top_block.py | 25 | ||||
-rw-r--r-- | grc/core/ports/port.py | 54 | ||||
-rw-r--r-- | grc/gui/Application.py | 4 | ||||
-rw-r--r-- | grc/gui/canvas/block.py | 27 | ||||
-rw-r--r-- | grc/gui/canvas/connection.py | 2 | ||||
-rw-r--r-- | grc/gui/canvas/flowgraph.py | 8 | ||||
-rw-r--r-- | grc/gui/canvas/port.py | 9 |
11 files changed, 352 insertions, 35 deletions
diff --git a/grc/blocks/grc.tree.yml b/grc/blocks/grc.tree.yml index dc1067d2eb..50b08291ef 100644 --- a/grc/blocks/grc.tree.yml +++ b/grc/blocks/grc.tree.yml @@ -4,6 +4,10 @@ - pad_sink - virtual_source - virtual_sink + - bus_sink + - bus_source + - bus_structure_sink + - bus_structure_source - epy_block - epy_module - note diff --git a/grc/core/FlowGraph.py b/grc/core/FlowGraph.py index 04b73957b3..b57a93d551 100644 --- a/grc/core/FlowGraph.py +++ b/grc/core/FlowGraph.py @@ -284,6 +284,7 @@ class FlowGraph(Element): connection = self.parent_platform.Connection( parent=self, source=porta, sink=portb) self.connections.add(connection) + return connection def disconnect(self, *ports): @@ -377,6 +378,7 @@ class FlowGraph(Element): block.import_data(**block_data) + self.rewrite() # TODO: Figure out why this has to be called twice to populate bus ports correctly self.rewrite() # evaluate stuff like nports before adding connections # build the connections diff --git a/grc/core/blocks/block.py b/grc/core/blocks/block.py index 50c7873e9d..29976af282 100644 --- a/grc/core/blocks/block.py +++ b/grc/core/blocks/block.py @@ -86,9 +86,28 @@ class Block(Element): self.active_sources = [] # on rewrite self.active_sinks = [] # on rewrite - self.states = {'state': True} + self.states = {'state': True, 'bus_source': False, 'bus_sink': False, 'bus_structure': None} + if 'cpp' in self.flags: - self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++ + self.orig_cpp_templates = self.cpp_templates # The original template, in case we have to edit it when transpiling to C++ + + self.current_bus_structure = {'source': '', 'sink': ''} + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self.bus_structure_source + else: + bus_structure = self.bus_structure_sink + + if not bus_structure: + return '' # TODO: Don't like empty strings. should change this to None eventually + + try: + clean_bus_structure = self.evaluate(bus_structure) + return clean_bus_structure + except: + return '' + # region Rewrite_and_Validation def rewrite(self): @@ -112,12 +131,81 @@ class Block(Element): self._rewrite_nports(ports) rekey(ports) + self.update_bus_logic() # disconnect hidden ports self.parent_flowgraph.disconnect(*[p for p in self.ports() if p.hidden]) self.active_sources = [p for p in self.sources if not p.hidden] self.active_sinks = [p for p in self.sinks if not p.hidden] + def update_bus_logic(self): + ############################### + ## Bus Logic + ############################### + + for direc in {'source','sink'}: + if direc == 'source': + ports = self.sources + ports_gui = self.filter_bus_port(self.sources) + bus_structure = self.get_bus_structure('source') + bus_state = self.bus_source + else: + ports = self.sinks + ports_gui = self.filter_bus_port(self.sinks) + bus_structure = self.get_bus_structure('sink') + bus_state = self.bus_sink + + # Remove the bus ports + removed_bus_ports = [] + removed_bus_connections = [] + if 'bus' in map(lambda a: a.dtype, ports): + for port in ports_gui: + for c in self.parent_flowgraph.connections: + if port is c.source_port or port is c.sink_port: + removed_bus_ports.append(port) + removed_bus_connections.append(c) + ports.remove(port) + + + if (bus_state): + struct = self.form_bus_structure(direc) + self.current_bus_structure[direc] = struct + + # Hide ports that are not part of the bus structure + #TODO: Blocks where it is desired to only have a subset + # of ports included in the bus still has some issues + for idx, port in enumerate(ports): + if any([idx in bus for bus in self.current_bus_structure[direc]]): + if (port.stored_hidden_state is None): + port.stored_hidden_state = port.hidden + port.hidden = True + + # Add the Bus Ports to the list of ports + for i in range(len(struct)): + # self.sinks = [port_factory(parent=self, **params) for params in self.inputs_data] + port = self.parent.parent.make_port(self,direction=direc,id=str(len(ports)),label='bus',dtype='bus',bus_struct=struct[i]) + ports.append(port) + + for (saved_port, connection) in zip(removed_bus_ports, removed_bus_connections): + if port.key == saved_port.key: + self.parent_flowgraph.connections.remove(connection) + if saved_port.is_source: + connection.source_port = port + if saved_port.is_sink: + connection.sink_port = port + self.parent_flowgraph.connections.add(connection) + + + else: + self.current_bus_structure[direc] = None + + # Re-enable the hidden property of the ports + for port in ports: + port.hidden = port.stored_hidden_state + port.stored_hidden_state = None + + + def _rewrite_nports(self, ports): for port in ports: if hasattr(port, 'master_port'): # Not a master port and no left-over clones @@ -238,6 +326,44 @@ class Block(Element): """Get the enabled state of the block""" return self.state != 'disabled' + @property + def bus_sink(self): + """Gets the block's current Toggle Bus Sink state.""" + return self.states['bus_sink'] + + @bus_sink.setter + def bus_sink(self, value): + """Sets the Toggle Bus Sink state for the block.""" + self.states['bus_sink'] = value + + @property + def bus_source(self): + """Gets the block's current Toggle Bus Sink state.""" + return self.states['bus_source'] + + @bus_source.setter + def bus_source(self, value): + """Sets the Toggle Bus Source state for the block.""" + self.states['bus_source'] = value + + @property + def bus_structure_source(self): + """Gets the block's current source bus structure.""" + try: + bus_structure = self.params['bus_structure_source'].value or None + except: + bus_structure = None + return bus_structure + + @property + def bus_structure_sink(self): + """Gets the block's current source bus structure.""" + try: + bus_structure = self.params['bus_structure_sink'].value or None + except: + bus_structure = None + return bus_structure + # endregion ############################################## @@ -461,3 +587,96 @@ class Block(Element): # Store hash and call rewrite pre_rewrite_hash = get_hash() self.rewrite() + + ############################################## + # Controller Modify + ############################################## + def filter_bus_port(self, ports): + buslist = [p for p in ports if p.dtype == 'bus'] + return buslist or ports + + def type_controller_modify(self, direction): + """ + Change the type controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + type_param = None + for param in filter(lambda p: p.is_enum(), self.get_params()): + children = self.get_ports() + self.get_params() + # Priority to the type controller + if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + # Use param if type param is unset + if not type_param: + type_param = param + if type_param: + # Try to increment the enum by direction + try: + keys = type_param.get_option_keys() + old_index = keys.index(type_param.get_value()) + new_index = (old_index + direction + len(keys)) % len(keys) + type_param.set_value(keys[new_index]) + changed = True + except: + pass + return changed + + def form_bus_structure(self, direc): + if direc == 'source': + ports = self.sources + bus_structure = self.get_bus_structure('source') + else: + ports = self.sinks + bus_structure = self.get_bus_structure('sink') + + struct = [range(len(ports))] + # struct = list(range(len(ports))) + #TODO for more complicated port structures, this code is needed but not working yet + if any([p.multiplicity for p in ports]): + structlet = [] + last = 0 + # group the ports with > n inputs together on the bus + cnt = 0 + idx = 0 + for p in ports: + if cnt > 0: + cnt -= 1 + continue + + if p.multiplicity > 1: + cnt = p.multiplicity-1 + structlet.append([idx+j for j in range(p.multiplicity)]) + else: + structlet.append([idx]) + + struct = structlet + if bus_structure: + struct = bus_structure + + self.current_bus_structure[direc] = struct + return struct + + def bussify(self, direc): + if direc == 'source': + ports = self.sources + ports_gui = self.filter_bus_port(self.sources) + self.bus_structure = self.get_bus_structure('source') + self.bus_source = not self.bus_source + else: + ports = self.sinks + ports_gui = self.filter_bus_port(self.sinks) + self.bus_structure = self.get_bus_structure('sink') + self.bus_sink = not self.bus_sink + + # Disconnect all the connections when toggling the bus state + for port in ports: + l_connections = list(port.connections()) + for connect in l_connections: + self.parent.remove_element(connect) + + self.update_bus_logic() diff --git a/grc/core/generator/cpp_top_block.py b/grc/core/generator/cpp_top_block.py index 4e668f25ce..d7b9c80ed4 100644 --- a/grc/core/generator/cpp_top_block.py +++ b/grc/core/generator/cpp_top_block.py @@ -353,9 +353,30 @@ class CppTopBlockGenerator(TopBlockGenerator): rendered = [] for con in sorted(connections, key=by_domain_and_blocks): template = templates[con.type] - code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) - if not self._generate_options.startswith('hb'): - code = 'this->tb->' + code - rendered.append(code) + + if con.source_port.dtype != 'bus': + code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) + if not self._generate_options.startswith('hb'): + code = 'this->tb->' + code + rendered.append(code) + else: + # Bus ports need to iterate over the underlying connections and then render + # the code for each subconnection + porta = con.source_port + portb = con.sink_port + fg = self._flow_graph + + if porta.dtype == 'bus' and portb.dtype == 'bus': + # which bus port is this relative to the bus structure + if len(porta.bus_structure) == len(portb.bus_structure): + for port_num in porta.bus_structure: + hidden_porta = porta.parent.sources[port_num] + hidden_portb = portb.parent.sinks[port_num] + connection = fg.parent_platform.Connection( + parent=self, source=hidden_porta, sink=hidden_portb) + code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb) + if not self._generate_options.startswith('hb'): + code = 'this->tb->' + code + rendered.append(code) return rendered diff --git a/grc/core/generator/top_block.py b/grc/core/generator/top_block.py index d449d1baa9..32e8574aed 100644 --- a/grc/core/generator/top_block.py +++ b/grc/core/generator/top_block.py @@ -298,7 +298,28 @@ class TopBlockGenerator(object): rendered = [] for con in sorted(connections, key=by_domain_and_blocks): template = templates[con.type] - code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) - rendered.append(code) + if con.source_port.dtype != 'bus': + code = template.render(make_port_sig=make_port_sig, source=con.source_port, sink=con.sink_port) + rendered.append(code) + else: + # Bus ports need to iterate over the underlying connections and then render + # the code for each subconnection + porta = con.source_port + portb = con.sink_port + fg = self._flow_graph + + if porta.dtype == 'bus' and portb.dtype == 'bus': + # which bus port is this relative to the bus structure + if len(porta.bus_structure) == len(portb.bus_structure): + for port_num_a,port_num_b in zip(porta.bus_structure,portb.bus_structure): + hidden_porta = porta.parent.sources[port_num_a] + hidden_portb = portb.parent.sinks[port_num_b] + connection = fg.parent_platform.Connection( + parent=self, source=hidden_porta, sink=hidden_portb) + code = template.render(make_port_sig=make_port_sig, source=hidden_porta, sink=hidden_portb) + rendered.append(code) + + + return rendered diff --git a/grc/core/ports/port.py b/grc/core/ports/port.py index d1a32e915c..dcaca5cbc5 100644 --- a/grc/core/ports/port.py +++ b/grc/core/ports/port.py @@ -39,7 +39,7 @@ class Port(Element): optional = EvaluatedFlag() def __init__(self, parent, direction, id, label='', domain=Constants.DEFAULT_DOMAIN, dtype='', - vlen='', multiplicity=1, optional=False, hide=False, **_): + vlen='', multiplicity=1, optional=False, hide=False, bus_struct=None, **_): """Make a new port from nested data.""" Element.__init__(self, parent) @@ -47,6 +47,14 @@ class Port(Element): self.key = id if not label: label = id if not id.isdigit() else {'sink': 'in', 'source': 'out'}[direction] + if dtype == 'bus': + #TODO - add some error checking in here + busses = [p for p in self.parent.ports() if p._dir == self._dir and p.dtype == 'bus'] + bus_structure = self.parent.current_bus_structure[self._dir] + bus_index = len(busses) + number = str(len(busses)) + '#' + str(len(bus_structure[bus_index])) + label = dtype + number + self.name = self._base_name = label self.domain = domain @@ -60,6 +68,9 @@ class Port(Element): self.multiplicity = multiplicity self.optional = optional self.hidden = hide + self.stored_hidden_state = None + self.bus_structure = bus_struct + # end of args ######################################################## self.clones = [] # References to cloned ports (for nports > 1) @@ -203,5 +214,42 @@ class Port(Element): enabled: None for all, True for enabled only, False for disabled only """ for con in self.parent_flowgraph.connections: - if self in con and (enabled is None or enabled == con.enabled): - yield con + #TODO clean this up - but how to get past this validation + # things don't compare simply with an x in y because + # bus ports are created differently. + port_in_con = False + if self.dtype == 'bus': + if self.is_sink: + if (self.parent.name == con.sink_port.parent.name and + self.name == con.sink_port.name): + port_in_con = True + elif self.is_source: + if (self.parent.name == con.source_port.parent.name and + self.name == con.source_port.name): + port_in_con = True + + if port_in_con: + yield con + + else: + if self in con and (enabled is None or enabled == con.enabled): + yield con + + def get_associated_ports(self): + if not self.dtype == 'bus': + return [self] + else: + if self.is_source: + get_ports = self.parent.sources + bus_structure = self.parent.current_bus_structure['source'] + else: + get_ports = self.parent.sinks + bus_structure = self.parent.current_bus_structure['sink'] + + ports = [i for i in get_ports if not i.dtype == 'bus'] + if bus_structure: + busses = [i for i in get_ports if i.dtype == '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/gui/Application.py b/grc/gui/Application.py index 8489580b06..fff663177b 100644 --- a/grc/gui/Application.py +++ b/grc/gui/Application.py @@ -802,8 +802,8 @@ class Application(Gtk.Application): Actions.BLOCK_CREATE_HIER.set_enabled(bool(selected_blocks)) Actions.OPEN_HIER.set_enabled(bool(selected_blocks)) - #Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks)) - #Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks)) + Actions.BUSSIFY_SOURCES.set_enabled(bool(selected_blocks)) + Actions.BUSSIFY_SINKS.set_enabled(bool(selected_blocks)) Actions.RELOAD_BLOCKS.enable() Actions.FIND_BLOCKS.enable() diff --git a/grc/gui/canvas/block.py b/grc/gui/canvas/block.py index 31a474fae2..e55c76f2cf 100644 --- a/grc/gui/canvas/block.py +++ b/grc/gui/canvas/block.py @@ -128,16 +128,14 @@ class Block(CoreBlock, Drawable): self._area = (0, 0, self.height, self.width) self.bounds_from_area(self._area) - # bussified = self.current_bus_structure['source'], self.current_bus_structure['sink'] - bussified = False, False + bussified = self.current_bus_structure['source'], self.current_bus_structure['sink'] for ports, has_busses in zip((self.active_sources, self.active_sinks), bussified): if not ports: continue port_separation = PORT_SEPARATION if not has_busses else ports[0].height + PORT_SPACING offset = (self.height - (len(ports) - 1) * port_separation - ports[0].height) / 2 for port in ports: - port.create_shapes() - + port.create_shapes() port.coordinate = { 0: (+self.width, offset), 90: (offset, -port.width), @@ -193,24 +191,21 @@ class Block(CoreBlock, Drawable): def get_min_height_for_ports(ports): min_height = 2 * PORT_BORDER_SEPARATION + len(ports) * PORT_SEPARATION - if ports: - min_height -= ports[-1].height + # If any of the ports are bus ports - make the min height larger + if any([p.dtype == 'bus' for p in ports]): + min_height = 2 * PORT_BORDER_SEPARATION + sum( + port.height + PORT_SPACING for port in ports if port.dtype == 'bus' + ) - PORT_SPACING + + else: + if ports: + min_height -= ports[-1].height return min_height height = max(height, get_min_height_for_ports(self.active_sinks), get_min_height_for_ports(self.active_sources)) - # def get_min_height_for_bus_ports(ports): - # return 2 * PORT_BORDER_SEPARATION + sum( - # port.height + PORT_SPACING for port in ports if port.dtype == 'bus' - # ) - PORT_SPACING - # - # if self.current_bus_structure['sink']: - # height = max(height, get_min_height_for_bus_ports(self.active_sinks)) - # if self.current_bus_structure['source']: - # height = max(height, get_min_height_for_bus_ports(self.active_sources)) - self.width, self.height = width, height = Utils.align_to_grid((width, height)) self._surface_layouts_offsets = [ diff --git a/grc/gui/canvas/connection.py b/grc/gui/canvas/connection.py index 56dab45570..2accfaf4d6 100644 --- a/grc/gui/canvas/connection.py +++ b/grc/gui/canvas/connection.py @@ -113,7 +113,7 @@ class Connection(CoreConnection, Drawable): self._make_path() # no cr set --> only sets bounding_points for extent def _make_path(self, cr=None): - x_pos, y_pos = self.coordinate # is source connector coordinate + x_pos, y_pos = self.source_port.connector_coordinate_absolute # x_start, y_start = self.source_port.get_connector_coordinate() x_end, y_end = self.sink_port.connector_coordinate_absolute diff --git a/grc/gui/canvas/flowgraph.py b/grc/gui/canvas/flowgraph.py index ca27406e0e..5bdda4745e 100644 --- a/grc/gui/canvas/flowgraph.py +++ b/grc/gui/canvas/flowgraph.py @@ -487,7 +487,13 @@ class FlowGraph(CoreFlowgraph, Drawable): element.create_labels(cr) def create_shapes(self): - for element in self._elements_to_draw: + #TODO - this is a workaround for bus ports not having a proper coordinate + # until the shape is drawn. The workaround is to draw blocks before connections + + for element in filter(lambda x: x.is_block, self._elements_to_draw) : + element.create_shapes() + + for element in filter(lambda x: not x.is_block, self._elements_to_draw): element.create_shapes() def _drawables(self): diff --git a/grc/gui/canvas/port.py b/grc/gui/canvas/port.py index 2ea35f3dd3..b1a3867149 100644 --- a/grc/gui/canvas/port.py +++ b/grc/gui/canvas/port.py @@ -124,17 +124,18 @@ class Port(CorePort, Drawable): label_width, label_height = self.label_layout.get_size() self.width = 2 * Constants.PORT_LABEL_PADDING + label_width / Pango.SCALE - self.height = 2 * Constants.PORT_LABEL_PADDING + label_height / Pango.SCALE + self.height = (2 * Constants.PORT_LABEL_PADDING + label_height*(3 if self.dtype == 'bus' else 1) ) / Pango.SCALE self._label_layout_offsets = [0, Constants.PORT_LABEL_PADDING] - # if self.dtype == 'bus': - # self.height += Constants.PORT_EXTRA_BUS_HEIGHT - # self._label_layout_offsets[1] += Constants.PORT_EXTRA_BUS_HEIGHT / 2 + self.height += self.height % 2 # uneven height def draw(self, cr): """ Draw the socket with a label. """ + if self.hidden: + return + border_color = self._border_color cr.set_line_width(self._line_width_factor * cr.get_line_width()) cr.translate(*self.coordinate) |