Ejemplo n.º 1
0
 def draw_axis_overlay_y(self, context):
     graph_left = round(self.xmin * context.xfactor)
     graph_right = round(self.xmax * context.xfactor)
     for tickPos in self.ytickmarks[:-1]:
         tickY = tickPos * context.yfactor
         p1 = context.trpoint(Point(graph_left, tickY))
         p2 = context.trpoint(Point(graph_right, tickY))
         self.view.draw_line(p1, p2, PenID.AxisOverlay)
Ejemplo n.º 2
0
 def draw_axis_overlay_x(self, context):
     graph_bottom = round(self.ymin * context.yfactor)
     if graph_bottom < 0:
         graph_bottom -= self.YAXIS_EXTRA_SPACE_ON_NEGATIVE
     graph_top = round(self.ymax * context.yfactor)
     for tickPos in self.xtickmarks[:-1]:
         tickX = tickPos * context.xfactor
         p1 = context.trpoint(Point(tickX, graph_bottom))
         p2 = context.trpoint(Point(tickX, graph_top))
         self.view.draw_line(p1, p2, PenID.AxisOverlay)
Ejemplo n.º 3
0
def point_in_circle(center, radius, angle):
    # Returns the point at the edge of a circle with specified center/radius/angle
    # a/sin(A) = b/sin(B) = c/sin(C) = 2R
    # the start point is (center.x + radius, center.y) and goes counterclockwise
    angle = angle % 360
    C = radians(90)
    A = radians(angle % 90)
    B = C - A
    c = radius
    ratio = c / sin(C)
    b = ratio * sin(B)
    a = ratio * sin(A)
    if angle > 270:
        return Point(center.x + a, center.y - b)
    elif angle > 180:
        return Point(center.x - b, center.y - a)
    elif angle > 90:
        return Point(center.x - a, center.y + b)
    else:
        return Point(center.x + b, center.y + a)
Ejemplo n.º 4
0
    def draw_graph(self, context):
        if len(self.data) < 2:
            return

        points = [
            Point(x * context.xfactor, y * context.yfactor)
            for x, y in self.data
        ]

        # close the polygons and fill them.
        # The closing point depends if we have a positive graph, a negative one or a mixed up
        if self.ymin >= 0:  # positive
            yClose = round(self.ymin * context.yfactor)
        elif self.ymax < 0:  # negative
            yClose = round(self.ymax * context.yfactor)
        else:  # mixed up
            yClose = 0
        # painter.setPen(QPen(Qt.NoPen))
        xTodayfactored = self._offset_xpos(date.today().toordinal() +
                                           1) * context.xfactor
        pastPoints = [p for p in points if p.x <= xTodayfactored]
        futurePoints = [p for p in points if p.x > xTodayfactored]
        if pastPoints and futurePoints:
            meetingPoint = Point(xTodayfactored, pastPoints[-1].y)
            pastPoints.append(meetingPoint)
            futurePoints.insert(0, meetingPoint)
        else:
            meetingPoint = None
        # start with past
        if pastPoints:
            firstPoint = pastPoints[0]
            lastPoint = pastPoints[-1]
            pastPoints.append(Point(lastPoint.x, yClose))
            pastPoints.append(Point(firstPoint.x, yClose))
            self.view.draw_polygon(context.trpoints(pastPoints), None,
                                   BrushID.GraphNormal)
        if futurePoints:
            firstPoint = futurePoints[0]
            lastPoint = futurePoints[-1]
            futurePoints.append(Point(lastPoint.x, yClose))
            futurePoints.append(Point(firstPoint.x, yClose))
            self.view.draw_polygon(context.trpoints(futurePoints), None,
                                   BrushID.GraphFuture)
        if meetingPoint is not None:
            p1 = context.trpoint(Point(xTodayfactored, yClose))
            p2 = context.trpoint(meetingPoint)
            self.view.draw_line(p1, p2, PenID.TodayLine)

        self.draw_axis_overlay_y(context)
        self.draw_axis_overlay_x(context)

        # draw the main graph line. It looks better when that line is drawn after the overlay.
        self.view.draw_polygon(context.trpoints(points), PenID.Graph, None)
Ejemplo n.º 5
0
    def draw_graph(self, context):
        for x1, x2, h1, h2 in self.data:
            x1 *= context.xfactor
            x2 *= context.xfactor
            h1 *= context.yfactor
            h2 *= context.yfactor

            # Compute and fill past and future rectangles
            different_side = (h1 >= 0) != (h2 >= 0)
            past_rect = Rect(x1, 0, x2 - x1, abs(h1))
            if h2:
                future_height = abs(h2 if different_side else h2 - h1)
            else:
                future_height = 0
            future_rect = Rect(x1, 0, x2 - x1, future_height)
            if h1 >= 0:
                past_rect.bottom = h1
            else:
                past_rect.top = h1
            if h2 >= 0:
                future_rect.bottom = h2
            else:
                future_rect.top = h2
            self.view.draw_rect(context.trrect(past_rect), None,
                                BrushID.NormalBar)
            self.view.draw_rect(context.trrect(future_rect), None,
                                BrushID.FutureBar)

            # Compute and draw rect lines
            union = past_rect.united(future_rect)
            if (union.top < 0) and (union.bottom >
                                    0):  # we draw 4 sides instead of 3
                self.view.draw_rect(context.trrect(union), PenID.Bar, None)
            else:
                # One of bottom and top is 0. Use the other one. We're working with floats here,
                # comparison with 0 are hazardous, so I'm avoiding them.
                h = union.top if abs(union.top) >= abs(
                    union.bottom) else union.bottom
                points = [
                    Point(x1, 0),
                    Point(x1, h),
                    Point(x2, h),
                    Point(x2, 0)
                ]
                self.view.draw_polygon(context.trpoints(points), PenID.Bar,
                                       None)

            # draw red line
            if (h1 != 0) and (h2 != 0):
                lineY = 0 if different_side else h1
                p1 = context.trpoint(Point(x1, lineY))
                p2 = context.trpoint(Point(x2, lineY))
                context.today_line = (
                    p1, p2)  # will be drawn in draw_graph_after_axis()

        # We don't draw the X overlay in a bar graph
        self.draw_axis_overlay_y(context)
Ejemplo n.º 6
0
 def trpoint(self, p):
     x, y = p
     x += self.xoffset
     y += self.yoffset
     return Point(x, y)
Ejemplo n.º 7
0
    def draw_chart(self):
        if not hasattr(self, 'xmax'):  # we haven't computed yet
            return
        view_rect = Rect(0, 0, *self.view_size)
        data_width = self.xmax - self.xmin
        data_height = self.ymax - self.ymin
        y_labels_width = max(
            self.view.text_size(label['text'], FontID.AxisLabel)[0]
            for label in self.ylabels)
        labels_height = self.view.text_size('', FontID.AxisLabel)[1]
        title = "{} ({})".format(self.title, self.currency.code)
        title_width, title_height = self.view.text_size(title, FontID.Title)
        titley = view_rect.h - self.TITLE_PADDING - title_height
        graphx = y_labels_width + self.PADDING
        graphy = labels_height + self.PADDING
        graph_width = view_rect.w - graphx - self.PADDING
        graph_height = view_rect.h - graphy - title_height - self.TITLE_PADDING
        graph_rect = Rect(graphx, graphy, graph_width, graph_height)
        xfactor = graph_width / data_width
        yfactor = graph_height / data_height
        graph_left = round(self.xmin * xfactor)
        graph_bottom = round(self.ymin * yfactor)
        if graph_bottom < 0:
            # We have a graph with negative values and we need some extra space to draw the lowest values
            graph_bottom -= self.YAXIS_EXTRA_SPACE_ON_NEGATIVE
        graph_top = round(self.ymax * yfactor)
        xoffset = graph_rect.left
        yoffset = -(graph_bottom - graph_rect.y)
        context = GraphContext(xfactor, yfactor, xoffset, yoffset)

        self.draw_graph(context)

        # X/Y axis
        p1 = context.trpoint(Point(0, graph_bottom))
        p2 = context.trpoint(Point(graph_width, graph_bottom))
        p3 = context.trpoint(Point(0, graph_top))
        self.view.draw_line(p1, p2, PenID.Axis)
        self.view.draw_line(p1, p3, PenID.Axis)
        if graph_bottom < 0:
            p1 = context.trpoint(Point(0, 0))
            p2 = context.trpoint(Point(graph_width, 0))
            self.view.draw_line(p1, p2, PenID.Axis)

        # X tickmarks
        tickBottomY = graph_bottom - self.TICKMARKS_LENGTH
        for tickPos in self.xtickmarks:
            tickX = tickPos * xfactor
            p1 = context.trpoint(Point(tickX, graph_bottom))
            p2 = context.trpoint(Point(tickX, tickBottomY))
            self.view.draw_line(p1, p2, PenID.Axis)

        # Y tickmarks
        tickLeftX = graph_left - self.TICKMARKS_LENGTH
        for tickPos in self.ytickmarks:
            tickY = tickPos * yfactor
            p1 = context.trpoint(Point(graph_left, tickY))
            p2 = context.trpoint(Point(tickLeftX, tickY))
            self.view.draw_line(p1, p2, PenID.Axis)

        # X Labels
        labelY = graph_bottom - labels_height - self.XLABELS_PADDING
        for label in self.xlabels:
            labelText = label['text']
            labelWidth = self.view.text_size(labelText, FontID.AxisLabel)[0]
            labelX = (label['pos'] * xfactor) - (labelWidth / 2)
            text_rect = context.trrect(
                Rect(labelX, labelY, labelWidth, labels_height))
            self.view.draw_text(labelText, text_rect, FontID.AxisLabel)

        # Y Labels
        for label in self.ylabels:
            labelText = label['text']
            labelWidth = self.view.text_size(labelText, FontID.AxisLabel)[0]
            labelX = graph_left - self.YLABELS_PADDING - labelWidth
            labelY = (label['pos'] * yfactor) - (labels_height / 2)
            text_rect = context.trrect(
                Rect(labelX, labelY, labelWidth, labels_height))
            self.view.draw_text(labelText, text_rect, FontID.AxisLabel)

        # Title
        self.view.draw_text(title, Rect(0, titley, view_rect.w, title_height),
                            FontID.Title)

        self.draw_graph_after_axis(context)
Ejemplo n.º 8
0
 def mouse_move(self, x, y):
     # only call when the mouse button is currently down
     self._last_mouse_pos = Point(x, y)
     self.view.refresh()
Ejemplo n.º 9
0
 def mouse_down(self, x, y):
     self._last_mouse_down = Point(x, y)
     self._last_mouse_pos = Point(x, y)
     self.view.refresh()