Exemple #1
0
class GraphicsTextWidget(QGraphicsWidget):
    def __init__(self, text, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.labelItem = QGraphicsTextItem(self)
        self.setHtml(text)

        self.labelItem.document().documentLayout().documentSizeChanged.connect(
            self.onLayoutChanged
        )

    def setGeometry(self, rect):
        self.prepareGeometryChange()
        super().setGeometry(rect)

    def onLayoutChanged(self, *args):
        self.updateGeometry()

    def sizeHint(self, which, constraint=QSizeF()):
        if which == Qt.MinimumSize:
            return self.labelItem.boundingRect().size()
        else:
            return self.labelItem.boundingRect().size()

    def setTextWidth(self, width):
        self.labelItem.setTextWidth(width)

    def setHtml(self, text):
        self.labelItem.setHtml(text)
def bgc_name_face(node, *args, **kargs):
    """
    This is the item generator. It must receive a node object, and
    returns a Qt4 graphics item that can be used as a node face.
    """
    # Receive an arbitrary number of arguments, in this case width and
    # Height of the faces and the information about the BGC
    width = args[0]
    height = args[1]
    # Add the popup
    interactive_face = InteractiveItem("Class : {}\nRelated MIBiG : {}\nCluster family : {}".format(args[2], args[4], args[3]), 0, 0, width, height)
    # Keep a link within the item to access node info
    interactive_face.node = node
    # Remove border around the masterItem
    interactive_face.setPen(QPen(QtCore.Qt.NoPen))
    # Add ellipse around text
    ellipse = QGraphicsEllipseItem(interactive_face.rect())
    ellipse.setParentItem(interactive_face)
    # Change ellipse color
    ellipse.setBrush(QBrush(QColor(args[6])))
    # Add node name within the ellipse
    text = QGraphicsTextItem(args[5])
    text.setTextWidth(50)
    text.setParentItem(ellipse)
    # Center text according to masterItem size
    text_width = text.boundingRect().width()
    text_height = text.boundingRect().height()
    center = interactive_face.boundingRect().center()
    text.setPos(center.x()-text_width/2, center.y()-text_height/2)
    return interactive_face
def scientific_name_face(node, *args, **kwargs):
    scientific_name_text = QGraphicsTextItem()
    #underscore = node.name.replace("_", " ")
    words = node.name.split("_")
    text = []
    if len(words) < 2:
        # some sort of acronym or bin name, leave it alone
        text = words
    elif len(words) > 2:
        if len(words) >= 5:
            text.extend(
                ['<b>' + words[0] + ' <i> ' + words[1], words[2] + ' </i> '])
            text.extend(words[3:] + ['</b>'])

        elif len(words) == 3:
            text.extend([
                ' <span style="color:grey"><i> ' + words[0],
                words[1] + words[2] + '  </i></span>'
            ])

        else:
            # assume that everything after the
            # second word is strain name
            # which should not get italicized
            text.extend([
                ' <span style="color:grey"><i> ' + words[0],
                words[1] + '  </i></span>'
            ])
            text.extend(words[2:])
    else:
        text.extend([
            ' <span style="color:grey"><i> ' + words[0],
            words[1] + ' </i></span> '
        ])

    scientific_name_text.setHtml(' '.join(text))

    # below is a bit of a hack - I've found that the height of the bounding
    # box gives a bit too much padding around the name, so I just minus 10
    # from the height and recenter it. Don't know whether this is a generally
    # applicable number to use
    #myFont = QFont()
    masterItem = QGraphicsRectItem(
        0, 0,
        scientific_name_text.boundingRect().width(),
        scientific_name_text.boundingRect().height() - 10)

    scientific_name_text.setParentItem(masterItem)
    center = masterItem.boundingRect().center()
    scientific_name_text.setPos(
        masterItem.boundingRect().x(),
        center.y() - scientific_name_text.boundingRect().height() / 2)

    # I don't want a border around the masterItem
    masterItem.setPen(QPen(QtCore.Qt.NoPen))

    return masterItem
Exemple #4
0
def scientific_name_face(node, *args, **kwargs):
    scientific_name_text = QGraphicsTextItem()
    words = node.visual_label.split()
    text = []
    if hasattr(node, 'bg_col'):
        container_div = '<div style="background-color:{};">'.format(node.bgcolor)
        text.append(container_div)
    if len(words) < 2:
        # some sort of acronym or bin name, leave it alone
        text.extend(words)

    elif len(words) > 2:
        if words[0] == 'Candidatus':
            # for candidatus names, only the Candidatus part is italicised
            # name shortening it for brevity
            text.append('<i>Ca.</i>')
            text.extend(words[1:])
        elif re.match('^[A-Z]+$', words[0]):
            # If the first word is in all caps then it is an abreviation
            # so we don't want to italicize that at all
            text.extend(words)
        else:
            # assume that everything after the second word is strain name
            # which should not get italicised
            text.extend(['<i>'+words[0],words[1]+'</i>'])
            text.extend(words[2:])
    else:
        text.extend(['<i>'+words[0],words[1]+'</i>'])

    if hasattr(node, 'bg_col'):
        text.append('</div>')
    scientific_name_text.setHtml(' '.join(text))
    #print(scientific_name_text.boundingRect().width(), scientific_name_text.boundingRect().height())

    # below is a bit of a hack - I've found that the height of the bounding
    # box gives a bit too much padding around the name, so I just minus 10
    # from the height and recenter it. Don't know whether this is a generally
    # applicable number to use
    masterItem = QGraphicsRectItem(0, 0, scientific_name_text.boundingRect().width(), scientific_name_text.boundingRect().height() - 10)


    scientific_name_text.setParentItem(masterItem)
    center = masterItem.boundingRect().center()
    scientific_name_text.setPos(masterItem.boundingRect().x(), center.y() - scientific_name_text.boundingRect().height()/2)
    # I dont want a border around the masterItem
    masterItem.setPen(QPen(QtCore.Qt.NoPen))


    return masterItem
Exemple #5
0
class OWLegendItem(QGraphicsObject):
    """
        Represents a legend item with a title and a point symbol.
        
        :param name: The text to display
        :type name: str
        
        :param point: The point symbol
        :type point: :obj:`.OWPoint`
        
        :param parent: The parent item, passed to QGraphicsItem
        :type parent: :obj:`QGraphicsItem`
        
        .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`. 
    """
    def __init__(self, name, point, parent):
        QGraphicsObject.__init__(self, parent)
        self.text_item = QGraphicsTextItem(name, self)
        if point:
            s = point.size()
            height = max(2 * s, self.text_item.boundingRect().height())
        else:
            height = self.text_item.boundingRect().height()
        p = 0.5 * height
        self.text_item.setPos(height, 0)
        self.point_item = point
        if point:
            self.point_item.setParentItem(self)
            self.point_item.setPos(p, p)
        self._rect = QRectF(0, 0,
                            height + self.text_item.boundingRect().width(),
                            height)
        self.rect_item = QGraphicsRectItem(self._rect, self)
        self.rect_item.setPen(QPen(Qt.NoPen))
        self.rect_item.stackBefore(self.text_item)
        if self.point_item:
            self.rect_item.stackBefore(self.point_item)

    def boundingRect(self):
        return self._rect

    def paint(self, painter, option, widget):
        pass
Exemple #6
0
class OWLegendTitle(QGraphicsObject):
    """
        A legend item that shows ``text`` with a bold font and no symbol. 
    """
    def __init__(self, text, parent):
        QGraphicsObject.__init__(self, parent)
        self.text_item = QGraphicsTextItem(text + ':', self)
        f = self.text_item.font()
        f.setBold(True)
        self.text_item.setFont(f)
        self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self)
        self.rect_item.setPen(QPen(Qt.NoPen))
        self.rect_item.stackBefore(self.text_item)

    def boundingRect(self):
        return self.text_item.boundingRect()

    def paint(self, painter, option, widget):
        pass
Exemple #7
0
class OWLegendTitle(QGraphicsObject):
    """
        A legend item that shows ``text`` with a bold font and no symbol.
    """
    def __init__(self, text, parent):
        QGraphicsObject.__init__(self, parent)
        self.text_item = QGraphicsTextItem(text + ':', self)
        f = self.text_item.font()
        f.setBold(True)
        self.text_item.setFont(f)
        self.rect_item = QGraphicsRectItem(self.text_item.boundingRect(), self)
        self.rect_item.setPen(QPen(Qt.NoPen))
        self.rect_item.stackBefore(self.text_item)

    def boundingRect(self):
        return self.text_item.boundingRect()

    def paint(self, painter, option, widget):
        pass
Exemple #8
0
class OWLegendItem(QGraphicsObject):
    """
        Represents a legend item with a title and a point symbol.

        :param name: The text to display
        :type name: str

        :param point: The point symbol
        :type point: :obj:`.OWPoint`

        :param parent: The parent item, passed to QGraphicsItem
        :type parent: :obj:`QGraphicsItem`

        .. seealso:: :meth:`.OWLegend.add_item`, :meth:`.OWLegend.add_curve`.
    """
    def __init__(self, name, point, parent):
        QGraphicsObject.__init__(self, parent)
        self.text_item = QGraphicsTextItem(name, self)
        if point:
            s = point.size()
            height = max(2*s, self.text_item.boundingRect().height())
        else:
            height = self.text_item.boundingRect().height()
        p = 0.5 * height
        self.text_item.setPos(height, 0)
        self.point_item = point
        if point:
            self.point_item.setParentItem(self)
            self.point_item.setPos(p, p)
        self._rect = QRectF(0, 0, height + self.text_item.boundingRect().width(), height )
        self.rect_item = QGraphicsRectItem(self._rect, self)
        self.rect_item.setPen(QPen(Qt.NoPen))
        self.rect_item.stackBefore(self.text_item)
        if self.point_item:
            self.rect_item.stackBefore(self.point_item)

    def boundingRect(self):
        return self._rect

    def paint(self, painter, option, widget):
        pass
Exemple #9
0
def trinomial_face(binom,
                   spp,
                   ftype="Verdana",
                   fsize=10,
                   fgcolor="black",
                   fstyle="normal",
                   bold=False):
    """
    Stolen from:
    https://connorskennerton.wordpress.com/2016/11/16/python-ete3-formatting-organism-names-the-way-i-want/
    """

    font = QFont(ftype, fsize)
    font.setBold(bold)
    if fstyle == "italic":
        font.setStyle(QFont.StyleItalic)
    elif fstyle == "oblique":
        font.setStyle(QFont.StyleOblique)

    text = QGraphicsTextItem()
    text.setFont(font)
    if spp is None:
        text.setHtml("<i>{}</i>".format(binom))
    else:
        text.setHtml("<i>{}</i> {}".format(binom, spp))

    rect = QGraphicsRectItem(0, 0,
                             text.boundingRect().width(),
                             text.boundingRect().height() - 10)
    text.setParentItem(rect)
    center = rect.boundingRect().center()
    text.setPos(rect.boundingRect().x(),
                center.y() - text.boundingRect().height() / 2)

    # no border
    rect.setPen(QPen(QtCore.Qt.NoPen))

    return ete3.faces.StaticItemFace(rect)
Exemple #10
0
 def setPos(self, x, y):
     self.x, self.y = x, y
     rect = QGraphicsTextItem.boundingRect(self)
     if self.vertical:
         h, w = rect.height(), rect.width()
         rect.setWidth(h)
         rect.setHeight(-w)
     if int(self.alignment & Qt.AlignRight):
         x -= rect.width()
     elif int(self.alignment & Qt.AlignHCenter):
         x -= rect.width() / 2.
     if int(self.alignment & Qt.AlignBottom):
         y -= rect.height()
     elif int(self.alignment & Qt.AlignVCenter):
         y -= rect.height() / 2.
     QGraphicsTextItem.setPos(self, x, y)
Exemple #11
0
 def setPos(self, x, y):
     self.x, self.y = x, y
     rect = QGraphicsTextItem.boundingRect(self)
     if self.vertical:
         h, w = rect.height(), rect.width()
         rect.setWidth(h)
         rect.setHeight(-w)
     if int(self.alignment & Qt.AlignRight):
         x -= rect.width()
     elif int(self.alignment & Qt.AlignHCenter):
         x -= rect.width() / 2.
     if int(self.alignment & Qt.AlignBottom):
         y -= rect.height()
     elif int(self.alignment & Qt.AlignVCenter):
         y -= rect.height() / 2.
     QGraphicsTextItem.setPos(self, x, y)
Exemple #12
0
class TimestampItem(QGraphicsRectItem):
    """ 
    Represents a block in the diagram
    """
    def __init__(self, parent, name='Untitled', width=180, height=40):
        """
        Constructor
        """
        QGraphicsRectItem.__init__(self)

        self.parentWidget = parent

        if sys.version_info > (3, ):
            if isinstance(name, bytes):
                name = str(name, "utf8", errors="ignore")

        self.label = QGraphicsTextItem(name, self)

        self.setColor(blockColor="#FFFFFF")

        self.changeSize(width, height)

    def setColor(self, blockColor):
        """
        Set color
        """
        color = QColor(0, 0, 0)
        color.setNamedColor(blockColor)
        self.setPen(QPen(color, 1))

        self.setBrush(QBrush(color))

    def changeSize(self, w, h):
        """
        Resize block function
        """
        # Limit the block size
        self.setRect(0.0, 0.0, w, h)

        # center label:
        rect = self.label.boundingRect()
        lw, lh = rect.width(), rect.height()
        lx = (w - lw) / 2
        ly = (h - lh) / 2
        self.label.setPos(lx, ly)

        return w, h
class GradientLegendItem(QGraphicsObject, GraphicsWidgetAnchor):
    gradient_width = 20

    def __init__(self, title, palette, values, parent):
        QGraphicsObject.__init__(self, parent)
        GraphicsWidgetAnchor.__init__(self)
        self.parent = self.legend = parent
        self.palette = palette
        self.values = values

        self.title = QGraphicsTextItem('%s:' % title, self)
        f = self.title.font()
        f.setBold(True)
        self.title.setFont(f)
        self.title_item = QGraphicsRectItem(self.title.boundingRect(), self)
        self.title_item.setPen(QPen(Qt.NoPen))
        self.title_item.stackBefore(self.title)

        self.label_items = [QGraphicsTextItem(text, self) for text in values]
        for i in self.label_items:
            i.setTextWidth(50)

        self.rect = QRectF()

        self.gradient_item = QGraphicsRectItem(self)
        self.gradient = QLinearGradient()
        self.gradient.setStops([(v * 0.1, self.palette[v * 0.1])
                                for v in range(11)])
        self.orientation = Qt.Horizontal
        self.set_orientation(Qt.Vertical)

        self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)

    def set_orientation(self, orientation):
        return
        if self.orientation == orientation:
            return

        self.orientation = orientation

        if self.orientation == Qt.Vertical:
            height = max(item.boundingRect().height()
                         for item in self.label_items)
            total_height = height * max(5, len(self.label_items))
            interval = (total_height -
                        self.label_items[-1].boundingRect().height()
                        ) / (len(self.label_items) - 1)
            self.gradient_item.setRect(10, 0, self.gradient_width, total_height)
            self.gradient.setStart(10, 0)
            self.gradient.setFinalStop(10, total_height)
            self.gradient_item.setBrush(QBrush(self.gradient))
            self.gradient_item.setPen(QPen(Qt.NoPen))
            y = -20   # hja, no; dela --> pri boundingRect() zato pristejem +20
            x = 0
            move_item_xy(self.title, x, y, False)
            y = 10
            x = 30
            for item in self.label_items:
                move_item_xy(item, x, y, False)
                                       # self.parent.graph.animate_plot)
                y += interval
            self.rect = QRectF(10, 0,
                               self.gradient_width +
                               max(item.boundingRect().width()
                                   for item in self.label_items),
                               self.label_items[0].boundingRect().height() *
                               max(5, len(self.label_items)))
        else:
            # za horizontalno orientacijo nisem dodajal title-a
            width = 50
            height = max(item.boundingRect().height()
                         for item in self.label_items)
            total_width = width * max(5, len(self.label_items))
            interval = (total_width -
                        self.label_items[-1].boundingRect().width()
                        ) / (len(self.label_items) - 1)

            self.gradient_item.setRect(0, 0, total_width, self.gradient_width)
            self.gradient.setStart(0, 0)
            self.gradient.setFinalStop(total_width, 0)
            self.gradient_item.setBrush(QBrush(self.gradient))
            self.gradient_item.setPen(QPen(Qt.NoPen))
            x = 0
            y = 30
            for item in self.label_items:
                move_item_xy(item, x, y, False)
                                       # self.parent.graph.animate_plot)
                x += interval
            self.rect = QRectF(0, 0, total_width, self.gradient_width + height)

    # noinspection PyPep8Naming
    def boundingRect(self):
        width = max(self.rect.width(), self.title_item.boundingRect().width())
        height = self.rect.height() + self.title_item.boundingRect().height()
        return QRectF(self.rect.left(), self.rect.top(), width, height)

    def paint(self, painter, option, widget):
        pass
Exemple #14
0
class VennIntersectionArea(QGraphicsPathItem):
    def __init__(self, parent=None, text=""):
        super(QGraphicsPathItem, self).__init__(parent)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))

        self.text = QGraphicsTextItem(self)
        layout = self.text.document().documentLayout()
        layout.documentSizeChanged.connect(self._onLayoutChanged)

        self._text = ""
        self._anchor = QPointF()

    def setText(self, text):
        if self._text != text:
            self._text = text
            self.text.setPlainText(text)

    def text(self):
        return self._text

    def setTextAnchor(self, pos):
        if self._anchor != pos:
            self._anchor = pos
            self._updateTextAnchor()

    def hoverEnterEvent(self, event):
        self.setZValue(self.zValue() + 1)
        return QGraphicsPathItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.setZValue(self.zValue() - 1)
        return QGraphicsPathItem.hoverLeaveEvent(self, event)

#     def mousePressEvent(self, event):
#         pos = event.pos()
#         parent = self.parentItem()
#         pbrect = parent.boundingRect()
#         w, h = pbrect.width(), pbrect.height()
#         print "(%.3f, %.3f)" % (pos.x() / w, pos.y() / h)
#         super(VennIntersectionArea, self).mousePressEvent(event)

    def paint(self, painter, option, widget=None):
        painter.save()
        path = self.path()
        brush = QBrush(self.brush())
        pen = QPen(self.pen())

        if option.state & QStyle.State_Selected:
            pen.setColor(Qt.red)
            brush.setStyle(Qt.DiagCrossPattern)
            brush.setColor(QColor(40, 40, 40, 100))

        elif option.state & QStyle.State_MouseOver:
            pen.setColor(Qt.blue)

        if option.state & QStyle.State_MouseOver:
            brush.setColor(QColor(100, 100, 100, 100))
            if brush.style() == Qt.NoBrush:
                # Make sure the highlight is actually visible.
                brush.setStyle(Qt.SolidPattern)

        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawPath(path)
        painter.restore()

    def itemChange(self, change, value):
        if change == QGraphicsPathItem.ItemSelectedHasChanged:
            if value.toBool():
                self.setZValue(self.zValue() + 1)
            else:
                self.setZValue(self.zValue() - 1)

        return QGraphicsPathItem.itemChange(self, change, value)

    def _updateTextAnchor(self):
        rect = self.text.boundingRect()
        pos = anchor_rect(rect, self._anchor)
        self.text.setPos(pos)

    def _onLayoutChanged(self):
        self._updateTextAnchor()
Exemple #15
0
class VennIntersectionArea(QGraphicsPathItem):
    def __init__(self, parent=None, text=""):
        super(QGraphicsPathItem, self).__init__(parent)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))

        self.text = QGraphicsTextItem(self)
        layout = self.text.document().documentLayout()
        layout.documentSizeChanged.connect(self._onLayoutChanged)

        self._text = ""
        self._anchor = QPointF()

    def setText(self, text):
        if self._text != text:
            self._text = text
            self.text.setPlainText(text)

    def text(self):
        return self._text

    def setTextAnchor(self, pos):
        if self._anchor != pos:
            self._anchor = pos
            self._updateTextAnchor()

    def hoverEnterEvent(self, event):
        self.setZValue(self.zValue() + 1)
        return QGraphicsPathItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.setZValue(self.zValue() - 1)
        return QGraphicsPathItem.hoverLeaveEvent(self, event)

    def paint(self, painter, option, widget=None):
        painter.save()
        path = self.path()
        brush = QBrush(self.brush())
        pen = QPen(self.pen())

        if option.state & QStyle.State_Selected:
            pen.setColor(Qt.red)
            brush.setStyle(Qt.DiagCrossPattern)
            brush.setColor(QColor(40, 40, 40, 100))

        elif option.state & QStyle.State_MouseOver:
            pen.setColor(Qt.blue)

        if option.state & QStyle.State_MouseOver:
            brush.setColor(QColor(100, 100, 100, 100))
            if brush.style() == Qt.NoBrush:
                # Make sure the highlight is actually visible.
                brush.setStyle(Qt.SolidPattern)

        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawPath(path)
        painter.restore()

    def itemChange(self, change, value):
        if change == QGraphicsPathItem.ItemSelectedHasChanged:
            self.setZValue(self.zValue() + (1 if value else -1))
        return QGraphicsPathItem.itemChange(self, change, value)

    def _updateTextAnchor(self):
        rect = self.text.boundingRect()
        pos = anchor_rect(rect, self._anchor)
        self.text.setPos(pos)

    def _onLayoutChanged(self):
        self._updateTextAnchor()
Exemple #16
0
class EFrameLayout(QGraphicsPolygonItem):

    def __init__(self, parent=None, scene=None):
        super(EFrameLayout, self).__init__(parent, scene)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setAcceptsHoverEvents(True)

        self.__name = QGraphicsTextItem()
        self.__name.setPlainText('Frame Layout')

        self.__handleWidth = 500
        self.__handleHeight = 20
        self.__separator = 5
        self.__boundExtra = 3 + self.pen().width()
        self.__handleRect = QRectF( 0.0 , 0.0 , self.__handleWidth, self.__handleHeight  )

        self.__controlsBound = 0.0
        self.__controls = []

        self.__isDefaultPen = False
        self.__isCollapsed = True

        self.__pens = { 0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen }
        self.setPen(self.__pens[self.__isDefaultPen])

    @property
    def Label(self): return self.__name.toPlainText()

    @Label.setter
    def Label(self, label): self.__name.setPlainText( str( label ) )

    @property
    def Width(self): return self.__handleWidth

    @Width.setter
    def Width(self, width):
        self.__handleWidth = width
        self.updateGeometry()

    def toggleContentVisibility(self):
        if not len(self.__controls): return

        if self.__controls[0].isVisible():
            [ control.hide() for control in self.__controls ]
            self.__isCollapsed = True
            return

        [ control.show() for control in self.__controls ]
        self.__isCollapsed = False

    def updateGeometry(self):

        self.__handleRect = QRectF( 0.0 , 0.0 , self.__handleWidth, self.__handleHeight  )

        if not len(self.__controls) or self.__isCollapsed:
            self.__controlsBound = 0.0
            return

        self.__controlsBound = 0.0
        self.__controlsBound = self.__separator
        self.__controlsBound += sum( [ control.boundingRect().height() + self.__separator for control in self.__controls ] )

        step = self.__handleHeight + self.__separator * 2

        for control in self.__controls:

            control.Name.setTextWidth( self.__controlNameWidth )
            control.Width = self.__handleRect.normalized().adjusted( 5.0, 0.0, -5.0, 0.0 ).width()

            control.setPos( 5.0 , step )
            step += control.boundingRect().height() + self.__separator

    def addControl(self, control):
        if not isinstance(control, EControlsGroup): raise AttributeError

        self.__controls.append(control)
        control.setParentItem(self)
        control.setFlag(QGraphicsItem.ItemIsMovable, False)

        self.__controlNameWidth = max([ control.Name.boundingRect().width() for control in self.__controls ]) + 5

        self.__isCollapsed = False
        self.updateGeometry()

    def clear(self):
        [ control.setParentItem(None) for control in self.__controls ]
        self.__controls = []

        self.updateGeometry()

    def mouseDoubleClickEvent(self, mouseEvent):
        QGraphicsPolygonItem.mouseDoubleClickEvent(self, mouseEvent)

        self.toggleContentVisibility()
        self.updateGeometry()

    def shape(self): return QGraphicsItem.shape(self)

    def boundingRect(self):
        return self.__handleRect.normalized().adjusted( 0.0, 0.0, 0.0, self.__controlsBound )

    def paint(self, painter, option, widget=None):

        if not self.__isCollapsed:
            painter.setPen( QPen( QColor( 0, 0, 0, 50 ), 2 , Qt.SolidLine ) )
            painter.setBrush( QColor( 0, 0, 0, 50 ) )
            painter.drawRect( self.boundingRect().adjusted( self.pen().width(), self.__handleHeight , -self.pen().width(), 0.0 ) )

        painter.setPen(self.pen())
        painter.setBrush( EDraw.EColor.LinearGradient( self.__handleRect, Qt.darkGray ) )
        #painter.drawPolygon( QPolygonF( self.__handleRect.normalized().adjusted(-self.__boundExtra, 0.0, self.__boundExtra, 0.0 ) ) )
        painter.drawPolygon( QPolygonF( self.__handleRect ) )

        painter.setPen(Qt.lightGray)
        r = QRectF( 0.0, 0.0, self.__name.boundingRect().width(), self.__handleRect.height() )
        painter.drawText( r, Qt.AlignCenter, self.__name.toPlainText() )
class VennIntersectionArea(QGraphicsPathItem):
    def __init__(self, parent=None, text=""):
        super(QGraphicsPathItem, self).__init__(parent)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))

        self.text = QGraphicsTextItem(self)
        layout = self.text.document().documentLayout()
        layout.documentSizeChanged.connect(self._onLayoutChanged)

        self._text = ""
        self._anchor = QPointF()

    def setText(self, text):
        if self._text != text:
            self._text = text
            self.text.setPlainText(text)

    def text(self):
        return self._text

    def setTextAnchor(self, pos):
        if self._anchor != pos:
            self._anchor = pos
            self._updateTextAnchor()

    def hoverEnterEvent(self, event):
        self.setZValue(self.zValue() + 1)
        return QGraphicsPathItem.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.setZValue(self.zValue() - 1)
        return QGraphicsPathItem.hoverLeaveEvent(self, event)

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            if event.modifiers() & Qt.AltModifier:
                self.setSelected(False)
            elif event.modifiers() & Qt.ControlModifier:
                self.setSelected(not self.isSelected())
            elif event.modifiers() & Qt.ShiftModifier:
                self.setSelected(True)
            else:
                for area in self.parentWidget().vennareas():
                    area.setSelected(False)
                self.setSelected(True)

    def mouseReleaseEvent(self, event):
        pass

    def paint(self, painter, option, widget=None):
        painter.save()
        path = self.path()
        brush = QBrush(self.brush())
        pen = QPen(self.pen())

        if option.state & QStyle.State_Selected:
            pen.setColor(Qt.red)
            brush.setStyle(Qt.DiagCrossPattern)
            brush.setColor(QColor(40, 40, 40, 100))

        elif option.state & QStyle.State_MouseOver:
            pen.setColor(Qt.blue)

        if option.state & QStyle.State_MouseOver:
            brush.setColor(QColor(100, 100, 100, 100))
            if brush.style() == Qt.NoBrush:
                # Make sure the highlight is actually visible.
                brush.setStyle(Qt.SolidPattern)

        painter.setPen(pen)
        painter.setBrush(brush)
        painter.drawPath(path)
        painter.restore()

    def itemChange(self, change, value):
        if change == QGraphicsPathItem.ItemSelectedHasChanged:
            self.setZValue(self.zValue() + (1 if value else -1))
        return QGraphicsPathItem.itemChange(self, change, value)

    def _updateTextAnchor(self):
        rect = self.text.boundingRect()
        pos = anchor_rect(rect, self._anchor)
        self.text.setPos(pos)

    def _onLayoutChanged(self):
        self._updateTextAnchor()
Exemple #18
0
class EFrameLayout(QGraphicsPolygonItem):
    def __init__(self, parent=None, scene=None):
        super(EFrameLayout, self).__init__(parent, scene)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setAcceptsHoverEvents(True)

        self.__name = QGraphicsTextItem()
        self.__name.setPlainText('Frame Layout')

        self.__handleWidth = 500
        self.__handleHeight = 20
        self.__separator = 5
        self.__boundExtra = 3 + self.pen().width()
        self.__handleRect = QRectF(0.0, 0.0, self.__handleWidth,
                                   self.__handleHeight)

        self.__controlsBound = 0.0
        self.__controls = []

        self.__isDefaultPen = False
        self.__isCollapsed = True

        self.__pens = {
            0: EDraw.EColor.DefaultLeaveHoverPen,
            1: EDraw.EColor.DefaultEnterHoverPen
        }
        self.setPen(self.__pens[self.__isDefaultPen])

    @property
    def Label(self):
        return self.__name.toPlainText()

    @Label.setter
    def Label(self, label):
        self.__name.setPlainText(str(label))

    @property
    def Width(self):
        return self.__handleWidth

    @Width.setter
    def Width(self, width):
        self.__handleWidth = width
        self.updateGeometry()

    def toggleContentVisibility(self):
        if not len(self.__controls): return

        if self.__controls[0].isVisible():
            [control.hide() for control in self.__controls]
            self.__isCollapsed = True
            return

        [control.show() for control in self.__controls]
        self.__isCollapsed = False

    def updateGeometry(self):

        self.__handleRect = QRectF(0.0, 0.0, self.__handleWidth,
                                   self.__handleHeight)

        if not len(self.__controls) or self.__isCollapsed:
            self.__controlsBound = 0.0
            return

        self.__controlsBound = 0.0
        self.__controlsBound = self.__separator
        self.__controlsBound += sum([
            control.boundingRect().height() + self.__separator
            for control in self.__controls
        ])

        step = self.__handleHeight + self.__separator * 2

        for control in self.__controls:

            control.Name.setTextWidth(self.__controlNameWidth)
            control.Width = self.__handleRect.normalized().adjusted(
                5.0, 0.0, -5.0, 0.0).width()

            control.setPos(5.0, step)
            step += control.boundingRect().height() + self.__separator

    def addControl(self, control):
        if not isinstance(control, EControlsGroup): raise AttributeError

        self.__controls.append(control)
        control.setParentItem(self)
        control.setFlag(QGraphicsItem.ItemIsMovable, False)

        self.__controlNameWidth = max([
            control.Name.boundingRect().width() for control in self.__controls
        ]) + 5

        self.__isCollapsed = False
        self.updateGeometry()

    def clear(self):
        [control.setParentItem(None) for control in self.__controls]
        self.__controls = []

        self.updateGeometry()

    def mouseDoubleClickEvent(self, mouseEvent):
        QGraphicsPolygonItem.mouseDoubleClickEvent(self, mouseEvent)

        self.toggleContentVisibility()
        self.updateGeometry()

    def shape(self):
        return QGraphicsItem.shape(self)

    def boundingRect(self):
        return self.__handleRect.normalized().adjusted(0.0, 0.0, 0.0,
                                                       self.__controlsBound)

    def paint(self, painter, option, widget=None):

        if not self.__isCollapsed:
            painter.setPen(QPen(QColor(0, 0, 0, 50), 2, Qt.SolidLine))
            painter.setBrush(QColor(0, 0, 0, 50))
            painter.drawRect(self.boundingRect().adjusted(
                self.pen().width(), self.__handleHeight, -self.pen().width(),
                0.0))

        painter.setPen(self.pen())
        painter.setBrush(
            EDraw.EColor.LinearGradient(self.__handleRect, Qt.darkGray))
        #painter.drawPolygon( QPolygonF( self.__handleRect.normalized().adjusted(-self.__boundExtra, 0.0, self.__boundExtra, 0.0 ) ) )
        painter.drawPolygon(QPolygonF(self.__handleRect))

        painter.setPen(Qt.lightGray)
        r = QRectF(0.0, 0.0,
                   self.__name.boundingRect().width(),
                   self.__handleRect.height())
        painter.drawText(r, Qt.AlignCenter, self.__name.toPlainText())
Exemple #19
0
class OWAxis(QGraphicsItem):

    Role = OWPalette.Axis

    def __init__(self,
                 id,
                 title='',
                 title_above=False,
                 title_location=AxisMiddle,
                 line=None,
                 arrows=AxisEnd,
                 plot=None):
        QGraphicsItem.__init__(self)
        self.setFlag(QGraphicsItem.ItemHasNoContents)
        self.setZValue(AxisZValue)
        self.id = id
        self.title = title
        self.title_location = title_location
        self.data_line = line
        self.plot = plot
        self.graph_line = None
        self.size = None
        self.scale = None
        self.tick_length = (10, 5, 0)
        self.arrows = arrows
        self.title_above = title_above
        self.line_item = QGraphicsLineItem(self)
        self.title_item = QGraphicsTextItem(self)
        self.end_arrow_item = None
        self.start_arrow_item = None
        self.show_title = False
        self.scale = None
        path = QPainterPath()
        path.setFillRule(Qt.WindingFill)
        path.moveTo(0, 3.09)
        path.lineTo(0, -3.09)
        path.lineTo(9.51, 0)
        path.closeSubpath()
        self.arrow_path = path
        self.label_items = []
        self.label_bg_items = []
        self.tick_items = []
        self._ticks = []
        self.zoom_transform = QTransform()
        self.labels = None
        self.auto_range = None
        self.auto_scale = True

        self.zoomable = False
        self.update_callback = None
        self.max_text_width = 50
        self.text_margin = 5
        self.always_horizontal_text = False

    def update_ticks(self):
        self._ticks = []
        major, medium, minor = self.tick_length
        if self.labels is not None and not self.auto_scale:
            for i, text in enumerate(self.labels):
                self._ticks.append((i, text, medium, 1))
        else:
            if self.scale and not self.auto_scale:
                min, max, step = self.scale
            elif self.auto_range:
                min, max = self.auto_range
                if min is not None and max is not None:
                    step = (max - min) / 10
                else:
                    return
            else:
                return

            if max == min:
                return

            magnitude = int(3 * log10(abs(max - min)) + 1)
            if magnitude % 3 == 0:
                first_place = 1
            elif magnitude % 3 == 1:
                first_place = 2
            else:
                first_place = 5
            magnitude = magnitude / 3 - 1
            step = first_place * pow(10, magnitude)
            val = ceil(min / step) * step
            while val <= max:
                self._ticks.append((val, "%.4g" % val, medium, step))
                val = val + step

    def update_graph(self):
        if self.update_callback:
            self.update_callback()

    def update(self, zoom_only=False):
        self.update_ticks()
        line_color = self.plot.color(OWPalette.Axis)
        text_color = self.plot.color(OWPalette.Text)
        if not self.graph_line or not self.scene():
            return
        self.line_item.setLine(self.graph_line)
        self.line_item.setPen(line_color)
        if self.title:
            self.title_item.setHtml('<b>' + self.title + '</b>')
            self.title_item.setDefaultTextColor(text_color)
        if self.title_location == AxisMiddle:
            title_p = 0.5
        elif self.title_location == AxisEnd:
            title_p = 0.95
        else:
            title_p = 0.05
        title_pos = self.graph_line.pointAt(title_p)
        v = self.graph_line.normalVector().unitVector()

        dense_text = False
        if hasattr(self, 'title_margin'):
            offset = self.title_margin
        elif self._ticks:
            if self.should_be_expanded():
                offset = 55
                dense_text = True
            else:
                offset = 35
        else:
            offset = 10

        if self.title_above:
            title_pos = title_pos + (v.p2() - v.p1()) * (
                offset + QFontMetrics(self.title_item.font()).height())
        else:
            title_pos = title_pos - (v.p2() - v.p1()) * offset
        ## TODO: Move it according to self.label_pos
        self.title_item.setVisible(self.show_title)
        self.title_item.setRotation(-self.graph_line.angle())
        c = self.title_item.mapToParent(
            self.title_item.boundingRect().center())
        tl = self.title_item.mapToParent(
            self.title_item.boundingRect().topLeft())
        self.title_item.setPos(title_pos - c + tl)

        ## Arrows
        if not zoom_only:
            if self.start_arrow_item:
                self.scene().removeItem(self.start_arrow_item)
                self.start_arrow_item = None
            if self.end_arrow_item:
                self.scene().removeItem(self.end_arrow_item)
                self.end_arrow_item = None

        if self.arrows & AxisStart:
            if not zoom_only or not self.start_arrow_item:
                self.start_arrow_item = QGraphicsPathItem(
                    self.arrow_path, self)
            self.start_arrow_item.setPos(self.graph_line.p1())
            self.start_arrow_item.setRotation(-self.graph_line.angle() + 180)
            self.start_arrow_item.setBrush(line_color)
            self.start_arrow_item.setPen(line_color)
        if self.arrows & AxisEnd:
            if not zoom_only or not self.end_arrow_item:
                self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self)
            self.end_arrow_item.setPos(self.graph_line.p2())
            self.end_arrow_item.setRotation(-self.graph_line.angle())
            self.end_arrow_item.setBrush(line_color)
            self.end_arrow_item.setPen(line_color)

        ## Labels

        n = len(self._ticks)
        resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self)
        resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self)
        resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self)

        test_rect = QRectF(self.graph_line.p1(),
                           self.graph_line.p2()).normalized()
        test_rect.adjust(-1, -1, 1, 1)

        n_v = self.graph_line.normalVector().unitVector()
        if self.title_above:
            n_p = n_v.p2() - n_v.p1()
        else:
            n_p = n_v.p1() - n_v.p2()
        l_v = self.graph_line.unitVector()
        l_p = l_v.p2() - l_v.p1()
        for i in range(n):
            pos, text, size, step = self._ticks[i]
            hs = 0.5 * step
            tick_pos = self.map_to_graph(pos)
            if not test_rect.contains(tick_pos):
                self.tick_items[i].setVisible(False)
                self.label_items[i].setVisible(False)
                continue
            item = self.label_items[i]
            item.setVisible(True)
            if not zoom_only:
                if self.id in XAxes or getattr(self, 'is_horizontal', False):
                    item.setHtml('<center>' + Qt.escape(text.strip()) +
                                 '</center>')
                else:
                    item.setHtml(Qt.escape(text.strip()))

            item.setTextWidth(-1)
            text_angle = 0
            if dense_text:
                w = min(item.boundingRect().width(), self.max_text_width)
                item.setTextWidth(w)
                if self.title_above:
                    label_pos = tick_pos + n_p * (
                        w + self.text_margin
                    ) + l_p * item.boundingRect().height() / 2
                else:
                    label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect(
                    ).height() / 2
                text_angle = -90 if self.title_above else 90
            else:
                w = min(
                    item.boundingRect().width(),
                    QLineF(self.map_to_graph(pos - hs),
                           self.map_to_graph(pos + hs)).length())
                label_pos = tick_pos + n_p * self.text_margin - l_p * w / 2
                item.setTextWidth(w)

            if not self.always_horizontal_text:
                if self.title_above:
                    item.setRotation(-self.graph_line.angle() - text_angle)
                else:
                    item.setRotation(self.graph_line.angle() - text_angle)

            item.setPos(label_pos)
            item.setDefaultTextColor(text_color)

            self.label_bg_items[i].setRect(item.boundingRect())
            self.label_bg_items[i].setPen(QPen(Qt.NoPen))
            self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas))

            item = self.tick_items[i]
            item.setVisible(True)
            tick_line = QLineF(v)
            tick_line.translate(-tick_line.p1())
            tick_line.setLength(size)
            if self.title_above:
                tick_line.setAngle(tick_line.angle() + 180)
            item.setLine(tick_line)
            item.setPen(line_color)
            item.setPos(self.map_to_graph(pos))

    @staticmethod
    def make_title(label, unit=None):
        lab = '<i>' + label + '</i>'
        if unit:
            lab = lab + ' [' + unit + ']'
        return lab

    def set_line(self, line):
        self.graph_line = line
        self.update()

    def set_title(self, title):
        self.title = title
        self.update()

    def set_show_title(self, b):
        self.show_title = b
        self.update()

    def set_labels(self, labels):
        self.labels = labels
        self.graph_line = None
        self.auto_scale = False
        self.update_ticks()
        self.update_graph()

    def set_scale(self, min, max, step_size):
        self.scale = (min, max, step_size)
        self.graph_line = None
        self.auto_scale = False
        self.update_ticks()
        self.update_graph()

    def set_tick_length(self, minor, medium, major):
        self.tick_length = (minor, medium, major)
        self.update()

    def map_to_graph(self, x):
        min, max = self.plot.bounds_for_axis(self.id)
        if min == max:
            return QPointF()
        line_point = self.graph_line.pointAt((x - min) / (max - min))
        end_point = line_point * self.zoom_transform
        return self.projection(end_point, self.graph_line)

    @staticmethod
    def projection(point, line):
        norm = line.normalVector()
        norm.translate(point - norm.p1())
        p = QPointF()
        type = line.intersect(norm, p)
        return p

    def continuous_labels(self):
        min, max, step = self.scale
        magnitude = log10(abs(max - min))

    def paint(self, painter, option, widget):
        pass

    def boundingRect(self):
        return QRectF()

    def ticks(self):
        if not self._ticks:
            self.update_ticks()
        return self._ticks

    def bounds(self):
        if self.labels:
            return -0.2, len(self.labels) - 0.8
        elif self.scale:
            min, max, _step = self.scale
            return min, max
        elif self.auto_range:
            return self.auto_range
        else:
            return 0, 1

    def should_be_expanded(self):
        self.update_ticks()
        return self.id in YAxes or self.always_horizontal_text or sum(
            len(t[1]) for t in self._ticks) * 12 > self.plot.width()
Exemple #20
0
class EControlsGroup(QGraphicsPolygonItem):

    def __init__(self, parent=None, scene=None):
        super(EControlsGroup, self).__init__(parent, scene)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setAcceptsHoverEvents(True)

        self.__kId = str( uuid.uuid1() )
        self.__name = QGraphicsTextItem()
        self.__name.setPlainText('EddGroup')

        self.__sWidth = 500
        self.__sHeight = 20
        self.__sExtra = 8
        self.__controlsOffset = 5

        self.__isDefaultPen = False

        self.__pens = { 0: EDraw.EColor.DefaultLeaveHoverPen, 1: EDraw.EColor.DefaultEnterHoverPen }
        self.setPen(self.__pens[self.__isDefaultPen])


        self.__shapeRect = QRectF( 0 , 0, self.__sWidth, self.__sHeight  )
        self.__controls = []

    def updateGeometry(self):

        adjustableControls = [ control for control in self.__controls if control.Adjustable ]

        if len(adjustableControls):
            fixedControlsBounds = self.__name.boundingRect().width()
            fixedControlsBounds += sum( map( int, [ control.boundingRect().width() for control in self.__controls if not control.Adjustable ] ))

            adjustableControlsWidth = int(( self.__sWidth - fixedControlsBounds )) / len(adjustableControls)

        step = self.__name.boundingRect().width()

        for control in self.__controls:
            if control.Adjustable:
                control.Width = adjustableControlsWidth - self.__controlsOffset

            control.setPos( step, 0.0 )
            step += control.boundingRect().width() + self.__controlsOffset

        self.__shapeRect = QRectF( 0 , -(self.__sExtra / 2), self.__sWidth, max([ control.Height for control in self.__controls ]) + self.__sExtra )

    @property
    def kId(self): return self.__kId

    @property
    def Name(self): return self.__name

    @Name.setter
    def Name(self, name ):
        " %s" % self.__name.setPlainText(name)

        self.updateGeometry()

    @property
    def Width(self): return self.__sWidth

    @Width.setter
    def Width(self, width):
        self.__sWidth = width #- self.__controlsOffset * len( self.__controls )
        self.updateGeometry()

    @property
    def Height(self): return self.__sHeight

    @property
    def Controls(self): return self.__controls

    @Controls.setter
    def Controls(self, control):
        control.setParentItem(self)
        self.__controls.append(control)
        self.updateGeometry()

    def toggleHighlight(self):
        if self.__isDefaultPen:
            self.__isDefaultPen = False
            self.setPen(self.__pens[self.__isDefaultPen])
            return

        self.__isDefaultPen = True
        self.setPen(self.__pens[self.__isDefaultPen])

    def hoverEnterEvent(self, mouseEvent):
        QGraphicsPolygonItem.hoverEnterEvent(self, mouseEvent)
        self.toggleHighlight()

    def hoverLeaveEvent(self, mouseEvent):
        QGraphicsPolygonItem.hoverLeaveEvent(self, mouseEvent)
        self.toggleHighlight()

    def shape(self): return QGraphicsItem.shape(self)

    def boundingRect(self):
        return self.__shapeRect

    def paint(self, painter, option, widget=None):


        painter.setPen(self.pen())
        painter.setBrush( EDraw.EColor.LinearGradient( self.boundingRect(), Qt.darkGray ) )
        painter.drawPolygon( QPolygonF( self.boundingRect() ) )

        painter.setPen(Qt.lightGray)

        r = QRectF( 0.0, -(self.__sExtra / 2), self.__name.boundingRect().width(), self.boundingRect().height() )
        painter.drawText( r , Qt.AlignRight | Qt.AlignCenter , "%s: " % self.__name.toPlainText() )
Exemple #21
0
class OWAxis(QGraphicsItem):

    Role = OWPalette.Axis

    def __init__(self, id, title = '', title_above = False, title_location = AxisMiddle, line = None, arrows = 0, plot = None):
        QGraphicsItem.__init__(self)
        self.setFlag(QGraphicsItem.ItemHasNoContents)
        self.setZValue(AxisZValue)
        self.id = id
        self.title = title
        self.title_location = title_location
        self.data_line = line
        self.plot = plot
        self.graph_line = None
        self.size = None
        self.scale = None
        self.tick_length = (10, 5, 0)
        self.arrows = arrows
        self.title_above = title_above
        self.line_item = QGraphicsLineItem(self)
        self.title_item = QGraphicsTextItem(self)
        self.end_arrow_item = None
        self.start_arrow_item = None
        self.show_title = False
        self.scale = None
        path = QPainterPath()
        path.setFillRule(Qt.WindingFill)
        path.moveTo(0, 3.09)
        path.lineTo(0, -3.09)
        path.lineTo(9.51, 0)
        path.closeSubpath()
        self.arrow_path = path
        self.label_items = []
        self.label_bg_items = []
        self.tick_items = []
        self._ticks = []
        self.zoom_transform = QTransform()
        self.labels = None
        self.auto_range = None
        self.auto_scale = True

        self.zoomable = False
        self.update_callback = None
        self.max_text_width = 50
        self.text_margin = 5
        self.always_horizontal_text = False

    @staticmethod
    def compute_scale(min, max):
        magnitude = int(3*log10(abs(max-min)) + 1)
        if magnitude % 3 == 0:
            first_place = 1
        elif magnitude % 3 == 1:
            first_place = 2
        else:
            first_place = 5
        magnitude = magnitude // 3 - 1
        step = first_place * pow(10, magnitude)
        first_val = ceil(min/step) * step
        return first_val, step

    def update_ticks(self):
        self._ticks = []
        major, medium, minor = self.tick_length
        if self.labels is not None and not self.auto_scale:
            for i, text in enumerate(self.labels):
                self._ticks.append( ( i, text, medium, 1 ) )
        else:
            if self.scale and not self.auto_scale:
                min, max, step = self.scale
            elif self.auto_range:
                min, max = self.auto_range
                if min is not None and max is not None:
                    step = (max - min)/10
                else:
                    return
            else:
                return

            if max == min:
                return

            val, step = self.compute_scale(min, max)
            while val <= max:
                self._ticks.append( ( val, "%.4g" % val, medium, step ) )
                val += step

    def update_graph(self):
        if self.update_callback:
            self.update_callback()


    def update(self, zoom_only = False):
        self.update_ticks()
        line_color = self.plot.color(OWPalette.Axis)
        text_color = self.plot.color(OWPalette.Text)
        if not self.graph_line or not self.scene():
            return
        self.line_item.setLine(self.graph_line)
        self.line_item.setPen(line_color)
        if self.title:
            self.title_item.setHtml('<b>' + self.title + '</b>')
            self.title_item.setDefaultTextColor(text_color)
        if self.title_location == AxisMiddle:
            title_p = 0.5
        elif self.title_location == AxisEnd:
            title_p = 0.95
        else:
            title_p = 0.05
        title_pos = self.graph_line.pointAt(title_p)
        v = self.graph_line.normalVector().unitVector()

        dense_text = False
        if hasattr(self, 'title_margin'):
            offset = self.title_margin
        elif self._ticks:
            if self.should_be_expanded():
                offset = 55
                dense_text = True
            else:
                offset = 35
        else:
            offset = 10

        if self.title_above:
            title_pos = title_pos + (v.p2() - v.p1())*(offset + QFontMetrics(self.title_item.font()).height())
        else:
            title_pos = title_pos - (v.p2() - v.p1())*offset
        ## TODO: Move it according to self.label_pos
        self.title_item.setVisible(self.show_title)
        self.title_item.setRotation(-self.graph_line.angle())
        c = self.title_item.mapToParent(self.title_item.boundingRect().center())
        tl = self.title_item.mapToParent(self.title_item.boundingRect().topLeft())
        self.title_item.setPos(title_pos - c + tl)

        ## Arrows
        if not zoom_only:
            if self.start_arrow_item:
                self.scene().removeItem(self.start_arrow_item)
                self.start_arrow_item = None
            if self.end_arrow_item:
                self.scene().removeItem(self.end_arrow_item)
                self.end_arrow_item = None

        if self.arrows & AxisStart:
            if not zoom_only or not self.start_arrow_item:
                self.start_arrow_item = QGraphicsPathItem(self.arrow_path, self)
            self.start_arrow_item.setPos(self.graph_line.p1())
            self.start_arrow_item.setRotation(-self.graph_line.angle() + 180)
            self.start_arrow_item.setBrush(line_color)
            self.start_arrow_item.setPen(line_color)
        if self.arrows & AxisEnd:
            if not zoom_only or not self.end_arrow_item:
                self.end_arrow_item = QGraphicsPathItem(self.arrow_path, self)
            self.end_arrow_item.setPos(self.graph_line.p2())
            self.end_arrow_item.setRotation(-self.graph_line.angle())
            self.end_arrow_item.setBrush(line_color)
            self.end_arrow_item.setPen(line_color)

        ## Labels

        n = len(self._ticks)
        resize_plot_item_list(self.label_items, n, QGraphicsTextItem, self)
        resize_plot_item_list(self.label_bg_items, n, QGraphicsRectItem, self)
        resize_plot_item_list(self.tick_items, n, QGraphicsLineItem, self)

        test_rect = QRectF(self.graph_line.p1(),  self.graph_line.p2()).normalized()
        test_rect.adjust(-1, -1, 1, 1)

        n_v = self.graph_line.normalVector().unitVector()
        if self.title_above:
            n_p = n_v.p2() - n_v.p1()
        else:
            n_p = n_v.p1() - n_v.p2()
        l_v = self.graph_line.unitVector()
        l_p = l_v.p2() - l_v.p1()
        for i in range(n):
            pos, text, size, step = self._ticks[i]
            hs = 0.5 * step
            tick_pos = self.map_to_graph( pos )
            if not test_rect.contains(tick_pos):
                self.tick_items[i].setVisible(False)
                self.label_items[i].setVisible(False)
                continue
            item = self.label_items[i]
            item.setVisible(True)
            if not zoom_only:
                if self.id in XAxes or getattr(self, 'is_horizontal', False):
                    item.setHtml( '<center>' + Qt.escape(text.strip()) + '</center>')
                else:
                    item.setHtml(Qt.escape(text.strip()))

            item.setTextWidth(-1)
            text_angle = 0
            if dense_text:
                w = min(item.boundingRect().width(), self.max_text_width)
                item.setTextWidth(w)
                if self.title_above:
                    label_pos = tick_pos + n_p * (w + self.text_margin) + l_p * item.boundingRect().height()/2
                else:
                    label_pos = tick_pos + n_p * self.text_margin + l_p * item.boundingRect().height()/2
                text_angle = -90 if self.title_above else 90
            else:
                w = min(item.boundingRect().width(), QLineF(self.map_to_graph(pos - hs), self.map_to_graph(pos + hs) ).length())
                label_pos = tick_pos + n_p * self.text_margin - l_p * w/2
                item.setTextWidth(w)

            if not self.always_horizontal_text:
                if self.title_above:
                    item.setRotation(-self.graph_line.angle() - text_angle)
                else:
                    item.setRotation(self.graph_line.angle() - text_angle)

            item.setPos(label_pos)
            item.setDefaultTextColor(text_color)

            self.label_bg_items[i].setRect(item.boundingRect())
            self.label_bg_items[i].setPen(QPen(Qt.NoPen))
            self.label_bg_items[i].setBrush(self.plot.color(OWPalette.Canvas))

            item = self.tick_items[i]
            item.setVisible(True)
            tick_line = QLineF(v)
            tick_line.translate(-tick_line.p1())
            tick_line.setLength(size)
            if self.title_above:
                tick_line.setAngle(tick_line.angle() + 180)
            item.setLine( tick_line )
            item.setPen(line_color)
            item.setPos(self.map_to_graph(pos))

    @staticmethod
    def make_title(label, unit = None):
        lab = '<i>' + label + '</i>'
        if unit:
            lab = lab + ' [' + unit + ']'
        return lab

    def set_line(self, line):
        self.graph_line = line
        self.update()

    def set_title(self, title):
        self.title = title
        self.update()

    def set_show_title(self, b):
        self.show_title = b
        self.update()

    def set_labels(self, labels):
        self.labels = labels
        self.graph_line = None
        self.auto_scale = False
        self.update_ticks()
        self.update_graph()

    def set_scale(self, min, max, step_size):
        self.scale = (min, max, step_size)
        self.graph_line = None
        self.auto_scale = False
        self.update_ticks()
        self.update_graph()

    def set_tick_length(self, minor, medium, major):
        self.tick_length = (minor, medium, major)
        self.update()

    def map_to_graph(self, x):
        min, max = self.plot.bounds_for_axis(self.id)
        if min == max:
            return QPointF()
        line_point = self.graph_line.pointAt( (x-min)/(max-min) )
        end_point = line_point * self.zoom_transform
        return self.projection(end_point, self.graph_line)

    @staticmethod
    def projection(point, line):
        norm = line.normalVector()
        norm.translate(point - norm.p1())
        p = QPointF()
        type = line.intersect(norm, p)
        return p

    def continuous_labels(self):
        min, max, step = self.scale
        magnitude = log10(abs(max-min))

    def paint(self, painter, option, widget):
        pass

    def boundingRect(self):
        return QRectF()

    def ticks(self):
        if not self._ticks:
            self.update_ticks()
        return self._ticks

    def bounds(self):
        if self.labels:
            return -0.2, len(self.labels) -0.8
        elif self.scale:
            min, max, _step = self.scale
            return min, max
        elif self.auto_range:
            return self.auto_range
        else:
            return 0, 1

    def should_be_expanded(self):
        self.update_ticks()
        return self.id in YAxes or self.always_horizontal_text or sum(len(t[1]) for t in self._ticks) * 12 > self.plot.width()
Exemple #22
0
	def __init__(self, world):

		QGraphicsPolygonItem.__init__(self)
		
		#############################
		### Build graph
		#############################

		graph = pydot.Dot()
		graph.set_node_defaults(color = 'red', fontcolor = 'red', label = '\<orphan\>')
		graph.set('overlap', 'prism')
		
		# build adjacency graph from world
		for area in world.areas:
		
			# create node for each room
			node = pydot.Node(area.id)
			node.set( 'label', area.name )
			if area == world.player.currentArea:
				node.set( 'color', 'blue' )
				node.set( 'fontcolor', 'blue' )
			else:
				node.set( 'color', 'black' )
				node.set( 'fontcolor', 'black' )

			graph.add_node(node)

			# link to adjacent rooms
			for feature in area.features:
				for action in feature.actions:
					finalEvent = None
					for event in action.events:
						if type(event) == events.PlayerMoveEvent:
							finalEvent = pydot.Edge( src=area.id, dst=event.properties['destination'] )

					if finalEvent is not None:
						graph.add_edge( finalEvent )
	
		################################
		### Generate SVG from graph
		################################

		ps = graph.create_svg(prog='neato')

		#########################################
		### Build graphics items from SVG
		#########################################

		# build xml tree
		ns = {'svg': 'http://www.w3.org/2000/svg'}
		doc = ET.fromstring(ps)

		# grab the root node properties
		rootNode = doc.xpath('/svg:svg/svg:g[1]', namespaces=ns)[0]
		polygon = rootNode.xpath('./svg:polygon', namespaces=ns)[0]
		pointStr = polygon.xpath('./@points', namespaces=ns)[0]
		penColor = QString(polygon.xpath('./@stroke', namespaces=ns)[0])
		fillColor = QString(polygon.xpath('./@fill', namespaces=ns)[0])

		# parse root polygon path
		path = QPolygonF()
		for pair in pointStr.split(' '):
			dims = pair.split(',')
			point = QPointF( float(dims[0]), float(dims[1]) )
			path.append(point)
		self.setPolygon(path)

		# fill in root node colors
		if QColor.isValidColor(penColor):
			self.setPen( QColor(penColor) )
		if QColor.isValidColor(fillColor):
			self.setBrush( QColor(fillColor) )

		# build each graph node
		for xmlNode in rootNode.xpath('./svg:g', namespaces=ns):

			group = QGraphicsRectItem(self)
			group.setPen( Qt.transparent )
			group.setBrush( Qt.transparent )


			if xmlNode.attrib['class'] == 'node':

				# find the area object
				name = xmlNode.xpath('./svg:title', namespaces=ns)[0].text
				group.setData( 0, QString(world.areas[world.areaLookup[name]].id) )
				
				# get the ellipse info
				ellipseNode = xmlNode.xpath('./svg:ellipse', namespaces=ns)[0]
				elProps = { k: float(ellipseNode.attrib[k]) for k in ['cx', 'cy', 'rx', 'ry']}
				rect = QRectF( elProps['cx']-elProps['rx'], elProps['cy']-elProps['ry'], 2*elProps['rx'], 2*elProps['ry'])
				penColor = QString(ellipseNode.attrib['stroke'])
				ellipseItem = QGraphicsEllipseItem(rect, group)
				if QColor.isValidColor(penColor):
					ellipseItem.setPen( QColor(penColor) )

				# get the text info
				textNode = xmlNode.xpath('./svg:text', namespaces=ns)[0]
				text = textNode.text
				textItem = QGraphicsTextItem(text, group)
				penColor = textNode.attrib.get('fill', 'black')
				nodePoint = QPointF(float(textNode.attrib['x']), float(textNode.attrib['y']))
				textItem.setPos( nodePoint - textItem.boundingRect().center() + QPointF(0.0,-4.0))
				if QColor.isValidColor(penColor):
					textItem.setDefaultTextColor( QColor(penColor) )

				group.setRect( ellipseItem.boundingRect() )
				group.setFlags( QGraphicsRectItem.ItemIsSelectable )

			elif xmlNode.attrib['class'] == 'edge':

 				# parse the line portion of the arrow
 				line = xmlNode.xpath('./svg:path', namespaces=ns)[0]
				path = QPainterPath()

				# pull info from xml file
				linePath = line.attrib['d']
				lineColor = line.attrib['stroke']
					
				# parse path coords
				points = re.findall( '(-?\d+\.\d+),(-?\d+\.\d+)', linePath )
				if len(points) != 4:
					continue
				startPoint = QPointF( float(points[0][0]), float(points[0][1]) )
				path.moveTo(startPoint)
				curvePoints = []
				for pointCoord in points[1:]:
					curvePoints.append( QPointF(float(pointCoord[0]), float(pointCoord[1])) )
				path.cubicTo( curvePoints[0], curvePoints[1], curvePoints[2] )

				# construct path item
				pathItem = QGraphicsPathItem(path, group)
				if QColor.isValidColor(lineColor):
					pathItem.setPen( QColor(lineColor) )

				polyNode = xmlNode.xpath('./svg:polygon', namespaces=ns)[0]

				# pull info from xml file
				pointStr = polyNode.xpath('./@points', namespaces=ns)[0]
				penColor = QString(polyNode.xpath('./@stroke', namespaces=ns)[0])
				fillColor = QString(polyNode.xpath('./@fill', namespaces=ns)[0])

				# parse polygon path
				path = QPolygonF()
				for pair in pointStr.split(' '):
					dims = pair.split(',')
					point = QPointF( float(dims[0]), float(dims[1]) )
					path.append(point)

				# construct polygon item
				polygonItem = QGraphicsPolygonItem(path, group)
				if QColor.isValidColor(penColor):
					polygonItem.setPen( QColor(penColor) )
				if QColor.isValidColor(fillColor):
					polygonItem.setBrush( QColor(fillColor) )

				group.setRect( pathItem.boundingRect() and polygonItem.boundingRect() )
Exemple #23
0
class LinkItem(QGraphicsObject):
    """
    A Link item in the canvas that connects two :class:`.NodeItem`\s in the
    canvas.

    The link curve connects two `Anchor` items (see :func:`setSourceItem`
    and :func:`setSinkItem`). Once the anchors are set the curve
    automatically adjusts its end points whenever the anchors move.

    An optional source/sink text item can be displayed above the curve's
    central point (:func:`setSourceName`, :func:`setSinkName`)

    """

    #: Z value of the item
    Z_VALUE = 0

    def __init__(self, *args):
        QGraphicsObject.__init__(self, *args)
        self.setFlag(QGraphicsItem.ItemHasNoContents, True)
        self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton)
        self.setAcceptHoverEvents(True)

        self.setZValue(self.Z_VALUE)

        self.sourceItem = None
        self.sourceAnchor = None
        self.sinkItem = None
        self.sinkAnchor = None

        self.curveItem = LinkCurveItem(self)

        self.sourceIndicator = LinkAnchorIndicator(self)
        self.sinkIndicator = LinkAnchorIndicator(self)
        self.sourceIndicator.hide()
        self.sinkIndicator.hide()

        self.linkTextItem = QGraphicsTextItem(self)

        self.__sourceName = ""
        self.__sinkName = ""

        self.__dynamic = False
        self.__dynamicEnabled = False

        self.hover = False

    def setSourceItem(self, item, anchor=None):
        """
        Set the source `item` (:class:`.NodeItem`). Use `anchor`
        (:class:`.AnchorPoint`) as the curve start point (if ``None`` a new
        output anchor will be created using ``item.newOutputAnchor()``).

        Setting item to ``None`` and a valid anchor is a valid operation
        (for instance while mouse dragging one end of the link).

        """
        if item is not None and anchor is not None:
            if anchor not in item.outputAnchors():
                raise ValueError("Anchor must be belong to the item")

        if self.sourceItem != item:
            if self.sourceAnchor:
                # Remove a previous source item and the corresponding anchor
                self.sourceAnchor.scenePositionChanged.disconnect(
                    self._sourcePosChanged
                )

                if self.sourceItem is not None:
                    self.sourceItem.removeOutputAnchor(self.sourceAnchor)

                self.sourceItem = self.sourceAnchor = None

            self.sourceItem = item

            if item is not None and anchor is None:
                # Create a new output anchor for the item if none is provided.
                anchor = item.newOutputAnchor()

            # Update the visibility of the start point indicator.
            self.sourceIndicator.setVisible(bool(item))

        if anchor != self.sourceAnchor:
            if self.sourceAnchor is not None:
                self.sourceAnchor.scenePositionChanged.disconnect(
                    self._sourcePosChanged
                )

            self.sourceAnchor = anchor

            if self.sourceAnchor is not None:
                self.sourceAnchor.scenePositionChanged.connect(
                    self._sourcePosChanged
                )

        self.__updateCurve()

    def setSinkItem(self, item, anchor=None):
        """
        Set the sink `item` (:class:`.NodeItem`). Use `anchor`
        (:class:`.AnchorPoint`) as the curve end point (if ``None`` a new
        input anchor will be created using ``item.newInputAnchor()``).

        Setting item to ``None`` and a valid anchor is a valid operation
        (for instance while mouse dragging one and of the link).

        """
        if item is not None and anchor is not None:
            if anchor not in item.inputAnchors():
                raise ValueError("Anchor must be belong to the item")

        if self.sinkItem != item:
            if self.sinkAnchor:
                # Remove a previous source item and the corresponding anchor
                self.sinkAnchor.scenePositionChanged.disconnect(
                    self._sinkPosChanged
                )

                if self.sinkItem is not None:
                    self.sinkItem.removeInputAnchor(self.sinkAnchor)

                self.sinkItem = self.sinkAnchor = None

            self.sinkItem = item

            if item is not None and anchor is None:
                # Create a new input anchor for the item if none is provided.
                anchor = item.newInputAnchor()

            # Update the visibility of the end point indicator.
            self.sinkIndicator.setVisible(bool(item))

        if self.sinkAnchor != anchor:
            if self.sinkAnchor is not None:
                self.sinkAnchor.scenePositionChanged.disconnect(
                    self._sinkPosChanged
                )

            self.sinkAnchor = anchor

            if self.sinkAnchor is not None:
                self.sinkAnchor.scenePositionChanged.connect(
                    self._sinkPosChanged
                )

        self.__updateCurve()

    def setFont(self, font):
        """
        Set the font for the channel names text item.
        """
        if font != self.font():
            self.linkTextItem.setFont(font)
            self.__updateText()

    def font(self):
        """
        Return the font for the channel names text.
        """
        return self.linkTextItem.font()

    def setChannelNamesVisible(self, visible):
        """
        Set the visibility of the channel name text.
        """
        self.linkTextItem.setVisible(visible)

    def setSourceName(self, name):
        """
        Set the name of the source (used in channel name text).
        """
        if self.__sourceName != name:
            self.__sourceName = name
            self.__updateText()

    def sourceName(self):
        """
        Return the source name.
        """
        return self.__sourceName

    def setSinkName(self, name):
        """
        Set the name of the sink (used in channel name text).
        """
        if self.__sinkName != name:
            self.__sinkName = name
            self.__updateText()

    def sinkName(self):
        """
        Return the sink name.
        """
        return self.__sinkName

    def _sinkPosChanged(self, *arg):
        self.__updateCurve()

    def _sourcePosChanged(self, *arg):
        self.__updateCurve()

    def __updateCurve(self):
        self.prepareGeometryChange()
        if self.sourceAnchor and self.sinkAnchor:
            source_pos = self.sourceAnchor.anchorScenePos()
            sink_pos = self.sinkAnchor.anchorScenePos()
            source_pos = self.curveItem.mapFromScene(source_pos)
            sink_pos = self.curveItem.mapFromScene(sink_pos)

            # Adaptive offset for the curve control points to avoid a
            # cusp when the two points have the same y coordinate
            # and are close together
            delta = source_pos - sink_pos
            dist = math.sqrt(delta.x() ** 2 + delta.y() ** 2)
            cp_offset = min(dist / 2.0, 60.0)

            # TODO: make the curve tangent orthogonal to the anchors path.
            path = QPainterPath()
            path.moveTo(source_pos)
            path.cubicTo(source_pos + QPointF(cp_offset, 0),
                         sink_pos - QPointF(cp_offset, 0),
                         sink_pos)

            self.curveItem.setPath(path)
            self.sourceIndicator.setPos(source_pos)
            self.sinkIndicator.setPos(sink_pos)
            self.__updateText()
        else:
            self.setHoverState(False)
            self.curveItem.setPath(QPainterPath())

    def __updateText(self):
        self.prepareGeometryChange()

        if self.__sourceName or self.__sinkName:
            if self.__sourceName != self.__sinkName:
                text = u"{0} \u2192 {1}".format(self.__sourceName,
                                                self.__sinkName)
            else:
                # If the names are the same show only one.
                # Is this right? If the sink has two input channels of the
                # same type having the name on the link help elucidate
                # the scheme.
                text = self.__sourceName
        else:
            text = ""

        self.linkTextItem.setPlainText(text)

        path = self.curveItem.path()
        if not path.isEmpty():
            center = path.pointAtPercent(0.5)
            angle = path.angleAtPercent(0.5)

            brect = self.linkTextItem.boundingRect()

            transform = QTransform()
            transform.translate(center.x(), center.y())
            transform.rotate(-angle)

            # Center and move above the curve path.
            transform.translate(-brect.width() / 2, -brect.height())

            self.linkTextItem.setTransform(transform)

    def removeLink(self):
        self.setSinkItem(None)
        self.setSourceItem(None)
        self.__updateCurve()

    def setHoverState(self, state):
        if self.hover != state:
            self.prepareGeometryChange()
            self.hover = state
            self.sinkIndicator.setHoverState(state)
            self.sourceIndicator.setHoverState(state)
            self.curveItem.setHoverState(state)

    def hoverEnterEvent(self, event):
        # Hover enter event happens when the mouse enters any child object
        # but we only want to show the 'hovered' shadow when the mouse
        # is over the 'curveItem', so we install self as an event filter
        # on the LinkCurveItem and listen to its hover events.
        self.curveItem.installSceneEventFilter(self)
        return QGraphicsObject.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        # Remove the event filter to prevent unnecessary work in
        # scene event filter when not needed
        self.curveItem.removeSceneEventFilter(self)
        return QGraphicsObject.hoverLeaveEvent(self, event)

    def sceneEventFilter(self, obj, event):
        if obj is self.curveItem:
            if event.type() == QEvent.GraphicsSceneHoverEnter:
                self.setHoverState(True)
            elif event.type() == QEvent.GraphicsSceneHoverLeave:
                self.setHoverState(False)

        return QGraphicsObject.sceneEventFilter(self, obj, event)

    def boundingRect(self):
        return self.childrenBoundingRect()

    def shape(self):
        return self.curveItem.shape()

    def setEnabled(self, enabled):
        """
        Reimplemented from :class:`QGraphicsObject`

        Set link enabled state. When disabled the link is rendered with a
        dashed line.

        """
        # This getter/setter pair override a property from the base class.
        # They should be renamed to e.g. setLinkEnabled/linkEnabled
        self.curveItem.setLinkEnabled(enabled)

    def isEnabled(self):
        return self.curveItem.isLinkEnabled()

    def setDynamicEnabled(self, enabled):
        """
        Set the link's dynamic enabled state.

        If the link is `dynamic` it will be rendered in red/green color
        respectively depending on the state of the dynamic enabled state.

        """
        if self.__dynamicEnabled != enabled:
            self.__dynamicEnabled = enabled
            if self.__dynamic:
                self.__updatePen()

    def isDynamicEnabled(self):
        """
        Is the link dynamic enabled.
        """
        return self.__dynamicEnabled

    def setDynamic(self, dynamic):
        """
        Mark the link as dynamic (i.e. it responds to
        :func:`setDynamicEnabled`).

        """
        if self.__dynamic != dynamic:
            self.__dynamic = dynamic
            self.__updatePen()

    def isDynamic(self):
        """
        Is the link dynamic.
        """
        return self.__dynamic

    def __updatePen(self):
        self.prepareGeometryChange()
        if self.__dynamic:
            if self.__dynamicEnabled:
                color = QColor(0, 150, 0, 150)
            else:
                color = QColor(150, 0, 0, 150)

            normal = QPen(QBrush(color), 2.0)
            hover = QPen(QBrush(color.darker(120)), 2.1)
        else:
            normal = QPen(QBrush(QColor("#9CACB4")), 2.0)
            hover = QPen(QBrush(QColor("#7D7D7D")), 2.1)

        self.curveItem.setCurvePenSet(normal, hover)
class TaskGraphicsItem (QGraphicsEllipseItem):

    def __init__(self, rect=None):
        super(TaskGraphicsItem, self).__init__()

        if rect is not None:
            self.setRect(rect)

        self.setPen(QPen(Qt.NoPen))

        # Setup the text item
        self.textItem = QGraphicsTextItem()
        self.textItem.setParentItem(self)
        self.textItem.rotate(-90)
        self.textItem.setDefaultTextColor(QColor(255, 255, 255))

        # The dimensions to reach via a LERP.
        self.startPos = QPointF(0, 0)
        self.endPos = QPointF(0, 0)
        self.startDiameter = 1
        self.endDiameter = 1

        self.centerMark = QGraphicsEllipseItem()
        self.centerMark.setBrush(QBrush(Qt.white))
        self.centerMark.setPen(QPen(Qt.NoPen))
        self.centerMark.setParentItem(self)

        self.pid = -1

        # To determine if it is associated with an active process.
        self.used = False

    def mousePressEvent(self, event):
        print "Clicked On Ellipse at: ", self.rect().topLeft()

    def set_pid(self, pid):
        self.pid = pid

    def set_name(self, str_name):
        self.textItem.setPlainText(str_name)

    def update_name_pos(self):

        rect = self.boundingRect()
        text_rect = self.textItem.boundingRect()

        # Center text (on the x-axis) and offset (on the y-axis) so it doesn't overlap the ellipse item.
        x_text = rect.x() + rect.width()/2 - text_rect.height()/2
        y_text = rect.y() + 100 + text_rect.width() + rect.height()

        self.textItem.setPos(x_text, y_text)

    # Time step is in seconds.
    def update(self, time_step):

        diameter = self.rect().width() + self.lerp_rate(self.startDiameter, self.endDiameter, time_step)
        if diameter <= 1:
            diameter = 1

        pos = self.rect().topLeft()

        x = pos.x() + self.lerp_rate(self.startPos.x(), self.endPos.x(), time_step)
        y = pos.y() + self.lerp_rate(self.startPos.y(), self.endPos.y(), time_step)

        self.setRect(QRectF(x, y, diameter, diameter))
        self.update_name_pos()
        self.update_center_mark()

    def update_center_mark(self):
        scale = self.scene().views()[0].currentScale

        hwidth = self.rect().width() / 2.0
        diam = 2.0 / scale

        # Only mark center for large enough items.
        if hwidth * 0.2 > diam:

            self.centerMark.setVisible(True)

            hdiam = diam / 2.0
            pos = self.rect().topLeft()

            x = pos.x() - hdiam + hwidth
            y = pos.y() - hdiam + hwidth
            self.centerMark.setRect(QRectF(x, y, diam, diam))

        else:
            self.centerMark.setVisible(False)

    # Return the linear interpolation rate. Reach start to end at a rate of 'growth rate'
    @staticmethod
    def lerp_rate(start, end, time_step):
        return (end - start) * time_step
Exemple #25
0
class BlockItem(QGraphicsRectItem):
    """ 
    Represents a block in the diagram
    """
    def __init__(self,
                 parent,
                 name='Untitled',
                 width=180,
                 height=40,
                 blockColor="#A5A2A5",
                 data=None,
                 bold=False,
                 italic=False,
                 status=True):
        """
        Constructor
        """
        QGraphicsRectItem.__init__(self)

        self.parentWidget = parent
        self.internalData = data
        self.status = status

        color = QColor(0, 0, 0)
        color.setNamedColor(blockColor)
        self.setPen(QPen(color, 2))

        if sys.version_info > (3, ) and isinstance(name, bytes):
            name = str(name, "utf8", errors="ignore")

        self.label = QGraphicsTextItem(name, self)

        self.setFlags(self.ItemIsSelectable)
        self.setCursor(QCursor(Qt.PointingHandCursor))

        self.changeSize(width, height)

    def setData(self, data):
        """
        Set data
        """
        self.internalData = data

    def setColor(self, blockColor):
        """
        Set color
        """
        color = QColor(0, 0, 0)
        color.setNamedColor(blockColor)
        self.setPen(QPen(color, 1))

        self.setBrush(QBrush(color))

    def changeSize(self, w, h):
        """
        Resize block function
        """
        # Limit the block size
        self.setRect(0.0, 0.0, w, h)

        # center label:
        rect = self.label.boundingRect()
        lw, lh = rect.width(), rect.height()
        lx = (w - lw) / 2
        ly = (h - lh) / 2
        self.label.setPos(lx, ly)

        return w, h

    def mouseReleaseEvent(self, event):
        """
        On mouse release event
        """
        if self.internalData is not None:
            data = self.internalData
            if isinstance(self.internalData, list):
                # [('Step 1', {'expected': 'result expected', 'action': 'step description',
                # 'actual': 'success', 'result': 'PASS',
                # 'summary': 'step sample'}), ('%payload-v1%', {'raw': '',
                # 'len': 0, 'time': 1421484488.314928})]
                data_tmp = self.internalData[0]
                data = "%s\n" % data_tmp[0]

                data += "\nSummary: %s" % data_tmp[1]['summary']

                data += "\n\nAction: %s" % data_tmp[1]['action']
                data += "\nExpected: %s" % data_tmp[1]['expected']

                data += "\n\nResult: %s" % data_tmp[1]['result']

                if 'actual' in data_tmp[1]:
                    data += "\nActual: %s" % data_tmp[1]['actual']

            self.parentWidget.logEdit.setText(data)
        else:
            self.parentWidget.logEdit.setText("")
    def drawHistogram(self):
        if self.result is None:
            return
        # Label the histogram
        #self.minValueLabel.setText(str(self.minValue))
        #self.maxValueLabel.setText(str(self.maxValue))
        minvaltext = QGraphicsTextItem(str(self.minValue))
        minvaltextheight = minvaltext.boundingRect().height()
        maxvaltext = QGraphicsTextItem(str(self.maxValue))
        maxvaltextwidth = maxvaltext.boundingRect().width()

        #self.showInfo(str(self.result))
        # Check which element should be used for the histogram
        element = 1
        maxvalue = 0.0
        for i in range(len(self.result)):
            if self.result[i][element] > maxvalue:
                maxvalue = self.result[i][element]
                # Find the maximum value for scaling
        cutoffvalue = maxvalue
        if (self.frequencyRangeSpinBox.value() > 0):
            cutoffvalue = self.frequencyRangeSpinBox.value()
        #self.maxNumberLabel.setText(str(maxvalue))
        #self.maxNumberLabel.setText(str(cutoffvalue))
        self.scene.clear()
        if maxvalue == 0:
            return
        viewprect = QRectF(self.histogramGraphicsView.viewport().rect())
        self.histogramGraphicsView.setSceneRect(viewprect)
        bottom = self.histogramGraphicsView.sceneRect().bottom()
        top = self.histogramGraphicsView.sceneRect().top()
        left = self.histogramGraphicsView.sceneRect().left()
        right = self.histogramGraphicsView.sceneRect().right()
        height = bottom - top - 1
        width = right - left - 1
        padding = 3
        toppadding = 3
        bottompadding = minvaltextheight
        # Determine the width of the left margin (depends on the y range)
        clog = log(cutoffvalue,10)
        clogint = int(clog)
        #clogrem = clog % clogint
        yincr = pow(10,clogint)
        dummytext = QGraphicsTextItem(str(yincr))
        # The left padding must accomodate the y labels
        leftpadding = dummytext.boundingRect().width()
        #leftpadding = 30
        # Find the width of the maximium frequency label
        maxfreqtext = QGraphicsTextItem(str(cutoffvalue))
        maxfreqtextwidth = maxvaltext.boundingRect().width()
        rightpadding = maxfreqtextwidth
        width = width - (leftpadding + rightpadding)
        height = height - (toppadding + bottompadding)
        barwidth = width / self.bins
        #center = QPoint(left + width / 2.0, top + height / 2.0)
        #start = QPointF(self.histogramGraphicsView.mapToScene(center))

        # Create the histogram
        for i in range(self.bins):
            #barheight = height * self.result[i][element] / maxvalue
            barheight = height * self.result[i][element] / cutoffvalue
            barrect = QGraphicsRectItem(QRectF(leftpadding + barwidth * i,
                        height-barheight+toppadding, barwidth, barheight))
            barbrush = QBrush(QColor(255,153,102))
            barrect.setBrush(barbrush)
            self.scene.addItem(barrect)
        
        # Determine the increments for the horizontal lines
        if (cutoffvalue // yincr <= 5 and yincr > 1 ):
            yincr = yincr / 2
            if (cutoffvalue // yincr < 5 and yincr > 10 ):
                yincr = yincr / 2
        # Draw horizontal lines with labels
        yval = 0
        while (yval <= cutoffvalue):
            scval = height + toppadding - yval * height / cutoffvalue
            hline = QGraphicsLineItem(QLineF(leftpadding-2, scval,width+(leftpadding),scval))
            hlinepen = QPen(QColor(153,153,153))
            hline.setPen(hlinepen)
            self.scene.addItem(hline)
            ylabtext = QGraphicsTextItem(str(int(yval)))
            ylabtextheight = ylabtext.boundingRect().height()
            ylabtextwidth = ylabtext.boundingRect().width()
            ylabtext.setPos(leftpadding - ylabtextwidth, scval - ylabtextheight/2)
            if (scval - ylabtextheight/2 > 0):
                self.scene.addItem(ylabtext)
            yval = yval + yincr

            
        #yincr = (cutoffvalue / 10)
        minvaltextwidth = minvaltext.boundingRect().width()
        minvaltext.setPos(leftpadding - minvaltextwidth/2, height + toppadding + bottompadding - minvaltextheight)
        self.scene.addItem(minvaltext)
        maxvaltext.setPos(leftpadding + width - maxvaltextwidth/2, height + toppadding + bottompadding - minvaltextheight)
        self.scene.addItem(maxvaltext)
        maxfreqtext.setPos(leftpadding + width, 0)
        self.scene.addItem(maxfreqtext)
Exemple #27
0
class EControlsGroup(QGraphicsPolygonItem):
    def __init__(self, parent=None, scene=None):
        super(EControlsGroup, self).__init__(parent, scene)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setAcceptsHoverEvents(True)

        self.__kId = str(uuid.uuid1())
        self.__name = QGraphicsTextItem()
        self.__name.setPlainText('EddGroup')

        self.__sWidth = 500
        self.__sHeight = 20
        self.__sExtra = 8
        self.__controlsOffset = 5

        self.__isDefaultPen = False

        self.__pens = {
            0: EDraw.EColor.DefaultLeaveHoverPen,
            1: EDraw.EColor.DefaultEnterHoverPen
        }
        self.setPen(self.__pens[self.__isDefaultPen])

        self.__shapeRect = QRectF(0, 0, self.__sWidth, self.__sHeight)
        self.__controls = []

    def updateGeometry(self):

        adjustableControls = [
            control for control in self.__controls if control.Adjustable
        ]

        if len(adjustableControls):
            fixedControlsBounds = self.__name.boundingRect().width()
            fixedControlsBounds += sum(
                map(int, [
                    control.boundingRect().width()
                    for control in self.__controls if not control.Adjustable
                ]))

            adjustableControlsWidth = int(
                (self.__sWidth -
                 fixedControlsBounds)) / len(adjustableControls)

        step = self.__name.boundingRect().width()

        for control in self.__controls:
            if control.Adjustable:
                control.Width = adjustableControlsWidth - self.__controlsOffset

            control.setPos(step, 0.0)
            step += control.boundingRect().width() + self.__controlsOffset

        self.__shapeRect = QRectF(
            0, -(self.__sExtra / 2), self.__sWidth,
            max([control.Height
                 for control in self.__controls]) + self.__sExtra)

    @property
    def kId(self):
        return self.__kId

    @property
    def Name(self):
        return self.__name

    @Name.setter
    def Name(self, name):
        " %s" % self.__name.setPlainText(name)

        self.updateGeometry()

    @property
    def Width(self):
        return self.__sWidth

    @Width.setter
    def Width(self, width):
        self.__sWidth = width  #- self.__controlsOffset * len( self.__controls )
        self.updateGeometry()

    @property
    def Height(self):
        return self.__sHeight

    @property
    def Controls(self):
        return self.__controls

    @Controls.setter
    def Controls(self, control):
        control.setParentItem(self)
        self.__controls.append(control)
        self.updateGeometry()

    def toggleHighlight(self):
        if self.__isDefaultPen:
            self.__isDefaultPen = False
            self.setPen(self.__pens[self.__isDefaultPen])
            return

        self.__isDefaultPen = True
        self.setPen(self.__pens[self.__isDefaultPen])

    def hoverEnterEvent(self, mouseEvent):
        QGraphicsPolygonItem.hoverEnterEvent(self, mouseEvent)
        self.toggleHighlight()

    def hoverLeaveEvent(self, mouseEvent):
        QGraphicsPolygonItem.hoverLeaveEvent(self, mouseEvent)
        self.toggleHighlight()

    def shape(self):
        return QGraphicsItem.shape(self)

    def boundingRect(self):
        return self.__shapeRect

    def paint(self, painter, option, widget=None):

        painter.setPen(self.pen())
        painter.setBrush(
            EDraw.EColor.LinearGradient(self.boundingRect(), Qt.darkGray))
        painter.drawPolygon(QPolygonF(self.boundingRect()))

        painter.setPen(Qt.lightGray)

        r = QRectF(0.0, -(self.__sExtra / 2),
                   self.__name.boundingRect().width(),
                   self.boundingRect().height())
        painter.drawText(r, Qt.AlignRight | Qt.AlignCenter,
                         "%s: " % self.__name.toPlainText())
Exemple #28
0
class LinkItem(QGraphicsObject):
    """
    A Link item in the canvas that connects two :class:`.NodeItem`\s in the
    canvas.

    The link curve connects two `Anchor` items (see :func:`setSourceItem`
    and :func:`setSinkItem`). Once the anchors are set the curve
    automatically adjusts its end points whenever the anchors move.

    An optional source/sink text item can be displayed above the curve's
    central point (:func:`setSourceName`, :func:`setSinkName`)

    """

    #: Z value of the item
    Z_VALUE = 0

    def __init__(self, *args):
        QGraphicsObject.__init__(self, *args)
        self.setFlag(QGraphicsItem.ItemHasNoContents, True)
        self.setAcceptedMouseButtons(Qt.RightButton | Qt.LeftButton)
        self.setAcceptHoverEvents(True)

        self.setZValue(self.Z_VALUE)

        self.sourceItem = None
        self.sourceAnchor = None
        self.sinkItem = None
        self.sinkAnchor = None

        self.curveItem = LinkCurveItem(self)

        self.sourceIndicator = LinkAnchorIndicator(self)
        self.sinkIndicator = LinkAnchorIndicator(self)
        self.sourceIndicator.hide()
        self.sinkIndicator.hide()

        self.linkTextItem = QGraphicsTextItem(self)

        self.__sourceName = ""
        self.__sinkName = ""

        self.__dynamic = False
        self.__dynamicEnabled = False

        self.hover = False

    def setSourceItem(self, item, anchor=None):
        """
        Set the source `item` (:class:`.NodeItem`). Use `anchor`
        (:class:`.AnchorPoint`) as the curve start point (if ``None`` a new
        output anchor will be created using ``item.newOutputAnchor()``).

        Setting item to ``None`` and a valid anchor is a valid operation
        (for instance while mouse dragging one end of the link).

        """
        if item is not None and anchor is not None:
            if anchor not in item.outputAnchors():
                raise ValueError("Anchor must be belong to the item")

        if self.sourceItem != item:
            if self.sourceAnchor:
                # Remove a previous source item and the corresponding anchor
                self.sourceAnchor.scenePositionChanged.disconnect(
                    self._sourcePosChanged)

                if self.sourceItem is not None:
                    self.sourceItem.removeOutputAnchor(self.sourceAnchor)

                self.sourceItem = self.sourceAnchor = None

            self.sourceItem = item

            if item is not None and anchor is None:
                # Create a new output anchor for the item if none is provided.
                anchor = item.newOutputAnchor()

            # Update the visibility of the start point indicator.
            self.sourceIndicator.setVisible(bool(item))

        if anchor != self.sourceAnchor:
            if self.sourceAnchor is not None:
                self.sourceAnchor.scenePositionChanged.disconnect(
                    self._sourcePosChanged)

            self.sourceAnchor = anchor

            if self.sourceAnchor is not None:
                self.sourceAnchor.scenePositionChanged.connect(
                    self._sourcePosChanged)

        self.__updateCurve()

    def setSinkItem(self, item, anchor=None):
        """
        Set the sink `item` (:class:`.NodeItem`). Use `anchor`
        (:class:`.AnchorPoint`) as the curve end point (if ``None`` a new
        input anchor will be created using ``item.newInputAnchor()``).

        Setting item to ``None`` and a valid anchor is a valid operation
        (for instance while mouse dragging one and of the link).

        """
        if item is not None and anchor is not None:
            if anchor not in item.inputAnchors():
                raise ValueError("Anchor must be belong to the item")

        if self.sinkItem != item:
            if self.sinkAnchor:
                # Remove a previous source item and the corresponding anchor
                self.sinkAnchor.scenePositionChanged.disconnect(
                    self._sinkPosChanged)

                if self.sinkItem is not None:
                    self.sinkItem.removeInputAnchor(self.sinkAnchor)

                self.sinkItem = self.sinkAnchor = None

            self.sinkItem = item

            if item is not None and anchor is None:
                # Create a new input anchor for the item if none is provided.
                anchor = item.newInputAnchor()

            # Update the visibility of the end point indicator.
            self.sinkIndicator.setVisible(bool(item))

        if self.sinkAnchor != anchor:
            if self.sinkAnchor is not None:
                self.sinkAnchor.scenePositionChanged.disconnect(
                    self._sinkPosChanged)

            self.sinkAnchor = anchor

            if self.sinkAnchor is not None:
                self.sinkAnchor.scenePositionChanged.connect(
                    self._sinkPosChanged)

        self.__updateCurve()

    def setFont(self, font):
        """
        Set the font for the channel names text item.
        """
        if font != self.font():
            self.linkTextItem.setFont(font)
            self.__updateText()

    def font(self):
        """
        Return the font for the channel names text.
        """
        return self.linkTextItem.font()

    def setChannelNamesVisible(self, visible):
        """
        Set the visibility of the channel name text.
        """
        self.linkTextItem.setVisible(visible)

    def setSourceName(self, name):
        """
        Set the name of the source (used in channel name text).
        """
        if self.__sourceName != name:
            self.__sourceName = name
            self.__updateText()

    def sourceName(self):
        """
        Return the source name.
        """
        return self.__sourceName

    def setSinkName(self, name):
        """
        Set the name of the sink (used in channel name text).
        """
        if self.__sinkName != name:
            self.__sinkName = name
            self.__updateText()

    def sinkName(self):
        """
        Return the sink name.
        """
        return self.__sinkName

    def _sinkPosChanged(self, *arg):
        self.__updateCurve()

    def _sourcePosChanged(self, *arg):
        self.__updateCurve()

    def __updateCurve(self):
        self.prepareGeometryChange()
        if self.sourceAnchor and self.sinkAnchor:
            source_pos = self.sourceAnchor.anchorScenePos()
            sink_pos = self.sinkAnchor.anchorScenePos()
            source_pos = self.curveItem.mapFromScene(source_pos)
            sink_pos = self.curveItem.mapFromScene(sink_pos)

            # Adaptive offset for the curve control points to avoid a
            # cusp when the two points have the same y coordinate
            # and are close together
            delta = source_pos - sink_pos
            dist = math.sqrt(delta.x()**2 + delta.y()**2)
            cp_offset = min(dist / 2.0, 60.0)

            # TODO: make the curve tangent orthogonal to the anchors path.
            path = QPainterPath()
            path.moveTo(source_pos)
            path.cubicTo(source_pos + QPointF(cp_offset, 0),
                         sink_pos - QPointF(cp_offset, 0), sink_pos)

            self.curveItem.setPath(path)
            self.sourceIndicator.setPos(source_pos)
            self.sinkIndicator.setPos(sink_pos)
            self.__updateText()
        else:
            self.setHoverState(False)
            self.curveItem.setPath(QPainterPath())

    def __updateText(self):
        self.prepareGeometryChange()

        if self.__sourceName or self.__sinkName:
            if self.__sourceName != self.__sinkName:
                text = u"{0} \u2192 {1}".format(self.__sourceName,
                                                self.__sinkName)
            else:
                # If the names are the same show only one.
                # Is this right? If the sink has two input channels of the
                # same type having the name on the link help elucidate
                # the scheme.
                text = self.__sourceName
        else:
            text = ""

        self.linkTextItem.setPlainText(text)

        path = self.curveItem.path()
        if not path.isEmpty():
            center = path.pointAtPercent(0.5)
            angle = path.angleAtPercent(0.5)

            brect = self.linkTextItem.boundingRect()

            transform = QTransform()
            transform.translate(center.x(), center.y())
            transform.rotate(-angle)

            # Center and move above the curve path.
            transform.translate(-brect.width() / 2, -brect.height())

            self.linkTextItem.setTransform(transform)

    def removeLink(self):
        self.setSinkItem(None)
        self.setSourceItem(None)
        self.__updateCurve()

    def setHoverState(self, state):
        if self.hover != state:
            self.prepareGeometryChange()
            self.hover = state
            self.sinkIndicator.setHoverState(state)
            self.sourceIndicator.setHoverState(state)
            self.curveItem.setHoverState(state)

    def hoverEnterEvent(self, event):
        # Hover enter event happens when the mouse enters any child object
        # but we only want to show the 'hovered' shadow when the mouse
        # is over the 'curveItem', so we install self as an event filter
        # on the LinkCurveItem and listen to its hover events.
        self.curveItem.installSceneEventFilter(self)
        return QGraphicsObject.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        # Remove the event filter to prevent unnecessary work in
        # scene event filter when not needed
        self.curveItem.removeSceneEventFilter(self)
        return QGraphicsObject.hoverLeaveEvent(self, event)

    def sceneEventFilter(self, obj, event):
        if obj is self.curveItem:
            if event.type() == QEvent.GraphicsSceneHoverEnter:
                self.setHoverState(True)
            elif event.type() == QEvent.GraphicsSceneHoverLeave:
                self.setHoverState(False)

        return QGraphicsObject.sceneEventFilter(self, obj, event)

    def boundingRect(self):
        return self.childrenBoundingRect()

    def shape(self):
        return self.curveItem.shape()

    def setEnabled(self, enabled):
        """
        Reimplemented from :class:`QGraphicsObject`

        Set link enabled state. When disabled the link is rendered with a
        dashed line.

        """
        # This getter/setter pair override a property from the base class.
        # They should be renamed to e.g. setLinkEnabled/linkEnabled
        self.curveItem.setLinkEnabled(enabled)

    def isEnabled(self):
        return self.curveItem.isLinkEnabled()

    def setDynamicEnabled(self, enabled):
        """
        Set the link's dynamic enabled state.

        If the link is `dynamic` it will be rendered in red/green color
        respectively depending on the state of the dynamic enabled state.

        """
        if self.__dynamicEnabled != enabled:
            self.__dynamicEnabled = enabled
            if self.__dynamic:
                self.__updatePen()

    def isDynamicEnabled(self):
        """
        Is the link dynamic enabled.
        """
        return self.__dynamicEnabled

    def setDynamic(self, dynamic):
        """
        Mark the link as dynamic (i.e. it responds to
        :func:`setDynamicEnabled`).

        """
        if self.__dynamic != dynamic:
            self.__dynamic = dynamic
            self.__updatePen()

    def isDynamic(self):
        """
        Is the link dynamic.
        """
        return self.__dynamic

    def __updatePen(self):
        self.prepareGeometryChange()
        if self.__dynamic:
            if self.__dynamicEnabled:
                color = QColor(0, 150, 0, 150)
            else:
                color = QColor(150, 0, 0, 150)

            normal = QPen(QBrush(color), 2.0)
            hover = QPen(QBrush(color.darker(120)), 2.1)
        else:
            normal = QPen(QBrush(QColor("#9CACB4")), 2.0)
            hover = QPen(QBrush(QColor("#7D7D7D")), 2.1)

        self.curveItem.setCurvePenSet(normal, hover)