Beispiel #1
0
 def dateTimeRect( self, dateTime ):
     """
     Returns the rect that is defined by the inputed date time.
     
     :return     <QRectF>
     """
     data = self._dateTimeGrid.get(dateTime.toTime_t())
     if ( data ):
         return QRectF(data[1])
     return QRectF()
Beispiel #2
0
 def dateRect( self, date ):
     """
     Returns the rect that is defined by the inputed date.
     
     :return     <QRectF>
     """
     data = self._dateGrid.get(date.toJulianDay())
     if ( data ):
         return QRectF(data[1])
     return QRectF()
Beispiel #3
0
 def setSceneRect(self, *args):
     """
     Overloads the set rect method to signal that the scene needs to be 
     rebuilt.
     
     :param      args | <arg variant>
     """
     curr = self.sceneRect()
     
     super(XGanttScene, self).setSceneRect(*args)
     
     if curr != QRectF(*args):
         self._dirty = True
Beispiel #4
0
    def viewportRect(self):
        """
        Returns the QRectF that represents the visible viewport rect for the
        current view.
        
        :return     <QRectF>
        """
        w = self.width()
        h = self.height()

        vbar = self.verticalScrollBar()
        hbar = self.horizontalScrollBar()

        if vbar.isVisible():
            w -= vbar.width()
        if hbar.isVisible():
            h -= hbar.height()

        top_l = self.mapToScene(QPoint(0, 0))
        bot_r = self.mapToScene(QPoint(w, h))

        return QRectF(top_l.x(), top_l.y(),
                      bot_r.x() - top_l.x(),
                      bot_r.y() - top_l.y())
Beispiel #5
0
    def rebuildGrid(self):
        """
        Rebuilds the ruler data.
        """
        vruler = self.verticalRuler()
        hruler = self.horizontalRuler()

        rect = self._buildData['grid_rect']

        # process the vertical ruler
        h_lines = []
        h_alt = []
        h_notches = []

        vpstart = vruler.padStart()
        vnotches = vruler.notches()
        vpend = vruler.padEnd()
        vcount = len(vnotches) + vpstart + vpend
        deltay = rect.height() / max((vcount - 1), 1)
        y = rect.bottom()
        alt = False

        for i in range(vcount):
            h_lines.append(QLineF(rect.left(), y, rect.right(), y))

            # store alternate color
            if (alt):
                alt_rect = QRectF(rect.left(), y, rect.width(), deltay)
                h_alt.append(alt_rect)

            # store notch information
            nidx = i - vpstart
            if (0 <= nidx and nidx < len(vnotches)):
                notch = vnotches[nidx]
                notch_rect = QRectF(0, y - 3, rect.left() - 3, deltay)
                h_notches.append((notch_rect, notch))

            y -= deltay
            alt = not alt

        self._buildData['grid_h_lines'] = h_lines
        self._buildData['grid_h_alt'] = h_alt
        self._buildData['grid_h_notches'] = h_notches

        # process the horizontal ruler
        v_lines = []
        v_alt = []
        v_notches = []

        hpstart = hruler.padStart()
        hnotches = hruler.notches()
        hpend = hruler.padEnd()
        hcount = len(hnotches) + hpstart + hpend
        deltax = rect.width() / max((hcount - 1), 1)
        x = rect.left()
        alt = False

        for i in range(hcount):
            v_lines.append(QLineF(x, rect.top(), x, rect.bottom()))

            # store alternate info
            if (alt):
                alt_rect = QRectF(x - deltax, rect.top(), deltax,
                                  rect.height())
                v_alt.append(alt_rect)

            # store notch information
            nidx = i - hpstart
            if (0 <= nidx and nidx < len(hnotches)):
                notch = hnotches[nidx]
                notch_rect = QRectF(x - (deltax / 2.0),
                                    rect.bottom() + 3, deltax, 13)
                v_notches.append((notch_rect, notch))

            x += deltax
            alt = not alt

        self._buildData['grid_v_lines'] = v_lines
        self._buildData['grid_v_alt'] = v_alt
        self._buildData['grid_v_notches'] = v_notches

        # draw the axis lines
        axis_lines = []
        axis_lines.append(
            QLineF(rect.left(), rect.top(), rect.left(), rect.bottom()))

        axis_lines.append(
            QLineF(rect.left(), rect.bottom(), rect.right(), rect.bottom()))

        self._buildData['axis_lines'] = axis_lines
Beispiel #6
0
    def rebuild(self):
        """
        Rebuilds the data for this scene to draw with.
        """
        global XChartWidgetItem
        if (XChartWidgetItem is None):
            from projexui.widgets.xchartwidget.xchartwidgetitem \
            import XChartWidgetItem

        self._buildData = {}

        # build the grid location
        x = 8
        y = 8
        w = self.sceneRect().width()
        h = self.sceneRect().height()
        hpad = self.horizontalPadding()
        vpad = self.verticalPadding()
        hmax = self.horizontalRuler().maxNotchSize(Qt.Horizontal)

        left_offset = hpad + self.verticalRuler().maxNotchSize(Qt.Vertical)
        right_offset = left_offset + hpad
        top_offset = vpad
        bottom_offset = top_offset + vpad + hmax

        left = x + left_offset
        right = w - right_offset
        top = y + top_offset
        bottom = h - bottom_offset

        rect = QRectF()
        rect.setLeft(left)
        rect.setRight(right)
        rect.setBottom(bottom)
        rect.setTop(top)

        self._buildData['grid_rect'] = rect

        # rebuild the ruler data
        self.rebuildGrid()
        self._dirty = False

        # rebuild all the items
        padding = self.horizontalPadding() + self.verticalPadding()
        grid = self.sceneRect()
        filt = lambda x: isinstance(x, XChartWidgetItem)
        items = filter(filt, self.items())
        height = float(grid.height())
        if height == 0:
            ratio = 1
        else:
            ratio = grid.width() / height
        count = len(items)

        if (not count):
            return

        if (ratio >= 1):
            radius = (grid.height() - padding * 2) / 2.0
            x = rect.center().x()
            y = rect.center().y()
            dx = radius * 2.5
            dy = 0
        else:
            radius = (grid.width() - padding * 2) / 2.0
            x = rect.center().x()
            y = rect.center().y()
            dx = 0
            dy = radius * 2.5

        for item in items:
            item.setPieCenter(QPointF(x, y))
            item.setRadius(radius)
            item.rebuild()

            x += dx
            y += dy

        if (self._trackerItem and self._trackerItem()):
            self._trackerItem().rebuild(self._buildData['grid_rect'])
Beispiel #7
0
    def layout(self,
               scene,
               nodes,
               center=None,
               padX=None,
               padY=None,
               direction=None,
               animationGroup=None):
        """
        Lays out the nodes for this scene based on a block layering algorithm.
        
        :param      scene          | <XNodeScene>
                    nodes          | [<XNode>, ..]
                    center         | <QPointF> || None
                    padX           | <int> || None
                    padY           | <int> || None
                    direction      | <Qt.Direction>
                    animationGroup | <QAnimationGroup> || None
        
        :return     {<XNode>: <QRectF>, ..} | new rects per affected node
        """
        nodes = filter(lambda x: x is not None and x.isVisible(), nodes)

        # make sure we have at least 1 node, otherwise, it is already laid out
        if not nodes or len(nodes) == 1:
            return {}

        # calculate the default padding based on the scene
        if padX == None:
            if direction == Qt.Vertical:
                padX = 2 * scene.cellWidth()
            else:
                padX = 4 * scene.cellWidth()

        if padY == None:
            if direction == Qt.Vertical:
                padY = 4 * scene.cellHeight()
            else:
                padY = 2 * scene.cellWidth()

        # step 1: create a mapping of the connections
        connection_map = self.connectionMap(scene, nodes)

        # step 2: organize the nodes into layers based on their connection chain
        layers = self.generateLayers(scene, nodes, connection_map)

        # step 3: calculate the total dimensions for the layout
        bounds = QRectF()

        # step 3.1: compare the nodes together that have common connections
        layer_widths = []
        layer_heights = []
        node_heights = {}
        node_widths = {}

        for layer_index, layer in enumerate(layers):
            layer_w = 0
            layer_h = 0

            layer_node_w = []
            layer_node_h = []

            for node in layer:
                rect = node.rect()

                layer_node_w.append(rect.width())
                layer_node_h.append(rect.height())

                if direction == Qt.Vertical:
                    layer_w += rect.width()
                    layer_h = max(rect.height(), layer_h)
                else:
                    layer_w = max(rect.width(), layer_w)
                    layer_h += rect.height()

            # update the bounding area
            if direction == Qt.Vertical:
                layer_w += padX * 1 - len(layer)
                bounds.setWidth(max(layer_w, bounds.width()))
                bounds.setHeight(bounds.height() + layer_h)
            else:
                layer_h += padY * 1 - len(layer)
                bounds.setWidth(bounds.width() + layer_w)
                bounds.setHeight(max(layer_h, bounds.height()))

            node_widths[layer_index] = layer_node_w
            node_heights[layer_index] = layer_node_h

            layer_widths.append(layer_w)
            layer_heights.append(layer_h)

        if not center:
            center = scene.sceneRect().center()

        w = bounds.width()
        h = bounds.height()
        bounds.setX(center.x() - bounds.width() / 2.0)
        bounds.setY(center.y() - bounds.height() / 2.0)
        bounds.setWidth(w)
        bounds.setHeight(h)

        # step 4: assign positions for each node by layer
        processed_nodes = {}
        layer_grps = [(i, layer) for i, layer in enumerate(layers)]
        layer_grps.sort(key=lambda x: len(x[1]))

        used_rects = [n.sceneRect() for n in scene.nodes() if not n in nodes]

        for layer_index, layer in reversed(layer_grps):
            layer_width = layer_widths[layer_index]
            layer_height = layer_heights[layer_index]

            # determine the starting point for this layer
            if direction == Qt.Vertical:
                offset = layer_index * padY + sum(layer_heights[:layer_index])
                point = QPointF(bounds.x(), offset + bounds.y())
            else:
                offset = layer_index * padX + sum(layer_widths[:layer_index])
                point = QPointF(offset + bounds.x(), bounds.y())

            # assign node positions based on existing connections
            for node_index, node in enumerate(layer):
                max_, min_ = (None, None)
                inputs, outputs = connection_map[node]
                for connected_node in inputs + outputs:
                    if not connected_node in processed_nodes:
                        continue

                    npos = processed_nodes[connected_node]
                    nrect = connected_node.rect()
                    rect = QRectF(npos.x(), npos.y(), nrect.width(),
                                  nrect.height())

                    if direction == Qt.Vertical:
                        if min_ is None:
                            min_ = rect.left()
                        min_ = min(rect.left(), min_)
                        max_ = max(rect.right(), max_)
                    else:
                        if min_ is None:
                            min_ = rect.top()
                        min_ = min(rect.top(), min_)
                        max_ = max(rect.bottom(), max_)

                if direction == Qt.Vertical:
                    off_x = 0
                    off_y = (layer_height - node.rect().height()) / 2.0
                    start_x = (bounds.width() - layer_width)
                    start_y = 0
                else:
                    off_x = (layer_width - node.rect().width()) / 2.0
                    off_y = 0
                    start_x = 0
                    start_y = (bounds.height() - layer_height)

                # align against existing nodes
                point_x = -1
                point_y = -1
                offset = 0
                before = True
                found_point = True
                new_rect = QRectF()

                while found_point:
                    if not None in (min_, max_):
                        if direction == Qt.Vertical:
                            off_x = (max_ -
                                     min_) / 2.0 - node.rect().width() / 2.0

                            if before:
                                off_x -= offset
                                offset += node.rect().width() + padX
                            else:
                                off_x += offset

                            point_x = min_ + off_x
                            point_y = point.y() + off_y
                        else:
                            off_y = (max_ -
                                     min_) / 2.0 - node.rect().height() / 2.0

                            if before:
                                off_y -= offset
                                offset += node.rect().height() + padY
                            else:
                                off_y += offset

                            point_x = point.x() + off_x
                            point_y = min_ + off_y

                    # otherwise, align based on its position in the layer
                    else:
                        if direction == Qt.Vertical:
                            off_x = sum(node_widths[layer_index][:node_index])
                            off_x += node_index * padX
                            off_x += start_x

                            if before:
                                off_x -= offset
                                offset += node.rect().width() + padX
                            else:
                                off_x += offset

                            point_x = point.x() + off_x
                            point_y = point.y() + off_y
                        else:
                            off_y = sum(node_heights[layer_index][:node_index])
                            off_y += node_index * padY
                            off_y += start_y

                            if before:
                                off_y -= offset
                                offset += node.rect().height() + padY
                            else:
                                off_y += offset

                            point_x = point.x() + off_x
                            point_y = point.y() + off_y

                    # determine if we've already used this point before
                    before = not before
                    found_point = False
                    orect = node.rect()
                    new_rect = QRectF(point_x, point_y, orect.width(),
                                      orect.height())
                    for used_rect in used_rects:
                        if used_rect.intersects(new_rect):
                            found_point = True
                            break

                used_rects.append(new_rect)

                if not animationGroup:
                    node.setPos(point_x, point_y)
                else:
                    anim = XNodeAnimation(node, 'setPos')
                    anim.setStartValue(node.pos())
                    anim.setEndValue(QPointF(point_x, point_y))
                    animationGroup.addAnimation(anim)

                processed_nodes[node] = QPointF(point_x, point_y)

                if self._testing:
                    QApplication.processEvents()
                    time.sleep(0.5)

        return processed_nodes
Beispiel #8
0
    def rebuildTiles(self):
        # create the foreground pixmap
        gantt  = self.ganttWidget()
        header = gantt.treeWidget().header()
        width  = self.sceneRect().width()
        height = header.height()
        
        # create the main color
        palette     = gantt.palette()
        color       = palette.color(palette.Button)
        textColor   = palette.color(palette.ButtonText)
        borderColor = color.darker(140)
        text_align  = Qt.AlignBottom | Qt.AlignHCenter
        
        # create the gradient
        gradient = QLinearGradient()
        gradient.setStart(0, 0)
        gradient.setFinalStop(0, height)
        gradient.setColorAt(0, color)
        gradient.setColorAt(1, color.darker(120))
        
        # generate the tiles
        tiles = []
        painters = []
        for rect, label in self._topLabels:
            tile_rect = QRectF(rect.x(), 0, rect.width(), height)
            pixmap = QPixmap(rect.width(), height)
            
            with XPainter(pixmap) as painter:
                painter.setBrush(QBrush(gradient))
                painter.drawRect(tile_rect)
                
                rx = 0
                ry = 0
                rw = rect.width()
                rh = rect.height()
                
                painter.setPen(borderColor)
                painter.drawRect(rx, ry, rw, rh)
                
                painter.setPen(textColor)
                painter.drawText(rx, ry, rw, rh - 2, text_align, label)
                
                tiles.append((tile_rect, pixmap))
                painters.append((tile_rect, pixmap, painter))
        
        # add bottom labels
        for rect, label in self._labels:
            for tile_rect, tile, painter in painters:
                if tile_rect.x() <= rect.x() and \
                   rect.right() <= tile_rect.right():
                    rx = rect.x() - tile_rect.x()
                    ry = rect.y()
                    rw = rect.width()
                    rh = rect.height()
                    
                    with painter:
                        painter.setPen(borderColor)
                        painter.drawRect(rx, ry, rw, rh)
                        
                        painter.setPen(textColor)
                        painter.drawText(rx, ry, rw, rh - 2, text_align, label)

        self._tiles = tiles
    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
Beispiel #10
0
 def rebuild( self ):
     """
     Rebuilds the data for this scene to draw with.
     """
     global XChartWidgetItem
     if ( XChartWidgetItem is None ):
         from projexui.widgets.xchartwidget.xchartwidgetitem \
         import XChartWidgetItem
     
     self._buildData = {}
     
     # build the grid location
     x      = 8
     y      = 8
     w      = self.sceneRect().width()
     h      = self.sceneRect().height()
     hpad   = self.horizontalPadding()
     vpad   = self.verticalPadding()
     hmax   = self.horizontalRuler().maxNotchSize(Qt.Horizontal)
     
     left_offset   = hpad + self.verticalRuler().maxNotchSize(Qt.Vertical)
     right_offset  = left_offset + hpad
     top_offset    = vpad
     bottom_offset = top_offset + vpad + hmax
     
     left    = x + left_offset
     right   = w - right_offset
     top     = y + top_offset
     bottom  = h - bottom_offset
     
     rect = QRectF()
     rect.setLeft(left)
     rect.setRight(right)
     rect.setBottom(bottom)
     rect.setTop(top)
     
     self._buildData['grid_rect'] = rect
     
     # rebuild the ruler data
     self.rebuildGrid()
     self._dirty = False
     
     # rebuild all the items
     padding  = self.horizontalPadding() + self.verticalPadding()
     grid     = self.sceneRect()
     filt     = lambda x: isinstance(x, XChartWidgetItem)
     items    = filter(filt, self.items())
     height   = float(grid.height())
     if height == 0:
         ratio = 1
     else:
         ratio = grid.width() / height
     count    = len(items)
     
     if ( not count ):
         return
     
     if ( ratio >= 1 ):
         radius   = (grid.height() - padding * 2) / 2.0
         x        = rect.center().x()
         y        = rect.center().y()
         dx       = radius * 2.5
         dy       = 0
     else:
         radius   = (grid.width() - padding * 2) / 2.0
         x        = rect.center().x()
         y        = rect.center().y()
         dx       = 0
         dy       = radius * 2.5
     
     for item in items:
         item.setPieCenter(QPointF(x, y))
         item.setRadius(radius)
         item.rebuild()
         
         x += dx
         y += dy
     
     if ( self._trackerItem and self._trackerItem() ):
         self._trackerItem().rebuild(self._buildData['grid_rect'])
Beispiel #11
0
    def calculateDatasets(self, scene, axes, datasets):
        """
        Builds the datasets for this renderer.  Each renderer will need to
        subclass and implemenent this method, otherwise, no data will be
        shown in the chart.
        
        :param      scene | <XChartScene>
                    axes | [<
                    datasets | [<XChartDataset>, ..]
        """
        items = self.calculateDatasetItems(scene, datasets)
        if not items:
            scene.clear()
            return

        xaxis = scene.chart().horizontalAxis()
        yaxis = scene.chart().verticalAxis()
        data_axis = None
        all_values = []

        # determine if we're mapping data aginst the sets themselves, in
        # which case, create a pie chart of the dataset vs. its
        if isinstance(xaxis, XDatasetAxis):
            per_dataset = False
            data_axis = yaxis
            total = 1

        elif isinstance(yaxis, XDatasetAxis):
            per_dataset = False
            total = 1
            data_axis = xaxis

        else:
            per_dataset = True
            total = len(items)

        if not per_dataset:
            all_values = [dataset.sum(data_axis) \
                          for dataset in datasets]

        # generate the build information
        rect = self.buildData('grid_rect')
        rect.setX(rect.x() + 10)
        rect.setY(rect.y() + 10)
        rect.setWidth(rect.width() - 20)
        rect.setHeight(rect.height() - 20)

        if rect.width() > rect.height():
            radius = min(rect.width() / 2.0, rect.height() / 2.0)
            x = rect.left()
            y = rect.top() + radius
            deltax = min(radius * 2, rect.width() / float(total + 1))
            deltay = 0
        else:
            radius = min(rect.height() / 2.0, rect.width() / 2.0)
            x = rect.left() + radius
            y = rect.top()
            deltax = 0
            deltay = min(radius * 2, rect.height() / float(total + 1))

        # initialize the first pie chart
        angle = 0
        cx = x + deltax
        cy = y + deltay

        x += deltax
        y += deltay

        self.setBuildData('center', QPointF(cx, cy))
        self.setBuildData('radius', radius)

        for dataset in datasets:
            item = items.get(dataset)
            if not item:
                continue

            item.setBuildData('center', QPointF(cx, cy))
            item.setBuildData('radius', radius)

            subpaths = []
            bound = QRectF(cx - radius, cy - radius, radius * 2, radius * 2)
            path = QPainterPath()
            if per_dataset:
                data_values = dataset.values(yaxis.name())
                andle = 0
                for value in dataset.values():
                    perc = yaxis.percentOfTotal(value.get(yaxis.name()),
                                                data_values)

                    # calculate the angle as the perc
                    item_angle = perc * 360

                    subpath = QPainterPath()
                    subpath.moveTo(cx, cy)
                    subpath.arcTo(bound, angle, item_angle)
                    subpath.lineTo(cx, cy)

                    path.addPath(subpath)
                    subpaths.append((value.get(xaxis.name()), subpath))

                    angle += item_angle

                cx = x + deltax
                cy = y + deltay

                x += deltax
                y += deltay
            else:
                value = dataset.sum(data_axis)
                perc = data_axis.percentOfTotal(value, all_values)

                # calculate the angle as the perc
                item_angle = perc * 360

                subpath = QPainterPath()
                subpath.moveTo(cx, cy)
                subpath.arcTo(bound, angle, item_angle)
                subpath.lineTo(cx, cy)

                path.addPath(subpath)
                subpaths.append((value, subpath))

                angle += item_angle

            item.setPath(path)
            item.setBuildData('subpaths', subpaths)
Beispiel #12
0
 def rebuildDays( self ):
     """
     Rebuilds the interface as a week display.
     """
     time = QTime(0, 0, 0)
     hour = True
     
     x = 6
     y = 6 + 24
     
     w = self.width() - 12 - 25
     
     dh         = 48
     indent     = 58
     text_data  = []
     
     vlines      = []
     hlines      = [QLine(x, y, w, y)]
     time_grids  = []
     
     for i in range(48):
         if ( hour ):
             hlines.append(QLine(x, y, w, y))
             text_data.append((x,
                               y + 6, 
                               indent - 6, 
                               dh, 
                               Qt.AlignRight | Qt.AlignTop,
                               time.toString('hap')))
         else:
             hlines.append(QLine(x + indent, y, w, y))
         
         time_grids.append((time, y, dh / 2))
         
         # move onto the next line
         hour = not hour
         time = time.addSecs(30 * 60)
         y += dh / 2
     
     hlines.append(QLine(x, y, w, y))
     
     h = y
     y = 6 + 24
     
     # load the grid
     vlines.append(QLine(x, y, x, h))
     vlines.append(QLine(x + indent, y, x + indent, h))
     vlines.append(QLine(w, y, w, h))
     
     today     = QDate.currentDate()
     curr_date = self.currentDate()
     
     # load the days
     if ( self.currentMode() == XCalendarScene.Mode.Week ):
         date = self.currentDate()
         day_of_week = date.dayOfWeek()
         if ( day_of_week == 7 ):
             day_of_week = 0
         
         min_date = date.addDays(-day_of_week)
         max_date = date.addDays(6-day_of_week)
         
         self._minimumDate = min_date
         self._maximumDate = max_date
         
         dw    = (w - (x + indent)) / 7.0
         vx    = x + indent
         date  = min_date
         
         for i in range(7):
             vlines.append(QLine(vx, y, vx, h))
             
             text_data.append((vx + 6,
                               6,
                               dw,
                               24,
                               Qt.AlignCenter,
                               date.toString('ddd MM/dd')))
             
             self._dateGrid[date.toJulianDay()] = ((0, i),
                                                   QRectF(vx, y, dw, h - y))
             
             # create the date grid for date time options
             for r, data in enumerate(time_grids):
                 time, ty, th = data
                 dtime = QDateTime(date, time)
                 key = dtime.toTime_t()
                 self._dateTimeGrid[key] = ((r, i), QRectF(vx, ty, dw, th))
             
             if ( date == curr_date ):
                 self._buildData['curr_date'] = QRectF(vx, y, dw, h - 29)
             elif ( date == today ):
                 self._buildData['today'] = QRectF(vx, y, dw, h - 29)
             
             date = date.addDays(1)
             vx += dw
     
     # load a single day
     else:
         date = self.currentDate()
         
         self._maximumDate = date
         self._minimumDate = date
         
         text_data.append((x + indent,
                           6,
                           w,
                           24,
                           Qt.AlignCenter,
                           date.toString('ddd MM/dd')))
         
         self._dateGrid[date.toJulianDay()] = ((0, 0), 
                                               QRectF(x, y, w - x, h - y))
         
         # create the date grid for date time options
         for r, data in enumerate(time_grids):
             time, ty, th = data
             dtime = QDateTime(date, time)
             key = dtime.toTime_t()
             rect = QRectF(x + indent, ty, w - (x + indent), th)
             self._dateTimeGrid[key] = ((r, 0), rect)
     
     self._buildData['grid'] = hlines + vlines
     self._buildData['regular_text'] = text_data
     
     rect = self.sceneRect()
     rect.setHeight(h + 6)
     
     super(XCalendarScene, self).setSceneRect(rect)
Beispiel #13
0
 def rebuildMonth( self ):
     """
     Rebuilds the month for this scene.
     """
     # make sure we start at 0 for sunday vs. 7 for sunday
     day_map     = dict([(i+1, i+1) for i in range(7)])
     day_map[7]  = 0
     
     today   = QDate.currentDate()
     curr    = self.currentDate()
     first   = QDate(curr.year(), curr.month(), 1)
     last    = QDate(curr.year(), curr.month(), curr.daysInMonth())
     first   = first.addDays(-day_map[first.dayOfWeek()])
     last    = last.addDays(6-day_map[last.dayOfWeek()])
     
     cols    = 7
     rows    = (first.daysTo(last) + 1) / cols
     
     hlines  = []
     vlines  = []
     
     padx    = 6
     pady    = 6
     header  = 24
     
     w       = self.width() - (2 * padx)
     h       = self.height() - (2 * pady)
     
     dw      = (w / cols) - 1
     dh      = ((h - header) / rows) - 1
     
     x0      = padx
     y0      = pady + header
     
     x       = x0
     y       = y0
     
     for row in range(rows + 1):
         hlines.append(QLine(x0, y, w, y))
         y += dh
     
     for col in range(cols + 1):
         vlines.append(QLine(x, y0, x, h))
         x += dw
     
     self._buildData['grid'] = hlines + vlines
     
     # draw the date fields
     date = first
     row  = 0
     col  = 0
     
     # draw the headers
     x = x0
     y = pady
     
     regular_text = []
     mid_text     = []
     self._buildData['regular_text'] = regular_text
     self._buildData['mid_text']     = mid_text
     
     for day in ('Sun', 'Mon','Tue','Wed','Thu','Fri','Sat'):
         regular_text.append((x + 5,
                              y,
                              dw,
                              y0,
                              Qt.AlignLeft | Qt.AlignVCenter,
                              day))
         x += dw
     
     for i in range(first.daysTo(last) + 1):
         top    = (y0 + (row * dh))
         left   = (x0 + (col * dw))
         rect   = QRectF(left - 1, top, dw, dh)
         
         # mark the current date on the calendar
         if ( date == curr ):
             self._buildData['curr_date'] = rect
         
         # mark today's date on the calendar
         elif ( date == today ):
             self._buildData['today'] = rect
         
         # determine how to draw the calendar
         format = 'd'
         if ( date.day() == 1 ):
             format = 'MMM d'
         
         # determine the color to draw the text
         if ( date.month() == curr.month() ):
             text = regular_text
         else:
             text = mid_text
         
         # draw the text
         text.append((left + 2,
                      top + 2,
                      dw - 4,
                      dh - 4,
                      Qt.AlignTop | Qt.AlignLeft,
                      date.toString(format)))
         
         # update the limits
         if ( not i ):
             self._minimumDate = date
         self._maximumDate = date
         
         self._dateGrid[date.toJulianDay()] = ((row, col), rect)
         if ( col == (cols - 1) ):
             row += 1
             col = 0
         else:
             col += 1
             
         date = date.addDays(1)
Beispiel #14
0
    def calculate(self, scene, xaxis, yaxis):
        """
        Calculates the grid data before rendering.
        
        :param      scene       | <XChartScene>
                    xaxis       | <XChartAxis>
                    yaxis       | <XChartAxis>
        """
        # build the grid location
        sceneRect = scene.sceneRect()

        # process axis information
        h_lines = []
        h_alt = []
        h_labels = []
        v_lines = []
        v_alt = []
        v_labels = []

        xlabels = []
        xcount = 1
        xsections = 1
        xdelta = 0
        xdeltamin = 0

        ylabels = []
        ycount = 1
        ysections = 1
        ydeltamin = 0
        ydelta = 0

        axis_lft = 0
        axis_rht = 0
        axis_bot = 0
        axis_top = 0

        # precalculate xaxis information for width changes
        if xaxis and self.showXAxis():
            size = sceneRect.width()
            xdeltamin = xaxis.minimumLabelWidth()
            result = self.calculateAxis(xaxis, size, xdeltamin)

            xlabels, xcount, xsections, newWidth, xdelta = result
            if newWidth != size:
                sceneRect.setWidth(newWidth)

        # precalculate yaxis information for height changes
        if yaxis and self.showYAxis():
            size = sceneRect.height()
            ydeltamin = yaxis.minimumLabelHeight()
            result = self.calculateAxis(yaxis, size, ydeltamin)

            ylabels, ycount, ysections, newHeight, ydelta = result
            if newHeight != size:
                sceneRect.setHeight(newHeight)

        # generate the xaxis
        if xaxis and self.showXAxis():
            x = sceneRect.left() + xdeltamin / 2
            axis_lft = x
            axis_rht = x
            alt = False

            for i in range(xcount):
                v_lines.append(
                    QLineF(x, sceneRect.top(), x, sceneRect.bottom()))

                # store alternate info
                if alt:
                    alt_rect = QRectF(x - xdelta, sceneRect.top(), xdelta,
                                      sceneRect.height())
                    v_alt.append(alt_rect)

                # store label information
                v_labels.append((x, xdelta, xlabels[i]))
                axis_rht = x

                x += xdelta
                alt = not alt

        # generate the yaxis
        if yaxis and self.showYAxis():
            y = sceneRect.bottom() - ydeltamin / 2
            axis_bot = y
            axis_top = y
            alt = False

            for i in range(ycount):
                h_lines.append(
                    QLineF(sceneRect.left(), y, sceneRect.right(), y))

                # store alternate color
                if alt:
                    alt_rect = QRectF(sceneRect.left(), y, sceneRect.width(),
                                      ydelta)
                    h_alt.append(alt_rect)

                # store the vertical information
                h_labels.append((y, ydelta, ylabels[i]))
                axis_top = y

                y -= ydelta
                alt = not alt

        # assign the build data
        self._buildData['grid_h_lines'] = h_lines
        self._buildData['grid_h_alt'] = h_alt
        self._buildData['grid_h_labels'] = h_labels
        self._buildData['grid_v_lines'] = v_lines
        self._buildData['grid_v_alt'] = v_alt
        self._buildData['grid_v_labels'] = v_labels
        self._buildData['grid_rect'] = sceneRect
        self._buildData['axis_rect'] = QRectF(axis_lft, axis_top,
                                              axis_rht - axis_lft,
                                              axis_bot - axis_top)

        scene.setSceneRect(sceneRect)
        return sceneRect
Beispiel #15
0
 def layout(self,
            scene,
            nodes,
            center=None,
            padX=None,
            padY=None,
            direction=None,
            animationGroup=None):
     """
     Lays out the nodes for this scene based on a block layering algorithm.
     
     :param      scene          | <XNodeScene>
                 nodes          | [<XNode>, ..]
                 center         | <QPointF> || None
                 padX           | <int> || None
                 padY           | <int> || None
                 direction      | <Qt.Direction>
                 animationGroup | <QAnimationGroup> || None
     
     :return     {<XNode>: <QRectF>, ..} | new rects per affected node
     """
     nodes = filter(lambda x: x is not None and x.isVisible(), nodes)
     
     # make sure we have at least 1 node, otherwise, it is already laid out
     if not nodes or len(nodes) == 1:
         return {}
     
     # calculate the default padding based on the scene
     if padX == None:
         if direction == Qt.Vertical:
             padX = 2 * scene.cellWidth()
         else:
             padX = 4 * scene.cellWidth()
         
     if padY == None:
         if direction == Qt.Vertical:
             padY = 4 * scene.cellHeight()
         else:
             padY = 2 * scene.cellWidth()
     
     # step 1: create a mapping of the connections
     connection_map = self.connectionMap(scene, nodes)
     
     # step 2: organize the nodes into layers based on their connection chain
     layers = self.generateLayers(scene, nodes, connection_map)
     
     # step 3: calculate the total dimensions for the layout
     bounds = QRectF()
     
     # step 3.1: compare the nodes together that have common connections
     layer_widths = []
     layer_heights = []
     node_heights = {}
     node_widths = {}
     
     for layer_index, layer in enumerate(layers):
         layer_w = 0
         layer_h = 0
         
         layer_node_w = []
         layer_node_h = []
         
         for node in layer:
             rect = node.rect()
             
             layer_node_w.append(rect.width())
             layer_node_h.append(rect.height())
             
             if direction == Qt.Vertical:
                 layer_w += rect.width()
                 layer_h = max(rect.height(), layer_h)
             else:
                 layer_w = max(rect.width(), layer_w)
                 layer_h += rect.height()
         
         # update the bounding area
         if direction == Qt.Vertical:
             layer_w += padX * 1 - len(layer)
             bounds.setWidth(max(layer_w, bounds.width()))
             bounds.setHeight(bounds.height() + layer_h)
         else:
             layer_h += padY * 1 - len(layer)
             bounds.setWidth(bounds.width() + layer_w)
             bounds.setHeight(max(layer_h, bounds.height()))
         
         node_widths[layer_index] = layer_node_w
         node_heights[layer_index] = layer_node_h
         
         layer_widths.append(layer_w)
         layer_heights.append(layer_h)
         
     if not center:
         center = scene.sceneRect().center()
     
     w = bounds.width()
     h = bounds.height()
     bounds.setX(center.x() - bounds.width() / 2.0)
     bounds.setY(center.y() - bounds.height() / 2.0)
     bounds.setWidth(w)
     bounds.setHeight(h)
     
     # step 4: assign positions for each node by layer
     processed_nodes = {}
     layer_grps = [(i, layer) for i, layer in enumerate(layers)]
     layer_grps.sort(key=lambda x: len(x[1]))
     
     used_rects = [n.sceneRect() for n in scene.nodes() if not n in nodes]
     
     for layer_index, layer in reversed(layer_grps):
         layer_width  = layer_widths[layer_index]
         layer_height = layer_heights[layer_index]
         
         # determine the starting point for this layer
         if direction == Qt.Vertical:
             offset = layer_index * padY + sum(layer_heights[:layer_index])
             point = QPointF(bounds.x(), offset + bounds.y())
         else:
             offset = layer_index * padX + sum(layer_widths[:layer_index])
             point = QPointF(offset + bounds.x(), bounds.y())
         
         # assign node positions based on existing connections
         for node_index, node in enumerate(layer):
             max_, min_ = (None, None)
             inputs, outputs = connection_map[node]
             for connected_node in inputs + outputs:
                 if not connected_node in processed_nodes:
                     continue
                 
                 npos  = processed_nodes[connected_node]
                 nrect = connected_node.rect()
                 rect  = QRectF(npos.x(), 
                                npos.y(), 
                                nrect.width(), 
                                nrect.height())
                 
                 if direction == Qt.Vertical:
                     if min_ is None:
                         min_ = rect.left()
                     min_ = min(rect.left(), min_)
                     max_ = max(rect.right(), max_)
                 else:
                     if min_ is None:
                         min_ = rect.top()
                     min_ = min(rect.top(), min_)
                     max_ = max(rect.bottom(), max_)
             
             if direction == Qt.Vertical:
                 off_x = 0
                 off_y = (layer_height - node.rect().height()) / 2.0
                 start_x = (bounds.width() - layer_width)
                 start_y = 0
             else:
                 off_x = (layer_width - node.rect().width()) / 2.0
                 off_y = 0
                 start_x = 0
                 start_y = (bounds.height() - layer_height)
             
             # align against existing nodes
             point_x = -1
             point_y = -1
             offset = 0
             before = True
             found_point = True
             new_rect = QRectF()
             
             while found_point:
                 if not None in (min_, max_):
                     if direction == Qt.Vertical:
                         off_x = (max_ - min_)/2.0 - node.rect().width()/2.0
                         
                         if before:
                             off_x -= offset
                             offset += node.rect().width() + padX
                         else:
                             off_x += offset
                         
                         point_x = min_ + off_x
                         point_y = point.y() + off_y
                     else:
                         off_y = (max_ - min_)/2.0 - node.rect().height()/2.0
                         
                         if before:
                             off_y -= offset
                             offset += node.rect().height() + padY
                         else:
                             off_y += offset
                         
                         point_x = point.x() + off_x
                         point_y = min_ + off_y
                     
                 # otherwise, align based on its position in the layer
                 else:
                     if direction == Qt.Vertical:
                         off_x  = sum(node_widths[layer_index][:node_index])
                         off_x += node_index * padX
                         off_x += start_x
                         
                         if before:
                             off_x -= offset
                             offset += node.rect().width() + padX
                         else:
                             off_x += offset
                         
                         point_x = point.x() + off_x
                         point_y = point.y() + off_y
                     else:
                         off_y  = sum(node_heights[layer_index][:node_index])
                         off_y += node_index * padY
                         off_y += start_y
                         
                         if before:
                             off_y -= offset
                             offset += node.rect().height() + padY
                         else:
                             off_y += offset
                         
                         point_x = point.x() + off_x
                         point_y = point.y() + off_y
                 
                 # determine if we've already used this point before
                 before = not before
                 found_point = False
                 orect = node.rect()
                 new_rect = QRectF(point_x, point_y,
                                   orect.width(), orect.height())
                 for used_rect in used_rects:
                     if used_rect.intersects(new_rect):
                         found_point = True
                         break
             
             used_rects.append(new_rect)
             
             if not animationGroup:
                 node.setPos(point_x, point_y)
             else:
                 anim = XNodeAnimation(node, 'setPos')
                 anim.setStartValue(node.pos())
                 anim.setEndValue(QPointF(point_x, point_y))
                 animationGroup.addAnimation(anim)
                 
             processed_nodes[node] = QPointF(point_x, point_y)
             
             if self._testing:
                 QApplication.processEvents()
                 time.sleep(0.5)
     
     return processed_nodes