""" Copyright 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GNU Radio GNU Radio Companion is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GNU Radio Companion is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from __future__ import absolute_import from . import Colors, Utils from .Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT, GR_MESSAGE_DOMAIN from .Element import Element from ..core.Element import nop_write from ..core.Connection import Connection as CoreConnection class Connection(CoreConnection, Element): """ A graphical connection for ports. The connection has 2 parts, the arrow and the wire. The coloring of the arrow and wire exposes the status of 3 states: enabled/disabled, valid/invalid, highlighted/non-highlighted. The wire coloring exposes the enabled and highlighted states. The arrow coloring exposes the enabled and valid states. """ def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) Element.__init__(self) self._line = [] self._line_width_factor = 1.0 self._color = self._color2 = self._arrow_color = None self._sink_rot = self._source_rot = None self._sink_coor = self._source_coor = None @nop_write @property def coordinate(self): return self.source_port.get_connector_coordinate() @nop_write @property def rotation(self): """ Get the 0 degree rotation. Rotations are irrelevant in connection. Returns: 0 """ return 0 def create_shapes(self): """Pre-calculate relative coordinates.""" self._sink_rot = None self._source_rot = None self._sink_coor = None self._source_coor = None #get the source coordinate try: connector_length = self.source_port.connector_length except: return # todo: why? self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.source_port.rotation) #get the sink coordinate connector_length = self.sink_port.connector_length + CONNECTOR_ARROW_HEIGHT self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.sink_port.rotation) #build the arrow self._arrow_base = [ (0, 0), Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2), self.sink_port.rotation), Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE/2), self.sink_port.rotation), ] if self.sink_block.state != 'bypassed' else [] source_domain = self.source_port.domain sink_domain = self.sink_port.domain def get_domain_color(domain_name): domain = self.parent_platform.domains.get(domain_name, {}) color_spec = domain.get('color') return Colors.get_color(color_spec) if color_spec else Colors.DEFAULT_DOMAIN_COLOR if source_domain == GR_MESSAGE_DOMAIN: self._line_width_factor = 1.0 self._color = None self._color2 = Colors.CONNECTION_ENABLED_COLOR else: if source_domain != sink_domain: self._line_width_factor = 2.0 self._color = get_domain_color(source_domain) self._color2 = get_domain_color(sink_domain) self._arrow_color = self._color2 if self.is_valid() else Colors.CONNECTION_ERROR_COLOR self._update_after_move() def _update_after_move(self): """Calculate coordinates.""" source = self.source_port sink = self.sink_port source_dir = source.get_connector_direction() sink_dir = sink.get_connector_direction() x_pos, y_pos = self.coordinate x_start, y_start = source.get_connector_coordinate() x_end, y_end = sink.get_connector_coordinate() p3 = x3, y3 = x_end - x_pos, y_end - y_pos p2 = x2, y2 = self.x2 + x3, self.y2 + y3 p1 = x1, y1 = self.x1, self.y1 p0 = x_start - x_pos, y_start - y_pos self._arrow = [(x + x3, y + y3) for x, y in self._arrow_base] if abs(source_dir - sink.get_connector_direction()) == 180: # 2 possible point sets to create a 3-line connector mid_x, mid_y = (x1 + x2) / 2.0, (y1 + y2) / 2.0 points = ((mid_x, y1), (mid_x, y2)) alt = ((x1, mid_y), (x2, mid_y)) # source connector -> points[0][0] should be in the direction of source (if possible) if Utils.get_angle_from_coordinates(p1, points[0]) != source_dir: points, alt = alt, points # points[0] -> sink connector should not be in the direction of sink if Utils.get_angle_from_coordinates(points[0], p2) == sink_dir: points, alt = alt, points # points[0] -> source connector should not be in the direction of source if Utils.get_angle_from_coordinates(points[0], p1) == source_dir: points, alt = alt, points # create 3-line connector i1, i2 = points self._line = [p0, p1, i1, i2, p2, p3] else: # 2 possible points to create a right-angled connector point, alt = [(x1, y2), (x2, y1)] # source connector -> point should be in the direction of source (if possible) if Utils.get_angle_from_coordinates(p1, point) != source_dir: point, alt = alt, point # point -> sink connector should not be in the direction of sink if Utils.get_angle_from_coordinates(point, p2) == sink_dir: point, alt = alt, point # point -> source connector should not be in the direction of source if Utils.get_angle_from_coordinates(point, p1) == source_dir: point, alt = alt, point # create right-angled connector self._line = [p0, p1, point, p2, p3] self.bounds_from_line(self._line) def draw(self, widget, cr): """ Draw the connection. """ sink = self.sink_port source = self.source_port # check for changes if self._sink_rot != sink.rotation or self._source_rot != source.rotation: self.create_shapes() self._sink_rot = sink.rotation self._source_rot = source.rotation elif self._sink_coor != sink.parent_block.coordinate or self._source_coor != source.parent_block.coordinate: self._update_after_move() self._sink_coor = sink.parent_block.coordinate self._source_coor = source.parent_block.coordinate # draw color1, color2, arrow_color = ( None if color is None else Colors.HIGHLIGHT_COLOR if self.highlighted else Colors.CONNECTION_DISABLED_COLOR if not self.enabled else color for color in (self._color, self._color2, self._arrow_color) ) cr.translate(*self.coordinate) cr.set_line_width(self._line_width_factor * cr.get_line_width()) for point in self._line: cr.line_to(*point) if color1: # not a message connection cr.set_source_rgba(*color1) cr.stroke_preserve() if color1 != color2: cr.save() cr.set_dash([5.0, 5.0], 5.0 if color1 else 0.0) cr.set_source_rgba(*color2) cr.stroke() cr.restore() else: cr.new_path() if self._arrow: cr.set_source_rgba(*arrow_color) cr.move_to(*self._arrow[0]) cr.line_to(*self._arrow[1]) cr.line_to(*self._arrow[2]) cr.close_path() cr.fill()