class Meterbar(Button):
    def __init__(self, max=255, filename=None, width=None, height=None, x=0, y=0, pixmap=None, group=None, pos=None, size=None, padding=None):
        Button.__init__(self, filename, width, height, x, y, pixmap, group, pos, size, padding)
        self.max = max
        self.outer = QGraphicsRectItem(x,y,width,max + 2)
        self.outer.setPen(QPen(QColor(Qt.black), 1, Qt.SolidLine))
        self.outer.setBrush(Qt.green)
#         self.outer.hide()
        self.inner = QGraphicsRectItem(x + 1,y + 1,width - 2,max)
        self.inner.setPen(QPen(QColor(Qt.green), 1, Qt.SolidLine))
        self.inner.setBrush(Qt.blue)
        self.items = [self.outer, self.inner]
        self.current = 255
        self.effect = QGraphicsDropShadowEffect()
        self.effect.setOffset(0, 0)
        self.effect.setBlurRadius(0)
        self.effect.setColor(Qt.green)
        self.item = self.outer
        self.addEffect('shadow', self.effect, True)
        self.addAnimation('glow', Glow(15, 300, self, maxRadius=80, minRadius=5))
#         self.test(10)
    
    def test(self, x):
        self.tl = QTimeLine(10000)
        self.tl.setFrameRange(0, 10000)
        self.a = QGraphicsItemAnimation()
        self.a.setItem(self.inner)
        self.a.setTimeLine(self.tl)
#         self.a.setPosAt(0, QPointF(self.getX(), self.current))
#         self.a.setTranslationAt(1, self.getX(), self.getY() + self.max - x + 1)
        self.a.setScaleAt(0, 1, 1)
        self.a.setScaleAt(1, 1, 0.1)
        self.current = x
        self.tl.start()
        
    def update(self, x):
        x2 = 1 - (float(x) * 1.0 / float(self.max))
#         print x
#         return
        self.tl = QTimeLine(10)
        self.tl.setFrameRange(0, 10)
        self.a = QGraphicsItemAnimation()
        self.a.setItem(self.inner)
        self.a.setTimeLine(self.tl)
#         self.a.setPosAt(0, QPointF(self.getX(), self.current))
#         self.a.setTranslationAt(1, self.getX(), self.getY() + self.max - x + 1)
        self.a.setScaleAt(0, 1, self.current)
        self.a.setScaleAt(1, 1, x2)
        self.current = x
        self.tl.start()
        if x > 3 :
            self.play('glow')
        
    def setScene(self, scene):
        self.scene = scene
        for item in self.items :
            self.scene.addItem(item)
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
Esempio n. 3
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
Esempio n. 4
0
class RectangleCurve(OWCurve):
    """
        A plot item that shows a rectangle. 
        
        This class accepts the same options as :obj:`.PolygonCurve`. 
        The rectangle is calculated as the smallest rectangle that contains all points in ``xData`` and ``yData``. 
    """
    def __init__(self, pen = QPen(Qt.black), brush = QBrush(Qt.white), xData = None, yData = None, tooltip = None):
        OWCurve.__init__(self, xData, yData, tooltip=tooltip)
        self.set_pen(pen)
        self.set_brush(brush)
        self._item = QGraphicsRectItem(self)
        
    def update_properties(self):
        self._item.setRect(self.graph_transform().mapRect(self.data_rect()))
        self._item.setPen(self.pen())
        self._item.setBrush(self.brush())
Esempio n. 5
0
class GraphicsScene(QGraphicsScene):

    selectionRectPointChanged = Signal(QPointF)

    def __init__(self, *args):
        QGraphicsScene.__init__(self, *args)
        self.selectionRect = None

    def mousePressEvent(self, event):
        QGraphicsScene.mousePressEvent(self, event)

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            screenPos = event.screenPos()
            buttonDown = event.buttonDownScreenPos(Qt.LeftButton)
            if (screenPos - buttonDown).manhattanLength() > 2.0:
                self.updateSelectionRect(event)
        QGraphicsScene.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.selectionRect:
                self.removeItem(self.selectionRect)
                self.selectionRect = None
        QGraphicsScene.mouseReleaseEvent(self, event)

    def updateSelectionRect(self, event):
        pos = event.scenePos()
        buttonDownPos = event.buttonDownScenePos(Qt.LeftButton)
        rect = QRectF(pos, buttonDownPos).normalized()
        rect = rect.intersected(self.sceneRect())
        if not self.selectionRect:
            self.selectionRect = QGraphicsRectItem()
            self.selectionRect.setBrush(QColor(10, 10, 10, 20))
            self.selectionRect.setPen(QPen(QColor(200, 200, 200, 200)))
            self.addItem(self.selectionRect)
        self.selectionRect.setRect(rect)
        if event.modifiers() & Qt.ControlModifier or \
                event.modifiers() & Qt.ShiftModifier:
            path = self.selectionArea()
        else:
            path = QPainterPath()
        path.addRect(rect)
        self.setSelectionArea(path)
        self.selectionRectPointChanged.emit(pos)
Esempio n. 6
0
class GraphicsScene(QGraphicsScene):

    selectionRectPointChanged = Signal(QPointF)

    def __init__(self, *args):
        QGraphicsScene.__init__(self, *args)
        self.selectionRect = None

    def mousePressEvent(self, event):
        QGraphicsScene.mousePressEvent(self, event)

    def mouseMoveEvent(self, event):
        if event.buttons() & Qt.LeftButton:
            screenPos = event.screenPos()
            buttonDown = event.buttonDownScreenPos(Qt.LeftButton)
            if (screenPos - buttonDown).manhattanLength() > 2.0:
                self.updateSelectionRect(event)
        QGraphicsScene.mouseMoveEvent(self, event)

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.selectionRect:
                self.removeItem(self.selectionRect)
                self.selectionRect = None
        QGraphicsScene.mouseReleaseEvent(self, event)

    def updateSelectionRect(self, event):
        pos = event.scenePos()
        buttonDownPos = event.buttonDownScenePos(Qt.LeftButton)
        rect = QRectF(pos, buttonDownPos).normalized()
        rect = rect.intersected(self.sceneRect())
        if not self.selectionRect:
            self.selectionRect = QGraphicsRectItem()
            self.selectionRect.setBrush(QColor(10, 10, 10, 20))
            self.selectionRect.setPen(QPen(QColor(200, 200, 200, 200)))
            self.addItem(self.selectionRect)
        self.selectionRect.setRect(rect)
        if event.modifiers() & Qt.ControlModifier or \
                event.modifiers() & Qt.ShiftModifier:
            path = self.selectionArea()
        else:
            path = QPainterPath()
        path.addRect(rect)
        self.setSelectionArea(path)
        self.selectionRectPointChanged.emit(pos)
Esempio n. 7
0
def ugly_name_face(node, *args, **kargs):
    """ This is my item generator. It must receive a node object, and
    returns a Qt4 graphics item that can be used as a node face.
    """
    width = node.dist * 2.5
    height = 12
    masterItem = InteractiveItem(0, 0, width, height)
    masterItem.node = node
    masterItem.setPen(QPen(QtCore.Qt.NoPen))

    color_rect = QGraphicsRectItem(masterItem.rect())
    color_rect.setParentItem(masterItem)
    color_rect.setBrush(QBrush(QColor(100, 100, 200, 100)))
    color_rect.setPen(QPen(QtCore.Qt.NoPen))

    masterItem.color_rect = color_rect

    return masterItem
Esempio n. 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
Esempio n. 9
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
Esempio n. 10
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
Esempio n. 11
0
def render_drop_shadow_frame(pixmap, shadow_rect, shadow_color,
                             offset, radius, rect_fill_color):
    pixmap.fill(QColor(0, 0, 0, 0))
    scene = QGraphicsScene()
    rect = QGraphicsRectItem(shadow_rect)
    rect.setBrush(QColor(rect_fill_color))
    rect.setPen(QPen(Qt.NoPen))
    scene.addItem(rect)
    effect = QGraphicsDropShadowEffect(color=shadow_color,
                                       blurRadius=radius,
                                       offset=offset)

    rect.setGraphicsEffect(effect)
    scene.setSceneRect(QRectF(QPointF(0, 0), QSizeF(pixmap.size())))
    painter = QPainter(pixmap)
    scene.render(painter)
    painter.end()
    scene.clear()
    scene.deleteLater()
    return pixmap
Esempio n. 12
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
Esempio n. 13
0
class RectangleCurve(OWCurve):
    """
        A plot item that shows a rectangle.

        This class accepts the same options as :obj:`.PolygonCurve`.
        The rectangle is calculated as the smallest rectangle that contains all points in ``xData`` and ``yData``.
    """
    def __init__(self,
                 pen=QPen(Qt.black),
                 brush=QBrush(Qt.white),
                 xData=None,
                 yData=None,
                 tooltip=None):
        OWCurve.__init__(self, xData, yData, tooltip=tooltip)
        self.set_pen(pen)
        self.set_brush(brush)
        self._item = QGraphicsRectItem(self)

    def update_properties(self):
        self._item.setRect(self.graph_transform().mapRect(self.data_rect()))
        self._item.setPen(self.pen())
        self._item.setBrush(self.brush())
Esempio n. 14
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)
Esempio n. 15
0
class OWLegendGradient(QGraphicsObject):

    gradient_width = 20

    def __init__(self, palette, values, parent):
        QGraphicsObject.__init__(self, parent)
        self.parent = parent
        self.palette = palette
        self.values = values
        self.legend = parent
        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)

    def set_orientation(self, orientation):
        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 = 0
            x = 30
            for item in self.label_items:
                move_item_xy(item, x, y, 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:
            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, self.parent.graph.animate_plot)
                x += interval
            self.rect = QRectF(0, 0, total_width, self.gradient_width + height)

    def boundingRect(self):
        return getattr(self, 'rect', QRectF())

    def paint(self, painter, option, widget):
        pass
Esempio n. 16
0
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
Esempio n. 17
0
class MJScene(QGraphicsScene):
    """our scene with a potential Qt bug fix"""
    def __init__(self):
        QGraphicsScene.__init__(self)
        self.__disableFocusRect = False
        self._focusBoard = None
        self.focusRect = QGraphicsRectItem()
        pen = QPen(QColor(Qt.blue))
        pen.setWidth(6)
        self.focusRect.setPen(pen)
        self.addItem(self.focusRect)
        self.focusRect.setZValue(ZValues.marker)
        self.focusRect.hide()

    def focusInEvent(self, event):
        """work around a qt bug. See https://bugreports.qt-project.org/browse/QTBUG-32890
        This can be reproduced as follows:
           ./kajongg.py --game=whatever --autoplay=SomeRuleset
               such that the human player is the first one to discard a tile.
           wait until the main screen has been built
           click with the mouse into the middle of that window
           press left arrow key
           this will violate the assertion in GraphicsTileItem.keyPressEvent """
        prev = self.focusItem()
        QGraphicsScene.focusInEvent(self, event)
        if prev and bool(prev.flags() & QGraphicsItem.ItemIsFocusable) and prev != self.focusItem():
            self.setFocusItem(prev)

    def __focusRectVisible(self):
        """should we show it?"""
        game = Internal.field.game
        board = self._focusBoard
        return bool(not self.__disableFocusRect
                and board
                and board.hasFocus
                and board.focusTile
                and game
                and not game.autoPlay)

    @property
    def disableFocusRect(self):
        """suppress focusrect"""
        return self.__disableFocusRect

    @disableFocusRect.setter
    def disableFocusRect(self, value):
        """always place or hide, even if value does not change"""
        self.__disableFocusRect = value
        if value:
            self.focusRect.hide()
        else:
            self.placeFocusRect()

    @property
    def focusBoard(self):
        """get / set the board that has its focusRect shown"""
        return self._focusBoard

    @focusBoard.setter
    def focusBoard(self, board):
        """get / set the board that has its focusRect shown"""
        self._focusBoard = board
        focusTile = board.focusTile if board else None
        if focusTile:
            focusTile.graphics.setFocus()
            self.placeFocusRect()
        self.focusRect.setVisible(self.__focusRectVisible())

    def placeFocusRect(self):
        """show a blue rect around tile"""
        board = self._focusBoard
        if isAlive(board) and self.__focusRectVisible():
            rect = board.tileFaceRect()
            rect.setWidth(rect.width()*board.focusRectWidth())
            self.focusRect.setRect(rect)
            self.focusRect.setPos(board.focusTile.graphics.pos())
            self.focusRect.setRotation(board.sceneRotation())
            self.focusRect.setScale(board.scale())
            self.focusRect.show()
        else:
            self.focusRect.hide()

    def graphicsTileItems(self):
        """returns all GraphicsTileItems in the scene"""
        return (x for x in self.items() if isinstance(x, GraphicsTileItem))

    def nonTiles(self):
        """returns all other items in the scene"""
        return (x for x in self.items() if not isinstance(x, GraphicsTileItem))

    def removeTiles(self):
        """remove all tiles from scene"""
        for item in self.graphicsTileItems():
            self.removeItem(item)
        self.focusRect.hide()
Esempio n. 18
0
class PatternLegendText(QGraphicsTextItem):

    Type = 70000 + 3


    def __init__(self, text, parent = None):

        super(PatternLegendText, self).__init__(text, parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setTextInteractionFlags(Qt.TextEditorInteraction)

        self._position = self.pos()
        self._outline = None


    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(self.boundingRect(), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()



    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()



    def keyPressEvent(self, event):
        """ Stuff to do during key press events.

        For now we have to adjust the outline box.

        """

        QGraphicsTextItem.keyPressEvent(self, event)
        self.adjust_size()



    def adjust_size(self):
        """ This function takes care of changing the size of the

        outline rectangle, e.g., when text is added or removed or
        during font size changes.

        """

        if self._outline:
            self._outline.setRect(self.boundingRect())



    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
            self.setTextInteractionFlags(Qt.NoTextInteraction)

        return QGraphicsTextItem.mousePressEvent(self, event)



    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        self.setTextInteractionFlags(Qt.TextEditorInteraction)
        QApplication.restoreOverrideCursor()

        # this is needed for undo/redo
        if self._position != self.pos():
           self.scene().canvas_item_position_changed(self, self._position,
                                                     self.pos())

        return QGraphicsTextItem.mouseReleaseEvent(self, event)
Esempio n. 19
0
class ImageScene2D(QGraphicsScene):
    """
    The 2D scene description of a tiled image generated by evaluating
    an overlay stack, together with a 2D cursor.
    """
    axesChanged = pyqtSignal(int, bool)
    dirtyChanged = pyqtSignal()

    @property
    def is_swapped(self):
        """
        Indicates whether the dimensions are swapped
        swapping the axis will swap the dimensions and rotating the roi will swap the dimensions
        :return: bool
        """
        return bool(self._swapped) != bool(self._rotation % 2)  # xor

    @property
    def stackedImageSources(self):
        return self._stackedImageSources

    @stackedImageSources.setter
    def stackedImageSources(self, s):
        self._stackedImageSources = s

    @property
    def showTileOutlines(self):
        return self._showTileOutlines
    @showTileOutlines.setter
    def showTileOutlines(self, show):
        self._showTileOutlines = show
        self.invalidate()

    @property
    def showTileProgress(self):
        return self._showTileProgress
    
    @showTileProgress.setter
    def showTileProgress(self, show):
        self._showTileProgress = show
        self._dirtyIndicator.setVisible(show)

    def resetAxes(self, finish=True):
        # rotation is in range(4) and indicates in which corner of the
        # view the origin lies. 0 = top left, 1 = top right, etc.
        self._rotation = 0
        self._swapped = self._swappedDefault # whether axes are swapped
        self._newAxes()
        self._setSceneRect()
        self.scene2data, isInvertible = self.data2scene.inverted()
        assert isInvertible
        if finish:
            self._finishViewMatrixChange()

    def _newAxes(self):
        """Given self._rotation and self._swapped, calculates and sets
        the appropriate data2scene transformation.

        """
        # TODO: this function works, but it is not elegant. There must
        # be a simpler way to calculate the appropriate transformation.

        w, h = self.dataShape
        assert self._rotation in range(0, 4)

        # unlike self._rotation, the local variable 'rotation'
        # indicates how many times to rotate clockwise after swapping
        # axes.

        # t1 : do axis swap
        t1 = QTransform()
        if self._swapped:
            t1 = QTransform(0, 1, 0,
                            1, 0, 0,
                            0, 0, 1)
            h, w = w, h

        # t2 : do rotation
        t2 = QTransform()
        t2.rotate(self._rotation * 90)

        # t3: shift to re-center
        rot2trans = {0 : (0, 0),
                     1 : (h, 0),
                     2 : (w, h),
                     3 : (0, w)}

        trans = rot2trans[self._rotation]
        t3 = QTransform.fromTranslate(*trans)

        self.data2scene = t1 * t2 * t3
        if self._tileProvider:
            self._tileProvider.axesSwapped = self._swapped
        self.axesChanged.emit(self._rotation, self._swapped)

    def rot90(self, direction):
        """ direction: left ==> -1, right ==> +1"""
        assert direction in [-1, 1]
        self._rotation = (self._rotation + direction) % 4
        self._newAxes()

    def swapAxes(self, transform):
        self._swapped = not self._swapped
        self._newAxes()

    def _onRotateLeft(self):
        self.rot90(-1)
        self._finishViewMatrixChange()

    def _onRotateRight(self):
        self.rot90(1)
        self._finishViewMatrixChange()

    def _onSwapAxes(self):
        self.swapAxes(self.data2scene)
        self._finishViewMatrixChange()

    def _finishViewMatrixChange(self):
        self.scene2data, isInvertible = self.data2scene.inverted()
        self._setSceneRect()
        self._tiling.data2scene = self.data2scene
        self._tileProvider._onSizeChanged()
        QGraphicsScene.invalidate(self, self.sceneRect())

    @property
    def sceneShape(self):
        return (self.sceneRect().width(), self.sceneRect().height())

    def _setSceneRect(self):
        w, h = self.dataShape
        rect = self.data2scene.mapRect(QRect(0, 0, w, h))
        sw, sh = rect.width(), rect.height()
        self.setSceneRect(0, 0, sw, sh)
        
        if self._dataRectItem is not None:
            self.removeItem( self._dataRectItem )
        
        #this property represent a parent to QGraphicsItems which should
        #be clipped to the data, such as temporary capped lines for brushing.
        #This works around ilastik issue #516.
        self._dataRectItem = QGraphicsRectItem(0,0,sw,sh)
        self._dataRectItem.setPen(QPen(QColor(0,0,0,0)))
        self._dataRectItem.setFlag(QGraphicsItem.ItemClipsChildrenToShape)
        self.addItem(self._dataRectItem)

    @property
    def dataRectItem(self):
        return self._dataRectItem

    @property
    def dataShape(self):
        """
        The shape of the scene in QGraphicsView's coordinate system.
        """
        return self._dataShape

    @dataShape.setter
    def dataShape(self, value):
        """
        Set the size of the scene in QGraphicsView's coordinate system.
        dataShape -- (widthX, widthY),
        where the origin of the coordinate system is in the upper left corner
        of the screen and 'x' points right and 'y' points down
        """
        assert len(value) == 2
        self._dataShape = value
        self.reset()
        self._finishViewMatrixChange()

    def setCacheSize(self, cache_size):
        self._tileProvider.set_cache_size(cache_size)

    def cacheSize(self):
        return self._tileProvider.cache_size

    def setPrefetchingEnabled(self, enable):
        self._prefetching_enabled = enable

    def setPreemptiveFetchNumber(self, n):
        if n > self.cacheSize() - 1:
            self._n_preemptive = self.cacheSize() - 1
        else:
            self._n_preemptive = n
    def preemptiveFetchNumber(self):
        return self._n_preemptive

    def invalidateViewports(self, sceneRectF):
        '''Call invalidate on the intersection of all observing viewport-rects and rectF.'''
        sceneRectF = sceneRectF if sceneRectF.isValid() else self.sceneRect()
        for view in self.views():
            QGraphicsScene.invalidate(self, sceneRectF.intersected(view.viewportRect()))

    def reset(self):
        """Reset rotations, tiling, etc. Called when first initialized
        and when the underlying data changes.

        """
        self.resetAxes(finish=False)

        self._tiling = Tiling(self._dataShape, self.data2scene, name=self.name)

        self._tileProvider = TileProvider(self._tiling, self._stackedImageSources)
        self._tileProvider.sceneRectChanged.connect(self.invalidateViewports)

        if self._dirtyIndicator:
            self.removeItem(self._dirtyIndicator)
        del self._dirtyIndicator
        self._dirtyIndicator = DirtyIndicator(self._tiling)
        self.addItem(self._dirtyIndicator)
        self._dirtyIndicator.setVisible(False)


    def __init__(self, posModel, along, preemptive_fetch_number=5,
                 parent=None, name="Unnamed Scene",
                 swapped_default=False):
        """
        * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off
        * swapped_default -- whether axes should be swapped by default.

        """
        QGraphicsScene.__init__(self, parent=parent)

        self._along = along
        self._posModel = posModel

        # QGraphicsItems can change this if they are in a state that should temporarily forbid brushing
        # (For example, when the slice intersection marker is in 'draggable' state.)
        self.allow_brushing = True

        self._dataShape = (0, 0)
        self._dataRectItem = None #A QGraphicsRectItem (or None)
        self._offsetX = 0
        self._offsetY = 0
        self.name = name

        self._stackedImageSources = StackedImageSources(LayerStackModel())
        self._showTileOutlines = False

        # FIXME: We don't show the red 'progress pies' because they look terrible.  
        #        If we could fix their timing, maybe it would be worth it.
        self._showTileProgress = False

        self._tileProvider = None
        self._dirtyIndicator = None
        self._prefetching_enabled = False
        
        self._swappedDefault = swapped_default
        self.reset()

        # BowWave preemptive caching
        self.setPreemptiveFetchNumber(preemptive_fetch_number)
        self._course = (1,1) # (along, pos or neg direction)
        self._time = self._posModel.time
        self._channel = self._posModel.channel
        self._posModel.timeChanged.connect(self._onTimeChanged)
        self._posModel.channelChanged.connect(self._onChannelChanged)
        self._posModel.slicingPositionChanged.connect(self._onSlicingPositionChanged)
        
        self._allTilesCompleteEvent = threading.Event()
        self.dirty = False
        
        # We manually keep track of the tile-wise QGraphicsItems that
        # we've added to the scene in this dict, otherwise we would need
        # to use O(N) lookups for every tile by calling QGraphicsScene.items()
        self.tile_graphicsitems = defaultdict(set) # [Tile.id] -> set(QGraphicsItems)

    def drawForeground(self, painter, rect):
        if self._tiling is None:
            return

        if self._showTileOutlines:
            tile_nos = self._tiling.intersected(rect)

            for tileId in tile_nos:
            ## draw tile outlines
                # Dashed black line
                pen = QPen()
                pen.setDashPattern([5,5])
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

                # Dashed white line
                # (offset to occupy the spaces in the dashed black line)
                pen = QPen()
                pen.setDashPattern([5,5])
                pen.setDashOffset(5)
                pen.setColor(QColor(Qt.white))
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

    def indicateSlicingPositionSettled(self, settled):
        if self._showTileProgress:
            self._dirtyIndicator.setVisible(settled)

    def drawBackground(self, painter, sceneRectF):
        if self._tileProvider is None:
            return

        tiles = self._tileProvider.getTiles(sceneRectF)
        allComplete = True
        for tile in tiles:
            #We always draw the tile, even though it might not be up-to-date
            #In ilastik's live mode, the user sees the old result while adding
            #new brush strokes on top
            #See also ilastik issue #132 and tests/lazy_test.py
            if tile.qimg is not None:
                painter.drawImage(tile.rectF, tile.qimg)

            # The tile also contains a list of any QGraphicsItems that were produced by the layers.
            # If there are any new ones, add them to the scene.
            new_items = set(tile.qgraphicsitems) - self.tile_graphicsitems[tile.id]
            obsolete_items = self.tile_graphicsitems[tile.id] - set(tile.qgraphicsitems)
            for g_item in obsolete_items:
                self.tile_graphicsitems[tile.id].remove(g_item)
                self.removeItem(g_item)
            for g_item in new_items:
                self.tile_graphicsitems[tile.id].add(g_item)
                self.addItem(g_item)

            if tile.progress < 1.0:
                allComplete = False
            if self._showTileProgress:
                self._dirtyIndicator.setTileProgress(tile.id, tile.progress)

        if allComplete:
            if self.dirty:
                self.dirty = False
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.set()
        else:
            if not self.dirty:
                self.dirty = True
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.clear()

        # preemptive fetching
        if self._prefetching_enabled:
            upcoming_through_slices = self._bowWave(self._n_preemptive)
            for through in upcoming_through_slices:
                self._tileProvider.prefetch(sceneRectF, through, layer_indexes=None)

    def triggerPrefetch(self, layer_indexes, time_range='current', spatial_axis_range='current', sceneRectF=None ):
        """
        Trigger a one-time prefetch for the given set of layers.
        
        TODO: I'm not 100% sure what happens here for layers with multiple channels.
        
        layer_indexes: list-of-ints, or None, which means 'all visible'.
        time_range: (start_time, stop_time)
        spatial_axis_range: (start_slice, stop_slice), meaning Z/Y/X depending on our projection (self.along)
        sceneRectF: Used to determine which tiles to request.
                    An invalid QRectF results in all tiles getting refreshed (visible or not).
        """
        # Process parameters
        sceneRectF = sceneRectF or QRectF()

        if time_range == 'current':
            time_range = (self._posModel.slicingPos5D[0],
                          self._posModel.slicingPos5D[0]+1)
        elif time_range == 'all':
            time_range = (0, self._posModel.shape5D[0])
        else:
            assert len(time_range) == 2
            assert time_range[0] >= 0 and time_range[1] < self._posModel.shape5D[0]

        spatial_axis = self._along[1]
        if spatial_axis_range == 'current':
            spatial_axis_range = (self._posModel.slicingPos5D[spatial_axis],
                                  self._posModel.slicingPos5D[spatial_axis]+1)
        elif spatial_axis_range == 'all':
            spatial_axis_range = (0, self._posModel.shape5D[spatial_axis])
        else:
            assert len(spatial_axis_range) == 2
            assert 0 <= spatial_axis_range[0] <  self._posModel.shape5D[spatial_axis]
            assert 0 <  spatial_axis_range[1] <= self._posModel.shape5D[spatial_axis]

        # Construct list of 'through' coordinates
        through_list = []
        for t in range( *time_range ):
            for s in range( *spatial_axis_range ):
                through_list.append( (t, s) )

        # Make sure the tile cache is big enough to hold the prefetched data.
        if self._tileProvider.cache_size < len(through_list):
            self._tileProvider.set_cache_size( len(through_list) )

        # Trigger prefetches
        for through in through_list:
            self._tileProvider.prefetch(sceneRectF, through, layer_indexes)
        

    def joinRenderingAllTiles(self, viewport_only=True, rect=None):
        """
        Wait until all tiles in the scene have been 100% rendered.
        If sceneRectF is None, use the viewport rect.
        If sceneRectF is an invalid QRectF(), then wait for all tiles.
        Note: If called from the GUI thread, the GUI thread will block until all tiles are rendered!
        """
        # If this is the main thread, keep repainting (otherwise we'll deadlock).
        if threading.current_thread().name == "MainThread":
            if viewport_only:
                sceneRectF = self.views()[0].viewportRect()
            else:
                if rect is None or not isinstance(rect, QRectF):
                    sceneRectF = QRectF() # invalid QRectF means 'get all tiles'
                else:
                    sceneRectF = rect
            self._tileProvider.waitForTiles(sceneRectF)
        else:
            self._allTilesCompleteEvent.wait()


    def _bowWave(self, n):
        through = [ self._posModel.slicingPos5D[axis] for axis in self._along[:-1] ]
        t_max = [ self._posModel.shape5D[axis] for axis in self._along[:-1] ]

        BowWave = []

        a = self._course[0]
        for d in xrange(1,n+1):
            m = through[a] + d * self._course[1]
            if m < t_max[a] and m >= 0:
                t = list(through)
                t[a] = m
                BowWave.append(tuple(t))
        return BowWave

    def _onSlicingPositionChanged(self, new, old):
        if (new[self._along[1] - 1] - old[self._along[1] - 1]) < 0:
            self._course = (1, -1)
        else:
            self._course = (1, 1)

    def _onChannelChanged(self, new):
        if (new - self._channel) < 0:
            self._course = (2, -1)
        else:
            self._course = (2, 1)
        self._channel = new

    def _onTimeChanged(self, new):
        if (new - self._time) < 0:
            self._course = (0, -1)
        else:
            self._course = (0, 1)
        self._time = new
Esempio n. 20
0
class SequencePlotFace_mod(faces.SequencePlotFace):
    def draw_x_axis(self):
        lineItem = QGraphicsLineItem(self.col_w / 2,
                                     self.coordY(self.ylim[0]) + 2,
                                     self.width - self.col_w / 2,
                                     self.coordY(self.ylim[0]) + 2,
                                     parent=self.item)
        lineItem.setPen(QPen(QColor('black')))
        lineItem.setZValue(10)
        all_vals = list(range(0, len(self.values), self.x_inter_values))
        if (len(self.values) - 1) % self.x_inter_values:
            all_vals += [len(self.values) - 1]

        hp_x = []
        if self.hp:
            for x in list(range(0, len(self.values))):
                if self.x_values[x] in self.hp:
                    hp_x.append(x)
                    if not x in all_vals:
                        all_vals += [x]
        all_vals.sort()

        for x in all_vals:
            lineItem = QGraphicsLineItem(0,
                                         self.coordY(self.ylim[0]) + 2,
                                         0,
                                         self.coordY(self.ylim[0]) + 6,
                                         parent=self.item)
            lineItem.setX(x * self.col_w + self.col_w / 2)
            lineItem.setPen(QPen(QColor('black')))
            lineItem.setZValue(10)
            if x in hp_x:
                text = QGraphicsSimpleTextItem("*" + str(self.x_values[x]))
                qfont = QFont("Arial", self.fsize - 1)
                #qfont.setBold(True)
                text.setFont(qfont)
            else:
                text = QGraphicsSimpleTextItem(" " + str(self.x_values[x]))
                text.setFont(QFont("Arial", self.fsize - 1))
            text.rotate(-90)
            text.setParentItem(self.item)
            text.setZValue(10)
            tw = text.boundingRect().width()
            th = text.boundingRect().height()
            # Center text according to masterItem size
            text.setPos(x * self.col_w - th / 2 + self.col_w / 2,
                        tw + self.coordY(self.ylim[0]) + 7)

    def draw_y_axis(self):
        lineItem = QGraphicsLineItem(0,
                                     self.coordY(self.ylim[0]),
                                     0,
                                     self.coordY(self.ylim[1]),
                                     parent=self.item)
        lineItem.setPen(QPen(QColor('black')))
        lineItem.setZValue(10)
        max_w = 0
        for y in set(self.hlines + list(self.ylim)):
            if y in list(self.ylim):
                lineItem = QGraphicsLineItem(0,
                                             self.coordY(y),
                                             -5,
                                             self.coordY(y),
                                             parent=self.item)
                lineItem.setPen(QPen(QColor('black')))
                lineItem.setZValue(10)
                text = QGraphicsSimpleTextItem(str(y))
                text.setFont(QFont("Arial", self.fsize - 2))
                text.setParentItem(self.item)
                tw = text.boundingRect().width()
                max_w = tw if tw > max_w else max_w
                th = text.boundingRect().height()
                # Center text according to masterItem size
                text.setPos(-tw - 5, self.coordY(y) - th / 2)
            else:
                text = QGraphicsSimpleTextItem(str(y))
                text.setFont(QFont("Arial", self.fsize - 4))
                text.setParentItem(self.item)
                tw = text.boundingRect().width()
                max_w = tw if tw > max_w else max_w
                th = text.boundingRect().height()
                # Center text according to masterItem size
                text.setPos(self.width + 5, self.coordY(y) - th / 2)

        if self.ylabel:
            text = QGraphicsSimpleTextItem(self.ylabel)
            text.setFont(QFont("Arial", self.fsize - 1))
            text.setParentItem(self.item)
            text.rotate(-90)
            tw = text.boundingRect().width()
            th = text.boundingRect().height()
            # Center text according to masterItem size
            text.setPos(-th - 5 - max_w,
                        tw / 2 + self.coordY(sum(self.ylim) / 2))

    def set_sticks_color(self):
        stick_colors = []
        y_values = self.values
        for y_val in y_values:
            if y_val >= 0.99:
                stick_colors.append("red")
            elif y_val >= 0.90:
                stick_colors.append("orange")
            elif y_val >= 0.80:
                stick_colors.append("#EFDB00")
            else:
                stick_colors.append("gray")
        self.colors = stick_colors

    def draw_colored_boxes(self, h):

        stick_colors = self.colors

        num_col_red = stick_colors.count("red")
        num_col_orange = stick_colors.count("orange")
        num_col_yellow = stick_colors.count("#EFDB00")
        num_col_gray = stick_colors.count("gray")

        colored_box_h = self.height + h - 5

        if num_col_red:
            rect_red = QGraphicsRectItem(self.col_w * 0 - 1,
                                         self.coordY(self.ylim[1]) - 5,
                                         self.col_w * num_col_red,
                                         colored_box_h)
            qpen = QPen(QColor('red'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_red.setPen(qpen)
            rect_red.setBrush(QColor("#FFD1D1"))
            rect_red.setZValue(-1)
            rect_red.setParentItem(self.item)
            #rect_red.setOpacity(0.5)

        if num_col_orange:
            rect_orange = QGraphicsRectItem(self.col_w * num_col_red + 1,
                                            self.coordY(self.ylim[1]) - 5,
                                            self.col_w * num_col_orange - 2,
                                            colored_box_h)
            qpen = QPen(QColor('orange'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_orange.setPen(qpen)
            rect_orange.setBrush(QColor("#FFE3B0"))
            rect_orange.setZValue(-1)
            rect_orange.setParentItem(self.item)
            #rect_orange.setOpacity(0.5)

        if num_col_yellow:
            rect_yellow = QGraphicsRectItem(
                self.col_w * (num_col_orange + num_col_red) + 1,
                self.coordY(self.ylim[1]) - 5, self.col_w * num_col_yellow - 2,
                colored_box_h)
            qpen = QPen(QColor('#EFDB00'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_yellow.setPen(qpen)
            rect_yellow.setBrush(QColor("#FBFFA5"))
            rect_yellow.setZValue(-1)
            rect_yellow.setParentItem(self.item)
            #rect_yellow.setOpacity(0.5)

        if num_col_gray:
            rect_gray = QGraphicsRectItem(
                self.col_w * (num_col_orange + num_col_red + num_col_yellow) +
                1,
                self.coordY(self.ylim[1]) - 5, self.col_w * num_col_gray,
                colored_box_h)
            qpen = QPen(QColor('gray'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_gray.setPen(qpen)
            rect_gray.setBrush(QColor("#E2E2E2"))
            rect_gray.setZValue(-1)
            rect_gray.setParentItem(self.item)
            #rect_gray.setOpacity(0.5)

    def update_items(self):
        self.item = QGraphicsRectItem(-30, 0, self.width + 40,
                                      self.height + 70)
        #self.item.setPen(QPen(QColor('gray')))
        self.item.setPen(QPen(QColor('white')))

        try:
            put_colored_boxes = self.put_colored_boxes
        except AttributeError:
            put_colored_boxes = (False, 0)

        if put_colored_boxes[0]:
            (_, tree_h) = put_colored_boxes
            self.draw_colored_boxes(tree_h)

        # draw lines
        for line, col in zip(self.hlines, self.hlines_col):
            self.draw_hlines(line, col)
        # draw plot
        width = self.col_w
        for i, val in enumerate(self.values):
            self.draw_fun(width * i + self.col_w / 2, val, i)
        # draw error bars
        if self.errors:
            for i in range(len(self.errors)):
                self.draw_errors(width * i + self.col_w / 2, i)
        # draw x axis
        self.draw_x_axis()
        # draw y axis
        self.draw_y_axis()
        # put header
        self.write_header()
Esempio n. 21
0
    def draw_colored_boxes(self, h):

        stick_colors = self.colors

        num_col_red = stick_colors.count("red")
        num_col_orange = stick_colors.count("orange")
        num_col_yellow = stick_colors.count("#EFDB00")
        num_col_gray = stick_colors.count("gray")

        colored_box_h = self.height + h - 5

        if num_col_red:
            rect_red = QGraphicsRectItem(self.col_w * 0 - 1,
                                         self.coordY(self.ylim[1]) - 5,
                                         self.col_w * num_col_red,
                                         colored_box_h)
            qpen = QPen(QColor('red'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_red.setPen(qpen)
            rect_red.setBrush(QColor("#FFD1D1"))
            rect_red.setZValue(-1)
            rect_red.setParentItem(self.item)
            #rect_red.setOpacity(0.5)

        if num_col_orange:
            rect_orange = QGraphicsRectItem(self.col_w * num_col_red + 1,
                                            self.coordY(self.ylim[1]) - 5,
                                            self.col_w * num_col_orange - 2,
                                            colored_box_h)
            qpen = QPen(QColor('orange'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_orange.setPen(qpen)
            rect_orange.setBrush(QColor("#FFE3B0"))
            rect_orange.setZValue(-1)
            rect_orange.setParentItem(self.item)
            #rect_orange.setOpacity(0.5)

        if num_col_yellow:
            rect_yellow = QGraphicsRectItem(
                self.col_w * (num_col_orange + num_col_red) + 1,
                self.coordY(self.ylim[1]) - 5, self.col_w * num_col_yellow - 2,
                colored_box_h)
            qpen = QPen(QColor('#EFDB00'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_yellow.setPen(qpen)
            rect_yellow.setBrush(QColor("#FBFFA5"))
            rect_yellow.setZValue(-1)
            rect_yellow.setParentItem(self.item)
            #rect_yellow.setOpacity(0.5)

        if num_col_gray:
            rect_gray = QGraphicsRectItem(
                self.col_w * (num_col_orange + num_col_red + num_col_yellow) +
                1,
                self.coordY(self.ylim[1]) - 5, self.col_w * num_col_gray,
                colored_box_h)
            qpen = QPen(QColor('gray'))
            qpen.setWidth(1)
            qpen.setStyle(
                5)  # dash line : http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum
            rect_gray.setPen(qpen)
            rect_gray.setBrush(QColor("#E2E2E2"))
            rect_gray.setZValue(-1)
            rect_gray.setParentItem(self.item)
Esempio n. 22
0
    def setAxisIntersect(self, intersect):
        self._axisIntersect = intersect
        #print "SkeletonsLayer(axis=%d) is updating intersect=%d" % (self._axis, self._axisIntersect)

        nodes, eIntersected, ePlane = self._3d._skeletons.intersect(
            self._axis, self._axisIntersect)

        #update existing items
        toRemove = []
        for node, item in self._node2view.iteritems():
            if node.pos[self._axis] != self._axisIntersect:
                self._scene.removeItem(item)
                toRemove.append(node)
            elif node.pointF(self._axis) != item.pos():
                item.setPos(self._scene.data2scene.map(node.pointF(
                    self._axis)))
            if node.isSelected() != item.isSelected():
                item.setSelected(node.isSelected())
                assert item.isSelected() == node.isSelected()
            i = 0
            newSize = [0, 0]
            for j in range(3):
                if j == self._axis:
                    continue
                newSize[i] = node.shape[j]
                i += 1
            newRectF = QRectF(0, 0, *newSize)
            newRectF = self._scene.data2scene.mapRect(newRectF)

            item.setRect(
                QRectF(-newRectF.width() / 2.0, -newRectF.height() / 2.0,
                       newRectF.width(), newRectF.height()))

        for r in toRemove:
            del self._node2view[r]

        #add new views for nodes
        for n in nodes:
            if n in self._node2view:
                continue

            pos2D = list(n.pos)
            del pos2D[self._axis]

            shape2D = n.shape2D(self._axis)
            itm = QGraphicsSkeletonNode(shape2D,
                                        skeletons=self._3d._skeletons,
                                        node=n)
            itm.setPos(self._scene.data2scene.map(QPointF(*pos2D)))
            itm.setSelected(n.isSelected())

            self._scene.addItem(itm)
            self._node2view[n] = itm

        for itm in self._edge2view.values():
            self._scene.removeItem(itm)
        self._edge2view = dict()

        for e in ePlane:
            l = QLineF(e[0].pointF(), e[1].pointF())

            c1 = e[0].color()
            c2 = e[1].color()
            mixColor = QColor((c1.red() + c2.red()) / 2,
                              (c1.green() + c2.green()) / 2,
                              (c1.blue() + c2.blue()) / 2)

            line = QGraphicsLineItem(self._scene.data2scene.map(l))
            line.setPen(QPen(mixColor))
            self._scene.addItem(line)
            self._edge2view[e] = line

        for theEdge, e in eIntersected:
            c1 = theEdge[0].color()
            c2 = theEdge[1].color()
            mixColor = QColor((c1.red() + c2.red()) / 2,
                              (c1.green() + c2.green()) / 2,
                              (c1.blue() + c2.blue()) / 2)

            nodeSize = 6
            p = QGraphicsRectItem(-nodeSize / 2, -nodeSize / 2, nodeSize,
                                  nodeSize)
            pos2D = list(e)
            del pos2D[self._axis]
            p.setPos(self._scene.data2scene.map(QPointF(*pos2D)))
            p.setPen(QPen(mixColor))
            self._scene.addItem(p)
            self._edge2view[e] = p
Esempio n. 23
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() )
Esempio n. 24
0
class MJScene(QGraphicsScene):
    """our scene with a potential Qt bug fix"""
    def __init__(self):
        QGraphicsScene.__init__(self)
        self.__disableFocusRect = False
        self._focusBoard = None
        self.focusRect = QGraphicsRectItem()
        pen = QPen(QColor(Qt.blue))
        pen.setWidth(6)
        self.focusRect.setPen(pen)
        self.addItem(self.focusRect)
        self.focusRect.setZValue(ZValues.marker)
        self.focusRect.hide()

    def __focusRectVisible(self):
        """should we show it?"""
        game = InternalParameters.field.game
        board = self._focusBoard
        return bool(not self.__disableFocusRect
                and board
                and board.hasFocus
                and board.focusTile
                and game
                and not game.autoPlay)

    @apply
    def disableFocusRect(): # pylint: disable=E0202
        """suppress focusrect"""
        def fget(self):
            # pylint: disable=W0212
            return self.__disableFocusRect
        def fset(self, value):
            # pylint: disable=W0212
            # always place or hide, even if value does not change
            self.__disableFocusRect = value
            if value:
                self.focusRect.hide()
            else:
                self.placeFocusRect()
        return property(**locals())

    @apply
    def focusBoard(): # pylint: disable=E0202
        """get / set the board that has its focusRect shown"""
        def fget(self):
            # pylint: disable=W0212
            return self._focusBoard
        def fset(self, board):
            # pylint: disable=W0212
            self._focusBoard = board
            focusTile = board.focusTile if board else None
            if focusTile:
                focusTile.graphics.setFocus()
                self.placeFocusRect()
            self.focusRect.setVisible(self.__focusRectVisible())
        return property(**locals())

    def placeFocusRect(self):
        """show a blue rect around tile"""
        board = self._focusBoard
        if isAlive(board) and self.__focusRectVisible():
            rect = board.tileFaceRect()
            rect.setWidth(rect.width()*board.focusRectWidth())
            self.focusRect.setRect(rect)
            self.focusRect.setPos(board.focusTile.graphics.pos())
            self.focusRect.setRotation(board.sceneRotation())
            self.focusRect.setScale(board.scale())
            self.focusRect.show()
        else:
            self.focusRect.hide()

    def graphicsTileItems(self):
        """returns all GraphicsTileItems in the scene"""
        return (x for x in self.items() if isinstance(x, GraphicsTileItem))

    def nonTiles(self):
        """returns all other items in the scene"""
        return (x for x in self.items() if not isinstance(x, GraphicsTileItem))

    def removeTiles(self):
        """remove all tiles from scene"""
        for item in self.graphicsTileItems():
            self.removeItem(item)
        self.focusRect.hide()
Esempio n. 25
0
class List90Face(faces.StaticItemFace):
    """Static text Face object
    :param l:        List of element to be drawn
    :param fsize:    Font size, e.g. 10,12,6, (default=10)
    :param fgcolor:  Foreground font color. RGB code or color name in :data:`SVG_COLORS`
    :param bgcolor:  Background font color. RGB code or color name in :data:`SVG_COLORS`
    """

    def __init__(self, l, ftype="Courier", fstyle="normal", fsize=10,
                 fgcolor="black", bgcolor="white", col_w=14.0, rotation=90):
        self.liste = l
        self.ftype = ftype
        self.fgcolor = fgcolor
        self.bgcolor = bgcolor
        self.fsize = fsize
        self.row_h = float(self.fsize + 1)
        self.col_w = col_w
        self.width = 0
        self.rot = rotation
        self.fstyle = fstyle
        self.coeff_h = max([len(str(x)) for x in self.liste])

        super(List90Face, self).__init__(None)

    def __repr__(self):
        return "Text Face [%s] (%s)" % (self._text, hex(self.__hash__()))

    def get_text(self):
        return self._text

    def update_items(self):
        self.item = QGraphicsRectItem(
            0, 0, self.width, self.row_h * self.coeff_h)
        seq_width = 0
        nopen = QPen(Qt.NoPen)
        self.item.setPen(nopen)
        font = QFont(self.ftype, self.fsize)
        if self.fstyle == "italic":
            font.setStyle(QFont.StyleItalic)
        elif self.fstyle == "oblique":
            font.setStyle(QFont.StyleOblique)
        rect_cls = QGraphicsRectItem
        for i, val in enumerate(self.liste):
            width = self.col_w
            height = self.row_h * len(str(val)) + 1
            rectitem = rect_cls(0, 0, width, height, parent=self.item)
            rectitem.setX(seq_width)  # to give correct X to children item
            rectitem.setBrush(QBrush(QColor(self.bgcolor)))
            rectitem.setPen(nopen)

            # write letter if enough space in height
            if height >= self.fsize:
                text = QGraphicsSimpleTextItem(str(val), parent=rectitem)
                text.setFont(font)
                text.setBrush(QBrush(QColor(self.fgcolor)))
                # Center text according to rectitem size
                # txtw = text.boundingRect().width()
                txth = text.boundingRect().height()
                text.setRotation(self.rot)
                text.setX(txth)
            seq_width += width
        self.width = seq_width
Esempio n. 26
0
def polygon_name_face(node, *args, **kwargs):
    """create a wedge shaped face in the style of ARB

    Args:
    width (int): size in pixels for the width of the wedge
    height (int): size in pixels for the height of the wedge
    width_percent (float): change the angle of the point of the wedge.
    This must be a number between 0 and 1

    Returns:
    QGraphicsRectItem: The Qt graphics item of the polygon
    """

    n_leaves = len(node.get_leaves())
    closest_leaf_dist = node.get_closest_leaf()[1]
    farthest_leaf_dist = node.get_farthest_leaf()[1]

    base_height = 30
    width = 60
    height = math.log(n_leaves, 2) + base_height

    width_percent = closest_leaf_dist / farthest_leaf_dist

    #print(width, height, width_percent)

    points = [
    (0.0, 0.0), # top left point
    (width, 0.0), # top right point
    (width * width_percent, height), # bottom right point
    (0.0, height), # bottom left point
    (0.0, 0.0) # back to the beginning
    ]
    shape = QPolygonF()
    for i in points:
        shape << QtCore.QPointF(*i)

    ## Creates a main master Item that will contain all other elements
    ## Items can be standard QGraphicsItem
    masterItem = QGraphicsRectItem(0, 0, width, height)

    # Keep a link within the item to access node info
    masterItem.node = node

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

    polygon = QGraphicsPolygonItem(shape, masterItem)
    # Make the wedge grey in color
    polygon.setBrush(QBrush(QColor( '#D3D3D3')))

    # Print the name of the node
    # Center text according to masterItem size
    center = masterItem.boundingRect().center()

    text = QGraphicsSimpleTextItem(node.name)
    text.setParentItem(polygon)

    tw = text.boundingRect().width()
    th = text.boundingRect().height()
    text.setPos(center.x() + tw/2, center.y() - th/2)

    # this is a hack to prevent the name being printed twice
    # we set the node name to blank after we write it with the QGraphicsSimpleTextItem
    # it must be set to a blank string for it not to be printed later
    node.name = ''


    # print the number of collapsed leaves in the polygon
    leaves_count_text = QGraphicsSimpleTextItem('('+str(n_leaves)+')')
    leaves_count_text.setParentItem(polygon)
    leaves_count_text.setFont(QFont('Veranda', 6))
    leaves_count_text.setPos(masterItem.boundingRect().x() + 5, center.y() - leaves_count_text.boundingRect().height()/2)

    polygon.setPos(0, masterItem.boundingRect().y()/1.5)

    return masterItem
Esempio n. 27
0
class RepeatLegendItem(QGraphicsRectItem):

    Type = 70000 + 7

    def __init__(self, color, parent=None):

        super(RepeatLegendItem, self).__init__(parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)

        self._position = self.pos()

        self.penColor = color
        self.itemHeight = 20
        self.itemWidth = 40

        self._pen = QPen()
        self._pen.setWidthF(3.0)
        self._pen.setColor(self.penColor)
        self.setPen(self._pen)

        self.setRect(0, 0, self.itemWidth, self.itemHeight)

        self.setAcceptsHoverEvents(True)
        self._outline = None

    @property
    def height(self):
        return self.itemHeight

    @property
    def width(self):
        return self.itemWidth

    @property
    def color(self):
        return self.penColor

    @color.setter
    def color(self, newColor):

        self.penColor = newColor
        self._pen.setColor(self.penColor)
        self.setPen(self._pen)

    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(\
                self.boundingRect().adjusted(-1,-1,1,1), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()

    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()

    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
        else:
            event.ignore()

        return QGraphicsRectItem.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        QApplication.restoreOverrideCursor()

        # this is needed for undo/redo
        if self._position != self.pos():
            self.scene().canvas_item_position_changed(self, self._position,
                                                      self.pos())

        return QGraphicsRectItem.mouseReleaseEvent(self, event)
Esempio n. 28
0
    def setAxisIntersect(self, intersect):
        self._axisIntersect = intersect
        #print "SkeletonsLayer(axis=%d) is updating intersect=%d" % (self._axis, self._axisIntersect)
        
        nodes, eIntersected, ePlane = self._3d._skeletons.intersect(self._axis, self._axisIntersect)
       
        #update existing items 
        toRemove = []
        for node, item in self._node2view.iteritems():
            if node.pos[self._axis] != self._axisIntersect:
                self._scene.removeItem(item)
                toRemove.append(node)
            elif node.pointF(self._axis) != item.pos():
                item.setPos( self._scene.data2scene.map( node.pointF(self._axis) ) )
            if node.isSelected() != item.isSelected():
                item.setSelected(node.isSelected())
                assert item.isSelected() == node.isSelected()
            i = 0 
            newSize = [0,0]
            for j in range(3):
                if j == self._axis:
                    continue
                newSize[i] = node.shape[j] 
                i += 1
            newRectF = QRectF(0,0,*newSize)
            newRectF = self._scene.data2scene.mapRect(newRectF)
            
            item.setRect(QRectF(-newRectF.width()/2.0, -newRectF.height()/2.0, newRectF.width(), newRectF.height()))
            
        for r in toRemove:
            del self._node2view[r]
               
        #add new views for nodes 
        for n in nodes:
            if n in self._node2view:
                continue
            
            pos2D = list(n.pos)
            del pos2D[self._axis]

            shape2D = n.shape2D(self._axis)
            itm = QGraphicsSkeletonNode(shape2D, skeletons=self._3d._skeletons, node = n)
            itm.setPos(self._scene.data2scene.map(QPointF(*pos2D)))
            itm.setSelected(n.isSelected())
            
            self._scene.addItem(itm)
            self._node2view[n] = itm

        for itm in self._edge2view.values():
            self._scene.removeItem(itm) 
        self._edge2view = dict()
        
        for e in ePlane:
            l = QLineF(e[0].pointF(), e[1].pointF())

            c1 = e[0].color()
            c2 = e[1].color()
            mixColor = QColor( (c1.red()+c2.red())/2,
                               (c1.green()+c2.green())/2,
                               (c1.blue()+c2.blue())/2 )

            line = QGraphicsLineItem(self._scene.data2scene.map(l))
            line.setPen(QPen(mixColor))
            self._scene.addItem(line)
            self._edge2view[e] = line
            
        for theEdge, e in eIntersected:
            c1 = theEdge[0].color()
            c2 = theEdge[1].color()
            mixColor = QColor( (c1.red()+c2.red())/2,
                               (c1.green()+c2.green())/2,
                               (c1.blue()+c2.blue())/2 )

            nodeSize = 6
            p = QGraphicsRectItem(-nodeSize/2, -nodeSize/2, nodeSize, nodeSize)
            pos2D = list(e)
            del pos2D[self._axis]
            p.setPos(self._scene.data2scene.map(QPointF(*pos2D)))
            p.setPen(QPen(mixColor))
            self._scene.addItem(p)
            self._edge2view[e] = p
Esempio n. 29
0
class PatternLegendText(QGraphicsTextItem):

    Type = 70000 + 3

    def __init__(self, text, itemID=0, parent=None):

        super(PatternLegendText, self).__init__(text, parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.itemID = itemID
        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)
        self.setTextInteractionFlags(Qt.TextEditorInteraction)

        self._position = self.pos()
        self._outline = None

    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(self.boundingRect(), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()

    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()

    def keyPressEvent(self, event):
        """ Stuff to do during key press events.

        For now we have to adjust the outline box.

        """

        QGraphicsTextItem.keyPressEvent(self, event)
        self.adjust_size()

    def adjust_size(self):
        """ This function takes care of changing the size of the

        outline rectangle, e.g., when text is added or removed or
        during font size changes.

        """

        if self._outline:
            self._outline.setRect(self.boundingRect())

    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
            self.setTextInteractionFlags(Qt.NoTextInteraction)

        return QGraphicsTextItem.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        self.setTextInteractionFlags(Qt.TextEditorInteraction)
        QApplication.restoreOverrideCursor()

        # this is needed for undo/redo
        if self._position != self.pos():
            self.scene().canvas_item_position_changed(self, self._position,
                                                      self.pos())

        return QGraphicsTextItem.mouseReleaseEvent(self, event)
Esempio n. 30
0
class OWLegendGradient(QGraphicsObject):

    gradient_width = 20

    def __init__(self, palette, values, parent):
        QGraphicsObject.__init__(self, parent)
        self.parent = parent
        self.palette = palette
        self.values = values
        self.legend = parent
        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)

    def set_orientation(self, orientation):
        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 = 0
            x = 30
            for item in self.label_items:
                move_item_xy(item, x, y, 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:
            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, self.parent.graph.animate_plot)
                x += interval
            self.rect = QRectF(0, 0, total_width, self.gradient_width + height)

    def boundingRect(self):
        return getattr(self, 'rect', QRectF())

    def paint(self, painter, option, widget):
        pass
Esempio n. 31
0
class PatternLegendItem(QGraphicsSvgItem):

    Type = 70000 + 2


    def __init__(self, unitDim, width, height,
                 defaultSymbol, defaultColor = QColor(Qt.white),
                 zValue = 1, parent = None):

        super(PatternLegendItem, self).__init__(parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.setZValue(zValue)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)

        self.origin = QPointF(0.0, 0.0)
        self.unitDim = unitDim
        self.width = width
        self.height = height
        self.size = QSizeF(self.unitDim.width() * width,
                              self.unitDim.height() * height)

        self.color = defaultColor

        self.symbol = None
        self._set_symbol(defaultSymbol)

        self._penSize = 1.0
        self._pen = QPen()
        self._pen.setWidthF(self._penSize)
        self._pen.setJoinStyle(Qt.MiterJoin)
        self._pen.setColor(Qt.black)

        self.setAcceptsHoverEvents(True)
        self._outline = None



    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(\
                self.boundingRect().adjusted(-1,-1,1,1), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()



    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()



    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
        else:
            event.ignore()

        return QGraphicsSvgItem.mousePressEvent(self, event)



    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        QApplication.restoreOverrideCursor()

        # this is needed for redo/undo
        if self._position != self.pos():
           self.scene().canvas_item_position_changed(self, self._position,
                                                     self.pos())

        return QGraphicsSvgItem.mouseReleaseEvent(self, event)



    def change_geometry(self, newDim):
        """ This slot changes the unit dimensions of the item. """

        self.unitDim = newDim
        self.size    = QSizeF(self.unitDim.width() * self.width,
                              self.unitDim.height() * self.height)



    @property
    def name(self):
        """ Return the name of the knitting symbol we contain. """

        return self.symbol["name"]



    def _set_symbol(self, newSymbol):
        """ Adds a new svg image of a knitting symbol to the scene. """

        self.symbol = newSymbol
        svgPath = newSymbol["svgPath"]
        if not self.renderer().load(svgPath):
            errorMessage = ("PatternLegendItem._set_symbol: failed to load "
                           "symbol %s" % svgPath)
            logger.error(errorMessage)
            return

        # apply color if present
        if "backgroundColor" in newSymbol:
            self.color = QColor(newSymbol["backgroundColor"])



    def boundingRect(self):
        """ Return the bounding rectangle of the item. """

        halfPen = self._penSize * 0.5
        return QRectF(self.origin, self.size).adjusted(halfPen, halfPen,
                                                       halfPen, halfPen)



    def paint(self, painter, option, widget):
        """ Paint ourselves. """

        painter.setPen(self._pen)
        brush = QBrush(self.color)
        painter.setBrush(brush)
        halfPen = self._penSize * 0.5
        painter.drawRect(\
            QRectF(self.origin, self.size).adjusted(halfPen, halfPen,
                                                    halfPen, halfPen))
        self.renderer().render(painter, QRectF(self.origin, self.size))
Esempio n. 32
0
class RectangleSelectionAction(UserInteraction):
    """
    Select items in the scene using a Rectangle selection
    """
    def __init__(self, document, *args, **kwargs):
        UserInteraction.__init__(self, document, *args, **kwargs)
        # The initial selection at drag start
        self.initial_selection = None
        # Selection when last updated in a mouseMoveEvent
        self.last_selection = None
        # A selection rect (`QRectF`)
        self.selection_rect = None
        # Keyboard modifiers
        self.modifiers = 0

    def mousePressEvent(self, event):
        pos = event.scenePos()
        any_item = self.scene.item_at(pos)
        if not any_item and event.button() & Qt.LeftButton:
            self.modifiers = event.modifiers()
            self.selection_rect = QRectF(pos, QSizeF(0, 0))
            self.rect_item = QGraphicsRectItem(
                self.selection_rect.normalized())

            self.rect_item.setPen(
                QPen(QBrush(QColor(51, 153, 255, 192)), 0.4, Qt.SolidLine,
                     Qt.RoundCap))

            self.rect_item.setBrush(QBrush(QColor(168, 202, 236, 192)))

            self.rect_item.setZValue(-100)

            # Clear the focus if necessary.
            if not self.scene.stickyFocus():
                self.scene.clearFocus()

            if not self.modifiers & Qt.ControlModifier:
                self.scene.clearSelection()

            event.accept()
            return True
        else:
            self.cancel(self.ErrorReason)
            return False

    def mouseMoveEvent(self, event):
        if not self.rect_item.scene():
            # Add the rect item to the scene when the mouse moves.
            self.scene.addItem(self.rect_item)
        self.update_selection(event)
        return True

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.initial_selection is None:
                # A single click.
                self.scene.clearSelection()
            else:
                self.update_selection(event)
        self.end()
        return True

    def update_selection(self, event):
        """
        Update the selection rectangle from a QGraphicsSceneMouseEvent
        `event` instance.

        """
        if self.initial_selection is None:
            self.initial_selection = set(self.scene.selectedItems())
            self.last_selection = self.initial_selection

        pos = event.scenePos()
        self.selection_rect = QRectF(self.selection_rect.topLeft(), pos)

        # Make sure the rect_item does not cause the scene rect to grow.
        rect = self._bound_selection_rect(self.selection_rect.normalized())

        # Need that 0.5 constant otherwise the sceneRect will still
        # grow (anti-aliasing correction by QGraphicsScene?)
        pw = self.rect_item.pen().width() + 0.5

        self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw))

        selected = self.scene.items(self.selection_rect.normalized(),
                                    Qt.IntersectsItemShape, Qt.AscendingOrder)

        selected = set([item for item in selected if \
                        item.flags() & Qt.ItemIsSelectable])

        if self.modifiers & Qt.ControlModifier:
            for item in selected | self.last_selection | \
                    self.initial_selection:
                item.setSelected((item in selected)
                                 ^ (item in self.initial_selection))
        else:
            for item in selected.union(self.last_selection):
                item.setSelected(item in selected)

        self.last_selection = set(self.scene.selectedItems())

    def end(self):
        self.initial_selection = None
        self.last_selection = None
        self.modifiers = 0

        self.rect_item.hide()
        if self.rect_item.scene() is not None:
            self.scene.removeItem(self.rect_item)
        UserInteraction.end(self)

    def viewport_rect(self):
        """
        Return the bounding rect of the document's viewport on the scene.
        """
        view = self.document.view()
        vsize = view.viewport().size()
        viewportrect = QRect(0, 0, vsize.width(), vsize.height())
        return view.mapToScene(viewportrect).boundingRect()

    def _bound_selection_rect(self, rect):
        """
        Bound the selection `rect` to a sensible size.
        """
        srect = self.scene.sceneRect()
        vrect = self.viewport_rect()
        maxrect = srect.united(vrect)
        return rect.intersected(maxrect)
Esempio n. 33
0
class RepeatLegendItem(QGraphicsRectItem):

    Type = 70000 + 7


    def __init__(self, color, parent = None):

        super(RepeatLegendItem, self).__init__(parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.setZValue(1)
        self.setFlag(QGraphicsItem.ItemIsMovable)

        self._position = self.pos()

        self.penColor = color
        self.itemHeight = 20
        self.itemWidth = 40

        self._pen = QPen()
        self._pen.setWidthF(3.0)
        self._pen.setColor(self.penColor)
        self.setPen(self._pen)

        self.setRect(0, 0, self.itemWidth, self.itemHeight)

        self.setAcceptsHoverEvents(True)
        self._outline = None


    @property
    def height(self):
        return self.itemHeight


    @property
    def width(self):
        return self.itemWidth



    @property
    def color(self):
        return self.penColor



    @color.setter
    def color(self, newColor):

        self.penColor = newColor
        self._pen.setColor(self.penColor)
        self.setPen(self._pen)



    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(\
                self.boundingRect().adjusted(-1,-1,1,1), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()



    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()



    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
        else:
            event.ignore()

        return QGraphicsRectItem.mousePressEvent(self, event)



    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        QApplication.restoreOverrideCursor()

        # this is needed for undo/redo
        if self._position != self.pos():
           self.scene().canvas_item_position_changed(self, self._position,
                                                     self.pos())

        return QGraphicsRectItem.mouseReleaseEvent(self, event)
Esempio n. 34
0
 def draw_fun(self, x, y, val, col_width=10, col_height=10, color="gray"):
     color = get_corr_color(val)
     rect = QGraphicsRectItem(x, y, col_width, col_height, parent=self.item)
     rect.setPen(QPen(QColor('black')))
     rect.setBrush(QColor(color))
Esempio n. 35
0
class PatternLegendItem(QGraphicsSvgItem):

    Type = 70000 + 2

    def __init__(self,
                 unitDim,
                 width,
                 height,
                 defaultSymbol,
                 itemID=0,
                 defaultColor=QColor(Qt.white),
                 zValue=1,
                 parent=None):

        super(PatternLegendItem, self).__init__(parent)

        # NOTE: need this distinction for cache mode based on
        # the Qt version otherwise rendering is broken
        if NO_ITEM_CACHING:
            self.setCacheMode(QGraphicsItem.NoCache)
        else:
            self.setCacheMode(QGraphicsItem.DeviceCoordinateCache)

        self.itemID = itemID
        self.setZValue(zValue)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)

        self.origin = QPointF(0.0, 0.0)
        self.unitDim = unitDim
        self.width = width
        self.height = height
        self.size = QSizeF(self.unitDim.width() * width,
                           self.unitDim.height() * height)

        self.color = defaultColor

        self.symbol = None
        self._set_symbol(defaultSymbol)

        self._penSize = 1.0
        self._pen = QPen()
        self._pen.setWidthF(self._penSize)
        self._pen.setJoinStyle(Qt.MiterJoin)
        self._pen.setColor(Qt.black)

        self.setAcceptsHoverEvents(True)
        self._outline = None

    def hoverEnterEvent(self, event):
        """ Stuff related to hover enter events.

        For now we just show a rectangular outline.

        """

        if not self._outline:
            self._outline = QGraphicsRectItem(\
                self.boundingRect().adjusted(-1,-1,1,1), self)
            highlightColor = QColor(Qt.blue)
            highlightColor.setAlpha(30)
            self._outline.setBrush(highlightColor)
            highlightPen = QPen(Qt.blue)
            highlightPen.setWidth(2)
            self._outline.setPen(highlightPen)
        else:
            self._outline.show()

    def hoverLeaveEvent(self, event):
        """ Stuff related to hover leave events.

        For now we just show a rectangular outline.

        """

        self._outline.hide()

    def mousePressEvent(self, event):
        """ We reimplement this function to store the position of
        the item when a user issues a mouse press.

        """

        self._position = self.pos()

        if (event.modifiers() & Qt.ControlModifier):
            QApplication.setOverrideCursor(QCursor(Qt.SizeAllCursor))
        else:
            event.ignore()

        return QGraphicsSvgItem.mousePressEvent(self, event)

    def mouseReleaseEvent(self, event):
        """ We reimplement this function to check if its position
        has changed since the last mouse click. If yes we
        let the canvas know so it can store the action as
        a Redo/Undo event.

        """

        QApplication.restoreOverrideCursor()

        # this is needed for redo/undo
        if self._position != self.pos():
            self.scene().canvas_item_position_changed(self, self._position,
                                                      self.pos())

        return QGraphicsSvgItem.mouseReleaseEvent(self, event)

    def change_geometry(self, newDim):
        """ This slot changes the unit dimensions of the item. """

        self.unitDim = newDim
        self.size = QSizeF(self.unitDim.width() * self.width,
                           self.unitDim.height() * self.height)

    @property
    def name(self):
        """ Return the name of the knitting symbol we contain. """

        return self.symbol["name"]

    def _set_symbol(self, newSymbol):
        """ Adds a new svg image of a knitting symbol to the scene. """

        self.symbol = newSymbol
        svgPath = newSymbol["svgPath"]
        if not self.renderer().load(svgPath):
            errorMessage = ("PatternLegendItem._set_symbol: failed to load "
                            "symbol %s" % svgPath)
            logger.error(errorMessage)
            return

        # apply color if present
        if "backgroundColor" in newSymbol:
            self.color = QColor(newSymbol["backgroundColor"])

    def boundingRect(self):
        """ Return the bounding rectangle of the item. """

        halfPen = self._penSize * 0.5
        return QRectF(self.origin, self.size).adjusted(halfPen, halfPen,
                                                       halfPen, halfPen)

    def paint(self, painter, option, widget):
        """ Paint ourselves. """

        painter.setPen(self._pen)
        brush = QBrush(self.color)
        painter.setBrush(brush)
        halfPen = self._penSize * 0.5
        painter.drawRect(\
            QRectF(self.origin, self.size).adjusted(halfPen, halfPen,
                                                    halfPen, halfPen))
        self.renderer().render(painter, QRectF(self.origin, self.size))
Esempio n. 36
0
class ImageScene2D(QGraphicsScene):
    """
    The 2D scene description of a tiled image generated by evaluating
    an overlay stack, together with a 2D cursor.
    """
    axesChanged = pyqtSignal(int, bool)

    @property
    def stackedImageSources(self):
        return self._stackedImageSources

    @stackedImageSources.setter
    def stackedImageSources(self, s):
        self._stackedImageSources = s
        s.sizeChanged.connect(self._onSizeChanged)

    @property
    def showTileOutlines(self):
        return self._showTileOutlines
    @showTileOutlines.setter
    def showTileOutlines(self, show):
        self._showTileOutlines = show
        self.invalidate()

    @property
    def showTileProgress(self):
        return self._showTileProgress
    
    @showTileProgress.setter
    def showTileProgress(self, show):
        self._showTileProgress = show
        self._dirtyIndicator.setVisible(show)

    def resetAxes(self, finish=True):
        # rotation is in range(4) and indicates in which corner of the
        # view the origin lies. 0 = top left, 1 = top right, etc.
        self._rotation = 0
        self._swapped = self._swappedDefault # whether axes are swapped
        self._newAxes()
        self._setSceneRect()
        self.scene2data, isInvertible = self.data2scene.inverted()
        assert isInvertible
        if finish:
            self._finishViewMatrixChange()

    def _newAxes(self):
        """Given self._rotation and self._swapped, calculates and sets
        the appropriate data2scene transformation.

        """
        # TODO: this function works, but it is not elegant. There must
        # be a simpler way to calculate the appropriate tranformation.

        w, h = self.dataShape
        assert self._rotation in range(0, 4)

        # unlike self._rotation, the local variable 'rotation'
        # indicates how many times to rotate clockwise after swapping
        # axes.

        # t1 : do axis swap
        t1 = QTransform()
        if self._swapped:
            t1 = QTransform(0, 1, 0, 1, 0, 0, 0, 0, 1)
            h, w = w, h

        # t2 : do rotation
        t2 = QTransform()
        t2.rotate(self._rotation * 90)

        # t3: shift to re-center
        rot2trans = {0 : (0, 0),
                     1 : (h, 0),
                     2 : (w, h),
                     3 : (0, w)}

        trans = rot2trans[self._rotation]
        t3 = QTransform.fromTranslate(*trans)

        self.data2scene = t1 * t2 * t3
        if self._tileProvider:
            self._tileProvider.axesSwapped = self._swapped
        self.axesChanged.emit(self._rotation, self._swapped)

    def rot90(self, transform, rect, direction):
        """ direction: left ==> -1, right ==> +1"""
        assert direction in [-1, 1]
        self._rotation = (self._rotation + direction) % 4
        self._newAxes()

    def swapAxes(self, transform):
        self._swapped = not self._swapped
        self._newAxes()

    def _onRotateLeft(self):
        self.rot90(self.data2scene, self.sceneRect(), -1)
        self._finishViewMatrixChange()

    def _onRotateRight(self):
        self.rot90(self.data2scene, self.sceneRect(), 1)
        self._finishViewMatrixChange()

    def _onSwapAxes(self):
        self.swapAxes(self.data2scene)
        self._finishViewMatrixChange()

    def _finishViewMatrixChange(self):
        self.scene2data, isInvertible = self.data2scene.inverted()
        self._setSceneRect()
        self._tiling.data2scene = self.data2scene
        self._tileProvider._onSizeChanged()
        QGraphicsScene.invalidate(self, self.sceneRect())

    @property
    def sceneShape(self):
        return (self.sceneRect().width(), self.sceneRect().height())

    def _setSceneRect(self):
        w, h = self.dataShape
        rect = self.data2scene.mapRect(QRect(0, 0, w, h))
        sw, sh = rect.width(), rect.height()
        self.setSceneRect(0, 0, sw, sh)
        
        #this property represent a parent to QGraphicsItems which should
        #be clipped to the data, such as temporary capped lines for brushing.
        #This works around ilastik issue #516.
        self.dataRect = QGraphicsRectItem(0,0,sw,sh)
        self.dataRect.setPen(QPen(QColor(0,0,0,0)))
        self.dataRect.setFlag(QGraphicsItem.ItemClipsChildrenToShape)
        self.addItem(self.dataRect)

    @property
    def dataShape(self):
        """
        The shape of the scene in QGraphicsView's coordinate system.
        """
        return self._dataShape

    @dataShape.setter
    def dataShape(self, value):
        """
        Set the size of the scene in QGraphicsView's coordinate system.
        dataShape -- (widthX, widthY),
        where the origin of the coordinate system is in the upper left corner
        of the screen and 'x' points right and 'y' points down
        """
        assert len(value) == 2
        self._dataShape = value
        self.reset()
        self._finishViewMatrixChange()

    def setCacheSize(self, cache_size):
        if cache_size != self._tileProvider._cache_size:
            self._tileProvider = TileProvider(self._tiling, self._stackedImageSources, cache_size=cache_size)
            self._tileProvider.sceneRectChanged.connect(self.invalidateViewports)

    def cacheSize(self):
        return self._tileProvider._cache_size

    def setPrefetchingEnabled(self, enable):
        self._prefetching_enabled = enable

    def setPreemptiveFetchNumber(self, n):
        if n > self.cacheSize() - 1:
            self._n_preemptive = self.cacheSize() - 1
        else:
            self._n_preemptive = n
    def preemptiveFetchNumber(self):
        return self._n_preemptive

    def invalidateViewports(self, sceneRectF):
        '''Call invalidate on the intersection of all observing viewport-rects and rectF.'''
        sceneRectF = sceneRectF if sceneRectF.isValid() else self.sceneRect()
        for view in self.views():
            QGraphicsScene.invalidate(self, sceneRectF.intersected(view.viewportRect()))

    def reset(self):
        """Reset rotations, tiling, etc. Called when first initialized
        and when the underlying data changes.

        """
        self.resetAxes(finish=False)

        self._tiling = Tiling(self._dataShape, self.data2scene, name=self.name)
        self._brushingLayer  = TiledImageLayer(self._tiling)

        if self._tileProvider:
            self._tileProvider.notifyThreadsToStop() # prevent ref cycle
        self._tileProvider = TileProvider(self._tiling, self._stackedImageSources)
        self._tileProvider.sceneRectChanged.connect(self.invalidateViewports)

        if self._dirtyIndicator:
            self.removeItem(self._dirtyIndicator)
        del self._dirtyIndicator
        self._dirtyIndicator = DirtyIndicator(self._tiling)
        self.addItem(self._dirtyIndicator)


    def __init__(self, posModel, along, preemptive_fetch_number=5,
                 parent=None, name="Unnamed Scene",
                 swapped_default=False):
        """
        * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off
        * swapped_default -- whether axes should be swapped by default.

        """
        QGraphicsScene.__init__(self, parent=parent)

        self._along = along
        self._posModel = posModel

        self._dataShape = (0, 0)
        self._dataRect = None #A QGraphicsRectItem (or None)
        self._offsetX = 0
        self._offsetY = 0
        self.name = name

        self._stackedImageSources = StackedImageSources(LayerStackModel())
        self._showTileOutlines = False
        self._showTileProgress = True

        self._tileProvider = None
        self._dirtyIndicator = None
        self._prefetching_enabled = False
        
        self._swappedDefault = swapped_default
        self.reset()

        # BowWave preemptive caching
        self.setPreemptiveFetchNumber(preemptive_fetch_number)
        self._course = (1,1) # (along, pos or neg direction)
        self._time = self._posModel.time
        self._channel = self._posModel.channel
        self._posModel.timeChanged.connect(self._onTimeChanged)
        self._posModel.channelChanged.connect(self._onChannelChanged)
        self._posModel.slicingPositionChanged.connect(self._onSlicingPositionChanged)
        
        self._allTilesCompleteEvent = threading.Event()

    def __del__(self):
        if self._tileProvider:
            self._tileProvider.notifyThreadsToStop()
        self.joinRendering()

    def _onSizeChanged(self):
        self._brushingLayer  = TiledImageLayer(self._tiling)

    def drawForeground(self, painter, rect):
        if self._tiling is None:
            return

        tile_nos = self._tiling.intersected(rect)

        for tileId in tile_nos:
            p = self._brushingLayer[tileId]
            if p.dataVer == p.imgVer:
                continue

            p.paint(painter) #access to the underlying image patch is serialized

            ## draw tile outlines
            if self._showTileOutlines:
                # Dashed black line
                pen = QPen()
                pen.setDashPattern([5,5])
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

                # Dashed white line
                # (offset to occupy the spaces in the dashed black line)
                pen = QPen()
                pen.setDashPattern([5,5])
                pen.setDashOffset(5)
                pen.setColor(QColor(Qt.white))
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

    def indicateSlicingPositionSettled(self, settled):
        if self._showTileProgress:
            self._dirtyIndicator.setVisible(settled)

    def drawBackground(self, painter, sceneRectF):
        if self._tileProvider is None:
            return

        tiles = self._tileProvider.getTiles(sceneRectF)
        allComplete = True
        for tile in tiles:
            #We always draw the tile, even though it might not be up-to-date
            #In ilastik's live mode, the user sees the old result while adding
            #new brush strokes on top
            #See also ilastik issue #132 and tests/lazy_test.py
            if tile.qimg is not None:
                painter.drawImage(tile.rectF, tile.qimg)
            if tile.progress < 1.0:
                allComplete = False
            if self._showTileProgress:
                self._dirtyIndicator.setTileProgress(tile.id, tile.progress)

        if allComplete:
            self._allTilesCompleteEvent.set()
        else:
            self._allTilesCompleteEvent.clear()

        # preemptive fetching
        if self._prefetching_enabled:
            for through in self._bowWave(self._n_preemptive):
                self._tileProvider.prefetch(sceneRectF, through)

    def joinRendering(self):
        return self._tileProvider.join()

    def joinRenderingAllTiles(self):
        """
        Wait until all tiles in the scene have been 100% rendered.
        Note: This is useful for testing only.  If called from the GUI thread, the GUI thread will block until all tiles are rendered!
        """
        # If this is the main thread, keep repainting (otherwise we'll deadlock).
        if threading.current_thread().name == "MainThread":
            finished = False
            sceneRectF = self.views()[0].viewportRect()
            while not finished:
                finished = True
                tiles = self._tileProvider.getTiles(sceneRectF)
                for tile in tiles:
                    finished &= tile.progress >= 1.0
        else:
            self._allTilesCompleteEvent.wait()

    def _bowWave(self, n):
        shape5d = self._posModel.shape5D
        sl5d = self._posModel.slicingPos5D
        through = [sl5d[self._along[i]] for i in xrange(3)]
        t_max = [shape5d[self._along[i]] for i in xrange(3)]

        BowWave = []

        a = self._course[0]
        for d in xrange(1,n+1):
            m = through[a] + d * self._course[1]
            if m < t_max[a] and m >= 0:
                t = list(through)
                t[a] = m
                BowWave.append(tuple(t))
        return BowWave

    def _onSlicingPositionChanged(self, new, old):
        if (new[self._along[1] - 1] - old[self._along[1] - 1]) < 0:
            self._course = (1, -1)
        else:
            self._course = (1, 1)

    def _onChannelChanged(self, new):
        if (new - self._channel) < 0:
            self._course = (2, -1)
        else:
            self._course = (2, 1)
        self._channel = new

    def _onTimeChanged(self, new):
        if (new - self._time) < 0:
            self._course = (0, -1)
        else:
            self._course = (0, 1)
        self._time = new
Esempio n. 37
0
class ImageScene2D(QGraphicsScene):
    """
    The 2D scene description of a tiled image generated by evaluating
    an overlay stack, together with a 2D cursor.
    """
    axesChanged = pyqtSignal(int, bool)
    dirtyChanged = pyqtSignal()

    @property
    def is_swapped(self):
        """
        Indicates whether the dimensions are swapped
        swapping the axis will swap the dimensions and rotating the roi will swap the dimensions
        :return: bool
        """
        return bool(self._swapped) != bool(self._rotation % 2)  # xor

    @property
    def stackedImageSources(self):
        return self._stackedImageSources

    @stackedImageSources.setter
    def stackedImageSources(self, s):
        self._stackedImageSources = s

    @property
    def showTileOutlines(self):
        return self._showTileOutlines

    @showTileOutlines.setter
    def showTileOutlines(self, show):
        self._showTileOutlines = show
        self.invalidate()

    @property
    def showTileProgress(self):
        return self._showTileProgress

    @showTileProgress.setter
    def showTileProgress(self, show):
        self._showTileProgress = show
        self._dirtyIndicator.setVisible(show)

    def resetAxes(self, finish=True):
        # rotation is in range(4) and indicates in which corner of the
        # view the origin lies. 0 = top left, 1 = top right, etc.
        self._rotation = 0
        self._swapped = self._swappedDefault  # whether axes are swapped
        self._newAxes()
        self._setSceneRect()
        self.scene2data, isInvertible = self.data2scene.inverted()
        assert isInvertible
        if finish:
            self._finishViewMatrixChange()

    def _newAxes(self):
        """Given self._rotation and self._swapped, calculates and sets
        the appropriate data2scene transformation.

        """
        # TODO: this function works, but it is not elegant. There must
        # be a simpler way to calculate the appropriate transformation.

        w, h = self.dataShape
        assert self._rotation in range(0, 4)

        # unlike self._rotation, the local variable 'rotation'
        # indicates how many times to rotate clockwise after swapping
        # axes.

        # t1 : do axis swap
        t1 = QTransform()
        if self._swapped:
            t1 = QTransform(0, 1, 0, 1, 0, 0, 0, 0, 1)
            h, w = w, h

        # t2 : do rotation
        t2 = QTransform()
        t2.rotate(self._rotation * 90)

        # t3: shift to re-center
        rot2trans = {0: (0, 0), 1: (h, 0), 2: (w, h), 3: (0, w)}

        trans = rot2trans[self._rotation]
        t3 = QTransform.fromTranslate(*trans)

        self.data2scene = t1 * t2 * t3
        if self._tileProvider:
            self._tileProvider.axesSwapped = self._swapped
        self.axesChanged.emit(self._rotation, self._swapped)

    def rot90(self, direction):
        """ direction: left ==> -1, right ==> +1"""
        assert direction in [-1, 1]
        self._rotation = (self._rotation + direction) % 4
        self._newAxes()

    def swapAxes(self, transform):
        self._swapped = not self._swapped
        self._newAxes()

    def _onRotateLeft(self):
        self.rot90(-1)
        self._finishViewMatrixChange()

    def _onRotateRight(self):
        self.rot90(1)
        self._finishViewMatrixChange()

    def _onSwapAxes(self):
        self.swapAxes(self.data2scene)
        self._finishViewMatrixChange()

    def _finishViewMatrixChange(self):
        self.scene2data, isInvertible = self.data2scene.inverted()
        self._setSceneRect()
        self._tiling.data2scene = self.data2scene
        self._tileProvider._onSizeChanged()
        QGraphicsScene.invalidate(self, self.sceneRect())

    @property
    def sceneShape(self):
        return (self.sceneRect().width(), self.sceneRect().height())

    def _setSceneRect(self):
        w, h = self.dataShape
        rect = self.data2scene.mapRect(QRect(0, 0, w, h))
        sw, sh = rect.width(), rect.height()
        self.setSceneRect(0, 0, sw, sh)

        if self._dataRectItem is not None:
            self.removeItem(self._dataRectItem)

        #this property represent a parent to QGraphicsItems which should
        #be clipped to the data, such as temporary capped lines for brushing.
        #This works around ilastik issue #516.
        self._dataRectItem = QGraphicsRectItem(0, 0, sw, sh)
        self._dataRectItem.setPen(QPen(QColor(0, 0, 0, 0)))
        self._dataRectItem.setFlag(QGraphicsItem.ItemClipsChildrenToShape)
        self.addItem(self._dataRectItem)

    @property
    def dataRectItem(self):
        return self._dataRectItem

    @property
    def dataShape(self):
        """
        The shape of the scene in QGraphicsView's coordinate system.
        """
        return self._dataShape

    @dataShape.setter
    def dataShape(self, value):
        """
        Set the size of the scene in QGraphicsView's coordinate system.
        dataShape -- (widthX, widthY),
        where the origin of the coordinate system is in the upper left corner
        of the screen and 'x' points right and 'y' points down
        """
        assert len(value) == 2
        self._dataShape = value
        self.reset()
        self._finishViewMatrixChange()

    def setCacheSize(self, cache_size):
        self._tileProvider.set_cache_size(cache_size)

    def cacheSize(self):
        return self._tileProvider.cache_size

    def setTileWidth(self, tileWidth):
        self._tileWidth = tileWidth
        PreferencesManager().set("ImageScene2D", "tileWidth", tileWidth)

    def tileWidth(self):
        return self._tileWidth

    def setPrefetchingEnabled(self, enable):
        self._prefetching_enabled = enable

    def setPreemptiveFetchNumber(self, n):
        if n > self.cacheSize() - 1:
            self._n_preemptive = self.cacheSize() - 1
        else:
            self._n_preemptive = n

    def preemptiveFetchNumber(self):
        return self._n_preemptive

    def invalidateViewports(self, sceneRectF):
        '''Call invalidate on the intersection of all observing viewport-rects and rectF.'''
        sceneRectF = sceneRectF if sceneRectF.isValid() else self.sceneRect()
        for view in self.views():
            QGraphicsScene.invalidate(
                self, sceneRectF.intersected(view.viewportRect()))

    def reset(self):
        """Reset rotations, tiling, etc. Called when first initialized
        and when the underlying data changes.

        """
        self.resetAxes(finish=False)

        self._tiling = Tiling(self._dataShape,
                              self.data2scene,
                              name=self.name,
                              blockSize=self.tileWidth())

        self._tileProvider = TileProvider(self._tiling,
                                          self._stackedImageSources)
        self._tileProvider.sceneRectChanged.connect(self.invalidateViewports)

        if self._dirtyIndicator:
            self.removeItem(self._dirtyIndicator)
        del self._dirtyIndicator
        self._dirtyIndicator = DirtyIndicator(self._tiling)
        self.addItem(self._dirtyIndicator)
        self._dirtyIndicator.setVisible(False)

    def mouseMoveEvent(self, event):
        """
        Normally our base class (QGraphicsScene) distributes mouse events to the
        various QGraphicsItems in the scene. But when the mouse is being dragged,
        it only sends events to the one object that was under the mouse when the
        button was first pressed.

        Here, we forward all events to QGraphicsItems on the drag path, even if
        they're just brushed by the mouse incidentally.
        """
        super(ImageScene2D, self).mouseMoveEvent(event)

        if not event.isAccepted() and event.buttons() != Qt.NoButton:
            if self.last_drag_pos is None:
                self.last_drag_pos = event.scenePos()

            # As a special feature, find the item and send it this event.
            path = QPainterPath(self.last_drag_pos)
            path.lineTo(event.scenePos())
            items = self.items(path)
            for item in items:
                item.mouseMoveEvent(event)
            self.last_drag_pos = event.scenePos()
        else:
            self.last_drag_pos = None

    def mousePressEvent(self, event):
        """
        By default, our base class (QGraphicsScene) only sends mouse press events to the top-most item under the mouse.
        When labeling edges, we want the edge label layer to accept mouse events, even if it isn't on top.
        Therefore, we send events to all items under the mouse, until the event is accepted.
        """
        super(ImageScene2D, self).mouseMoveEvent(event)
        if not event.isAccepted():
            items = self.items(event.scenePos())
            for item in items:
                item.mousePressEvent(event)
                if event.isAccepted():
                    break

    def __init__(self,
                 posModel,
                 along,
                 preemptive_fetch_number=5,
                 parent=None,
                 name="Unnamed Scene",
                 swapped_default=False):
        """
        * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off
        * swapped_default -- whether axes should be swapped by default.

        """
        QGraphicsScene.__init__(self, parent=parent)

        self._along = along
        self._posModel = posModel

        # QGraphicsItems can change this if they are in a state that should temporarily forbid brushing
        # (For example, when the slice intersection marker is in 'draggable' state.)
        self.allow_brushing = True

        self._dataShape = (0, 0)
        self._dataRectItem = None  #A QGraphicsRectItem (or None)
        self._offsetX = 0
        self._offsetY = 0
        self.name = name
        self._tileWidth = PreferencesManager().get("ImageScene2D",
                                                   "tileWidth",
                                                   default=512)

        self._stackedImageSources = StackedImageSources(LayerStackModel())
        self._showTileOutlines = False

        # FIXME: We don't show the red 'progress pies' because they look terrible.
        #        If we could fix their timing, maybe it would be worth it.
        self._showTileProgress = False

        self._tileProvider = None
        self._dirtyIndicator = None
        self._prefetching_enabled = False

        self._swappedDefault = swapped_default
        self.reset()

        # BowWave preemptive caching
        self.setPreemptiveFetchNumber(preemptive_fetch_number)
        self._course = (1, 1)  # (along, pos or neg direction)
        self._time = self._posModel.time
        self._channel = self._posModel.channel
        self._posModel.timeChanged.connect(self._onTimeChanged)
        self._posModel.channelChanged.connect(self._onChannelChanged)
        self._posModel.slicingPositionChanged.connect(
            self._onSlicingPositionChanged)

        self._allTilesCompleteEvent = threading.Event()
        self.dirty = False

        # We manually keep track of the tile-wise QGraphicsItems that
        # we've added to the scene in this dict, otherwise we would need
        # to use O(N) lookups for every tile by calling QGraphicsScene.items()
        self.tile_graphicsitems = defaultdict(
            set)  # [Tile.id] -> set(QGraphicsItems)

        self.last_drag_pos = None  # See mouseMoveEvent()

    def drawForeground(self, painter, rect):
        if self._tiling is None:
            return

        if self._showTileOutlines:
            tile_nos = self._tiling.intersected(rect)

            for tileId in tile_nos:
                ## draw tile outlines
                # Dashed black line
                pen = QPen()
                pen.setDashPattern([5, 5])
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

                # Dashed white line
                # (offset to occupy the spaces in the dashed black line)
                pen = QPen()
                pen.setDashPattern([5, 5])
                pen.setDashOffset(5)
                pen.setColor(QColor(Qt.white))
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

    def indicateSlicingPositionSettled(self, settled):
        if self._showTileProgress:
            self._dirtyIndicator.setVisible(settled)

    def drawBackground(self, painter, sceneRectF):
        if self._tileProvider is None:
            return

        # FIXME: For some strange reason, drawBackground is called with
        #        a much larger sceneRectF than necessasry sometimes.
        #        This can happen after panSlicingViews(), for instance.
        #        Somehow, the QGraphicsScene gets confused about how much area
        #        it needs to draw immediately after the ImageView's scrollbar is panned.
        #        As a workaround, we manually check the amount of the scene that needs to be drawn,
        #        instead of relying on the above sceneRectF parameter to be correct.
        if self.views():
            sceneRectF = self.views()[0].viewportRect().intersected(sceneRectF)

        if not sceneRectF.isValid():
            return

        tiles = self._tileProvider.getTiles(sceneRectF)
        allComplete = True
        for tile in tiles:
            #We always draw the tile, even though it might not be up-to-date
            #In ilastik's live mode, the user sees the old result while adding
            #new brush strokes on top
            #See also ilastik issue #132 and tests/lazy_test.py
            if tile.qimg is not None:
                painter.drawImage(tile.rectF, tile.qimg)

            # The tile also contains a list of any QGraphicsItems that were produced by the layers.
            # If there are any new ones, add them to the scene.
            new_items = set(
                tile.qgraphicsitems) - self.tile_graphicsitems[tile.id]
            obsolete_items = self.tile_graphicsitems[tile.id] - set(
                tile.qgraphicsitems)
            for g_item in obsolete_items:
                self.tile_graphicsitems[tile.id].remove(g_item)
                self.removeItem(g_item)
            for g_item in new_items:
                self.tile_graphicsitems[tile.id].add(g_item)
                self.addItem(g_item)

            if tile.progress < 1.0:
                allComplete = False
            if self._showTileProgress:
                self._dirtyIndicator.setTileProgress(tile.id, tile.progress)

        if allComplete:
            if self.dirty:
                self.dirty = False
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.set()
        else:
            if not self.dirty:
                self.dirty = True
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.clear()

        # preemptive fetching
        if self._prefetching_enabled:
            upcoming_through_slices = self._bowWave(self._n_preemptive)
            for through in upcoming_through_slices:
                self._tileProvider.prefetch(sceneRectF,
                                            through,
                                            layer_indexes=None)

    def triggerPrefetch(self,
                        layer_indexes,
                        time_range='current',
                        spatial_axis_range='current',
                        sceneRectF=None):
        """
        Trigger a one-time prefetch for the given set of layers.
        
        TODO: I'm not 100% sure what happens here for layers with multiple channels.
        
        layer_indexes: list-of-ints, or None, which means 'all visible'.
        time_range: (start_time, stop_time)
        spatial_axis_range: (start_slice, stop_slice), meaning Z/Y/X depending on our projection (self.along)
        sceneRectF: Used to determine which tiles to request.
                    An invalid QRectF results in all tiles getting refreshed (visible or not).
        """
        # Process parameters
        sceneRectF = sceneRectF or QRectF()

        if time_range == 'current':
            time_range = (self._posModel.slicingPos5D[0],
                          self._posModel.slicingPos5D[0] + 1)
        elif time_range == 'all':
            time_range = (0, self._posModel.shape5D[0])
        else:
            assert len(time_range) == 2
            assert time_range[0] >= 0 and time_range[
                1] < self._posModel.shape5D[0]

        spatial_axis = self._along[1]
        if spatial_axis_range == 'current':
            spatial_axis_range = (self._posModel.slicingPos5D[spatial_axis],
                                  self._posModel.slicingPos5D[spatial_axis] +
                                  1)
        elif spatial_axis_range == 'all':
            spatial_axis_range = (0, self._posModel.shape5D[spatial_axis])
        else:
            assert len(spatial_axis_range) == 2
            assert 0 <= spatial_axis_range[0] < self._posModel.shape5D[
                spatial_axis]
            assert 0 < spatial_axis_range[1] <= self._posModel.shape5D[
                spatial_axis]

        # Construct list of 'through' coordinates
        through_list = []
        for t in range(*time_range):
            for s in range(*spatial_axis_range):
                through_list.append((t, s))

        # Make sure the tile cache is big enough to hold the prefetched data.
        if self._tileProvider.cache_size < len(through_list):
            self._tileProvider.set_cache_size(len(through_list))

        # Trigger prefetches
        for through in through_list:
            self._tileProvider.prefetch(sceneRectF, through, layer_indexes)

    def joinRenderingAllTiles(self, viewport_only=True, rect=None):
        """
        Wait until all tiles in the scene have been 100% rendered.
        If sceneRectF is None, use the viewport rect.
        If sceneRectF is an invalid QRectF(), then wait for all tiles.
        Note: If called from the GUI thread, the GUI thread will block until all tiles are rendered!
        """
        # If this is the main thread, keep repainting (otherwise we'll deadlock).
        if threading.current_thread().name == "MainThread":
            if viewport_only:
                sceneRectF = self.views()[0].viewportRect()
            else:
                if rect is None or not isinstance(rect, QRectF):
                    sceneRectF = QRectF(
                    )  # invalid QRectF means 'get all tiles'
                else:
                    sceneRectF = rect
            self._tileProvider.waitForTiles(sceneRectF)
        else:
            self._allTilesCompleteEvent.wait()

    def _bowWave(self, n):
        through = [
            self._posModel.slicingPos5D[axis] for axis in self._along[:-1]
        ]
        t_max = [self._posModel.shape5D[axis] for axis in self._along[:-1]]

        BowWave = []

        a = self._course[0]
        for d in xrange(1, n + 1):
            m = through[a] + d * self._course[1]
            if m < t_max[a] and m >= 0:
                t = list(through)
                t[a] = m
                BowWave.append(tuple(t))
        return BowWave

    def _onSlicingPositionChanged(self, new, old):
        if (new[self._along[1] - 1] - old[self._along[1] - 1]) < 0:
            self._course = (1, -1)
        else:
            self._course = (1, 1)

    def _onChannelChanged(self, new):
        if (new - self._channel) < 0:
            self._course = (2, -1)
        else:
            self._course = (2, 1)
        self._channel = new

    def _onTimeChanged(self, new):
        if (new - self._time) < 0:
            self._course = (0, -1)
        else:
            self._course = (0, 1)
        self._time = new
Esempio n. 38
0
class ImageScene2D(QGraphicsScene):
    """
    The 2D scene description of a tiled image generated by evaluating
    an overlay stack, together with a 2D cursor.
    """
    axesChanged = pyqtSignal(int, bool)
    dirtyChanged = pyqtSignal()

    @property
    def stackedImageSources(self):
        return self._stackedImageSources

    @stackedImageSources.setter
    def stackedImageSources(self, s):
        self._stackedImageSources = s
        s.sizeChanged.connect(self._onSizeChanged)

    @property
    def showTileOutlines(self):
        return self._showTileOutlines

    @showTileOutlines.setter
    def showTileOutlines(self, show):
        self._showTileOutlines = show
        self.invalidate()

    @property
    def showTileProgress(self):
        return self._showTileProgress

    @showTileProgress.setter
    def showTileProgress(self, show):
        self._showTileProgress = show
        self._dirtyIndicator.setVisible(show)

    def resetAxes(self, finish=True):
        # rotation is in range(4) and indicates in which corner of the
        # view the origin lies. 0 = top left, 1 = top right, etc.
        self._rotation = 0
        self._swapped = self._swappedDefault  # whether axes are swapped
        self._newAxes()
        self._setSceneRect()
        self.scene2data, isInvertible = self.data2scene.inverted()
        assert isInvertible
        if finish:
            self._finishViewMatrixChange()

    def _newAxes(self):
        """Given self._rotation and self._swapped, calculates and sets
        the appropriate data2scene transformation.

        """
        # TODO: this function works, but it is not elegant. There must
        # be a simpler way to calculate the appropriate tranformation.

        w, h = self.dataShape
        assert self._rotation in range(0, 4)

        # unlike self._rotation, the local variable 'rotation'
        # indicates how many times to rotate clockwise after swapping
        # axes.

        # t1 : do axis swap
        t1 = QTransform()
        if self._swapped:
            t1 = QTransform(0, 1, 0, 1, 0, 0, 0, 0, 1)
            h, w = w, h

        # t2 : do rotation
        t2 = QTransform()
        t2.rotate(self._rotation * 90)

        # t3: shift to re-center
        rot2trans = {0: (0, 0), 1: (h, 0), 2: (w, h), 3: (0, w)}

        trans = rot2trans[self._rotation]
        t3 = QTransform.fromTranslate(*trans)

        self.data2scene = t1 * t2 * t3
        if self._tileProvider:
            self._tileProvider.axesSwapped = self._swapped
        self.axesChanged.emit(self._rotation, self._swapped)

    def rot90(self, transform, rect, direction):
        """ direction: left ==> -1, right ==> +1"""
        assert direction in [-1, 1]
        self._rotation = (self._rotation + direction) % 4
        self._newAxes()

    def swapAxes(self, transform):
        self._swapped = not self._swapped
        self._newAxes()

    def _onRotateLeft(self):
        self.rot90(self.data2scene, self.sceneRect(), -1)
        self._finishViewMatrixChange()

    def _onRotateRight(self):
        self.rot90(self.data2scene, self.sceneRect(), 1)
        self._finishViewMatrixChange()

    def _onSwapAxes(self):
        self.swapAxes(self.data2scene)
        self._finishViewMatrixChange()

    def _finishViewMatrixChange(self):
        self.scene2data, isInvertible = self.data2scene.inverted()
        self._setSceneRect()
        self._tiling.data2scene = self.data2scene
        self._tileProvider._onSizeChanged()
        QGraphicsScene.invalidate(self, self.sceneRect())

    @property
    def sceneShape(self):
        return (self.sceneRect().width(), self.sceneRect().height())

    def _setSceneRect(self):
        w, h = self.dataShape
        rect = self.data2scene.mapRect(QRect(0, 0, w, h))
        sw, sh = rect.width(), rect.height()
        self.setSceneRect(0, 0, sw, sh)

        #this property represent a parent to QGraphicsItems which should
        #be clipped to the data, such as temporary capped lines for brushing.
        #This works around ilastik issue #516.
        self.dataRect = QGraphicsRectItem(0, 0, sw, sh)
        self.dataRect.setPen(QPen(QColor(0, 0, 0, 0)))
        self.dataRect.setFlag(QGraphicsItem.ItemClipsChildrenToShape)
        self.addItem(self.dataRect)

    @property
    def dataShape(self):
        """
        The shape of the scene in QGraphicsView's coordinate system.
        """
        return self._dataShape

    @dataShape.setter
    def dataShape(self, value):
        """
        Set the size of the scene in QGraphicsView's coordinate system.
        dataShape -- (widthX, widthY),
        where the origin of the coordinate system is in the upper left corner
        of the screen and 'x' points right and 'y' points down
        """
        assert len(value) == 2
        self._dataShape = value
        self.reset()
        self._finishViewMatrixChange()

    def setCacheSize(self, cache_size):
        if cache_size != self._tileProvider._cache_size:
            self._tileProvider = TileProvider(self._tiling,
                                              self._stackedImageSources,
                                              cache_size=cache_size)
            self._tileProvider.sceneRectChanged.connect(
                self.invalidateViewports)

    def cacheSize(self):
        return self._tileProvider._cache_size

    def setPrefetchingEnabled(self, enable):
        self._prefetching_enabled = enable

    def setPreemptiveFetchNumber(self, n):
        if n > self.cacheSize() - 1:
            self._n_preemptive = self.cacheSize() - 1
        else:
            self._n_preemptive = n

    def preemptiveFetchNumber(self):
        return self._n_preemptive

    def invalidateViewports(self, sceneRectF):
        '''Call invalidate on the intersection of all observing viewport-rects and rectF.'''
        sceneRectF = sceneRectF if sceneRectF.isValid() else self.sceneRect()
        for view in self.views():
            QGraphicsScene.invalidate(
                self, sceneRectF.intersected(view.viewportRect()))

    def reset(self):
        """Reset rotations, tiling, etc. Called when first initialized
        and when the underlying data changes.

        """
        self.resetAxes(finish=False)

        self._tiling = Tiling(self._dataShape, self.data2scene, name=self.name)
        self._brushingLayer = TiledImageLayer(self._tiling)

        self._tileProvider = TileProvider(self._tiling,
                                          self._stackedImageSources)
        self._tileProvider.sceneRectChanged.connect(self.invalidateViewports)

        if self._dirtyIndicator:
            self.removeItem(self._dirtyIndicator)
        del self._dirtyIndicator
        self._dirtyIndicator = DirtyIndicator(self._tiling)
        self.addItem(self._dirtyIndicator)
        self._dirtyIndicator.setVisible(False)

    def __init__(self,
                 posModel,
                 along,
                 preemptive_fetch_number=5,
                 parent=None,
                 name="Unnamed Scene",
                 swapped_default=False):
        """
        * preemptive_fetch_number -- number of prefetched slices; 0 turns the feature off
        * swapped_default -- whether axes should be swapped by default.

        """
        QGraphicsScene.__init__(self, parent=parent)

        self._along = along
        self._posModel = posModel

        # QGraphicsItems can change this if they are in a state that should temporarily forbid brushing
        # (For example, when the slice intersection marker is in 'draggable' state.)
        self.allow_brushing = True

        self._dataShape = (0, 0)
        self._dataRect = None  #A QGraphicsRectItem (or None)
        self._offsetX = 0
        self._offsetY = 0
        self.name = name

        self._stackedImageSources = StackedImageSources(LayerStackModel())
        self._showTileOutlines = False

        # FIXME: We don't show the red 'progress pies' because they look terrible.
        #        If we could fix their timing, maybe it would be worth it.
        self._showTileProgress = False

        self._tileProvider = None
        self._dirtyIndicator = None
        self._prefetching_enabled = False

        self._swappedDefault = swapped_default
        self.reset()

        # BowWave preemptive caching
        self.setPreemptiveFetchNumber(preemptive_fetch_number)
        self._course = (1, 1)  # (along, pos or neg direction)
        self._time = self._posModel.time
        self._channel = self._posModel.channel
        self._posModel.timeChanged.connect(self._onTimeChanged)
        self._posModel.channelChanged.connect(self._onChannelChanged)
        self._posModel.slicingPositionChanged.connect(
            self._onSlicingPositionChanged)

        self._allTilesCompleteEvent = threading.Event()
        self.dirty = False

    def _onSizeChanged(self):
        self._brushingLayer = TiledImageLayer(self._tiling)

    def drawForeground(self, painter, rect):
        if self._tiling is None:
            return

        tile_nos = self._tiling.intersected(rect)

        for tileId in tile_nos:
            p = self._brushingLayer[tileId]
            if p.dataVer == p.imgVer:
                continue

            p.paint(
                painter)  #access to the underlying image patch is serialized

            ## draw tile outlines
            if self._showTileOutlines:
                # Dashed black line
                pen = QPen()
                pen.setDashPattern([5, 5])
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

                # Dashed white line
                # (offset to occupy the spaces in the dashed black line)
                pen = QPen()
                pen.setDashPattern([5, 5])
                pen.setDashOffset(5)
                pen.setColor(QColor(Qt.white))
                painter.setPen(pen)
                painter.drawRect(self._tiling.imageRects[tileId])

    def indicateSlicingPositionSettled(self, settled):
        if self._showTileProgress:
            self._dirtyIndicator.setVisible(settled)

    def drawBackground(self, painter, sceneRectF):
        if self._tileProvider is None:
            return

        tiles = self._tileProvider.getTiles(sceneRectF)
        allComplete = True
        for tile in tiles:
            #We always draw the tile, even though it might not be up-to-date
            #In ilastik's live mode, the user sees the old result while adding
            #new brush strokes on top
            #See also ilastik issue #132 and tests/lazy_test.py
            if tile.qimg is not None:
                painter.drawImage(tile.rectF, tile.qimg)
            if tile.progress < 1.0:
                allComplete = False
            if self._showTileProgress:
                self._dirtyIndicator.setTileProgress(tile.id, tile.progress)

        if allComplete:
            if self.dirty:
                self.dirty = False
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.set()
        else:
            if not self.dirty:
                self.dirty = True
                self.dirtyChanged.emit()
            self._allTilesCompleteEvent.clear()

        # preemptive fetching
        if self._prefetching_enabled:
            for through in self._bowWave(self._n_preemptive):
                self._tileProvider.prefetch(sceneRectF, through)

    def joinRenderingAllTiles(self, viewport_only=True):
        """
        Wait until all tiles in the scene have been 100% rendered.
        If sceneRectF is None, use the viewport rect.
        If sceneRectF is an invalid QRectF(), then wait for all tiles.
        Note: This is useful for testing only.  If called from the GUI thread, the GUI thread will block until all tiles are rendered!
        """
        # If this is the main thread, keep repainting (otherwise we'll deadlock).
        if threading.current_thread().name == "MainThread":
            if viewport_only:
                sceneRectF = self.views()[0].viewportRect()
            else:
                sceneRectF = QRectF()  # invalid QRectF means 'get all tiles'
            self._tileProvider.waitForTiles(sceneRectF)
        else:
            self._allTilesCompleteEvent.wait()

    def _bowWave(self, n):
        shape5d = self._posModel.shape5D
        sl5d = self._posModel.slicingPos5D
        through = [sl5d[self._along[i]] for i in xrange(3)]
        t_max = [shape5d[self._along[i]] for i in xrange(3)]

        BowWave = []

        a = self._course[0]
        for d in xrange(1, n + 1):
            m = through[a] + d * self._course[1]
            if m < t_max[a] and m >= 0:
                t = list(through)
                t[a] = m
                BowWave.append(tuple(t))
        return BowWave

    def _onSlicingPositionChanged(self, new, old):
        if (new[self._along[1] - 1] - old[self._along[1] - 1]) < 0:
            self._course = (1, -1)
        else:
            self._course = (1, 1)

    def _onChannelChanged(self, new):
        if (new - self._channel) < 0:
            self._course = (2, -1)
        else:
            self._course = (2, 1)
        self._channel = new

    def _onTimeChanged(self, new):
        if (new - self._time) < 0:
            self._course = (0, -1)
        else:
            self._course = (0, 1)
        self._time = new
Esempio n. 39
0
class RectangleSelectionAction(UserInteraction):
    """
    Select items in the scene using a Rectangle selection
    """
    def __init__(self, document, *args, **kwargs):
        UserInteraction.__init__(self, document, *args, **kwargs)
        # The initial selection at drag start
        self.initial_selection = None
        # Selection when last updated in a mouseMoveEvent
        self.last_selection = None
        # A selection rect (`QRectF`)
        self.selection_rect = None
        # Keyboard modifiers
        self.modifiers = 0

    def mousePressEvent(self, event):
        pos = event.scenePos()
        any_item = self.scene.item_at(pos)
        if not any_item and event.button() & Qt.LeftButton:
            self.modifiers = event.modifiers()
            self.selection_rect = QRectF(pos, QSizeF(0, 0))
            self.rect_item = QGraphicsRectItem(
                self.selection_rect.normalized()
            )

            self.rect_item.setPen(
                QPen(QBrush(QColor(51, 153, 255, 192)),
                     0.4, Qt.SolidLine, Qt.RoundCap)
            )

            self.rect_item.setBrush(
                QBrush(QColor(168, 202, 236, 192))
            )

            self.rect_item.setZValue(-100)

            # Clear the focus if necessary.
            if not self.scene.stickyFocus():
                self.scene.clearFocus()

            if not self.modifiers & Qt.ControlModifier:
                self.scene.clearSelection()

            event.accept()
            return True
        else:
            self.cancel(self.ErrorReason)
            return False

    def mouseMoveEvent(self, event):
        if not self.rect_item.scene():
            # Add the rect item to the scene when the mouse moves.
            self.scene.addItem(self.rect_item)
        self.update_selection(event)
        return True

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            if self.initial_selection is None:
                # A single click.
                self.scene.clearSelection()
            else:
                self.update_selection(event)
        self.end()
        return True

    def update_selection(self, event):
        """
        Update the selection rectangle from a QGraphicsSceneMouseEvent
        `event` instance.

        """
        if self.initial_selection is None:
            self.initial_selection = set(self.scene.selectedItems())
            self.last_selection = self.initial_selection

        pos = event.scenePos()
        self.selection_rect = QRectF(self.selection_rect.topLeft(), pos)

        # Make sure the rect_item does not cause the scene rect to grow.
        rect = self._bound_selection_rect(self.selection_rect.normalized())

        # Need that 0.5 constant otherwise the sceneRect will still
        # grow (anti-aliasing correction by QGraphicsScene?)
        pw = self.rect_item.pen().width() + 0.5

        self.rect_item.setRect(rect.adjusted(pw, pw, -pw, -pw))

        selected = self.scene.items(self.selection_rect.normalized(),
                                    Qt.IntersectsItemShape,
                                    Qt.AscendingOrder)

        selected = set([item for item in selected if \
                        item.flags() & Qt.ItemIsSelectable])

        if self.modifiers & Qt.ControlModifier:
            for item in selected | self.last_selection | \
                    self.initial_selection:
                item.setSelected(
                    (item in selected) ^ (item in self.initial_selection)
                )
        else:
            for item in selected.union(self.last_selection):
                item.setSelected(item in selected)

        self.last_selection = set(self.scene.selectedItems())

    def end(self):
        self.initial_selection = None
        self.last_selection = None
        self.modifiers = 0

        self.rect_item.hide()
        if self.rect_item.scene() is not None:
            self.scene.removeItem(self.rect_item)
        UserInteraction.end(self)

    def viewport_rect(self):
        """
        Return the bounding rect of the document's viewport on the scene.
        """
        view = self.document.view()
        vsize = view.viewport().size()
        viewportrect = QRect(0, 0, vsize.width(), vsize.height())
        return view.mapToScene(viewportrect).boundingRect()

    def _bound_selection_rect(self, rect):
        """
        Bound the selection `rect` to a sensible size.
        """
        srect = self.scene.sceneRect()
        vrect = self.viewport_rect()
        maxrect = srect.united(vrect)
        return rect.intersected(maxrect)
Esempio n. 40
0
class SequenceScoreFace(StaticItemFace):
    def __init__(self, dict_values_pcoc, col_height=10, col_width=10, fsize=9):
        faces.Face.__init__(self)
        self.type = "item"
        self.item = None
        self.dict_values_pcoc = dict_values_pcoc
        self.nb_values = len(dict_values_pcoc.values()[0])
        self.col_w = float(col_width)
        self.col_h = float(col_height)
        self.fsize = fsize
        self.nb_models = len(dict_values_pcoc.keys())
        self.height = (self.nb_models + 1) * self.col_h
        self.width = self.nb_values * self.col_w
        self.x_axis = False
        self.hp = []

    def draw_fun(self, x, y, val, col_width=10, col_height=10, color="gray"):
        color = get_corr_color(val)
        rect = QGraphicsRectItem(x, y, col_width, col_height, parent=self.item)
        rect.setPen(QPen(QColor('black')))
        rect.setBrush(QColor(color))

    def draw_legend(self):
        legend_h = self.height * ((self.nb_models - 1) / float(self.nb_models))
        if legend_h < 35:
            legend_h = 35
        legend_rect = QGraphicsRectItem(-20, 0, 10, legend_h, parent=self.item)
        x0 = -20
        n_cat = 6.
        for y, str_y in [(1, 1), (1 / n_cat * 5, 0.99), (1 / n_cat * 4, 0.9),
                         (1 / n_cat * 3, 0.8), (1 / n_cat * 2, 0.7),
                         (1 / n_cat * 1, 0.5), (1 / n_cat * 0, 0)]:
            y_stick = legend_h - y * legend_h
            lineItem = QGraphicsLineItem(x0 - 5,
                                         y_stick,
                                         x0,
                                         y_stick,
                                         parent=self.item)
            lineItem.setPen(QPen(QColor('black')))
            text = QGraphicsSimpleTextItem(str(str_y))
            text.setFont(QFont("Arial", self.fsize - 4))
            text.setParentItem(self.item)
            tw = text.boundingRect().width()
            th = text.boundingRect().height()
            # Center text according to masterItem size
            text.setPos(x0 - tw - 7, y_stick - th / 2)

        for (y1, y2, c) in [(1, 1 / n_cat * 5, 0.99),
                            (1 / n_cat * 5, 1 / n_cat * 4, 0.9),
                            (1 / n_cat * 4, 1 / n_cat * 3, 0.8),
                            (1 / n_cat * 3, 1 / n_cat * 2, 0.7),
                            (1 / n_cat * 2, 1 / n_cat * 1, 0.5),
                            (1 / n_cat * 1, 1 / n_cat * 0, 0)]:
            y1_stick = legend_h - y1 * legend_h
            y2_stick = legend_h - y2 * legend_h
            self.draw_fun(x0,
                          y1_stick,
                          c,
                          col_width=10,
                          col_height=y2_stick - y1_stick)

    def draw_x_axis(self):
        y0 = self.nb_models * self.col_h + 5
        lineItem = QGraphicsLineItem(self.col_w / 2,
                                     y0,
                                     self.width - self.col_w / 2,
                                     y0,
                                     parent=self.item)
        lineItem.setPen(QPen(QColor('black')))
        lineItem.setZValue(10)
        all_vals = list(range(0, self.nb_values, self.x_inter_values))
        if (self.nb_values - 1) % self.x_inter_values:
            all_vals += [self.nb_values - 1]

        hp_x = []
        if self.hp:
            for x in list(range(0, self.nb_values)):
                if self.x_values[x] in self.hp:
                    hp_x.append(x)
                    if not x in all_vals:
                        all_vals += [x]
        all_vals.sort()

        for x in all_vals:
            lineItem = QGraphicsLineItem(0, y0, 0, y0 + 6, parent=self.item)
            lineItem.setX(x * self.col_w + self.col_w / 2)
            lineItem.setPen(QPen(QColor('black')))
            lineItem.setZValue(10)
            if x in hp_x:
                text = QGraphicsSimpleTextItem("*" + str(self.x_values[x]))
                qfont = QFont("Arial", self.fsize - 1)
                #qfont.setBold(True)
                text.setFont(qfont)
            else:
                text = QGraphicsSimpleTextItem(" " + str(self.x_values[x]))
                text.setFont(QFont("Arial", self.fsize - 1))
            text.rotate(-90)
            text.setParentItem(self.item)
            text.setZValue(10)
            tw = text.boundingRect().width()
            th = text.boundingRect().height()
            # Center text according to masterItem size
            text.setPos(x * self.col_w - th / 2 + self.col_w / 2, tw + y0 + 7)

    def update_items(self):
        rect_h = self.height
        if self.x_axis:
            rect_h += 30
        self.item = QGraphicsRectItem(0, 0, self.width + 40, rect_h)
        self.item.setPen(QPen(QColor('white')))

        #X axis

        if self.x_axis:
            self.draw_x_axis()

        # Legend

        self.draw_legend()

        # Y axes and colo rect
        yi = -1
        for model in ["PCOC", "PC", "OC", "Topological", "Identical"]:
            if self.dict_values_pcoc.has_key(model):
                yi += 1
                y = yi * self.col_w

                # Y axes
                ## Stick
                yaxis = (yi + 0.5) * self.col_w
                lineItem = QGraphicsLineItem(self.width,
                                             yaxis,
                                             self.width + 5,
                                             yaxis,
                                             parent=self.item)
                lineItem.setPen(QPen(QColor('black')))
                ## Text
                text = QGraphicsSimpleTextItem(model)
                text.setFont(QFont("Arial", self.fsize - 2))
                text.setParentItem(self.item)
                tw = text.boundingRect().width()
                th = text.boundingRect().height()
                ## Center text according to masterItem size
                text.setPos(self.width + 5, yaxis - th / 2)

                # Color rect for each model
                values = self.dict_values_pcoc[model]
                for i, val in enumerate(values):
                    self.draw_fun(i * self.col_w, y, val, col_width=self.col_w)