Esempio n. 1
0
    def __updatePen(self):
        self.prepareGeometryChange()
        self.__boundingRect = None
        if self.__dynamic:
            if self.__dynamicEnabled:
                color = QColor(0, 150, 0, 150)
            else:
                color = QColor(150, 0, 0, 150)

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

        if self.__state & LinkItem.Empty:
            pen_style = Qt.DashLine
        else:
            pen_style = Qt.SolidLine

        normal.setStyle(pen_style)
        hover.setStyle(pen_style)

        if self.hover:
            pen = hover
        else:
            pen = normal

        self.curveItem.setPen(pen)
Esempio n. 2
0
    def __updatePen(self):
        self.prepareGeometryChange()
        self.__boundingRect = None
        if self.__dynamic:
            if self.__dynamicEnabled:
                color = QColor(0, 150, 0, 150)
            else:
                color = QColor(150, 0, 0, 150)

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

        if self.__state & LinkItem.Empty:
            pen_style = Qt.DashLine
        else:
            pen_style = Qt.SolidLine

        normal.setStyle(pen_style)
        hover.setStyle(pen_style)

        if self.hover:
            pen = hover
        else:
            pen = normal

        self.curveItem.setPen(pen)
Esempio n. 3
0
 def shape(self):
     if self.__shape is None:
         path = self.curvePath()
         pen = QPen(self.pen())
         pen.setWidthF(max(pen.widthF(), 7.0))
         pen.setStyle(Qt.SolidLine)
         self.__shape = stroke_path(path, pen)
     return self.__shape
Esempio n. 4
0
 def shape(self):
     if self.__shape is None:
         path = self.curvePath()
         pen = QPen(self.pen())
         pen.setWidthF(max(pen.widthF(), 7.0))
         pen.setStyle(Qt.SolidLine)
         self.__shape = stroke_path(path, pen)
     return self.__shape
def make_pen(brush=Qt.black, width=1, style=Qt.SolidLine,
             cap_style=Qt.SquareCap, join_style=Qt.BevelJoin,
             cosmetic=False):
    pen = QPen(brush)
    pen.setWidth(width)
    pen.setStyle(style)
    pen.setCapStyle(cap_style)
    pen.setJoinStyle(join_style)
    pen.setCosmetic(cosmetic)
    return pen
    def __init__(self, parent):
        super().__init__(parent)
        self.__range = None  # type: Tuple[float]
        self.__value_range = None  # type: Tuple[float]
        self.__model_output = None  # type: float
        self.__base_value = None  # type: float

        self.__group = QGraphicsItemGroup(self)
        low_color, high_color = QColor(*RGB_LOW), QColor(*RGB_HIGH)

        self.__low_item = QGraphicsRectItem()
        self.__low_item.setPen(QPen(low_color))
        self.__low_item.setBrush(QBrush(low_color))

        self.__high_item = QGraphicsRectItem()
        self.__high_item.setPen(QPen(high_color))
        self.__high_item.setBrush(QBrush(high_color))

        self.__low_cover_item = LowCoverItem()
        self.__high_cover_item = HighCoverItem()

        pen = QPen(IndicatorItem.COLOR)
        pen.setStyle(Qt.DashLine)
        pen.setWidth(1)
        self.__model_output_line = QGraphicsLineItem()
        self.__model_output_line.setPen(pen)
        self.__base_value_line = QGraphicsLineItem()
        self.__base_value_line.setPen(pen)

        self.__model_output_ind = IndicatorItem("Model prediction: {}")
        self.__base_value_ind = IndicatorItem("Base value: {}\nThe average "
                                              "prediction for selected class.")

        self.__group.addToGroup(self.__low_item)
        self.__group.addToGroup(self.__high_item)
        self.__group.addToGroup(self.__low_cover_item)
        self.__group.addToGroup(self.__high_cover_item)
        self.__group.addToGroup(self.__model_output_line)
        self.__group.addToGroup(self.__base_value_line)
        self.__group.addToGroup(self.__model_output_ind)
        self.__group.addToGroup(self.__base_value_ind)

        self.__low_parts = []  # type: List[LowPartItem]
        self.__high_parts = []  # type: List[HighPartItem]
Esempio n. 7
0
def update_pen(pen,
               brush=None,
               width=None,
               style=None,
               cap_style=None,
               join_style=None,
               cosmetic=None):
    pen = QPen(pen)
    if brush is not None:
        pen.setBrush(QBrush(brush))
    if width is not None:
        pen.setWidth(width)
    if style is not None:
        pen.setStyle(style)
    if cap_style is not None:
        pen.setCapStyle(cap_style)
    if join_style is not None:
        pen.setJoinStyle(join_style)
    if cosmetic is not None:
        pen.setCosmetic(cosmetic)
    return pen
class GraphAttributes:
    """
    Creates entire graph of explanations, paint function is the main one, it delegates painting of attributes to draw_attribute, 
    header and scale are dealt with in draw_header_footer. Header is fixed in size.

    Parameters
    ----------
    scene: QGraphicsScene
        scene to add elements to
    num_of_atr : int
        number of attributes to plot
    space: int
        space between columns with atr names, values
    offset_y : int
        distance between the line border of attribute box plot and the box itself
    rect_height : int
        height of a rectangle, representing score of the attribute
    """
    def __init__(self,
                 scene,
                 num_of_atr=3,
                 space=35,
                 offset_y=10,
                 rect_height=40):
        self.scene = scene
        self.num_of_atr = num_of_atr
        self.space = space
        self.graph_space = 80
        self.offset_y = offset_y
        self.black_pen = QPen(Qt.black, 2)
        self.gray_pen = QPen(Qt.gray, 1)
        self.light_gray_pen = QPen(QColor("#DFDFDF"), 1)
        self.light_gray_pen.setStyle(Qt.DashLine)
        self.brush = QBrush(QColor(0x33, 0x88, 0xff, 0xc0))
        self.blue_pen = QPen(QBrush(QColor(0x33, 0x00, 0xff)), 2)
        """placeholders"""
        self.rect_height = rect_height
        self.max_contrib = None
        self.atr_area_h = None
        self.atr_area_w = None
        self.scale = None

    def get_needed_offset(self, explanations):
        max_n = 0
        word = ""
        max_v = 0
        val = ""
        for e in explanations:
            if max_n < len(str(e._metas[0])):
                word = str(e._metas[0])
                max_n = len(str(e._metas[0]))
            if max_v < len(str(e._metas[1])):
                val = str(e._metas[1])
                max_v = len(str(e._metas[1]))
        w = QGraphicsSimpleTextItem(word, None)
        v = QGraphicsSimpleTextItem(val, None)
        return w.boundingRect().width(), v.boundingRect().width()

    def paint(self, wp, explanations=None, header_h=100):
        """
        Coordinates drawing
        Parameters
        ----------
        wp : QWidget
            current viewport
        explanations : Orange.data.table
            data table with name, value, score and error of attributes to plot
        header_h : int
            space to be left on the top and the bottom of graph for header and scale
        """
        self.name_w, self.val_w = self.get_needed_offset(explanations)
        self.offset_left = self.space + self.name_w + \
            self.space + self.val_w + self.graph_space
        self.offset_right = self.graph_space + 50

        self.atr_area_h = wp.height() / 2 - header_h
        self.atr_area_w = (wp.width() - self.offset_left -
                           self.offset_right) / 2

        coords = self.split_boxes_area(self.atr_area_h, self.num_of_atr,
                                       header_h)
        self.max_contrib = np.max(
            abs(explanations.X[:, 0]) + explanations.X[:, 1])
        self.unit = self.get_scale()
        unit_pixels = np.floor(self.atr_area_w /
                               (self.max_contrib / self.unit))
        self.scale = unit_pixels / self.unit

        self.draw_header_footer(wp, header_h, unit_pixels,
                                coords[self.num_of_atr - 1], coords[0])

        for y, e in zip(coords, explanations[:self.num_of_atr]):
            self.draw_attribute(y,
                                atr_name=str(e._metas[0]),
                                atr_val=str(e._metas[1]),
                                atr_contrib=e._x[0],
                                error=e._x[1])

    def draw_header_footer(self,
                           wp,
                           header_h,
                           unit_pixels,
                           last_y,
                           first_y,
                           marking_len=15):
        """header"""
        max_x = self.max_contrib * self.scale

        atr_label = QGraphicsSimpleTextItem("Name", None)
        val_label = QGraphicsSimpleTextItem("Value", None)
        score_label = QGraphicsSimpleTextItem("Score", None)

        font = score_label.font()
        font.setBold(True)
        font.setPointSize(13)
        atr_label.setFont(font)
        val_label.setFont(font)
        score_label.setFont(font)

        white_pen = QPen(Qt.white, 3)

        fix = self.offset_left + self.atr_area_w

        self.place_left(val_label, -self.atr_area_h - header_h * 0.85)
        self.place_left_edge(atr_label, -self.atr_area_h - header_h * 0.85)
        self.place_right(score_label, -self.atr_area_h - header_h * 0.85)

        self.scene.addLine(-max_x + fix, -self.atr_area_h - header_h,
                           max_x + fix, -self.atr_area_h - header_h, white_pen)
        """footer"""
        line_y = max(first_y + wp.height() + header_h / 2 - 10,
                     last_y + header_h / 2 + self.rect_height)
        self.scene.addLine(-max_x + fix, line_y, max_x + fix, line_y,
                           self.black_pen)

        previous = 0
        recomended_d = 35
        for i in range(0, int(self.max_contrib / self.unit) + 1):
            x = unit_pixels * i
            """grid lines"""
            self.scene.addLine(x + fix, first_y, x + fix, line_y,
                               self.light_gray_pen)
            self.scene.addLine(-x + fix, first_y, -x + fix, line_y,
                               self.light_gray_pen)

            self.scene.addLine(x + fix, line_y, x + fix, line_y + marking_len,
                               self.black_pen)
            self.scene.addLine(-x + fix, line_y, -x + fix,
                               line_y + marking_len, self.black_pen)
            """markings on the ruler"""
            if x + fix - previous > recomended_d:
                self.place_centered(self.format_marking(i * self.unit),
                                    x + fix, line_y + marking_len + 5)
                if x > 0:
                    self.place_centered(self.format_marking(-i * self.unit),
                                        -x + fix, line_y + marking_len + 5)
                previous = x + fix

    def format_marking(self, x, places=2):
        return QGraphicsSimpleTextItem(str(round(x, places)), None)

    def get_scale(self):
        """figures out on what scale is max score (1, .1, .01)
        TESTING NEEDED, maybe something more elegant.
        """
        if self.max_contrib > 10:
            return 10
        elif self.max_contrib > 1:
            return 1
        elif self.max_contrib > 0.1:
            return 0.1
        else:
            return 0.01

    def draw_attribute(self, y, atr_name, atr_val, atr_contrib, error):
        fix = (self.offset_left + self.atr_area_w)
        """vertical line where x = 0"""
        self.scene.addLine(0 + fix, y, 0 + fix, y + self.rect_height,
                           self.black_pen)
        """borders"""
        self.scene.addLine(self.offset_left, y, fix + self.atr_area_w, y,
                           self.gray_pen)
        self.scene.addLine(self.offset_left, y + self.rect_height,
                           fix + self.atr_area_w, y + self.rect_height,
                           self.gray_pen)

        if atr_name is not None and atr_val is not None and atr_contrib is not None:
            atr_contrib_x = atr_contrib * self.scale + fix
            error_x = error * self.scale

            padded_rect = self.rect_height - 2 * self.offset_y
            len_rec = 2 * error_x
            graphed_rect = QGraphicsRectItem(atr_contrib_x - error_x,
                                             y + self.offset_y, len_rec,
                                             padded_rect)
            graphed_rect.setBrush(self.brush)
            graphed_rect.setPen(QPen(Qt.NoPen))
            self.scene.addItem(graphed_rect)
            """vertical line marks calculated contribution of attribute"""
            self.atr_line = self.scene.addLine(
                atr_contrib_x, y + self.offset_y + 2, atr_contrib_x,
                y + self.rect_height - self.offset_y - 2, self.blue_pen)
            """atr name and value on the left"""
            self.place_left(QGraphicsSimpleTextItem(atr_val, None),
                            y + self.rect_height / 2)
            self.place_left_edge(QGraphicsSimpleTextItem(atr_name, None),
                                 y + self.rect_height / 2)
            """atr score on the right"""
            self.place_right(self.format_marking(atr_contrib),
                             y + self.rect_height / 2)

    def place_left(self, text, y):
        """places text to the left"""
        self.place_centered(text,
                            2 * self.space + self.name_w + self.val_w / 2, y)

    def place_left_edge(self, text, y):
        """places text more left than place_left"""
        self.scene.addLine(0, y, 0 - 10, y + 2, QPen(Qt.white, 0))
        self.place_centered(text, self.space + self.name_w / 2, y)

    def place_right(self, text, y):
        x = self.offset_left + 2 * self.atr_area_w + self.graph_space + 15
        self.scene.addLine(x, y, x, y + 2, QPen(Qt.white, 0))
        self.place_centered(
            text, self.offset_left + 2 * self.atr_area_w + self.graph_space, y)

    def place_centered(self, text, x, y):
        """centers the text around given coordinates"""
        to_center = text.boundingRect().width() / 2
        text.setPos(x - to_center, y)
        self.scene.addItem(text)

    def split_boxes_area(self, h, num_boxes, header_h):
        """calculates y coordinates of boxes to be plotted, calculates rect_height
        Parameters
        ---------
        h : int
            height of area
        num_boxes : int
            number of boxes to fill our area
        header_h : int
            height of header
        Returns:
            list y_coordinates
        """
        return [(-h + i * self.rect_height) for i in range(num_boxes)]
class GraphAttributes:
    """
    Creates entire graph of explanations, paint function is the main one, it delegates painting of attributes to draw_attribute, 
    header and scale are dealt with in draw_header_footer. Header is fixed in size.

    Parameters
    ----------
    scene: QGraphicsScene
        scene to add elements to
    num_of_atr : int
        number of attributes to plot
    space: int
        space between columns with atr names, values
    offset_y : int
        distance between the line border of attribute box plot and the box itself
    rect_height : int
        height of a rectangle, representing score of the attribute
    """

    def __init__(self, scene, num_of_atr=3, space=35, offset_y=10, rect_height=40):
        self.scene = scene
        self.num_of_atr = num_of_atr
        self.space = space
        self.graph_space = 80
        self.offset_y = offset_y
        self.black_pen = QPen(Qt.black, 2)
        self.gray_pen = QPen(Qt.gray, 1)
        self.light_gray_pen = QPen(QColor("#DFDFDF"), 1)
        self.light_gray_pen.setStyle(Qt.DashLine)
        self.brush = QBrush(QColor(0x33, 0x88, 0xff, 0xc0))
        self.blue_pen = QPen(QBrush(QColor(0x33, 0x00, 0xff)), 2)
        """placeholders"""
        self.rect_height = rect_height
        self.max_contrib = None
        self.atr_area_h = None
        self.atr_area_w = None
        self.scale = None

    def get_needed_offset(self, explanations):
        max_n = 0
        word = ""
        max_v = 0
        val = ""
        for e in explanations:
            if max_n < len(str(e._metas[0])):
                word = str(e._metas[0])
                max_n = len(str(e._metas[0]))
            if max_v < len(str(e._metas[1])):
                val = str(e._metas[1])
                max_v = len(str(e._metas[1]))
        w = QGraphicsSimpleTextItem(word, None)
        v = QGraphicsSimpleTextItem(val, None)
        return w.boundingRect().width(), v.boundingRect().width()

    def paint(self, wp, explanations=None, header_h=100):
        """
        Coordinates drawing
        Parameters
        ----------
        wp : QWidget
            current viewport
        explanations : Orange.data.table
            data table with name, value, score and error of attributes to plot
        header_h : int
            space to be left on the top and the bottom of graph for header and scale
        """
        self.name_w, self.val_w = self.get_needed_offset(explanations)
        self.offset_left = self.space + self.name_w + \
            self.space + self.val_w + self.graph_space
        self.offset_right = self.graph_space + 50

        self.atr_area_h = wp.height()/2 - header_h
        self.atr_area_w = (wp.width() - self.offset_left -
                           self.offset_right) / 2

        coords = self.split_boxes_area(
            self.atr_area_h, self.num_of_atr, header_h)
        self.max_contrib = np.max(
            abs(explanations.X[:, 0]) + explanations.X[:, 1])
        self.unit = self.get_scale()
        unit_pixels = np.floor(self.atr_area_w/(self.max_contrib/self.unit))
        self.scale = unit_pixels / self.unit

        self.draw_header_footer(
            wp, header_h, unit_pixels, coords[self.num_of_atr - 1], coords[0])

        for y, e in zip(coords, explanations[:self.num_of_atr]):
            self.draw_attribute(y, atr_name=str(e._metas[0]), atr_val=str(
                e._metas[1]), atr_contrib=e._x[0], error=e._x[1])

    def draw_header_footer(self, wp, header_h, unit_pixels, last_y, first_y, marking_len=15):
        """header"""
        max_x = self.max_contrib * self.scale

        atr_label = QGraphicsSimpleTextItem("Name", None)
        val_label = QGraphicsSimpleTextItem("Value", None)
        score_label = QGraphicsSimpleTextItem("Score", None)

        font = score_label.font()
        font.setBold(True)
        font.setPointSize(13)
        atr_label.setFont(font)
        val_label.setFont(font)
        score_label.setFont(font)

        white_pen = QPen(Qt.white, 3)

        fix = self.offset_left + self.atr_area_w

        self.place_left(val_label, -self.atr_area_h - header_h*0.85)
        self.place_left_edge(atr_label, -self.atr_area_h - header_h*0.85)
        self.place_right(score_label, -self.atr_area_h - header_h*0.85)

        self.scene.addLine(-max_x + fix, -self.atr_area_h - header_h,
                           max_x + fix, -self.atr_area_h - header_h, white_pen)

        """footer"""
        line_y = max(first_y + wp.height() + header_h/2 - 10,
                     last_y + header_h/2 + self.rect_height)
        self.scene.addLine(-max_x + fix, line_y, max_x +
                           fix, line_y, self.black_pen)

        previous = 0
        recomended_d = 35
        for i in range(0, int(self.max_contrib / self.unit) + 1):
            x = unit_pixels * i
            """grid lines"""
            self.scene.addLine(x + fix, first_y, x + fix,
                               line_y, self.light_gray_pen)
            self.scene.addLine(-x + fix, first_y, -x + fix,
                               line_y, self.light_gray_pen)

            self.scene.addLine(x + fix, line_y, x + fix, line_y +
                               marking_len, self.black_pen)
            self.scene.addLine(-x + fix, line_y, -x + fix, line_y +
                               marking_len, self.black_pen)
            """markings on the ruler"""
            if x + fix - previous > recomended_d:
                self.place_centered(self.format_marking(
                    i*self.unit), x + fix, line_y + marking_len + 5)
                if x > 0:
                    self.place_centered(
                        self.format_marking(-i*self.unit), -x + fix, line_y + marking_len + 5)
                previous = x + fix

    def format_marking(self, x, places=2):
        return QGraphicsSimpleTextItem(str(round(x, places)), None)

    def get_scale(self):
        """figures out on what scale is max score (1, .1, .01)
        TESTING NEEDED, maybe something more elegant.
        """
        if self.max_contrib > 10:
            return 10
        elif self.max_contrib > 1:
            return 1
        elif self.max_contrib > 0.1:
            return 0.1
        else:
            return 0.01

    def draw_attribute(self, y, atr_name, atr_val, atr_contrib, error):
        fix = (self.offset_left + self.atr_area_w)
        """vertical line where x = 0"""
        self.scene.addLine(0 + fix, y, 0 + fix, y +
                           self.rect_height, self.black_pen)
        """borders"""
        self.scene.addLine(self.offset_left,
                           y, fix + self.atr_area_w, y, self.gray_pen)
        self.scene.addLine(self.offset_left, y + self.rect_height,
                           fix + self.atr_area_w, y + self.rect_height, self.gray_pen)

        if atr_name is not None and atr_val is not None and atr_contrib is not None:
            atr_contrib_x = atr_contrib * self.scale + fix
            error_x = error * self.scale

            padded_rect = self.rect_height - 2 * self.offset_y
            len_rec = 2 * error_x
            graphed_rect = QGraphicsRectItem(
                atr_contrib_x - error_x, y + self.offset_y, len_rec, padded_rect)
            graphed_rect.setBrush(self.brush)
            graphed_rect.setPen(QPen(Qt.NoPen))
            self.scene.addItem(graphed_rect)
            """vertical line marks calculated contribution of attribute"""
            self.atr_line = self.scene.addLine(atr_contrib_x, y + self.offset_y + 2, atr_contrib_x,
                                               y + self.rect_height - self.offset_y - 2, self.blue_pen)

            """atr name and value on the left"""
            self.place_left(QGraphicsSimpleTextItem(
                atr_val, None), y + self.rect_height/2)
            self.place_left_edge(QGraphicsSimpleTextItem(
                atr_name, None), y + self.rect_height/2)

            """atr score on the right"""
            self.place_right(self.format_marking(
                atr_contrib), y + self.rect_height/2)

    def place_left(self, text, y):
        """places text to the left"""
        self.place_centered(text, 2 * self.space +
                            self.name_w + self.val_w/2, y)

    def place_left_edge(self, text, y):
        """places text more left than place_left"""
        self.scene.addLine(0, y, 0 - 10, y + 2, QPen(Qt.white, 0))
        self.place_centered(text, self.space + self.name_w/2, y)

    def place_right(self, text, y):
        x = self.offset_left + 2 * self.atr_area_w + self.graph_space + 15
        self.scene.addLine(x, y, x, y + 2, QPen(Qt.white, 0))
        self.place_centered(text, self.offset_left + 2 *
                            self.atr_area_w + self.graph_space, y)

    def place_centered(self, text, x, y):
        """centers the text around given coordinates"""
        to_center = text.boundingRect().width()/2
        text.setPos(x - to_center, y)
        self.scene.addItem(text)

    def split_boxes_area(self, h, num_boxes, header_h):
        """calculates y coordinates of boxes to be plotted, calculates rect_height
        Parameters
        ---------
        h : int
            height of area
        num_boxes : int
            number of boxes to fill our area
        header_h : int
            height of header
        Returns:
            list y_coordinates
        """
        return [(-h + i*self.rect_height) for i in range(num_boxes)]