summaryrefslogtreecommitdiff
path: root/grc
diff options
context:
space:
mode:
Diffstat (limited to 'grc')
-rw-r--r--grc/blocks/grc.tree.yml4
-rw-r--r--grc/core/FlowGraph.py2
-rw-r--r--grc/core/blocks/block.py223
-rw-r--r--grc/core/generator/cpp_top_block.py29
-rw-r--r--grc/core/generator/top_block.py25
-rw-r--r--grc/core/ports/port.py54
-rw-r--r--grc/gui/Application.py4
-rw-r--r--grc/gui/canvas/block.py27
-rw-r--r--grc/gui/canvas/connection.py2
-rw-r--r--grc/gui/canvas/flowgraph.py8
-rw-r--r--grc/gui/canvas/port.py9
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)