Example #1
0
 def drawItem(self, item, painter, option):
     """
     Draws the inputed item as a bar graph.
     
     :param      item    | <XChartDatasetItem>
                 painter | <QPainter>
                 option  | <QStyleOptionGraphicsItem>
     """
     dataset = item.dataset()
     
     painter.save()
     painter.setRenderHint(painter.Antialiasing)
     
     pen = QPen(dataset.color())
     pen.setWidth(0.75)
     painter.setPen(pen)
     
     for path in item.buildData('subpaths', []):
         gradient = QLinearGradient()
         
         clr = QColor(dataset.color())
         clr.setAlpha(220)
         
         gradient.setColorAt(0.0,  clr.lighter(180))
         gradient.setColorAt(0.1,  clr.lighter(160))
         gradient.setColorAt(0.25, clr.lighter(140))
         gradient.setColorAt(1.0,  clr.lighter(125))
         
         if self.orientation() == Qt.Vertical:
             gradient.setStart(0, path.boundingRect().bottom())
             gradient.setFinalStop(0, path.boundingRect().top())
         else:
             gradient.setStart(path.boundingRect().left(), 0)
             gradient.setFinalStop(path.boundingRect().right(), 0)
         
         painter.setBrush(gradient)
         painter.drawPath(path)
     
     painter.restore()
Example #2
0
    def drawItem(self, item, painter, option):
        """
        Draws the inputed item as a bar graph.
        
        :param      item    | <XChartDatasetItem>
                    painter | <QPainter>
                    option  | <QStyleOptionGraphicsItem>
        """
        dataset = item.dataset()

        painter.save()
        painter.setRenderHint(painter.Antialiasing)

        pen = QPen(dataset.color())
        pen.setWidth(0.75)
        painter.setPen(pen)

        for path in item.buildData('subpaths', []):
            gradient = QLinearGradient()

            clr = QColor(dataset.color())
            clr.setAlpha(220)

            gradient.setColorAt(0.0, clr.lighter(180))
            gradient.setColorAt(0.1, clr.lighter(160))
            gradient.setColorAt(0.25, clr.lighter(140))
            gradient.setColorAt(1.0, clr.lighter(125))

            if self.orientation() == Qt.Vertical:
                gradient.setStart(0, path.boundingRect().bottom())
                gradient.setFinalStop(0, path.boundingRect().top())
            else:
                gradient.setStart(path.boundingRect().left(), 0)
                gradient.setFinalStop(path.boundingRect().right(), 0)

            painter.setBrush(gradient)
            painter.drawPath(path)

        painter.restore()
Example #3
0
class XChartWidgetItem(QGraphicsPathItem):
    def __init__(self):
        super(XChartWidgetItem, self).__init__()

        self.setAcceptHoverEvents(True)

        # set default information
        self._chartType = None
        self._pieCenter = QPointF(0, 0)
        self._subpaths = []
        self._keyColors = {}
        self._ellipses = []
        self._keyToolTips = {}
        self._showPointsInLine = True
        self._shaded = True
        self._dragData = {}
        self._radius = 6

        self._title = ''
        self._color = self.randomColor()
        self._alternateColor = self._color.lighter(140)
        self._points = []
        self._barSize = 30
        self._orientation = Qt.Horizontal
        self._pieAxis = Qt.YAxis

        self._pointRadius = 6
        self._horizontalOffset = 0
        self._verticalOffset = 0

        self._hoveredPath = None
        self._dirty = False
        self._buildData = {}

    def addPoint(self, x, y):
        """
        Adds a new chart point to this item.
        
        :param      x | <variant>
                    y | <variant>
        """
        self._points.append((x, y))
        self._dirty = True

    def alternateColor(self):
        """
        Returns the alternate color for this item.
        
        :return     <QColor>
        """
        return QColor(self._alternateColor)

    def barSize(self):
        """
        Returns the size that the bar chart should be rendered with.
        
        :return     <int>
        """
        return self._barSize

    def chartType(self):
        """
        Returns the chart type for this item.  If no type is explicitely set,
        then the scenes chart type will be utilized.
        
        :return     <XChartScene.Type>
        """
        if (self._chartType):
            return self._chartType

        scene = self.scene()
        if (not scene):
            return 0

        return scene.chartType()

    def clear(self):
        """
        Clears the chart points from this item.
        """
        self._points = []
        self._dirty = True

    def color(self):
        """
        Returns the primary color for this item.
        
        :return     <QColor>
        """
        return QColor(self._color)

    def dragData(self, x=None, y=None):
        """
        Returns any drag information that will be used from this chart item.
        
        :return     <QMimeData> || None
        """
        # look for specific drag information for this item
        first = (x, y)
        second = (x, None)
        third = (None, y)
        fourth = (None, None)

        for key in (first, second, third, fourth):
            data = self._dragData.get(key)
            if data:
                return data

        return None

    def hasCustomType(self):
        """
        Returns true if this item defines its own chart type.
        
        :return     <bool>
        """
        return self._chartType != None

    def horizontalRuler(self):
        """
        Returns the horizontal ruler for this widget item.
        
        :return     <projexui.widgets.xchartwidget.XChartRuler> || None
        """
        if (not self.scene()):
            return None
        return self.scene().horizontalRuler()

    def horizontalOffset(self):
        """
        Returns the horizontal offset for this item.
        
        :return     <int>
        """
        return self._horizontalOffset

    def hoverMoveEvent(self, event):
        """
        Tracks whether or not this item is being hovered.
        
        :param      event | <QEvent>
        """
        point = event.pos()

        found_key = ''
        found = None
        for key, value, subpath in self._subpaths:
            if subpath.contains(point):
                found = subpath
                found_key = key
                break

        if found:
            # update the tooltip
            tip = self.keyToolTip(found_key)
            if (tip):
                widget = self.scene().chartWidget()
                anchor = XPopupWidget.Anchor.RightCenter

                # show the popup widget
                XPopupWidget.showToolTip(tip,
                                         anchor=anchor,
                                         parent=widget,
                                         foreground=self.color().darker(120),
                                         background=self.alternateColor())

            if (found != self._hoveredPath):
                self._hoveredPath = found
                self.update()

    def hoverLeaveEvent(self, event):
        """
        Tracks whether or not this item is being hovered.
        
        :param      event | <QEvent>
        """
        super(XChartWidgetItem, self).hoverEnterEvent(event)

        self._hoveredPath = None
        self.update()

    def isShaded(self):
        """
        Returns the shaded state for this item.
        
        :return     <bool>
        """
        return self._shaded

    def keyColor(self, key):
        """
        Returns a color for the inputed key (used in pie charts).
        
        :param      key | <str>
        
        :return     <QColor>
        """
        self._keyColors.setdefault(nativestring(key), self.color())
        return self._keyColors[nativestring(key)]

    def keyToolTip(self, key):
        """
        Returns the tool tip for this key.
        
        :param      key | <str>
        
        :return     <str>
        """
        return self._keyToolTips.get(nativestring(key), '')

    def mousePressEvent(self, event):
        """
        Creates the drag event for this item.
        
        :param      event | <QMousePressEvent>
        """
        near_x, near_y = self.nearestPoint(event.pos())

        data = self.dragData(x=near_x, y=near_y)
        self.startDrag(data)

        super(XChartWidgetItem, self).mousePressEvent(event)

    def nearestPoint(self, pos):
        """
        Returns the nearest graphing point for this item based on the
        inputed graph position.
        
        :param      pos | <QPoint>
        
        :return     (<variant> x, <variant> y)
        """
        # lookup subpaths
        for x, y, path in self._subpaths:
            if path.contains(pos):
                return (x, y)

        return (None, None)

    def orientation(self):
        """
        Returns the orienatation for this item (used in bar charts).
        
        :return     <Qt.Orienation>
        """
        return self._orientation

    def paint(self, painter, option, widget):
        """
        Draws this item with the inputed painter.
        
        :param      painter | <QPainter>
                    rect    | <QRect>
        """
        if self._dirty:
            self.rebuild()

        scene = self.scene()
        if not scene:
            return

        grid = scene.gridRect()
        typ = self.chartType()

        # draw the line chart
        if typ == XChartScene.Type.Line:
            painter.setRenderHint(painter.Antialiasing)

            # draw the path area
            area = self._buildData.get('path_area')
            if area and self.isShaded():
                clr = QColor(self.color())

                clr.setAlpha(120)
                painter.setPen(Qt.NoPen)
                painter.setBrush(clr)

                painter.drawPath(area)

            # draw the line data
            pen = QPen(self.color())
            pen.setWidth(2)

            painter.setPen(pen)
            painter.setBrush(Qt.NoBrush)

            painter.drawPath(self.path())

            if (self.showPointsInLine()):
                palette = QApplication.palette()
                pen = QPen(palette.color(palette.Base))
                pen.setWidth(2)

                painter.setBrush(self.color())
                painter.setPen(pen)

                for point in self._ellipses:
                    painter.drawEllipse(point, self.pointRadius(),
                                        self.pointRadius())

        # draw a bar chart
        elif typ == XChartScene.Type.Bar:
            painter.setRenderHint(painter.Antialiasing)

            pen = QPen(self.color())
            pen.setWidth(1)
            painter.setPen(pen)

            for key, value, sub_path in self._subpaths:
                gradient = QLinearGradient()

                clr = QColor(self.color())

                if (sub_path != self._hoveredPath):
                    clr.setAlpha(130)

                gradient.setColorAt(0.0, clr.lighter(140))
                gradient.setColorAt(0.1, clr.lighter(120))
                gradient.setColorAt(0.25, clr.lighter(110))
                gradient.setColorAt(1.0, clr.lighter(105))

                if (self.orientation() == Qt.Horizontal):
                    gradient.setStart(0, sub_path.boundingRect().top())
                    gradient.setFinalStop(0, sub_path.boundingRect().bottom())
                else:
                    gradient.setStart(sub_path.boundingRect().left(), 0)
                    gradient.setFinalStop(sub_path.boundingRect().right(), 0)

                painter.setBrush(gradient)
                painter.drawPath(sub_path)

        # draw a simple pie chart (calculated by scene)
        elif typ == XChartScene.Type.Pie:
            painter.setRenderHint(painter.Antialiasing)

            center = self.pieCenter()
            radius = self.radius()

            for key, value, sub_path in self._subpaths:
                clr = self.keyColor(key)

                gradient = QRadialGradient(QPointF(0, 0), radius)

                a = QColor(clr.lighter(140))
                b = QColor(clr.lighter(110))

                a.setAlpha(40)
                b.setAlpha(80)

                # look for mouse over
                if (sub_path == self._hoveredPath):
                    a.setAlpha(100)
                    b.setAlpha(200)

                gradient.setColorAt(0, a)
                gradient.setColorAt(1, b)

                pen = QPen(clr)
                pen.setWidth(1)

                painter.setBrush(gradient)
                painter.setPen(pen)

                painter.drawPath(sub_path)

    def pieAxis(self):
        """
        Returns the axis that is used as the pie value for this item.
        
        :return     <Qt.Axis>
        """
        return self._pieAxis

    def pieCenter(self):
        """
        Returns the center point for the pie for this item.
        
        :return     <QPointF>
        """
        return self._pieCenter

    def pointRadius(self):
        """
        Returns the radius for individual points on the line.
        
        :return     <int>
        """
        return self._pointRadius

    def randomColor(self):
        """
        Generates a random color.
        
        :return     <QColor>
        """
        r = random.randint(120, 180)
        g = random.randint(120, 180)
        b = random.randint(120, 180)

        return QColor(r, g, b)

    def radius(self):
        """
        Returns the radius for this item for the pie chart.
        
        :return     <float>
        """
        return self._radius

    def rebuild(self):
        """
        Rebuilds the item based on the current points.
        """
        scene = self.scene()
        if not scene:
            return

        self._subpaths = []

        grid = scene.gridRect()
        typ = self.chartType()

        hruler = scene.horizontalRuler()
        vruler = scene.verticalRuler()

        path = QPainterPath()
        area = QPainterPath()

        self._buildData.clear()
        self._buildData['path_area'] = area

        self.setPos(0, 0)

        # draw a line item
        if typ == XChartScene.Type.Line:
            first = True
            pos = None
            home = None
            self._ellipses = []

            points = self.points()
            if (self.orientation() == Qt.Horizontal):
                points.sort(hruler.compareValues, key=lambda x: x[0])
            else:
                points.sort(vruler.compareValues, key=lambda y: y[1])
                points.reverse()

            for x, y in self.points():
                pos = scene.mapFromChart(x, y)
                if first:
                    home = QPointF(pos.x(), grid.bottom())
                    area.moveTo(home)
                    area.lineTo(pos)
                    path.moveTo(pos)

                    self._ellipses.append(pos)

                    first = False
                else:
                    path.lineTo(pos)
                    area.lineTo(pos)

                    self._ellipses.append(pos)

            if pos and home:
                area.lineTo(pos.x(), grid.bottom())
                area.lineTo(home)

        # draw a bar item
        elif typ == XChartScene.Type.Bar:
            barsize = self.barSize()
            horiz = self.orientation() == Qt.Horizontal

            for x, y in self.points():
                pos = scene.mapFromChart(x, y)
                subpath = QPainterPath()
                if horiz:
                    r = min(grid.bottom() - pos.y(), 8)

                    subpath.moveTo(pos.x() - barsize / 2.0, grid.bottom())
                    subpath.lineTo(pos.x() - barsize / 2.0, pos.y() + r)
                    subpath.quadTo(pos.x() - barsize / 2.0, pos.y(),
                                   pos.x() - barsize / 2.0 + r, pos.y())

                    subpath.lineTo(pos.x() + barsize / 2.0 - r, pos.y())
                    subpath.quadTo(pos.x() + barsize / 2.0, pos.y(),
                                   pos.x() + barsize / 2.0,
                                   pos.y() + r)

                    subpath.lineTo(pos.x() + barsize / 2.0, grid.bottom())
                    subpath.lineTo(pos.x() - barsize / 2.0, grid.bottom())
                else:
                    subpath.moveTo(grid.left(), pos.y() - barsize / 2.0)
                    subpath.lineTo(pos.x(), pos.y() - barsize / 2.0)
                    subpath.lineTo(pos.x(), pos.y() + barsize / 2.0)
                    subpath.lineTo(grid.left(), pos.y() + barsize / 2.0)
                    subpath.lineTo(grid.left(), pos.y() - barsize / 2.0)

                path.addPath(subpath)
                self._subpaths.append((x, y, subpath))

        # draw a pie chart
        elif typ == XChartScene.Type.Pie:
            if self.orientation() == Qt.Horizontal:
                key_index = 0
                value_index = 1
                value_ruler = self.verticalRuler()
            else:
                key_index = 1
                value_index = 0
                value_ruler = self.horizontalRuler()

            pie_values = {}

            for point in self.points():
                key = point[key_index]
                value = point[value_index]

                pie_values.setdefault(key, [])
                pie_values[key].append(value)

            for key, values in pie_values.items():
                pie_values[key] = value_ruler.calcTotal(values)

            total = max(1, value_ruler.calcTotal(pie_values.values()))

            # calculate drawing parameters
            center = self.pieCenter()
            radius = self.radius()
            diameter = radius * 2
            angle = 0
            bound = QRectF(-radius, -radius, diameter, diameter)

            for key, value in sorted(pie_values.items(), key=lambda x: x[1]):
                # calculate the percentage
                perc = float(value) / total

                # calculate the angle as the perc * 360
                item_angle = perc * 360
                self.setPos(center)

                sub_path = QPainterPath()
                sub_path.arcTo(bound, angle, item_angle)
                sub_path.lineTo(0, 0)

                path.addPath(sub_path)
                self._subpaths.append((key, value, sub_path))

                angle += item_angle

        self.setPath(path)
        self._dirty = False

    def points(self):
        """
        Returns a list of the points for this item.
        
        :return     [(<variant> x, <variant> y), ..]
        """
        return self._points

    def setAlternateColor(self, color):
        """
        Sets the alternate color for this widget to the inputed color.
        
        :param      color | <QColor>
        """
        self._alternateColor = QColor(color)

    def setChartType(self, chartType):
        """
        Sets the chart type for this item.  Setting the item to a None chart 
        type will signal it to use the default scenes chart type.
        
        :param      chartType | <XChartScene.Type>
        """
        self._chartType = chartType
        self._dirty = True

    def setColor(self, color):
        """
        Sets the color for this widget to the inputed color.
        
        :param      color | <QColor>
        """
        self._color = QColor(color)
        self.setAlternateColor(self._color.lighter(140))

    def setDirty(self, state=True):
        """
        Sets whether or not this item is dirty.
        
        :param      state | <bool>
        """
        self._dirty

    def setDragData(self, data, x=None, y=None):
        """
        Sets the drag data for this chart item to the inputed data.
        
        :param      data | <QMimeData> || None
        """
        self._dragData[(x, y)] = data

    def setKeyColor(self, key, color):
        """
        Sets the color used when rendering pie charts.
        
        :param      key   | <str>
                    color | <QColor>
        """
        self._keyColors[nativestring(key)] = QColor(color)

    def setKeyToolTip(self, key, tip):
        """
        Sets the tool tip for the specified key.
        
        :param      key | <str>
                    tip | <str>
        """
        self._keyToolTips[nativestring(key)] = tip

    def setHorizontalOffset(self, offset):
        """
        Sets the horizontal offset for this item.
        
        :param     offset | <int>
        """
        self._horizontalOffset = offset

    def setPieCenter(self, center):
        """
        Sets the center for the pie for the chart.
        
        :param      center | <QPointF>
        """
        self._pieCenter = center

    def setPointRadius(self, radius):
        """
        Sets the point radius for this line.
        
        :param      radius | <int>
        """
        self._pointRadius = radius

    def setPoints(self, points):
        """
        Sets the points values for this chart widget item.
        
        :param      points | [(<variant> x, <variant> y), ..]
        """
        self._points = points[:]

    def setPos(self, *args):
        super(XChartWidgetItem, self).setPos(*args)

        if (self._horizontalOffset or self._verticalOffset):
            offset = QPointF(self._horizontalOffset, self._verticalOffset)
            super(XChartWidgetItem, self).setPos(self.pos() + offset)

    def setPieAxis(self, pieAxis):
        """
        Sets the axis that will be used for the pie chart for this item.  This
        will only apply when the item itself defines itself as a pie chart, 
        otherwise, the scene will determine it when the scene is rendered as a
        pie chart.
        
        :param      pieAxis | <Qt.Axis>
        """
        self._pieAxis = pieAxis

    def setRadius(self, radius):
        """
        Sets the radius size for this item.
        
        :param      radius | <float>
        """
        self._radius = radius

    def setShaded(self, state):
        """
        Sets whether or not to shade line items in this graph.
        
        :param      state | <bool>
        """
        self._shaded = state

    def setShowPointsInLine(self, state):
        """
        Sets whether or not to show points in line mode.
        
        :param      state | <bool>
        """
        self._showPointsInLine = state

    def setTitle(self, title):
        """
        Sets the title text for this chart widget item.
        
        :param      title | <str>
        """
        self._title = title

    def startDrag(self, data):
        """
        Starts dragging information from this chart widget based on the
        dragData associated with this item.
        """
        if not data:
            return

        widget = self.scene().chartWidget()
        drag = QDrag(widget)
        drag.setMimeData(data)
        drag.exec_()

    def showPointsInLine(self):
        """
        Returns whether or not to show points in the line.
        
        :return     <bool>
        """
        return self._showPointsInLine

    def title(self):
        """
        Returns the title for this item.
        
        :return     <str>
        """
        return self._title

    def verticalRuler(self):
        """
        Returns the vertical ruler for this widget item.
        
        :return     <projexui.widgets.xchartwidget.XChartRuler> || None
        """
        if (not self.scene()):
            return None
        return self.scene().verticalRuler()
Example #4
0
    def paint(self, painter, option, widget):
        """
        Draws this item with the inputed painter.
        
        :param      painter | <QPainter>
                    rect    | <QRect>
        """
        if self._dirty:
            self.rebuild()

        scene = self.scene()
        if not scene:
            return

        grid = scene.gridRect()
        typ = self.chartType()

        # draw the line chart
        if typ == XChartScene.Type.Line:
            painter.setRenderHint(painter.Antialiasing)

            # draw the path area
            area = self._buildData.get('path_area')
            if area and self.isShaded():
                clr = QColor(self.color())

                clr.setAlpha(120)
                painter.setPen(Qt.NoPen)
                painter.setBrush(clr)

                painter.drawPath(area)

            # draw the line data
            pen = QPen(self.color())
            pen.setWidth(2)

            painter.setPen(pen)
            painter.setBrush(Qt.NoBrush)

            painter.drawPath(self.path())

            if (self.showPointsInLine()):
                palette = QApplication.palette()
                pen = QPen(palette.color(palette.Base))
                pen.setWidth(2)

                painter.setBrush(self.color())
                painter.setPen(pen)

                for point in self._ellipses:
                    painter.drawEllipse(point, self.pointRadius(),
                                        self.pointRadius())

        # draw a bar chart
        elif typ == XChartScene.Type.Bar:
            painter.setRenderHint(painter.Antialiasing)

            pen = QPen(self.color())
            pen.setWidth(1)
            painter.setPen(pen)

            for key, value, sub_path in self._subpaths:
                gradient = QLinearGradient()

                clr = QColor(self.color())

                if (sub_path != self._hoveredPath):
                    clr.setAlpha(130)

                gradient.setColorAt(0.0, clr.lighter(140))
                gradient.setColorAt(0.1, clr.lighter(120))
                gradient.setColorAt(0.25, clr.lighter(110))
                gradient.setColorAt(1.0, clr.lighter(105))

                if (self.orientation() == Qt.Horizontal):
                    gradient.setStart(0, sub_path.boundingRect().top())
                    gradient.setFinalStop(0, sub_path.boundingRect().bottom())
                else:
                    gradient.setStart(sub_path.boundingRect().left(), 0)
                    gradient.setFinalStop(sub_path.boundingRect().right(), 0)

                painter.setBrush(gradient)
                painter.drawPath(sub_path)

        # draw a simple pie chart (calculated by scene)
        elif typ == XChartScene.Type.Pie:
            painter.setRenderHint(painter.Antialiasing)

            center = self.pieCenter()
            radius = self.radius()

            for key, value, sub_path in self._subpaths:
                clr = self.keyColor(key)

                gradient = QRadialGradient(QPointF(0, 0), radius)

                a = QColor(clr.lighter(140))
                b = QColor(clr.lighter(110))

                a.setAlpha(40)
                b.setAlpha(80)

                # look for mouse over
                if (sub_path == self._hoveredPath):
                    a.setAlpha(100)
                    b.setAlpha(200)

                gradient.setColorAt(0, a)
                gradient.setColorAt(1, b)

                pen = QPen(clr)
                pen.setWidth(1)

                painter.setBrush(gradient)
                painter.setPen(pen)

                painter.drawPath(sub_path)
Example #5
0
class XChartWidgetItem(QGraphicsPathItem):
    def __init__( self ):
        super(XChartWidgetItem, self).__init__()
        
        self.setAcceptHoverEvents(True)
        
        # set default information
        self._chartType         = None
        self._pieCenter         = QPointF(0, 0)
        self._subpaths          = []
        self._keyColors         = {}
        self._ellipses          = []
        self._keyToolTips       = {}
        self._showPointsInLine  = True
        self._shaded            = True
        self._dragData          = {}
        self._radius            = 6
        
        self._title             = ''
        self._color             = self.randomColor()
        self._alternateColor    = self._color.lighter(140)
        self._points            = []
        self._barSize           = 30
        self._orientation       = Qt.Horizontal
        self._pieAxis           = Qt.YAxis
        
        self._pointRadius       = 6
        self._horizontalOffset  = 0
        self._verticalOffset    = 0
        
        self._hoveredPath       = None
        self._dirty             = False
        self._buildData         = {}
    
    def addPoint( self, x, y ):
        """
        Adds a new chart point to this item.
        
        :param      x | <variant>
                    y | <variant>
        """
        self._points.append((x, y))
        self._dirty = True
    
    def alternateColor( self ):
        """
        Returns the alternate color for this item.
        
        :return     <QColor>
        """
        return QColor(self._alternateColor)
    
    def barSize( self ):
        """
        Returns the size that the bar chart should be rendered with.
        
        :return     <int>
        """
        return self._barSize
    
    def chartType( self ):
        """
        Returns the chart type for this item.  If no type is explicitely set,
        then the scenes chart type will be utilized.
        
        :return     <XChartScene.Type>
        """
        if ( self._chartType ):
            return self._chartType
        
        scene = self.scene()
        if ( not scene ):
            return 0
        
        return scene.chartType()
    
    def clear( self ):
        """
        Clears the chart points from this item.
        """
        self._points = []
        self._dirty = True
    
    def color( self ):
        """
        Returns the primary color for this item.
        
        :return     <QColor>
        """
        return QColor(self._color)
    
    def dragData(self, x=None, y=None):
        """
        Returns any drag information that will be used from this chart item.
        
        :return     <QMimeData> || None
        """
        # look for specific drag information for this item
        first  = (x, y)
        second = (x, None)
        third  = (None, y)
        fourth = (None, None)
        
        for key in (first, second, third, fourth):
            data = self._dragData.get(key)
            if data:
                return data
        
        return None
    
    def hasCustomType( self ):
        """
        Returns true if this item defines its own chart type.
        
        :return     <bool>
        """
        return self._chartType != None
    
    def horizontalRuler( self ):
        """
        Returns the horizontal ruler for this widget item.
        
        :return     <projexui.widgets.xchartwidget.XChartRuler> || None
        """
        if ( not self.scene() ):
            return None
        return self.scene().horizontalRuler()
    
    def horizontalOffset( self ):
        """
        Returns the horizontal offset for this item.
        
        :return     <int>
        """
        return self._horizontalOffset
    
    def hoverMoveEvent( self, event ):
        """
        Tracks whether or not this item is being hovered.
        
        :param      event | <QEvent>
        """
        point = event.pos()
        
        found_key = ''
        found = None
        for key, value, subpath in self._subpaths:
            if subpath.contains(point):
                found = subpath
                found_key = key
                break
        
        if found:
            # update the tooltip
            tip    = self.keyToolTip(found_key)
            if ( tip ):
                widget = self.scene().chartWidget()
                anchor = XPopupWidget.Anchor.RightCenter
                
                # show the popup widget
                XPopupWidget.showToolTip(tip, 
                                         anchor = anchor,
                                         parent = widget,
                                         foreground = self.color().darker(120),
                                         background = self.alternateColor())
            
            if ( found != self._hoveredPath ):
                self._hoveredPath = found
                self.update()
    
    def hoverLeaveEvent( self, event ):
        """
        Tracks whether or not this item is being hovered.
        
        :param      event | <QEvent>
        """
        super(XChartWidgetItem, self).hoverEnterEvent(event)
        
        self._hoveredPath = None
        self.update()
    
    def isShaded( self ):
        """
        Returns the shaded state for this item.
        
        :return     <bool>
        """
        return self._shaded
    
    def keyColor( self, key ):
        """
        Returns a color for the inputed key (used in pie charts).
        
        :param      key | <str>
        
        :return     <QColor>
        """
        self._keyColors.setdefault(nativestring(key), self.color())
        return self._keyColors[nativestring(key)]
    
    def keyToolTip( self, key ):
        """
        Returns the tool tip for this key.
        
        :param      key | <str>
        
        :return     <str>
        """
        return self._keyToolTips.get(nativestring(key), '')
    
    def mousePressEvent(self, event):
        """
        Creates the drag event for this item.
        
        :param      event | <QMousePressEvent>
        """
        near_x, near_y = self.nearestPoint(event.pos())
        
        data = self.dragData(x=near_x, y=near_y)
        self.startDrag(data)
        
        super(XChartWidgetItem, self).mousePressEvent(event)
    
    def nearestPoint(self, pos):
        """
        Returns the nearest graphing point for this item based on the
        inputed graph position.
        
        :param      pos | <QPoint>
        
        :return     (<variant> x, <variant> y)
        """
        # lookup subpaths
        for x, y, path in self._subpaths:
            if path.contains(pos):
                return (x, y)
        
        return (None, None)
    
    def orientation( self ):
        """
        Returns the orienatation for this item (used in bar charts).
        
        :return     <Qt.Orienation>
        """
        return self._orientation
    
    def paint( self, painter, option, widget ):
        """
        Draws this item with the inputed painter.
        
        :param      painter | <QPainter>
                    rect    | <QRect>
        """
        if self._dirty:
            self.rebuild()
        
        scene   = self.scene()
        if not scene:
            return
        
        grid    = scene.gridRect()
        typ     = self.chartType()
        
        # draw the line chart
        if typ == XChartScene.Type.Line:
            painter.setRenderHint(painter.Antialiasing)
            
            # draw the path area
            area = self._buildData.get('path_area')
            if area and self.isShaded():
                clr   = QColor(self.color())
                
                clr.setAlpha(120)
                painter.setPen(Qt.NoPen)
                painter.setBrush(clr)
                
                painter.drawPath(area)
            
            # draw the line data
            pen = QPen(self.color())
            pen.setWidth(2)
            
            painter.setPen(pen)
            painter.setBrush(Qt.NoBrush)
            
            painter.drawPath(self.path())
            
            if ( self.showPointsInLine() ):
                palette = QApplication.palette()
                pen = QPen(palette.color(palette.Base))
                pen.setWidth(2)
                
                painter.setBrush(self.color())
                painter.setPen(pen)
                
                for point in self._ellipses:
                    painter.drawEllipse(point, 
                                        self.pointRadius(), 
                                        self.pointRadius())
        
        # draw a bar chart
        elif typ == XChartScene.Type.Bar:
            painter.setRenderHint(painter.Antialiasing)
            
            pen = QPen(self.color())
            pen.setWidth(1)
            painter.setPen(pen)
            
            for key, value, sub_path in self._subpaths:
                gradient = QLinearGradient()
                
                clr = QColor(self.color())
                
                if ( sub_path != self._hoveredPath ):
                    clr.setAlpha(130)
                
                gradient.setColorAt(0.0,  clr.lighter(140))
                gradient.setColorAt(0.1,  clr.lighter(120))
                gradient.setColorAt(0.25, clr.lighter(110))
                gradient.setColorAt(1.0,  clr.lighter(105))
                
                if ( self.orientation() == Qt.Horizontal ):
                    gradient.setStart(0, sub_path.boundingRect().top())
                    gradient.setFinalStop(0, sub_path.boundingRect().bottom())
                else:
                    gradient.setStart(sub_path.boundingRect().left(), 0)
                    gradient.setFinalStop(sub_path.boundingRect().right(), 0)
                
                painter.setBrush(gradient)
                painter.drawPath(sub_path)
        
        # draw a simple pie chart (calculated by scene)
        elif typ == XChartScene.Type.Pie:
            painter.setRenderHint(painter.Antialiasing)
            
            center   = self.pieCenter()
            radius   = self.radius()
            
            for key, value, sub_path in self._subpaths:
                clr = self.keyColor(key)
                
                gradient = QRadialGradient(QPointF(0, 0), radius)
                
                a = QColor(clr.lighter(140))
                b = QColor(clr.lighter(110))
                
                a.setAlpha(40)
                b.setAlpha(80)
                
                # look for mouse over
                if ( sub_path == self._hoveredPath ):
                    a.setAlpha(100)
                    b.setAlpha(200)
                
                gradient.setColorAt(0, a)
                gradient.setColorAt(1, b)
                
                pen = QPen(clr)
                pen.setWidth(1)
                
                painter.setBrush(gradient)
                painter.setPen(pen)
                
                painter.drawPath(sub_path)
    
    def pieAxis( self ):
        """
        Returns the axis that is used as the pie value for this item.
        
        :return     <Qt.Axis>
        """
        return self._pieAxis
    
    def pieCenter( self ):
        """
        Returns the center point for the pie for this item.
        
        :return     <QPointF>
        """
        return self._pieCenter
    
    def pointRadius(self):
        """
        Returns the radius for individual points on the line.
        
        :return     <int>
        """
        return self._pointRadius
    
    def randomColor( self ):
        """
        Generates a random color.
        
        :return     <QColor>
        """
        r = random.randint(120, 180)
        g = random.randint(120, 180)
        b = random.randint(120, 180)
        
        return QColor(r, g, b)
    
    def radius( self ):
        """
        Returns the radius for this item for the pie chart.
        
        :return     <float>
        """
        return self._radius
    
    def rebuild( self ):
        """
        Rebuilds the item based on the current points.
        """
        scene       = self.scene()
        if not scene:
            return
        
        self._subpaths = []
        
        grid        = scene.gridRect()
        typ         = self.chartType()
        
        hruler      = scene.horizontalRuler()
        vruler      = scene.verticalRuler()
        
        path        = QPainterPath()
        area        = QPainterPath()
        
        self._buildData.clear()
        self._buildData['path_area'] = area
        
        self.setPos(0, 0)
        
        # draw a line item
        if typ == XChartScene.Type.Line:
            first = True
            pos   = None
            home  = None
            self._ellipses = []
            
            points = self.points()
            if ( self.orientation() == Qt.Horizontal ):
                points.sort(hruler.compareValues, key = lambda x: x[0])
            else:
                points.sort(vruler.compareValues, key = lambda y: y[1])
                points.reverse()
            
            for x, y in self.points():
                pos = scene.mapFromChart(x, y)
                if first:
                    home = QPointF(pos.x(), grid.bottom())
                    area.moveTo(home)
                    area.lineTo(pos)
                    path.moveTo(pos)
                    
                    self._ellipses.append(pos)
                    
                    first = False
                else:
                    path.lineTo(pos)
                    area.lineTo(pos)
                    
                    self._ellipses.append(pos)
            
            if pos and home:
                area.lineTo(pos.x(), grid.bottom())
                area.lineTo(home)
        
        # draw a bar item
        elif typ == XChartScene.Type.Bar:
            barsize = self.barSize()
            horiz   = self.orientation() == Qt.Horizontal
            
            for x, y in self.points():
                pos = scene.mapFromChart(x, y)
                subpath = QPainterPath()
                if horiz:
                    r = min(grid.bottom() - pos.y(), 8)
                    
                    subpath.moveTo(pos.x() - barsize / 2.0, grid.bottom())
                    subpath.lineTo(pos.x() - barsize / 2.0, pos.y() + r)
                    subpath.quadTo(pos.x() - barsize / 2.0, pos.y(),
                                   pos.x() - barsize / 2.0 + r, pos.y())
                    
                    subpath.lineTo(pos.x() + barsize / 2.0 - r, pos.y())
                    subpath.quadTo(pos.x() + barsize / 2.0, pos.y(),
                                   pos.x() + barsize / 2.0, pos.y() + r)
                    
                    subpath.lineTo(pos.x() + barsize / 2.0, grid.bottom())
                    subpath.lineTo(pos.x() - barsize / 2.0, grid.bottom())
                else:
                    subpath.moveTo(grid.left(), pos.y() - barsize / 2.0)
                    subpath.lineTo(pos.x(),     pos.y() - barsize / 2.0)
                    subpath.lineTo(pos.x(),     pos.y() + barsize / 2.0)
                    subpath.lineTo(grid.left(), pos.y() + barsize / 2.0)
                    subpath.lineTo(grid.left(), pos.y() - barsize / 2.0)
                
                path.addPath(subpath)
                self._subpaths.append((x, y, subpath))
        
        # draw a pie chart
        elif typ == XChartScene.Type.Pie:
            if self.orientation() == Qt.Horizontal:
                key_index   = 0
                value_index = 1
                value_ruler = self.verticalRuler()
            else:
                key_index   = 1
                value_index = 0
                value_ruler = self.horizontalRuler()
        
            pie_values = {}
            
            for point in self.points():
                key     = point[key_index]
                value   = point[value_index]
                
                pie_values.setdefault(key, [])
                pie_values[key].append(value)
        
            for key, values in pie_values.items():
                pie_values[key] = value_ruler.calcTotal(values)
            
            total = max(1, value_ruler.calcTotal(pie_values.values()))
            
            # calculate drawing parameters
            center   = self.pieCenter()
            radius   = self.radius()
            diameter = radius * 2
            angle    = 0
            bound    = QRectF(-radius, -radius, diameter, diameter)
            
            for key, value in sorted(pie_values.items(), key = lambda x: x[1]):
                # calculate the percentage
                perc  = float(value) / total
                
                # calculate the angle as the perc * 360
                item_angle = perc * 360
                self.setPos(center)
                
                sub_path = QPainterPath()
                sub_path.arcTo(bound, angle, item_angle)
                sub_path.lineTo(0, 0)
                
                path.addPath(sub_path)
                self._subpaths.append((key, value, sub_path))
                
                angle += item_angle
                
        self.setPath(path)
        self._dirty = False
    
    def points( self ):
        """
        Returns a list of the points for this item.
        
        :return     [(<variant> x, <variant> y), ..]
        """
        return self._points
    
    def setAlternateColor( self, color ):
        """
        Sets the alternate color for this widget to the inputed color.
        
        :param      color | <QColor>
        """
        self._alternateColor = QColor(color)
    
    def setChartType( self, chartType ):
        """
        Sets the chart type for this item.  Setting the item to a None chart 
        type will signal it to use the default scenes chart type.
        
        :param      chartType | <XChartScene.Type>
        """
        self._chartType = chartType
        self._dirty = True
    
    def setColor( self, color ):
        """
        Sets the color for this widget to the inputed color.
        
        :param      color | <QColor>
        """
        self._color = QColor(color)
        self.setAlternateColor(self._color.lighter(140))
    
    def setDirty( self, state = True ):
        """
        Sets whether or not this item is dirty.
        
        :param      state | <bool>
        """
        self._dirty
    
    def setDragData(self, data, x=None, y=None):
        """
        Sets the drag data for this chart item to the inputed data.
        
        :param      data | <QMimeData> || None
        """
        self._dragData[(x, y)] = data
    
    def setKeyColor( self, key, color ):
        """
        Sets the color used when rendering pie charts.
        
        :param      key   | <str>
                    color | <QColor>
        """
        self._keyColors[nativestring(key)] = QColor(color)
    
    def setKeyToolTip( self, key, tip ):
        """
        Sets the tool tip for the specified key.
        
        :param      key | <str>
                    tip | <str>
        """
        self._keyToolTips[nativestring(key)] = tip
    
    def setHorizontalOffset( self, offset ):
        """
        Sets the horizontal offset for this item.
        
        :param     offset | <int>
        """
        self._horizontalOffset = offset
    
    def setPieCenter( self, center ):
        """
        Sets the center for the pie for the chart.
        
        :param      center | <QPointF>
        """
        self._pieCenter = center
    
    def setPointRadius(self, radius):
        """
        Sets the point radius for this line.
        
        :param      radius | <int>
        """
        self._pointRadius = radius
    
    def setPoints(self, points):
        """
        Sets the points values for this chart widget item.
        
        :param      points | [(<variant> x, <variant> y), ..]
        """
        self._points = points[:]
    
    def setPos( self, *args ):
        super(XChartWidgetItem, self).setPos(*args)
        
        if ( self._horizontalOffset or self._verticalOffset ):
            offset = QPointF(self._horizontalOffset, self._verticalOffset)
            super(XChartWidgetItem, self).setPos(self.pos() + offset)
    
    def setPieAxis( self, pieAxis ):
        """
        Sets the axis that will be used for the pie chart for this item.  This
        will only apply when the item itself defines itself as a pie chart, 
        otherwise, the scene will determine it when the scene is rendered as a
        pie chart.
        
        :param      pieAxis | <Qt.Axis>
        """
        self._pieAxis = pieAxis
    
    def setRadius( self, radius ):
        """
        Sets the radius size for this item.
        
        :param      radius | <float>
        """
        self._radius = radius
    
    def setShaded( self, state ):
        """
        Sets whether or not to shade line items in this graph.
        
        :param      state | <bool>
        """
        self._shaded = state
    
    def setShowPointsInLine( self, state ):
        """
        Sets whether or not to show points in line mode.
        
        :param      state | <bool>
        """
        self._showPointsInLine = state
    
    def setTitle( self, title ):
        """
        Sets the title text for this chart widget item.
        
        :param      title | <str>
        """
        self._title = title
    
    def startDrag(self, data):
        """
        Starts dragging information from this chart widget based on the
        dragData associated with this item.
        """
        if not data:
            return
        
        widget = self.scene().chartWidget()
        drag = QDrag(widget)
        drag.setMimeData(data)
        drag.exec_()
    
    def showPointsInLine( self ):
        """
        Returns whether or not to show points in the line.
        
        :return     <bool>
        """
        return self._showPointsInLine
    
    def title( self ):
        """
        Returns the title for this item.
        
        :return     <str>
        """
        return self._title
    
    def verticalRuler( self ):
        """
        Returns the vertical ruler for this widget item.
        
        :return     <projexui.widgets.xchartwidget.XChartRuler> || None
        """
        if ( not self.scene() ):
            return None
        return self.scene().verticalRuler()
Example #6
0
 def paint( self, painter, option, widget ):
     """
     Draws this item with the inputed painter.
     
     :param      painter | <QPainter>
                 rect    | <QRect>
     """
     if self._dirty:
         self.rebuild()
     
     scene   = self.scene()
     if not scene:
         return
     
     grid    = scene.gridRect()
     typ     = self.chartType()
     
     # draw the line chart
     if typ == XChartScene.Type.Line:
         painter.setRenderHint(painter.Antialiasing)
         
         # draw the path area
         area = self._buildData.get('path_area')
         if area and self.isShaded():
             clr   = QColor(self.color())
             
             clr.setAlpha(120)
             painter.setPen(Qt.NoPen)
             painter.setBrush(clr)
             
             painter.drawPath(area)
         
         # draw the line data
         pen = QPen(self.color())
         pen.setWidth(2)
         
         painter.setPen(pen)
         painter.setBrush(Qt.NoBrush)
         
         painter.drawPath(self.path())
         
         if ( self.showPointsInLine() ):
             palette = QApplication.palette()
             pen = QPen(palette.color(palette.Base))
             pen.setWidth(2)
             
             painter.setBrush(self.color())
             painter.setPen(pen)
             
             for point in self._ellipses:
                 painter.drawEllipse(point, 
                                     self.pointRadius(), 
                                     self.pointRadius())
     
     # draw a bar chart
     elif typ == XChartScene.Type.Bar:
         painter.setRenderHint(painter.Antialiasing)
         
         pen = QPen(self.color())
         pen.setWidth(1)
         painter.setPen(pen)
         
         for key, value, sub_path in self._subpaths:
             gradient = QLinearGradient()
             
             clr = QColor(self.color())
             
             if ( sub_path != self._hoveredPath ):
                 clr.setAlpha(130)
             
             gradient.setColorAt(0.0,  clr.lighter(140))
             gradient.setColorAt(0.1,  clr.lighter(120))
             gradient.setColorAt(0.25, clr.lighter(110))
             gradient.setColorAt(1.0,  clr.lighter(105))
             
             if ( self.orientation() == Qt.Horizontal ):
                 gradient.setStart(0, sub_path.boundingRect().top())
                 gradient.setFinalStop(0, sub_path.boundingRect().bottom())
             else:
                 gradient.setStart(sub_path.boundingRect().left(), 0)
                 gradient.setFinalStop(sub_path.boundingRect().right(), 0)
             
             painter.setBrush(gradient)
             painter.drawPath(sub_path)
     
     # draw a simple pie chart (calculated by scene)
     elif typ == XChartScene.Type.Pie:
         painter.setRenderHint(painter.Antialiasing)
         
         center   = self.pieCenter()
         radius   = self.radius()
         
         for key, value, sub_path in self._subpaths:
             clr = self.keyColor(key)
             
             gradient = QRadialGradient(QPointF(0, 0), radius)
             
             a = QColor(clr.lighter(140))
             b = QColor(clr.lighter(110))
             
             a.setAlpha(40)
             b.setAlpha(80)
             
             # look for mouse over
             if ( sub_path == self._hoveredPath ):
                 a.setAlpha(100)
                 b.setAlpha(200)
             
             gradient.setColorAt(0, a)
             gradient.setColorAt(1, b)
             
             pen = QPen(clr)
             pen.setWidth(1)
             
             painter.setBrush(gradient)
             painter.setPen(pen)
             
             painter.drawPath(sub_path)