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