[4] | 1 | ############################################################################
|
---|
| 2 | #
|
---|
| 3 | # SAGE UI - A Graphical User Interface for SAGE
|
---|
| 4 | # Copyright (C) 2005 Electronic Visualization Laboratory,
|
---|
| 5 | # University of Illinois at Chicago
|
---|
| 6 | #
|
---|
| 7 | # All rights reserved.
|
---|
| 8 | #
|
---|
| 9 | # Redistribution and use in source and binary forms, with or without
|
---|
| 10 | # modification, are permitted provided that the following conditions are met:
|
---|
| 11 | #
|
---|
| 12 | # * Redistributions of source code must retain the above copyright
|
---|
| 13 | # notice, this list of conditions and the following disclaimer.
|
---|
| 14 | # * Redistributions in binary form must reproduce the above
|
---|
| 15 | # copyright notice, this list of conditions and the following disclaimer
|
---|
| 16 | # in the documentation and/or other materials provided with the distribution.
|
---|
| 17 | # * Neither the name of the University of Illinois at Chicago nor
|
---|
| 18 | # the names of its contributors may be used to endorse or promote
|
---|
| 19 | # products derived from this software without specific prior written permission.
|
---|
| 20 | #
|
---|
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
---|
| 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
---|
| 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
---|
| 24 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
---|
| 25 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
---|
| 26 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
---|
| 27 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
---|
| 28 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
---|
| 29 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
---|
| 30 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
---|
| 31 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
| 32 | #
|
---|
| 33 | # Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum
|
---|
| 34 | #
|
---|
| 35 | # Author: Allan Spale
|
---|
| 36 | # Ratko Jagodic
|
---|
| 37 | #
|
---|
| 38 | ############################################################################
|
---|
| 39 |
|
---|
| 40 |
|
---|
| 41 | import wx
|
---|
| 42 | import time
|
---|
| 43 | import wx.lib.newevent
|
---|
| 44 | import thread
|
---|
| 45 |
|
---|
| 46 | # (AKS 2005-04-14) Modified a single line of wx.lib.plot to remove
|
---|
| 47 | # an obstructed call if the platform was Mac
|
---|
| 48 | #import plot
|
---|
| 49 | from wx.lib import plot
|
---|
| 50 |
|
---|
| 51 | import string
|
---|
| 52 | import random
|
---|
| 53 | import sys
|
---|
| 54 | import time
|
---|
| 55 | import os
|
---|
| 56 | from globals import *
|
---|
| 57 | from Mywx import *
|
---|
| 58 |
|
---|
| 59 | # This creates a new Event class and a EVT binder function
|
---|
| 60 | (UpdateDataEvent, EVT_UPDATE_GRAPH) = wx.lib.newevent.NewEvent()
|
---|
| 61 |
|
---|
| 62 |
|
---|
| 63 |
|
---|
| 64 |
|
---|
| 65 |
|
---|
| 66 |
|
---|
| 67 |
|
---|
| 68 | ############################################################################
|
---|
| 69 |
|
---|
| 70 | # it gets the data from the SAGE messages (sageUIdataInfo).
|
---|
| 71 | # this is the actual graph (lines and data)
|
---|
| 72 | #
|
---|
| 73 |
|
---|
| 74 | class PerformanceGraph:
|
---|
| 75 | def __init__( self, appName, stDataTitle, iArraySize, iPerfUpdateInterval ):
|
---|
| 76 |
|
---|
| 77 | # CLASS CONSTANTS
|
---|
| 78 | # self.__fMin, self.__fMax, self.__fAvg, self.__fCurrent
|
---|
| 79 | self.I_MINIMUM = 0
|
---|
| 80 | self.I_MAXIMUM = 1
|
---|
| 81 | self.I_AVERAGE = 2
|
---|
| 82 | self.I_CURRENT = 3
|
---|
| 83 |
|
---|
| 84 | # instance variables
|
---|
| 85 | self.__fAvg = 0.0
|
---|
| 86 | self.__fMax = 0.0
|
---|
| 87 | self.__fMin = 0.0
|
---|
| 88 | self.__fCurrent = 0.0
|
---|
| 89 | self.__iArraySize = iArraySize
|
---|
| 90 | self.__stDataTitle = stDataTitle
|
---|
| 91 | self.__stLabel = ""
|
---|
| 92 | self.__stAppName = appName
|
---|
| 93 |
|
---|
| 94 | # (AKS 2005-05-03) Performance update interval
|
---|
| 95 | self.__iPerformanceUpdateInterval = iPerfUpdateInterval
|
---|
| 96 |
|
---|
| 97 |
|
---|
| 98 | # create panel
|
---|
| 99 | #self.__wxpGraphPanel = wx.Panel( self.__wxContainer, id=wx.ID_ANY,
|
---|
| 100 | # pos=(-1,-1), size=(-1,-1) )
|
---|
| 101 |
|
---|
| 102 | # setup data structure
|
---|
| 103 | self.__naData = [ [0]*2 for i in range(self.__iArraySize) ]
|
---|
| 104 |
|
---|
| 105 |
|
---|
| 106 | # (AKS 2004-10-26) Because data is in reverse order (most recent value
|
---|
| 107 | # is index 0), it is necessary to reverse the x values in the array
|
---|
| 108 | iTimeValue = -(self.__iArraySize-1) * self.__iPerformanceUpdateInterval
|
---|
| 109 | for index in range( 0, self.__iArraySize ):
|
---|
| 110 | # print self.__iArraySize - index - 1
|
---|
| 111 | #RJ self.__naData[ self.__iArraySize - index - 1, 0 ] = iTimeValue
|
---|
| 112 | self.__naData[ self.__iArraySize - index - 1][ 0 ] = iTimeValue
|
---|
| 113 |
|
---|
| 114 | iTimeValue = iTimeValue + self.__iPerformanceUpdateInterval
|
---|
| 115 |
|
---|
| 116 | #self.__naData[ index - self.__iArraySize, 0 ] = index
|
---|
| 117 |
|
---|
| 118 |
|
---|
| 119 | # create graph
|
---|
| 120 | self.__lines = plot.PolyLine(self.__naData, colour='red', width=3)
|
---|
| 121 |
|
---|
| 122 | self.__graph = plot.PlotGraphics( [self.__lines],"", "Time (s)",
|
---|
| 123 | self.__stDataTitle)
|
---|
| 124 |
|
---|
| 125 | #------------------------------------------------------------------------------
|
---|
| 126 |
|
---|
| 127 | def update( self, naNewData ):
|
---|
| 128 | # Because naData is sent as a reference, it is necessary to copy the
|
---|
| 129 | # data from naNewData to naData to preserve these references
|
---|
| 130 |
|
---|
| 131 | # (AKS 2005-05-09)
|
---|
| 132 | self.__fCurrent = float( naNewData[ 0 ] )
|
---|
| 133 |
|
---|
| 134 | fSum = 0.0
|
---|
| 135 |
|
---|
| 136 | # (AKS 2005-02-08) Max and min for a range
|
---|
| 137 | self.__fAvg = 0.0
|
---|
| 138 | self.__fMax = 0.0
|
---|
| 139 | self.__fMin = float( sys.maxint )
|
---|
| 140 |
|
---|
| 141 | for index in range( 0, self.__iArraySize ):
|
---|
| 142 | # (AKS 2005-05-09)
|
---|
| 143 | item = float( naNewData[ index ] )
|
---|
| 144 |
|
---|
| 145 | #RJ self.__naData[ index, 1 ] = item
|
---|
| 146 | self.__naData[ index ][ 1 ] = item
|
---|
| 147 | fSum = fSum + item
|
---|
| 148 |
|
---|
| 149 | # Set maximum value
|
---|
| 150 | if ( item > self.__fMax ):
|
---|
| 151 | self.__fMax = item
|
---|
| 152 |
|
---|
| 153 | # Set minimum value
|
---|
| 154 | elif ( item < self.__fMin ):
|
---|
| 155 | self.__fMin = item
|
---|
| 156 |
|
---|
| 157 | # Calculate average
|
---|
| 158 | #self.__fAvg = fSum / self.__iArraySize
|
---|
| 159 |
|
---|
| 160 | # (AKS 2005-05-09)
|
---|
| 161 | self.__fAvg = float( fSum / self.__iArraySize )
|
---|
| 162 |
|
---|
| 163 |
|
---|
| 164 | # (AKS 2005-03-08) Update graph
|
---|
| 165 | self.__lines = plot.PolyLine(self.__naData, colour='red', width=3)
|
---|
| 166 |
|
---|
| 167 | self.__graph = plot.PlotGraphics( [self.__lines],"", "Time (s)",
|
---|
| 168 | self.__stDataTitle)
|
---|
| 169 |
|
---|
| 170 |
|
---|
| 171 | #------------------------------------------------------------------------------
|
---|
| 172 |
|
---|
| 173 | def setGraphType( self, stType ):
|
---|
| 174 | if ( stType == 'frame rate' ):
|
---|
| 175 | self.__stLabel = " fps"
|
---|
| 176 | elif ( stType == 'bandwidth' ):
|
---|
| 177 | self.__stLabel = " Mbps"
|
---|
| 178 |
|
---|
| 179 | #------------------------------------------------------------------------------
|
---|
| 180 |
|
---|
| 181 | def getGraph( self ):
|
---|
| 182 | return self.__graph
|
---|
| 183 |
|
---|
| 184 | #------------------------------------------------------------------------------
|
---|
| 185 |
|
---|
| 186 | def getStatistics( self ):
|
---|
| 187 | return ( self.__fMin, self.__fMax, self.__fAvg, self.__fCurrent )
|
---|
| 188 |
|
---|
| 189 | #------------------------------------------------------------------------------
|
---|
| 190 |
|
---|
| 191 | def getCurrentValue( self ):
|
---|
| 192 | return self.__fCurrent
|
---|
| 193 |
|
---|
| 194 | #------------------------------------------------------------------------------
|
---|
| 195 |
|
---|
| 196 | def getPolyLine( self ):
|
---|
| 197 | return self.__lines
|
---|
| 198 |
|
---|
| 199 |
|
---|
| 200 |
|
---|
| 201 |
|
---|
| 202 |
|
---|
| 203 |
|
---|
| 204 |
|
---|
| 205 |
|
---|
| 206 |
|
---|
| 207 | ############################################################################
|
---|
| 208 |
|
---|
| 209 | # (AKS 2005-04-26) Acts like PerformanceGraph (used with SimpleGraph) but supports
|
---|
| 210 | # multiple plots of the same metric (i.e. bandwidth, frame rate) although this is
|
---|
| 211 | # more of a usage rule rather than a code-enforced rule. This class will contain
|
---|
| 212 | # an array of PerformanceGraph instances across applications.
|
---|
| 213 |
|
---|
| 214 | class MultiPerformanceGraph:
|
---|
| 215 | # NEED TO CHANGE listPerformanceGraphs to hashPerformanceGraphs to allow
|
---|
| 216 | # for easy addition and deletion as apps come and go.
|
---|
| 217 | def __init__( self, hashPerformanceGraphs, stDataTitle ):
|
---|
| 218 |
|
---|
| 219 | # CLASS CONSTANTS
|
---|
| 220 | # self.__fMin, self.__fMax, self.__fAvg, self.__fCurrent
|
---|
| 221 | self.I_MINIMUM = 0
|
---|
| 222 | self.I_MAXIMUM = 1
|
---|
| 223 | self.I_AVERAGE = 2
|
---|
| 224 | self.I_CURRENT = 3
|
---|
| 225 |
|
---|
| 226 | # instance variables
|
---|
| 227 | self.__fAvg = 0.0
|
---|
| 228 | self.__fMax = 0.0
|
---|
| 229 | self.__fMin = 0.0
|
---|
| 230 | self.__fCurrent = 0.0
|
---|
| 231 | self.__stDataTitle = stDataTitle
|
---|
| 232 | self.__stLabel = ""
|
---|
| 233 |
|
---|
| 234 | self.__listLines = []
|
---|
| 235 | self.__hashPerformanceGraphs = {}
|
---|
| 236 |
|
---|
| 237 | for windowId in hashPerformanceGraphs:
|
---|
| 238 | graph = hashPerformanceGraphs[ windowId ]
|
---|
| 239 | self.__hashPerformanceGraphs[ windowId ] = graph
|
---|
| 240 | self.__listLines.append( graph.getPolyLine() )
|
---|
| 241 |
|
---|
| 242 | # create graph
|
---|
| 243 | self.__graph = plot.PlotGraphics( self.__listLines, "", "Time (s)",
|
---|
| 244 | self.__stDataTitle)
|
---|
| 245 |
|
---|
| 246 | #------------------------------------------------------------------------------
|
---|
| 247 |
|
---|
| 248 | def update( self ):
|
---|
| 249 | if ( len( self.__hashPerformanceGraphs ) > 0 ):
|
---|
| 250 | # Because naData is sent as a reference, it is necessary to copy the
|
---|
| 251 | # data from naNewData to naData to preserve these references
|
---|
| 252 |
|
---|
| 253 | # Get current data
|
---|
| 254 | #print ">>> UPDATE WITH: ", naNewData
|
---|
| 255 | #print "new data size = ", len( naNewData )
|
---|
| 256 | #print "plottable data size = ", self.__naData.shape
|
---|
| 257 | self.__fCurrent = naNewData[ 0 ]
|
---|
| 258 |
|
---|
| 259 | fSum = 0.0
|
---|
| 260 |
|
---|
| 261 | # (AKS 2005-02-08) Max and min for a range
|
---|
| 262 | self.__fAvg = 0.0
|
---|
| 263 | self.__fMax = 0.0
|
---|
| 264 | self.__fMin = float( sys.maxint )
|
---|
| 265 |
|
---|
| 266 | for graph in self.__hashPerformanceGraphs.values():
|
---|
| 267 | fMin, fMax, fAvg, fCurrent = graph.getStatistics()
|
---|
| 268 |
|
---|
| 269 | #print index
|
---|
| 270 | fSum = fSum + fCurrent
|
---|
| 271 |
|
---|
| 272 | # Set maximum value
|
---|
| 273 | if ( fMax > self.__fMax ):
|
---|
| 274 | self.__fMax = fMax
|
---|
| 275 |
|
---|
| 276 | # Set minimum value
|
---|
| 277 | elif ( fMin < self.__fMin ):
|
---|
| 278 | self.__fMin = fMin
|
---|
| 279 |
|
---|
| 280 | # end loop
|
---|
| 281 |
|
---|
| 282 |
|
---|
| 283 | # Calculate average
|
---|
| 284 | self.__fAvg = fSum / self.__iArraySize
|
---|
| 285 |
|
---|
| 286 |
|
---|
| 287 | # (AKS 2005-03-08) Update graph
|
---|
| 288 | del self.__lines[ 0:len( self.__lines ) ]
|
---|
| 289 |
|
---|
| 290 | for windowId in self.__hashPerformanceGraphs:
|
---|
| 291 | graph = self.__hashPerformanceGraphs[ windowId ]
|
---|
| 292 | self.__listLines.append( graph.getPolyLine() )
|
---|
| 293 |
|
---|
| 294 | self.__graph = plot.PlotGraphics( self.__listLines,"", "Time",
|
---|
| 295 | self.__stDataTitle)
|
---|
| 296 | # end
|
---|
| 297 |
|
---|
| 298 | else:
|
---|
| 299 | self.__fAvg = 0.0
|
---|
| 300 | self.__fMax = 0.0
|
---|
| 301 | self.__fMin = 0.0
|
---|
| 302 | self.__fCurrent = 0.0
|
---|
| 303 |
|
---|
| 304 | # end if
|
---|
| 305 |
|
---|
| 306 | #------------------------------------------------------------------------------
|
---|
| 307 |
|
---|
| 308 | # (AKS 2005-04-29) Need this function because apps come and go.
|
---|
| 309 | def addPerformanceGraph( self, windowId, pgGraph ):
|
---|
| 310 | self.__hashPerformanceGraphs[ windowId ] = pgGraph
|
---|
| 311 |
|
---|
| 312 | #------------------------------------------------------------------------------
|
---|
| 313 |
|
---|
| 314 | def removePerformanceGraph( self, windowId ):
|
---|
| 315 | del self.__hashPerformanceGraphs[ windowId ]
|
---|
| 316 |
|
---|
| 317 | #------------------------------------------------------------------------------
|
---|
| 318 |
|
---|
| 319 | def setGraphType( self, stType ):
|
---|
| 320 | if ( stType == 'frame rate' ):
|
---|
| 321 | self.__stLabel = " fps"
|
---|
| 322 | elif ( stType == 'bandwidth' ):
|
---|
| 323 | self.__stLabel = " Mbps"
|
---|
| 324 |
|
---|
| 325 | #------------------------------------------------------------------------------
|
---|
| 326 |
|
---|
| 327 | def getGraph( self ):
|
---|
| 328 | return self.__graph
|
---|
| 329 |
|
---|
| 330 | #------------------------------------------------------------------------------
|
---|
| 331 |
|
---|
| 332 | def getStatistics( self ):
|
---|
| 333 | return ( self.__fCurrent, self.__fMin, self.__fMax, self.__fAvg )
|
---|
| 334 |
|
---|
| 335 | #------------------------------------------------------------------------------
|
---|
| 336 |
|
---|
| 337 |
|
---|
| 338 |
|
---|
| 339 |
|
---|
| 340 |
|
---|
| 341 |
|
---|
| 342 |
|
---|
| 343 |
|
---|
| 344 |
|
---|
| 345 | ############################################################################
|
---|
| 346 |
|
---|
| 347 | # the main panel that shows the totals for an application for all the
|
---|
| 348 | # different metrics.
|
---|
| 349 | # one of these exists for every application
|
---|
| 350 |
|
---|
| 351 | class SparklineGraphs( wx.Panel ):
|
---|
| 352 | def __init__( self, wxfParentFrame, list_pgGraph, list_stWindowTitle,
|
---|
| 353 | list_stGraphTitle, list_stYAxisTitle, stSingleWindowTitle, id, frame=None ):
|
---|
| 354 |
|
---|
| 355 | ### Setup element sizes
|
---|
| 356 | btnSize = ( 89, 24 )
|
---|
| 357 |
|
---|
| 358 | ### initial spacing and positioning constants
|
---|
| 359 | xOrigin = 5
|
---|
| 360 | yOrigin = 5
|
---|
| 361 | titleY = yOrigin
|
---|
| 362 | titleX = xOrigin
|
---|
| 363 | titleHeight = 25
|
---|
| 364 | renderX = xOrigin
|
---|
| 365 | renderY = titleY + titleHeight
|
---|
| 366 | renderHeight = 40 - titleHeight
|
---|
| 367 | btnX = xOrigin
|
---|
| 368 | btnY = renderY + renderHeight
|
---|
| 369 | btnHeight = btnSize[1]
|
---|
| 370 | btnWidth = btnSize[0]
|
---|
| 371 | labelXOffset = 15 #from the end of the buttons (or app label) to the right
|
---|
| 372 | labelX = btnX + btnWidth + labelXOffset
|
---|
| 373 | labelY = 0 #yOrigin
|
---|
| 374 | labelWidth = 60
|
---|
| 375 | labelHeight = titleHeight
|
---|
| 376 | perfLabelX = labelX
|
---|
| 377 | numArea1X = btnX + btnWidth
|
---|
| 378 | numArea1Y = btnY
|
---|
| 379 | numAreaWidth = 4*labelWidth + labelXOffset + 10 #numArea1X + 4*labelWidth
|
---|
| 380 | numAreaHeight = 2*btnHeight
|
---|
| 381 | numArea2X = numArea1X
|
---|
| 382 | numArea2Y = numArea1Y + numAreaHeight + renderHeight
|
---|
| 383 |
|
---|
| 384 | # panel size
|
---|
| 385 | width = numArea1X + numAreaWidth + xOrigin #415
|
---|
| 386 | height = numArea2Y + numAreaHeight
|
---|
| 387 | self._buffer = wx.EmptyBitmap(width, height) # for double buffering
|
---|
| 388 |
|
---|
| 389 | self.frame = frame
|
---|
| 390 | wx.Panel.__init__(self, wxfParentFrame, wx.ID_ANY, (3,3), (width,height))
|
---|
| 391 | self.Bind(wx.EVT_CLOSE, self.shutdown)
|
---|
| 392 | self.Bind(wx.EVT_PAINT, self.OnPaint)
|
---|
| 393 | self.SetBackgroundColour(appPanelColor)
|
---|
| 394 | # since SetBackgroundColour doesnt work always, manually draw the background
|
---|
| 395 | #self.background = MyImage( "images/app_info_background.png", 0,0, False, width, height)
|
---|
| 396 |
|
---|
| 397 | # Font setup
|
---|
| 398 | dc = wx.ClientDC(self) # this makes font the same size on all platforms
|
---|
| 399 | ppp = 72.0 / dc.GetPPI().height
|
---|
| 400 | del dc
|
---|
| 401 | self.boldFont = BoldFont()
|
---|
| 402 | self.normalFont = StandardFont()
|
---|
| 403 |
|
---|
| 404 | # Order of items in the list must adhere to the following constants
|
---|
| 405 | # INTERNAL CLASS CONSTANTS
|
---|
| 406 | self.I_RENDER_BW = 0
|
---|
| 407 | self.I_RENDER_FPS = 1
|
---|
| 408 | self.I_RENDER_NODES = 2
|
---|
| 409 | self.I_RENDER_STREAMS = 3
|
---|
| 410 | self.I_DISPLAY_BW = 4
|
---|
| 411 | self.I_DISPLAY_FPS = 5
|
---|
| 412 | self.I_DISPLAY_NODES = 6
|
---|
| 413 | self.I_DISPLAY_STREAMS = 7
|
---|
| 414 |
|
---|
| 415 | # Initialize instance variables
|
---|
| 416 | self.__windowId = id # (AKS 2005-03-10) Added for shutdown callback
|
---|
| 417 | self.__list_stGraphTitle = list_stGraphTitle
|
---|
| 418 | self.__list_stWindowTitle = list_stWindowTitle
|
---|
| 419 |
|
---|
| 420 | # FIX: delete the metrics we are not using this time... just use the bw and fps for rendering and display
|
---|
| 421 | self.__list_pgGraph = list_pgGraph
|
---|
| 422 | #self.__
|
---|
| 423 |
|
---|
| 424 | self.__list_stYAxisTitle = list_stYAxisTitle
|
---|
| 425 | self.__stAppName = stSingleWindowTitle
|
---|
| 426 |
|
---|
| 427 | ### Header Row
|
---|
| 428 | self.__colLabels = []
|
---|
| 429 | self.__colLabels.append((self.__stAppName + " " + str(self.__windowId), xOrigin, 0, btnWidth, titleHeight))
|
---|
| 430 | self.__colLabels.append(("Current", labelX, labelY, labelWidth, labelHeight))
|
---|
| 431 | labelX = labelX + labelWidth
|
---|
| 432 | self.__colLabels.append(("Min", labelX, labelY, labelWidth, labelHeight))
|
---|
| 433 | labelX = labelX + labelWidth
|
---|
| 434 | self.__colLabels.append(("Ave", labelX, labelY, labelWidth, labelHeight))
|
---|
| 435 | labelX = labelX + labelWidth
|
---|
| 436 | self.__colLabels.append(("Max", labelX, labelY, labelWidth, labelHeight))
|
---|
| 437 |
|
---|
| 438 | ### backgrounds for the two areas with the actual numbers
|
---|
| 439 | self.__numAreaBg = []
|
---|
| 440 | self.__numAreaBg.append(MyImage( "images/stats_green.jpg",
|
---|
| 441 | numArea1X, numArea1Y, False,
|
---|
| 442 | numAreaWidth, numAreaHeight))
|
---|
| 443 | self.__numAreaBg.append(MyImage( "images/stats_green.jpg",
|
---|
| 444 | numArea2X, numArea2Y, False,
|
---|
| 445 | numAreaWidth, numAreaHeight))
|
---|
| 446 |
|
---|
| 447 |
|
---|
| 448 | self.__divLabels = []
|
---|
| 449 | self.__divLabels.append(("RENDER", renderX+185, renderY-2, btnWidth, renderHeight))
|
---|
| 450 | self.__divLabels.append(("DISPLAY", renderX+185, btnY + btnHeight*2-2, btnWidth, renderHeight))
|
---|
| 451 |
|
---|
| 452 | # graphs and performance data
|
---|
| 453 | self.__graphButtons = {} #stores the graph buttons keyed by data type
|
---|
| 454 | self.__hash_sgSimpleGraph = {}
|
---|
| 455 | self.__perfNumLabels = {} # stores the top-left corners of the first label and the size of each label
|
---|
| 456 |
|
---|
| 457 | # buttons for showing the graphs for each metric
|
---|
| 458 | #self.__btnList = {0:"BW", 1:"FPS", 2:"Nodes", 3:"Streams", 4:"BW", 5:"FPS", 6:"Nodes", 7:"Streams"}
|
---|
| 459 | self.__btnList = {0:"BW", 1:"FPS", 4:"BW", 5:"FPS"}
|
---|
| 460 |
|
---|
| 461 |
|
---|
| 462 | # load the buttons and create the labels that will hold all the performance data
|
---|
| 463 | for row in [0,1,4,5]: #range( 0, len( self.__btnList ) ):
|
---|
| 464 | # Space between each row
|
---|
| 465 | if row == 4:
|
---|
| 466 | btnY = btnY + renderHeight
|
---|
| 467 | if row == 0:
|
---|
| 468 | pass #we already set the first one
|
---|
| 469 | else:
|
---|
| 470 | btnY = btnY + btnHeight
|
---|
| 471 |
|
---|
| 472 | btnId = row ##wx.ID_ANY ## RJ 05-08-2005 - id by row
|
---|
| 473 | btn = PerfToggleButton(self, btnId,
|
---|
| 474 | self.__createGraph,
|
---|
| 475 | self.removeGraph,
|
---|
| 476 | "images/stats_"+self.__btnList[btnId]+"_up.jpg",
|
---|
| 477 | "images/stats_"+self.__btnList[btnId]+"_down.jpg",
|
---|
| 478 | "images/stats_"+self.__btnList[btnId]+"_over.jpg",
|
---|
| 479 | ( btnX, btnY ), btnSize)
|
---|
| 480 | self.__graphButtons[btnId] = btn
|
---|
| 481 |
|
---|
| 482 | self.__perfNumLabels[row] = (perfLabelX, btnY, labelWidth, btnHeight)
|
---|
| 483 | # end of loop
|
---|
| 484 |
|
---|
| 485 | self.__redraw()
|
---|
| 486 |
|
---|
| 487 |
|
---|
| 488 | def Redraw(self):
|
---|
| 489 | self.__redraw()
|
---|
| 490 |
|
---|
| 491 | #------------------------------------------------------------------------------
|
---|
| 492 |
|
---|
| 493 | def registerShutdownCallback( self, function ):
|
---|
| 494 | self.__shutdownCallback = function
|
---|
| 495 |
|
---|
| 496 | #------------------------------------------------------------------------------
|
---|
| 497 |
|
---|
| 498 | def shutdown( self, evt=None ):
|
---|
| 499 | self.__shutdownCallback( self.__windowId )
|
---|
| 500 |
|
---|
| 501 | #------------------------------------------------------------------------------
|
---|
| 502 |
|
---|
| 503 | # (AKS 2005-03-17) This function will be called if the user closed a "higher-
|
---|
| 504 | # level" window that sends a "close event" down the window hierarchy. In this
|
---|
| 505 | # case, the higher-level window will be able to track what IDs to remove from
|
---|
| 506 | # its data structures and will not need to receive any "callback" function
|
---|
| 507 | # to indicate this information.
|
---|
| 508 |
|
---|
| 509 | def manualShutdown( self ):
|
---|
| 510 | # shutdown all the associated SingleGraphs
|
---|
| 511 | for iKey in self.__hash_sgSimpleGraph:
|
---|
| 512 | self.__hash_sgSimpleGraph[ iKey ].manualShutdown()
|
---|
| 513 |
|
---|
| 514 | # (AKS 2005-03-17) Clear all keys after graphs are shutdown
|
---|
| 515 | self.__hash_sgSimpleGraph.clear()
|
---|
| 516 | self.Destroy() # destroy panel
|
---|
| 517 |
|
---|
| 518 |
|
---|
| 519 | #------------------------------------------------------------------------------
|
---|
| 520 |
|
---|
| 521 | def getGraphList( self ):
|
---|
| 522 | return self.__list_pgGraph
|
---|
| 523 |
|
---|
| 524 | #------------------------------------------------------------------------------
|
---|
| 525 |
|
---|
| 526 | def removeGraph( self, iGraphID ):
|
---|
| 527 | self.__hash_sgSimpleGraph[ iGraphID ].manualShutdown()
|
---|
| 528 | del self.__hash_sgSimpleGraph[ iGraphID ]
|
---|
| 529 |
|
---|
| 530 | # Untoggle button
|
---|
| 531 | self.__graphButtons[ iGraphID ].SetValue( False )
|
---|
| 532 |
|
---|
| 533 | #------------------------------------------------------------------------------
|
---|
| 534 |
|
---|
| 535 | # Call in case no function was registered
|
---|
| 536 | def __ignoreFunction( self ):
|
---|
| 537 | pass
|
---|
| 538 |
|
---|
| 539 | #------------------------------------------------------------------------------
|
---|
| 540 |
|
---|
| 541 | def OnPaint(self, event):
|
---|
| 542 | # for some reason double buffered panel doesnt show up right on macs so just
|
---|
| 543 | # do the normal drawing... single buffering
|
---|
| 544 | if "__WXMAC__" in wx.PlatformInfo:
|
---|
| 545 | dc = wx.PaintDC(self)
|
---|
| 546 | self.__redraw(dc=dc)
|
---|
| 547 | else:
|
---|
| 548 | dc = wx.BufferedPaintDC(self, self._buffer)
|
---|
| 549 |
|
---|
| 550 |
|
---|
| 551 | def __redraw( self, evt=None, dc=None ):
|
---|
| 552 | if not self.IsShown(): #only redraw the panel if it's shown
|
---|
| 553 | return
|
---|
| 554 | if "__WXMAC__" in wx.PlatformInfo:
|
---|
| 555 | if dc == None: dc = wx.ClientDC(self)
|
---|
| 556 | else:
|
---|
| 557 | dc = wx.BufferedDC(wx.ClientDC(self), self._buffer)
|
---|
| 558 | dc.SetBackground( wx.Brush(self.GetBackgroundColour()))
|
---|
| 559 | dc.SetTextForeground(wx.WHITE)
|
---|
| 560 | dc.Clear()
|
---|
| 561 |
|
---|
| 562 | # draw the column labels
|
---|
| 563 | dc.SetFont(self.normalFont)
|
---|
| 564 | i = 0
|
---|
| 565 | for col in self.__colLabels:
|
---|
| 566 | if i==0:
|
---|
| 567 | dc.SetFont(self.boldFont)
|
---|
| 568 | dc.DrawLabel(col[0], wx.Rect(col[1], col[2], col[3], col[4]), wx.ALIGN_TOP | wx.ALIGN_LEFT)
|
---|
| 569 | dc.SetFont(self.normalFont)
|
---|
| 570 | else: dc.DrawLabel(col[0], wx.Rect(col[1], col[2], col[3], col[4]), wx.ALIGN_TOP | wx.ALIGN_RIGHT)
|
---|
| 571 | i=1
|
---|
| 572 |
|
---|
| 573 | # draw the "render" and "display" labels
|
---|
| 574 | dc.SetTextForeground(wx.Colour(102, 204, 153))
|
---|
| 575 | for div in self.__divLabels:
|
---|
| 576 | dc.DrawLabel(div[0], wx.Rect(div[1], div[2], div[3], div[4]), wx.ALIGN_LEFT)
|
---|
| 577 | dc.SetTextForeground(wx.WHITE)
|
---|
| 578 |
|
---|
| 579 | # draw the number area backgrounds
|
---|
| 580 | for im in self.__numAreaBg:
|
---|
| 581 | dc.DrawBitmap( im.GetBitmap(), im.GetX(), im.GetY(), im.IsTransparent())
|
---|
| 582 |
|
---|
| 583 | # update the SparklineGraph panel
|
---|
| 584 | for iIndex in [0,1,4,5]: #range( 0, 4): #len( self.__list_pgGraph ) ):
|
---|
| 585 | pgGraph = self.__list_pgGraph[ iIndex ]
|
---|
| 586 | (x,y, w,h) = self.__perfNumLabels[iIndex] # get the location and size of the first label in each row
|
---|
| 587 |
|
---|
| 588 | # Get statistics
|
---|
| 589 | tupleStats = pgGraph.getStatistics()
|
---|
| 590 |
|
---|
| 591 | # draw the labels for each metric
|
---|
| 592 | for index in (pgGraph.I_CURRENT, pgGraph.I_MINIMUM, pgGraph.I_AVERAGE, pgGraph.I_MAXIMUM):
|
---|
| 593 | if (iIndex == self.I_RENDER_BW) or (iIndex == self.I_RENDER_FPS) or (iIndex == self.I_DISPLAY_BW) or (iIndex == self.I_DISPLAY_FPS):
|
---|
| 594 | stFormatString = "%5.1f" % tupleStats[ index ]
|
---|
| 595 | else:
|
---|
| 596 | stFormatString = "%5d" % tupleStats[ index ]
|
---|
| 597 | dc.DrawLabel(stFormatString, wx.Rect(x,y,w,h), wx.ALIGN_CENTRE | wx.ALIGN_RIGHT)
|
---|
| 598 | x+=w # move the next label to the right by the width of the label
|
---|
| 599 |
|
---|
| 600 | # redraw the buttons on mac
|
---|
| 601 | if "__WXMAC__" in wx.PlatformInfo:
|
---|
| 602 | del dc
|
---|
| 603 | for btn in self.__graphButtons.itervalues():
|
---|
| 604 | btn.OnPaint()
|
---|
| 605 |
|
---|
| 606 | # now update all the SingleGraphs
|
---|
| 607 | for k, singleGraph in self.__hash_sgSimpleGraph.iteritems():
|
---|
| 608 | singleGraph.Redraw()
|
---|
| 609 |
|
---|
| 610 |
|
---|
| 611 |
|
---|
| 612 |
|
---|
| 613 | ### RJ 05-08-2005 - this function replaces the 8 below
|
---|
| 614 | def __OnToggleButton(self, event):
|
---|
| 615 | button = event.GetEventObject()
|
---|
| 616 | if button.GetValue(): #RJ 05-08-2005
|
---|
| 617 | self.__createGraph( button.GetId() )
|
---|
| 618 | else:
|
---|
| 619 | self.removeGraph( button.GetId() )
|
---|
| 620 |
|
---|
| 621 |
|
---|
| 622 |
|
---|
| 623 | #------------------------------------------------------------------------------
|
---|
| 624 |
|
---|
| 625 |
|
---|
| 626 | def __createGraph( self, iGraphCode ):
|
---|
| 627 | stRevisedWindowTitle = self.__stAppName + ": " + self.__list_stWindowTitle[ iGraphCode ]
|
---|
| 628 |
|
---|
| 629 | #if self.frame = None
|
---|
| 630 | self.__hash_sgSimpleGraph[ iGraphCode ] = SingleGraph(
|
---|
| 631 | self.frame, self.__list_pgGraph[ iGraphCode ], stRevisedWindowTitle,
|
---|
| 632 | self.__list_stGraphTitle[ iGraphCode ],
|
---|
| 633 | self.__list_stYAxisTitle[ iGraphCode ], iGraphCode )
|
---|
| 634 |
|
---|
| 635 | # registered call
|
---|
| 636 | #print "........creating graph and registering shutdown callback for graph #", iGraphCode
|
---|
| 637 | self.__hash_sgSimpleGraph[ iGraphCode ].registerShutdownCallback( self.removeGraph )
|
---|
| 638 |
|
---|
| 639 | #------------------------------------------------------------------------------
|
---|
| 640 |
|
---|
| 641 | # (AKS 2005-05-03) Send a list of PerformanceGraphs related to a single app.
|
---|
| 642 | # Then add each PerformanceGraph to the appropriate MultiPerformanceGraph
|
---|
| 643 | # (one MultiPerformanceGraph for each performance metric).
|
---|
| 644 | # CAUTION: Ordering of performance metrics is assumed to be identical
|
---|
| 645 | # to the related graphs in listPerformanceGraphs
|
---|
| 646 |
|
---|
| 647 | def addAppPerformanceGraphList( self, windowId, listPerformanceGraphs,
|
---|
| 648 | iPerformanceMetricCount ):
|
---|
| 649 |
|
---|
| 650 | # CAUTION: Only works for MultiPerformanceGraph instances
|
---|
| 651 | for iIndex in range( 0, iPerformanceMetricCount ):
|
---|
| 652 | self.__list_pgGraph[ iIndex ].addPerformanceGraph(
|
---|
| 653 | windowId, listPerformanceGraphs[ index ] )
|
---|
| 654 |
|
---|
| 655 | # end loop
|
---|
| 656 |
|
---|
| 657 | #------------------------------------------------------------------------------
|
---|
| 658 |
|
---|
| 659 | # (AKS 2005-05-03) Remove a PerformanceGraph with the given windowId from each
|
---|
| 660 | # MultiPerformanceGraph.
|
---|
| 661 | # CAUTION: Ordering of performance metrics is assumed to be identical
|
---|
| 662 | # to the related graphs in listPerformanceGraphs
|
---|
| 663 |
|
---|
| 664 | def reomveAppPerformanceGraphList( self, windowId, iPerformanceMetricCount ):
|
---|
| 665 | print "Removing from totals sparkline APP ID #", windowId
|
---|
| 666 |
|
---|
| 667 | for iIndex in range( 0, iPerformanceMetricCount ):
|
---|
| 668 | self.__list_pgGraph[ iIndex ].removePerformanceGraph( windowId )
|
---|
| 669 |
|
---|
| 670 | # end loop
|
---|
| 671 |
|
---|
| 672 | #------------------------------------------------------------------------------
|
---|
| 673 |
|
---|
| 674 | # (AKS 2005-05-04) ONLY CALL WHEN USING MultiPerformanceGraphs in this class
|
---|
| 675 | # so that totals can be calculated.
|
---|
| 676 |
|
---|
| 677 | def updateMultiPerformanceGraphs( self, iPerformanceMetricCount ):
|
---|
| 678 | print "Updating totals sparkline..."
|
---|
| 679 | for iIndex in range( 0, iPerformanceMetricCount ):
|
---|
| 680 | self.__list_pgGraph[ iIndex ].update()
|
---|
| 681 |
|
---|
| 682 |
|
---|
| 683 |
|
---|
| 684 |
|
---|
| 685 |
|
---|
| 686 |
|
---|
| 687 |
|
---|
| 688 | ############################################################################
|
---|
| 689 | # it displays only one metric as a graph in a frame
|
---|
| 690 | # it runs in a separate thread spawned by the SparklineGraph
|
---|
| 691 |
|
---|
| 692 | class SingleGraph( wx.Frame ):
|
---|
| 693 | def __init__( self, wxfParentFrame, pgGraph, stWindowTitle, stGraphTitle, stYAxisTitle, iGraphCode ):
|
---|
| 694 |
|
---|
| 695 | # Font setup
|
---|
| 696 | self.normalFont = StandardFont()#wx.Font( 11, wx.DEFAULT, wx.NORMAL, wx.NORMAL )
|
---|
| 697 |
|
---|
| 698 | # Initialize instance variables
|
---|
| 699 | self.__stGraphTitle = stGraphTitle
|
---|
| 700 | self.__stWindowTitle = stWindowTitle
|
---|
| 701 | self.__pgGraph = pgGraph
|
---|
| 702 | self.__stYAxisTitle = stYAxisTitle
|
---|
| 703 | self.__iGraphCode = iGraphCode
|
---|
| 704 |
|
---|
| 705 | # (AKS 2005-05-05) Pause playback flag
|
---|
| 706 | self.__bPausePlayback = False
|
---|
| 707 |
|
---|
| 708 | # Setup window
|
---|
| 709 | wx.Frame.__init__(self, wxfParentFrame, wx.ID_ANY, stWindowTitle, wx.DefaultPosition, size=( 400, 300 ))
|
---|
| 710 | wx.EVT_CLOSE(self, self.shutdown)
|
---|
| 711 |
|
---|
| 712 | self.CenterOnParent()
|
---|
| 713 |
|
---|
| 714 | # (AKS 2005-04-26) Use status bar instead
|
---|
| 715 | self.CreateStatusBar()
|
---|
| 716 |
|
---|
| 717 | # (AKS 2005-05-05) Create menu bar to pause
|
---|
| 718 | self.SetMenuBar( self.CreateMenu() )
|
---|
| 719 |
|
---|
| 720 | # Setup graph
|
---|
| 721 | self.__wxpcPlotCanvas = plot.PlotCanvas( self )#, wx.ID_ANY, pos=( 0, 0 ), size=( 400,300 ) )
|
---|
| 722 | self.__wxpcPlotCanvas.SetSize((400,300))
|
---|
| 723 | # this is a workaround for what appears to be a bug in wx.lib.plot
|
---|
| 724 | # On macs OnSize is not getting called therefore the buffer is not getting
|
---|
| 725 | # created anywhere so we forcefully call it here
|
---|
| 726 | #if wx.Platform == "__WXMAC__":
|
---|
| 727 | # print "calling on size..."
|
---|
| 728 | #self.__wxpcPlotCanvas.OnSize(None)
|
---|
| 729 |
|
---|
| 730 | #self.__wxpcPlotCanvas.OnSize(wx.EVT_SIZE)
|
---|
| 731 | self.__wxpcPlotCanvas.SetEnableZoom( True )
|
---|
| 732 | self.__wxpcPlotCanvas.SetEnableGrid( True )
|
---|
| 733 |
|
---|
| 734 | self.Bind( EVT_UPDATE_GRAPH, self.__redraw )
|
---|
| 735 | self.__redraw()
|
---|
| 736 | self.Show( True )
|
---|
| 737 |
|
---|
| 738 |
|
---|
| 739 | def Redraw(self):
|
---|
| 740 | self.__redraw()
|
---|
| 741 |
|
---|
| 742 | #------------------------------------------------------------------------------
|
---|
| 743 |
|
---|
| 744 | def shutdown( self, evt=None ):
|
---|
| 745 | # registered callback function to notify any listeners that graph
|
---|
| 746 | # was destroyed
|
---|
| 747 | self.__shutdownCallback( self.__iGraphCode )
|
---|
| 748 |
|
---|
| 749 | #------------------------------------------------------------------------------
|
---|
| 750 |
|
---|
| 751 | # (AKS 2005-03-17) This function will be called if the user closed a "higher-
|
---|
| 752 | # level" window that sends a "close event" down the window hierarchy. In this
|
---|
| 753 | # case, the higher-level window will be able to track what IDs to remove from
|
---|
| 754 | # its data structures and will not need to receive any "callback" function
|
---|
| 755 | # to indicate this information.
|
---|
| 756 |
|
---|
| 757 | def manualShutdown( self ):
|
---|
| 758 | self.Destroy()
|
---|
| 759 |
|
---|
| 760 | #------------------------------------------------------------------------------
|
---|
| 761 |
|
---|
| 762 | def registerShutdownCallback( self, function ):
|
---|
| 763 | self.__shutdownCallback = function
|
---|
| 764 |
|
---|
| 765 | #------------------------------------------------------------------------------
|
---|
| 766 |
|
---|
| 767 | def __redraw( self, evt=None ):
|
---|
| 768 | #print "...redraw..."
|
---|
| 769 | tupleStats = self.__pgGraph.getStatistics()
|
---|
| 770 |
|
---|
| 771 | if ( self.__bPausePlayback == False ):
|
---|
| 772 |
|
---|
| 773 | stStatusText = "Minimum: %4.2f Average: %4.2f Maximum: %4.2f Current: %4.2f" % (
|
---|
| 774 | tupleStats[ self.__pgGraph.I_MINIMUM ],
|
---|
| 775 | tupleStats[ self.__pgGraph.I_AVERAGE ],
|
---|
| 776 | tupleStats[ self.__pgGraph.I_MAXIMUM ],
|
---|
| 777 | tupleStats[ self.__pgGraph.I_CURRENT ] )
|
---|
| 778 |
|
---|
| 779 | #self.__wxsInfoLine.SetLabel( stStatusText )
|
---|
| 780 |
|
---|
| 781 | fNewMax = tupleStats[ self.__pgGraph.I_MAXIMUM ] * 1.2
|
---|
| 782 | # graphics are already stored in PerformanceGraph
|
---|
| 783 | self.__wxpcPlotCanvas.Draw( self.__pgGraph.getGraph(), yAxis=(0, fNewMax) ) #, xAxis="Time",
|
---|
| 784 | #yAxis=self.__stYAxisTitle )
|
---|
| 785 | else:
|
---|
| 786 | stStatusText = "|| DRAWING PAUSED || Minimum: %4.2f Average: %4.2f Maximum: %4.2f Current: %4.2f" % (
|
---|
| 787 | tupleStats[ self.__pgGraph.I_MINIMUM ],
|
---|
| 788 | tupleStats[ self.__pgGraph.I_AVERAGE ],
|
---|
| 789 | tupleStats[ self.__pgGraph.I_MAXIMUM ],
|
---|
| 790 | tupleStats[ self.__pgGraph.I_CURRENT ] )
|
---|
| 791 |
|
---|
| 792 | # end if
|
---|
| 793 |
|
---|
| 794 | # (AKS 2005-04-26) Use status bar instead
|
---|
| 795 | self.SetStatusText( stStatusText )
|
---|
| 796 |
|
---|
| 797 | #------------------------------------------------------------------------------
|
---|
| 798 |
|
---|
| 799 | def CreateMenu(self):
|
---|
| 800 | menuBar = wx.MenuBar()
|
---|
| 801 |
|
---|
| 802 | # Playback
|
---|
| 803 | menuFile = wx.Menu()
|
---|
| 804 | self.miPlaybackPause = menuFile.Append(-1, "&Pause\tAlt-P", "", wx.ITEM_CHECK)
|
---|
| 805 | self.Bind(wx.EVT_MENU, self.OnPause, self.miPlaybackPause)
|
---|
| 806 | menuBar.Append(menuFile, "&Playback")
|
---|
| 807 |
|
---|
| 808 | return menuBar
|
---|
| 809 |
|
---|
| 810 | #------------------------------------------------------------------------------
|
---|
| 811 |
|
---|
| 812 | def OnPause( self, evt ):
|
---|
| 813 | if ( self.miPlaybackPause.IsChecked() == True ):
|
---|
| 814 | self.__bPausePlayback = True
|
---|
| 815 | else:
|
---|
| 816 | self.__bPausePlayback = False
|
---|
| 817 |
|
---|
| 818 |
|
---|
| 819 |
|
---|
| 820 |
|
---|
| 821 |
|
---|
| 822 |
|
---|
| 823 |
|
---|
| 824 |
|
---|
| 825 |
|
---|
| 826 | ###########################################################################
|
---|
| 827 |
|
---|
| 828 | # it encapsulates all the performance data monitoring for all the apps
|
---|
| 829 | # it creates SparklineGraphs
|
---|
| 830 | # created only once during the execution of the program
|
---|
| 831 |
|
---|
| 832 | class GraphManager:
|
---|
| 833 | def __init__( self, wxfParentFrame, sageAppState ):
|
---|
| 834 |
|
---|
| 835 | # (AKS 2005-04-29) Put MultiPerformanceGraph instances in GraphManager.
|
---|
| 836 | # There is one MultiPerformanceGraph for each metric listed in
|
---|
| 837 | # self.__list_stGraphTitles. Multiple applications will post their
|
---|
| 838 | # PerformanceGraph PolyLine data into MultiPerformanceGraph.
|
---|
| 839 |
|
---|
| 840 |
|
---|
| 841 | # UTILITY FUNCTIONS FOR GUI BUILDING...
|
---|
| 842 | normalFont = StandardFont()#wx.Font( 11, wx.DEFAULT, wx.NORMAL, wx.NORMAL )
|
---|
| 843 |
|
---|
| 844 | self.__gmSageAppState = sageAppState
|
---|
| 845 | self.__wxfParentFrame = wxfParentFrame
|
---|
| 846 | self.__hashAppGraph = {}
|
---|
| 847 | self.__hashAppGraphUpdateFlag = {}
|
---|
| 848 | #self.__bCloseSparklines = False
|
---|
| 849 | self.__updateList = [] #all the containers that need updating
|
---|
| 850 |
|
---|
| 851 | # (AKS 2005-05-03) Use the variable to store the performance update interval
|
---|
| 852 | self.__iPerformanceUpdateInterval = 2
|
---|
| 853 |
|
---|
| 854 | self.__hashCallback = {}
|
---|
| 855 |
|
---|
| 856 |
|
---|
| 857 | self.__list_stGraphTitles = [ "Rendering Bandwidth (Mbps)",
|
---|
| 858 | "Rendering Frame Rate (Frames/Sec)", "Rendering Nodes", "Rendering Streams",
|
---|
| 859 | "Display Bandwidth (Mbps)", "Display Frame Rate (Frames/Sec)", "Display Nodes",
|
---|
| 860 | "Display Streams" ]
|
---|
| 861 |
|
---|
| 862 | self.__list_stYAxisTitles = [ "Mbps", "fps", "Nodes", "Streams",
|
---|
| 863 | "Mbps", "fps", "Nodes", "Streams" ]
|
---|
| 864 |
|
---|
| 865 | self.__list_stOverallGraphTitles = [ "Total Rendering Bandwidth",
|
---|
| 866 | "Rendering: Average Frames/Sec", "Total Rendering Streams",
|
---|
| 867 | "Total Rendering Bandwidth", "Rendering: Average Frames/Sec",
|
---|
| 868 | "Total Rendering Streams" ]
|
---|
| 869 |
|
---|
| 870 | self.__list_stOverallYAxisTitles = [ "Mbps", "fps", "Streams",
|
---|
| 871 | "Mbps", "fps", "Streams" ]
|
---|
| 872 |
|
---|
| 873 |
|
---|
| 874 | # (AKS 2005-05-03) Create totals launcher for multiplot graphs
|
---|
| 875 | self.__bTotalsLauncherExists = False
|
---|
| 876 |
|
---|
| 877 |
|
---|
| 878 | #------------------------------------------------------------------------------
|
---|
| 879 |
|
---|
| 880 | # Responds to 40002...remember, all data comes at once
|
---|
| 881 | def update( self, windowId ):
|
---|
| 882 | # Send the update message
|
---|
| 883 | if windowId in self.__hashAppGraph:
|
---|
| 884 |
|
---|
| 885 | # Get graphs from Sparklines
|
---|
| 886 | sgAppSparklines = self.__hashAppGraph[ windowId ]
|
---|
| 887 | list_pgGraphs = sgAppSparklines.getGraphList()
|
---|
| 888 |
|
---|
| 889 | #print "Get render bandwidth..."
|
---|
| 890 | list_pgGraphs[ sgAppSparklines.I_RENDER_BW ].update(
|
---|
| 891 | self.__gmSageAppState.getRenderItem( 'bandWidth', windowId, 30 ) )
|
---|
| 892 |
|
---|
| 893 | list_pgGraphs[ sgAppSparklines.I_RENDER_FPS ].update(
|
---|
| 894 | self.__gmSageAppState.getRenderItem( 'frameRate', windowId, 30 ) )
|
---|
| 895 |
|
---|
| 896 | list_pgGraphs[ sgAppSparklines.I_RENDER_NODES ].update(
|
---|
| 897 | self.__gmSageAppState.getRenderItem( 'nodes', windowId, 30 ) )
|
---|
| 898 |
|
---|
| 899 | list_pgGraphs[ sgAppSparklines.I_RENDER_STREAMS ].update(
|
---|
| 900 | self.__gmSageAppState.getRenderItem( 'cpu', windowId, 30 ) )
|
---|
| 901 |
|
---|
| 902 |
|
---|
| 903 | #print "Get display bandwidth..."
|
---|
| 904 | list_pgGraphs[ sgAppSparklines.I_DISPLAY_BW ].update(
|
---|
| 905 | self.__gmSageAppState.getDisplayItem( 'bandWidth', windowId, 30 ) )
|
---|
| 906 |
|
---|
| 907 | list_pgGraphs[ sgAppSparklines.I_DISPLAY_FPS ].update(
|
---|
| 908 | self.__gmSageAppState.getDisplayItem( 'frameRate', windowId, 30 ) )
|
---|
| 909 |
|
---|
| 910 | list_pgGraphs[ sgAppSparklines.I_DISPLAY_NODES ].update(
|
---|
| 911 | self.__gmSageAppState.getDisplayItem( 'nodes', windowId, 30 ) )
|
---|
| 912 |
|
---|
| 913 | list_pgGraphs[ sgAppSparklines.I_DISPLAY_STREAMS ].update(
|
---|
| 914 | self.__gmSageAppState.getDisplayItem( 'cpu', windowId, 30 ) )
|
---|
| 915 |
|
---|
| 916 |
|
---|
| 917 | self.__hashAppGraphUpdateFlag[ windowId ] = True
|
---|
| 918 |
|
---|
| 919 |
|
---|
| 920 |
|
---|
| 921 |
|
---|
| 922 | # send an update to all the windows
|
---|
| 923 | for win in self.__updateList:
|
---|
| 924 | try:
|
---|
| 925 | win.GetSize()
|
---|
| 926 | except Exception:
|
---|
| 927 | #print "exception occured... deleting the window"
|
---|
| 928 | del win
|
---|
| 929 | else:
|
---|
| 930 | win.Redraw() # call redraw on SparklineGraph
|
---|
| 931 |
|
---|
| 932 |
|
---|
| 933 | # (AKS 2005-05-04) ALWAYS EXECUTE...Call update function for totals Sparkline;
|
---|
| 934 | # instances of PerformanceGraphs in MultiPerformanceGRaphs in Sparkline
|
---|
| 935 | # have already updated since they share the same reference address
|
---|
| 936 | # (hopefully). Now, just call a special update that will get the
|
---|
| 937 | # totals Sparkline to calculate totals based on these updates.
|
---|
| 938 |
|
---|
| 939 | if ( self.__bTotalsLauncherExists == True ):
|
---|
| 940 | self.__sgTotalsLauncher.updateMultiPerformanceGraphs( self.__iPerformanceMetricCount )
|
---|
| 941 |
|
---|
| 942 |
|
---|
| 943 | #------------------------------------------------------------------------------
|
---|
| 944 |
|
---|
| 945 | # Responds to 40000, 40001...OR JUST RIGHT-CLICK
|
---|
| 946 | def addGraph( self, stAppName, windowId, parent=None ): #RJ added "parent=None"
|
---|
| 947 | if parent == None:
|
---|
| 948 | parent = self.__wxfParentFrame
|
---|
| 949 | list_pgGraphs = []
|
---|
| 950 |
|
---|
| 951 | for stGraphTitle in self.__list_stGraphTitles:
|
---|
| 952 | list_pgGraphs.append( PerformanceGraph(
|
---|
| 953 | stAppName, stGraphTitle, 30, self.__iPerformanceUpdateInterval ) )
|
---|
| 954 |
|
---|
| 955 |
|
---|
| 956 | # Create an application Sparkline graph launcher
|
---|
| 957 | # (AKS 2005-05-03) Only create the SparklineGraph if it has not already been created
|
---|
| 958 |
|
---|
| 959 | if ( windowId not in self.__hashAppGraph ):
|
---|
| 960 |
|
---|
| 961 | slTotalsGraphLauncher = SparklineGraphs( parent, #RJ - put parent instead of __wxf...
|
---|
| 962 | list_pgGraphs, self.__list_stGraphTitles,
|
---|
| 963 | self.__list_stGraphTitles, self.__list_stYAxisTitles, stAppName, windowId, self.__wxfParentFrame )
|
---|
| 964 |
|
---|
| 965 |
|
---|
| 966 | # (AKS 2005-03-10) Register callback to destroy graph when the sparklines
|
---|
| 967 | # window is closed.
|
---|
| 968 | slTotalsGraphLauncher.registerShutdownCallback( self.removeGraph )
|
---|
| 969 |
|
---|
| 970 | # Update the hashes
|
---|
| 971 | self.__hashAppGraph[ windowId ] = slTotalsGraphLauncher
|
---|
| 972 | self.__hashAppGraphUpdateFlag[ windowId ] = False
|
---|
| 973 | self.AddToUpdateList(slTotalsGraphLauncher)
|
---|
| 974 |
|
---|
| 975 | # (AKS 2005-05-04) Add newly created PerformanceGraphs to totals Sparkline
|
---|
| 976 | if ( self.__bTotalsLauncherExists == True ):
|
---|
| 977 | self.__sgTotalsLauncher.addAppPerformanceGraphList( windowId, list_pgGraphs,
|
---|
| 978 | self.__iPerformanceMetricCount )
|
---|
| 979 |
|
---|
| 980 | # end...else do nothing
|
---|
| 981 |
|
---|
| 982 |
|
---|
| 983 | # RJ 05-08-2005
|
---|
| 984 | def GetSparklineGraph(self, windowId):
|
---|
| 985 | if not windowId in self.__hashAppGraph:
|
---|
| 986 | return -1
|
---|
| 987 | else:
|
---|
| 988 | return self.__hashAppGraph[ windowId ]
|
---|
| 989 |
|
---|
| 990 |
|
---|
| 991 | def AddToUpdateList(self, win):
|
---|
| 992 | if not win in self.__updateList:
|
---|
| 993 | self.__updateList.append(win)
|
---|
| 994 |
|
---|
| 995 | def RemoveFromUpdateList(self, win):
|
---|
| 996 | if win in self.__updateList:
|
---|
| 997 | self.__updateList.remove(win)
|
---|
| 998 |
|
---|
| 999 |
|
---|
| 1000 | #------------------------------------------------------------------------------
|
---|
| 1001 |
|
---|
| 1002 | # Responds to 40003...or nothing
|
---|
| 1003 | def removeGraph( self, windowId ):
|
---|
| 1004 | if windowId in self.__hashAppGraph:
|
---|
| 1005 |
|
---|
| 1006 | # (AKS 2005-05-04) Remove performance data related to departing application
|
---|
| 1007 | # from totals Sparkline
|
---|
| 1008 | if ( self.__bTotalsLauncherExists == True ):
|
---|
| 1009 | self.__sgTotalsLauncher.reomveAppPerformanceGraphList( windowId,
|
---|
| 1010 | self.__iPerformanceMetricCount )
|
---|
| 1011 |
|
---|
| 1012 |
|
---|
| 1013 | sgAppSparklines = self.__hashAppGraph[ windowId ]
|
---|
| 1014 | self.RemoveFromUpdateList(sgAppSparklines)
|
---|
| 1015 | sgAppSparklines.manualShutdown()
|
---|
| 1016 |
|
---|
| 1017 | del self.__hashAppGraph[ windowId ] # remove app id
|
---|
| 1018 |
|
---|
| 1019 | # else, do nothing
|
---|
| 1020 |
|
---|
| 1021 | #------------------------------------------------------------------------------
|
---|
| 1022 |
|
---|
| 1023 | def showTotalsGraph( self ):
|
---|
| 1024 | self.__bCloseSparklines = False
|
---|
| 1025 |
|
---|
| 1026 | list_pgGraphs = []
|
---|
| 1027 |
|
---|
| 1028 | # Create totals graphs
|
---|
| 1029 | for iIndex in range( 0,len( self.__list_stOverallGraphTitles ) ):
|
---|
| 1030 | list_pgGraphs.append( PerformanceGraph( "SAGE Totals",
|
---|
| 1031 | self.__list_stOverallGraphTitles[ iIndex ], 30 ) )
|
---|
| 1032 |
|
---|
| 1033 | # (AKS 2005-03-10) Need "bogus app id" for totals graph; it will be 88888888
|
---|
| 1034 | self.__slTotalsGraphLauncher = SparklineGraphs(
|
---|
| 1035 | self.__wxfParentFrame, list_pgGraphs, self.__list_stOverallGraphTitles,
|
---|
| 1036 | self.__list_stOverallGraphTitles, self.__list_stOverallYAxisTitles, 88888888 )
|
---|
| 1037 |
|
---|
| 1038 | self.__slTotalsGraphLauncher.registerCallback( self.onCloseTotalsGraph )
|
---|
| 1039 |
|
---|
| 1040 | #------------------------------------------------------------------------------
|
---|
| 1041 |
|
---|
| 1042 | def onCloseTotalsGraph( self ):
|
---|
| 1043 | self.__slTotalsGraphLauncher = None
|
---|
| 1044 |
|
---|
| 1045 | #------------------------------------------------------------------------------
|
---|
| 1046 |
|
---|
| 1047 | def shutdown( self ):
|
---|
| 1048 | # For each graph, shut down
|
---|
| 1049 | keys = self.__hashAppGraph.keys()
|
---|
| 1050 | for key in keys:
|
---|
| 1051 | self.__hashAppGraph[ key ].shutdown()
|
---|
| 1052 |
|
---|
| 1053 | # Makes sure to only call shutdown if the window still exists
|
---|
| 1054 | if ( self.__bTotalsLauncherExists == True ):
|
---|
| 1055 | self.__bTotalsLauncherExists = False
|
---|
| 1056 | self.__slTotalsGraphLauncher.shutdown()
|
---|
| 1057 |
|
---|
| 1058 |
|
---|
| 1059 | #------------------------------------------------------------------------------
|
---|
| 1060 | # (AKS 2005-05-03) Get the performance data update interval
|
---|
| 1061 | def getPerformanceUpdateInterval( self ):
|
---|
| 1062 | return self.__iPerformanceUpdateInterval
|
---|
| 1063 |
|
---|
| 1064 | #------------------------------------------------------------------------------
|
---|
| 1065 | # (AKS 2005-05-03) Create the totals performance graph
|
---|
| 1066 |
|
---|
| 1067 | def createTotalsLauncher( self ):
|
---|
| 1068 | # Create list of MultiPerformanceGraphs...one for each performance metric
|
---|
| 1069 | list_mpgMultiPerfGraphs = []
|
---|
| 1070 |
|
---|
| 1071 | for index in range( 0, len( self.__list_stGraphTitles ) ):
|
---|
| 1072 | list_mpgMultiPerfGraphs.append( MultiPerformanceGraph(
|
---|
| 1073 | {}, self.__list_stGraphTitles ) )
|
---|
| 1074 |
|
---|
| 1075 | # end loop
|
---|
| 1076 |
|
---|
| 1077 | # Create the "totals" SparklineGraphs object...need bogus app id of -1
|
---|
| 1078 | # CAREFUL: DIFFERENT TYPE...SENDING LIST OF MultiPerformanceGraph instances
|
---|
| 1079 | # and not PerformanceGraph instances!!!
|
---|
| 1080 |
|
---|
| 1081 | self.__sgTotalsLauncher = SparklineGraphs( self.__wxfParentFrame,
|
---|
| 1082 | list_mpgMultiPerfGraphs, self.__list_stGraphTitles,
|
---|
| 1083 | self.__list_stGraphTitles, self.__list_stYAxisTitles,
|
---|
| 1084 | "SAGE Application Totals", -1 )
|
---|
| 1085 |
|
---|
| 1086 | self.__iPerformanceMetricCount = len( self.__list_stGraphTitles )
|
---|
| 1087 |
|
---|
| 1088 | # Populate the MultiPerformanceGraphs from existing app SparklineGraphs
|
---|
| 1089 | for windowId in self.__hashAppGraph:
|
---|
| 1090 | sgAppLauncher = self.__hashAppGraph[ windowId ]
|
---|
| 1091 | list_pgGraphs = sgAppLauncher.getGraphList()
|
---|
| 1092 |
|
---|
| 1093 | self.__sgTotalsLauncher.addPerformanceGraphList( list_pgGraphs,
|
---|
| 1094 | self.__iPerformanceMetricCount )
|
---|
| 1095 |
|
---|
| 1096 | # end loop
|
---|
| 1097 | self.__bTotalsLauncherExists = True
|
---|
| 1098 |
|
---|
| 1099 | #------------------------------------------------------------------------------
|
---|
| 1100 | # (AKS 2005-05-04) Shutdown the totals performance graph; since this
|
---|
| 1101 | # exists independent of the SparklineGraph objects, the totals launcher
|
---|
| 1102 | # can be destroyed independently and created independently at any time
|
---|
| 1103 |
|
---|
| 1104 | def shutdownTotalsLauncher( self ):
|
---|
| 1105 | if ( ( self.__sgTotalsGraphLauncher != None ) and
|
---|
| 1106 | ( self.__sgTotalsGraphLauncher.__class__.__name__ ==
|
---|
| 1107 | SparklineGraphs.__name__ ) ):
|
---|
| 1108 | self.__sgTotalsGraphLauncher.shutdown()
|
---|
| 1109 |
|
---|
| 1110 |
|
---|
| 1111 | def getSparkline( self, windowId ):
|
---|
| 1112 | if ( windowId in self.__hashAppGraph ):
|
---|
| 1113 | return self.__hashAppGraph[ windowId ]
|
---|
| 1114 |
|
---|
| 1115 |
|
---|
| 1116 |
|
---|