示例#1
0
class Channel(Connection):
    ''' Subclass of Connection used to draw channels between processes '''
    in_sig = out_sig = None

    def __init__(self, process):
        ''' Set generic parameters from Connection class '''
        super(Channel, self).__init__(process, process)
        self.label_in = QGraphicsTextItem('[]', parent=self)
        self.label_out = QGraphicsTextItem('[]', parent=self)
        if not Channel.in_sig:
            # keep at class level as long as only one process is supported
            # when copy-pasting a process the challel in/out signal lists
            # are not parsed. Workaround is to keep the list "global"
            # to allow a copy of both process and channel
            # Needed for the image exporter, that copies the scene to a
            # temporary one
            Channel.in_sig = '[{}]'.format(',\n'.join(
                sig['name'] for sig in process.input_signals))
            Channel.out_sig = '[{}]'.format(',\n'.join(
                sig['name'] for sig in process.output_signals))
        font = QFont('Ubuntu', pointSize=8)
        for each in (self.label_in, self.label_out):
            each.setFont(font)
            each.show()
        self.process = process
        self.reshape()

    @property
    def start_point(self):
        ''' Compute connection origin - redefined function '''
        parent_rect = self.process.boundingRect()
        return QPointF(parent_rect.x(), parent_rect.height() / 2)

    @property
    def end_point(self):
        ''' Compute connection end point - redefined function '''
        # Arrow always bumps at the screen edge
        try:
            view = self.scene().views()[0]
            view_pos = view.mapToScene(
                view.viewport().geometry()).boundingRect().topLeft()
            scene_pos_x = self.mapFromScene(view_pos).x()
            return QPointF(scene_pos_x, self.start_point.y())
        except (IndexError, AttributeError):
            # In case there is no view (e.g. Export PNG from cmd line)
            return QPointF(self.start_point.x() - 250, self.start_point.y())

    def reshape(self):
        ''' Redefine shape function to add the text areas '''
        super(Channel, self).reshape()

        self.label_in.setPlainText(self.in_sig)
        self.label_out.setPlainText(self.out_sig)

        width_in = self.label_in.boundingRect().width()

        self.label_in.setX(self.start_point.x() - width_in)
        self.label_in.setY(self.start_point.y() + 5)
        self.label_out.setX(self.end_point.x() + 10)
        self.label_out.setY(self.end_point.y() + 5)
示例#2
0
class Channel(Connection):
    ''' Subclass of Connection used to draw channels between processes '''
    in_sig = out_sig = None
    def __init__(self, process):
        ''' Set generic parameters from Connection class '''
        super(Channel, self).__init__(process, process)
        self.label_in = QGraphicsTextItem('[]', parent=self)
        self.label_out = QGraphicsTextItem('[]', parent=self)
        if not Channel.in_sig:
            # keep at class level as long as only one process is supported
            # when copy-pasting a process the challel in/out signal lists
            # are not parsed. Workaround is to keep the list "global"
            # to allow a copy of both process and channel
            # Needed for the image exporter, that copies the scene to a
            # temporary one
            Channel.in_sig = '[{}]'.format(',\n'.join(sig['name']
                                   for sig in process.input_signals))
            Channel.out_sig = '[{}]'.format(',\n'.join(sig['name']
                                    for sig in process.output_signals))
        font = QFont('Ubuntu', pointSize=8)
        for each in (self.label_in, self.label_out):
            each.setFont(font)
            each.show()
        self.process = process
        self.reshape()

    @property
    def start_point(self):
        ''' Compute connection origin - redefined function '''
        parent_rect = self.process.boundingRect()
        return QPointF(parent_rect.x(), parent_rect.height() / 2)

    @property
    def end_point(self):
        ''' Compute connection end point - redefined function '''
        # Arrow always bumps at the screen edge
        try:
            view = self.scene().views()[0]
            view_pos = view.mapToScene(
                           view.viewport().geometry()).boundingRect().topLeft()
            scene_pos_x = self.mapFromScene(view_pos).x()
            return QPointF(scene_pos_x, self.start_point.y())
        except (IndexError, AttributeError):
            # In case there is no view (e.g. Export PNG from cmd line)
            return QPointF(self.start_point.x() - 250, self.start_point.y())

    def reshape(self):
        ''' Redefine shape function to add the text areas '''
        super(Channel, self).reshape()

        self.label_in.setPlainText(self.in_sig)
        self.label_out.setPlainText(self.out_sig)

        width_in = self.label_in.boundingRect().width()

        self.label_in.setX(self.start_point.x() - width_in)
        self.label_in.setY(self.start_point.y() + 5)
        self.label_out.setX(self.end_point.x() + 10)
        self.label_out.setY(self.end_point.y() + 5)
示例#3
0
class Edge(Connection):
    ''' B-spline/Bezier connection shape '''
    def __init__(self, edge, graph):
        ''' Set generic parameters from Connection class '''
        self.text_label = None
        super(Edge, self).__init__(edge['source'], edge['target'])
        self.edge = edge
        self.graph = graph
        # Set connection points as not visible, by default
        self.bezier_visible = False

        # Initialize control point coordinates
        self.bezier = [self.mapFromScene(*self.edge['spline'][0])]
        # Bezier control points (groups of three points):
        assert (len(self.edge['spline']) % 3 == 1)
        for i in xrange(1, len(self.edge['spline']), 3):
            self.bezier.append([
                Controlpoint(self.mapFromScene(*self.edge['spline'][i + j]),
                             self) for j in range(3)
            ])

        # Create connection points at start and end of the edge
        self.source_connection = Connectionpoint(
            self.start_point or self.bezier[0], self, self.parent)
        self.parent.movable_points.append(self.source_connection)
        self.end_connection = Connectionpoint(
            self.end_point or self.bezier[-1], self, self.child)
        self.child.movable_points.append(self.end_connection)
        self.reshape()

    @property
    def start_point(self):
        ''' Compute connection origin - redefine in subclasses '''
        # Start point is optional - graphviz decision
        return self.mapFromScene(*self.edge['start']) \
               if self.edge.get('start') else None

    @property
    def end_point(self):
        ''' Compute connection end point - redefine in subclasses '''
        return self.mapFromScene(*self.edge['end']) \
               if self.edge.get('end') else None

    def bezier_set_visible(self, visible=True):
        ''' Display or hide the edge control points '''
        self.bezier_visible = visible
        for group in self.bezier[1:]:
            for ctrl_point in group:
                if visible:
                    ctrl_point.show()
                else:
                    ctrl_point.hide()
        if visible:
            self.end_connection.show()
            self.source_connection.show()
        else:
            self.end_connection.hide()
            self.source_connection.hide()
        self.update()

    def mousePressEvent(self, event):
        ''' On a mouse click, display the control points '''
        self.bezier_set_visible(True)

    # pylint: disable=R0914
    def reshape(self):
        ''' Update the shape of the edge (redefined function) '''
        path = QPainterPath()
        # If there is a starting point, draw a line to the first curve point
        if self.start_point:
            path.moveTo(self.source_connection.center)
            path.lineTo(self.bezier[0])
        else:
            path.moveTo(self.source_connection.center)
        # Loop over the curve points:
        for group in self.bezier[1:]:
            path.cubicTo(*[point.center for point in group])

        # If there is an ending point, draw a line to it
        if self.end_point:
            path.lineTo(self.end_connection.center)

        end_point = path.currentPosition()
        arrowhead = self.angle_arrow(path)
        path.lineTo(arrowhead[0])
        path.moveTo(end_point)
        path.lineTo(arrowhead[1])
        path.moveTo(end_point)
        try:
            # Add the transition label, if any (none for the START edge)
            font = QFont('arial', pointSize=8)
            metrics = QFontMetrics(font)
            label = self.edge.get('label', '') or ''
            lines = label.split('\n')
            width = metrics.width(max(lines))  # longest line
            height = metrics.height() * len(lines)
            # lp is the position of the center of the text
            pos = self.mapFromScene(*self.edge['lp'])
            if not self.text_label:
                self.text_label = QGraphicsTextItem(self.edge.get('label', ''),
                                                    parent=self)
                self.text_label.setX(pos.x() - width / 2)
                self.text_label.setY(pos.y() - height / 2)
                self.text_label.setFont(font)
                # Make horizontal center alignment, as dot does
                self.text_label.setTextWidth(
                    self.text_label.boundingRect().width())
                fmt = QTextBlockFormat()
                fmt.setAlignment(Qt.AlignHCenter)
                cursor = self.text_label.textCursor()
                cursor.select(QTextCursor.Document)
                cursor.mergeBlockFormat(fmt)
                cursor.clearSelection()
                self.text_label.setTextCursor(cursor)
                self.text_label.show()
        except KeyError:
            # no label
            pass
        self.setPath(path)

    def __str__(self):
        ''' user-friendly information about the edge coordinates '''
        return ('Edge between ' + self.edge['source'].name + ' and ' +
                self.edge['target'].name + str(self.edge['spline'][0]))

    def paint(self, painter, option, widget):
        ''' Apply anti-aliasing to Edge Connections '''
        painter.setRenderHint(QPainter.Antialiasing, True)
        super(Edge, self).paint(painter, option, widget)
        # Draw lines between connection points, if visible
        if self.bezier_visible:
            painter.setPen(QPen(Qt.lightGray, 0, Qt.SolidLine))
            painter.setBrush(Qt.NoBrush)
            points_flat = [
                point.center for sub1 in self.bezier[1:] for point in sub1
            ]
            painter.drawPolyline([self.source_connection.center] +
                                 points_flat + [self.end_connection.center])
示例#4
0
class Edge(Connection):
    ''' B-spline/Bezier connection shape '''
    def __init__(self, edge, graph):
        ''' Set generic parameters from Connection class '''
        self.text_label = None
        super(Edge, self).__init__(edge['source'], edge['target'])
        self.edge = edge
        self.graph = graph
        # Set connection points as not visible, by default
        self.bezier_visible = False

        # Initialize control point coordinates
        self.bezier = [self.mapFromScene(*self.edge['spline'][0])]
        # Bezier control points (groups of three points):
        assert(len(self.edge['spline']) % 3 == 1)
        for i in xrange(1, len(self.edge['spline']), 3):
            self.bezier.append([Controlpoint(
                          self.mapFromScene(*self.edge['spline'][i + j]), self)
                          for j in range(3)])

        # Create connection points at start and end of the edge
        self.source_connection = Connectionpoint(
                self.start_point or self.bezier[0], self, self.parent)
        self.parent.movable_points.append(self.source_connection)
        self.end_connection = Connectionpoint(
                self.end_point or self.bezier[-1], self, self.child)
        self.child.movable_points.append(self.end_connection)
        self.reshape()

    @property
    def start_point(self):
        ''' Compute connection origin - redefine in subclasses '''
        # Start point is optional - graphviz decision
        return self.mapFromScene(*self.edge['start']) \
               if self.edge.get('start') else None

    @property
    def end_point(self):
        ''' Compute connection end point - redefine in subclasses '''
        return self.mapFromScene(*self.edge['end']) \
               if self.edge.get('end') else None

    def bezier_set_visible(self, visible=True):
        ''' Display or hide the edge control points '''
        self.bezier_visible = visible
        for group in self.bezier[1:]:
            for ctrl_point in group:
                if visible:
                    ctrl_point.show()
                else:
                    ctrl_point.hide()
        if visible:
            self.end_connection.show()
            self.source_connection.show()
        else:
            self.end_connection.hide()
            self.source_connection.hide()
        self.update()

    def mousePressEvent(self, event):
        ''' On a mouse click, display the control points '''
        self.bezier_set_visible(True)

  # pylint: disable=R0914
    def reshape(self):
        ''' Update the shape of the edge (redefined function) '''
        path = QPainterPath()
        # If there is a starting point, draw a line to the first curve point
        if self.start_point:
            path.moveTo(self.source_connection.center)
            path.lineTo(self.bezier[0])
        else:
            path.moveTo(self.source_connection.center)
        # Loop over the curve points:
        for group in self.bezier[1:]:
            path.cubicTo(*[point.center for point in group])

        # If there is an ending point, draw a line to it
        if self.end_point:
            path.lineTo(self.end_connection.center)

        end_point = path.currentPosition()
        arrowhead = self.angle_arrow(path)
        path.lineTo(arrowhead[0])
        path.moveTo(end_point)
        path.lineTo(arrowhead[1])
        path.moveTo(end_point)
        try:
            # Add the transition label, if any (none for the START edge)
            font = QFont('arial', pointSize=8)
            width = QFontMetrics(font).width(
                    self.edge.get('label', 0))
            pos = self.mapFromScene(*self.edge['lp'])
            #path.addText(pos.x() - width/2, pos.y(),
            #        font, self.edge['label'])
            if not self.text_label:
                self.text_label = QGraphicsTextItem(
                                 self.edge.get('label', ''), parent=self)
            self.text_label.setX(pos.x() - width / 2)
            self.text_label.setY(pos.y())
            self.text_label.setFont(font)
            self.text_label.show()
        except KeyError:
            # no label
            pass
        self.setPath(path)

    def __str__(self):
        ''' user-friendly information about the edge coordinates '''
        return('Edge between ' + self.edge['source'].name + ' and ' +
                self.edge['target'].name + str(self.edge['spline'][0]))

    def paint(self, painter, option, widget):
        ''' Apply anti-aliasing to Edge Connections '''
        painter.setRenderHint(QPainter.Antialiasing, True)
        super(Edge, self).paint(painter, option, widget)
        # Draw lines between connection points, if visible
        if self.bezier_visible:
            painter.setPen(
                    QPen(Qt.lightGray, 0, Qt.SolidLine))
            painter.setBrush(Qt.NoBrush)
            points_flat = [point.center
                           for sub1 in self.bezier[1:] for point in sub1]
            painter.drawPolyline([self.source_connection.center]
                                  + points_flat + [self.end_connection.center])