00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 from PyQt4.QtCore import *
00019 from PyQt4.QtGui import *
00020 from AbstractGraphicsChartItem import *
00021 
00022 
00023 class BarChartBar(QGraphicsRectItem):
00024         def __init__(self, parent):
00025                 QGraphicsRectItem.__init__(self, parent)
00026 
00027 class AxisItem(QGraphicsPathItem):
00028         def __init__(self, parent):
00029                 QGraphicsPathItem.__init__(self, parent)
00030                 self._minimum = 0
00031                 self._maximum = 10
00032                 self._labels = None
00033                 self._orientation = Qt.Vertical
00034                 self._size = 100
00035                 self._items = []
00036                 self._showLabels = True
00037 
00038         def setMinimum( self, minimum ):
00039                 self._minimum = minimum
00040                 self.updatePath()
00041         
00042         def setMaximum( self, maximum ):
00043                 self._maximum = maximum
00044                 self.updatePath()
00045 
00046         def setLabels( self, labels ):
00047                 self._labels = labels
00048                 self._showLabels = True
00049                 self.updatePath()
00050 
00051         def hideLabels( self ):
00052                 self._showLabels = False
00053                 self.updatePath()
00054 
00055         def showLabels( self ):
00056                 self._showLabels = True
00057                 self.updatePath()
00058 
00059         def setSize( self, size ):
00060                 self._size = size
00061                 self.updatePath()
00062 
00063         def setOrientation( self, orientation ):
00064                 self._orientation = orientation
00065                 self.updatePath()
00066 
00067         def clear(self):
00068                 for item in self._items:
00069                         item.setParentItem( None )
00070                         del item
00071                 self._items = []
00072 
00073         def updatePath(self):
00074                 self.clear()
00075                 if self._minimum > self._maximum:
00076                         tmp = self._maximum
00077                         self._maximum = self._minimum
00078                         self._minimum = tmp
00079                 path = QPainterPath()   
00080                 path.moveTo( 0, 0 )
00081                 if self._orientation == Qt.Vertical:
00082                         path.lineTo( 0, self._size )
00083                 else:
00084                         path.lineTo( self._size, 0 )
00085                 
00086                 if self._showLabels:
00087                         font = QFont()
00088                         metrics = QFontMetrics( font )
00089 
00090                         diff = self._maximum - self._minimum
00091                         if self._labels:
00092                                 items = len(self._labels)
00093                         else:
00094                                 items = int( round( self._size / ( metrics.height() * 1.5 ) ) )
00095                                 items = max( min( items, 10 ), 1 )
00096 
00097                         width = float(self._size) / items
00098                         if not self._labels:
00099                                 items += 1
00100                         offset = width / 2
00101                         for x in range(items):
00102                                 p = x * width
00103                                 item = QGraphicsSimpleTextItem( self )
00104                                 if self._labels:
00105                                         text = self._labels[x]
00106                                 else:
00107                                         text = '%.2f' % ( self._minimum + ( diff / (items-1) ) * ( (items-1) - x ) )
00108                                 item.setText( text )
00109 
00110                                 if self._orientation == Qt.Vertical:
00111                                         path.moveTo( -5, p )
00112                                         path.lineTo( 0, p )
00113                                         item.setPos( -10 - metrics.width( text ), p - metrics.boundingRect(text).height() / 2.5 )
00114                                 else:
00115                                         path.moveTo( p + offset, 5 )
00116                                         path.lineTo( p + offset, 0 )
00117                                         item.setPos( p + offset, metrics.lineSpacing()  )
00118                                         item.rotate( 45 )
00119                                 self._items.append( item )
00120                 self.setPath( path )
00121 
00122 
00123 class GraphicsBarChartItem(AbstractGraphicsChartItem):
00124         def __init__(self, parent=None):
00125                 AbstractGraphicsChartItem.__init__(self, parent)
00126                 self._isAggregated = False
00127                 self.yAxis = AxisItem( self )
00128                 self.yAxis.hide()
00129                 self.xAxis = AxisItem( self )
00130                 self.xAxis.hide()
00131                 
00132                 self.xAxisNegative = AxisItem( self )
00133                 self.xAxisNegative.hide()
00134 
00135         def setAggregated(self, value):
00136                 self._isAggregated = value
00137 
00138         def isAggregated(self):
00139                 return self._isAggregated
00140 
00141         
00142         def barCount( self ):
00143                 if self._isAggregated:
00144                         if self._values:
00145                                 count = len(self._values)
00146                         else:
00147                                 count = len(self._categories)
00148                         if not count:
00149                                 count = 1
00150                 else:
00151                         count = len(flatten(self._values))
00152                         if not count:
00153                                 count = len(self._categories) * len(self._labels)
00154                         if not count:
00155                                 count = 1
00156                 return count
00157 
00158         def separatorCount( self ):
00159                 return len(self._categories) - 1
00160 
00161         def separatorWidth( self ):
00162                 defaultWidth = 5
00163                 if self.separatorCount() * defaultWidth >= self.width():
00164                         return 0
00165                 else:
00166                         return defaultWidth
00167                 
00168         def barWidth( self ):
00169                 return ( self.width() - self.separatorWidth() * self.separatorCount() ) / float(self.barCount())
00170 
00171         def updateChart(self):
00172                 self.clear()
00173                 if self._isAggregated:
00174                         vs = [sum(x) for x in self._values]
00175                 else:
00176                         vs = flatten(self._values)
00177                 if len(vs):
00178                         maximum = max(vs)
00179                         minimum = min(vs)
00180                 else:
00181                         maximum = 1.0
00182                         minimum = 0.0
00183                 if self._isAggregated:
00184                         minimum = 0.0
00185                 diff = maximum - minimum
00186                 
00187                 
00188                 if diff == 0:
00189                         
00190                         
00191                         if maximum >= 0:
00192                                 minimum = 0
00193                         else:
00194                                 maximum = 0
00195                         diff = maximum - minimum
00196                         
00197                         
00198                         if diff == 0:
00199                                 diff = 1.0
00200                                 maximum = 1.0
00201 
00202                 
00203                 if len(self._values):
00204                         self.xAxis.show()
00205                         self.yAxis.show()
00206                         self.xAxisNegative.show()
00207                 else:
00208                         self.xAxis.hide()
00209                         self.yAxis.hide()
00210                         self.xAxisNegative.hide()
00211 
00212                 lastPosition = 0
00213                 maximumHeight = self.height()
00214                 barWidth = self.barWidth()
00215                 separatorWidth = self.separatorWidth()
00216                 
00217                 zero = ( (0.0 - minimum) / diff * maximumHeight ) 
00218                 
00219                 zero = min( max( 0.0, zero ), maximumHeight )
00220                 for i in range(len(self._values)):
00221                         aggValues = 0.0
00222                         lastYPosition = 0.0
00223                         manager = ColorManager( len(self._values[i]) )
00224                         for j in range(len(self._values[i])):
00225                                 value = self._values[i][j]
00226                                 if self._isAggregated:
00227                                         if aggValues + value > minimum:
00228                                                 height = abs( ( (aggValues + value - minimum) / diff ) * maximumHeight ) 
00229                                                 
00230                                                 height = min( max( 0.0, height ), maximumHeight )
00231                                                 height -= lastYPosition
00232                                         else:
00233                                                 height = 0.0
00234                                 else:
00235                                         height = abs( ( (value-minimum) / diff ) * maximumHeight )
00236                                         
00237                                         height = min( max( 0.0, height ), maximumHeight )
00238 
00239                                 item = BarChartBar( self )
00240                                 if j < len(self._labels):
00241                                         item.setToolTip( '%s: %.2f' % (self._labels[j], value) )
00242                                 item.setBrush( manager.brush(j) )
00243                                 item.setPen( manager.pen(j) )
00244                                 if value >= 0.0:
00245                                         item.setRect( lastPosition, maximumHeight - lastYPosition - height - zero, barWidth, height )
00246                                 else:
00247                                         item.setRect( lastPosition, maximumHeight - lastYPosition - zero, barWidth, height )
00248                                 
00249                                 
00250                                 if height == 0.0:
00251                                         item.hide()
00252 
00253                                 self.addToGroup( item )
00254                                 self._items.append( item )
00255 
00256                                 if self._isAggregated:
00257                                         lastYPosition += height
00258                                         aggValues += value
00259                                 else:
00260                                         lastPosition += barWidth
00261                         lastPosition += separatorWidth
00262                         if self._isAggregated:
00263                                 lastPosition += barWidth
00264 
00265                 self.yAxis.setMinimum( minimum )
00266                 self.yAxis.setMaximum( maximum )
00267                 self.yAxis.setOrientation( Qt.Vertical )
00268                 self.yAxis.setSize( maximumHeight )
00269                 self.yAxis.setZValue( 1 )
00270                 self.addToGroup( self.yAxis )
00271 
00272                 self.xAxis.setLabels( self._categories )
00273                 self.xAxis.setOrientation( Qt.Horizontal )
00274                 self.xAxis.setSize( self.width() )
00275                 self.xAxis.setPos( 0, maximumHeight )
00276                 self.xAxis.setZValue( 1 )
00277                 self.addToGroup( self.xAxis )
00278 
00279                 if minimum < 0.0:
00280                         self.xAxisNegative.hideLabels()
00281                         self.xAxisNegative.setOrientation( Qt.Horizontal )
00282                         self.xAxisNegative.setSize( self.width() )
00283                         
00284                         if maximum < 0:
00285                                 self.xAxisNegative.setPos( 0, 0 )
00286                         else:
00287                                 pos = maximumHeight - ( 0 - minimum / diff * maximumHeight )
00288                                 self.xAxisNegative.setPos( 0, pos )
00289                         self.xAxisNegative.setZValue( 1 )
00290                         self.addToGroup( self.xAxisNegative )
00291                 else:
00292                         self.xAxisNegative.hide()
00293 
00294                 self._legend.place()
00295 
00296         def setCategories( self, categories ):
00297                 self._categories = categories
00298                 self.xAxis.setLabels( self._categories )
00299 
00300         def setData( self, data ):
00301                 
00302                 
00303                 categories = []
00304                 labels = []
00305                 if data:
00306                         for x in data:
00307                                 categories.append( x['name'] )
00308                         for y in data[0].keys():
00309                                 labels.append( y )
00310                         categories.sort()
00311                 self.setCategories( categories )
00312                 self.setLabels( labels )
00313                 values = []
00314                 for x in data:
00315                         value = []
00316                         for y in labels:
00317                                 value.append( x['values'][y] )
00318                         values.append( value )  
00319                 self.setValues( values )
00320