def __createLayout(self): """Creates the widget layout""" totalCalls = self.__stats.total_calls # The calls were not induced via recursion totalPrimitiveCalls = self.__stats.prim_calls totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + \ self.__params['arguments'] + "<br/>" \ "<b>Run at:</b> " + self.__reportTime + "<br/>" + \ str(totalCalls) + " function calls (" + \ str(totalPrimitiveCalls) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = HeaderFitLabel(self) summary.setText(txt) summary.setToolTip(txt) summary.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) summary.setMinimumWidth(10) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.sigEscapePressed.connect(self.__onESC) vLayout = QVBoxLayout() vLayout.setContentsMargins(0, 0, 0, 0) vLayout.setSpacing(0) vLayout.addWidget(summary) vLayout.addWidget(self.__viewer) self.setLayout(vLayout)
def __createLayout(self): " Creates the widget layout " totalCalls = self.__stats.total_calls totalPrimitiveCalls = self.__stats.prim_calls # The calls were not induced via recursion totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + self.__params.arguments + "<br>" \ "<b>Run at:</b> " + self.__reportTime + "<br>" + \ str( totalCalls ) + " function calls (" + \ str( totalPrimitiveCalls ) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = QLabel(txt) summary.setToolTip(txt) summary.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) summary.setFrameStyle(QFrame.StyledPanel) summary.setAutoFillBackground(True) summaryPalette = summary.palette() summaryBackground = summaryPalette.color(QPalette.Background) summaryBackground.setRgb(min(summaryBackground.red() + 30, 255), min(summaryBackground.green() + 30, 255), min(summaryBackground.blue() + 30, 255)) summaryPalette.setColor(QPalette.Background, summaryBackground) summary.setPalette(summaryPalette) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.escapePressed.connect(self.__onESC) vLayout = QVBoxLayout() vLayout.setContentsMargins(0, 0, 0, 0) vLayout.setSpacing(0) vLayout.addWidget(summary) vLayout.addWidget(self.__viewer) self.setLayout(vLayout) return
def __createLayout( self ): " Creates the widget layout " totalCalls = self.__stats.total_calls totalPrimitiveCalls = self.__stats.prim_calls # The calls were not induced via recursion totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + self.__params.arguments + "<br>" \ "<b>Run at:</b> " + self.__reportTime + "<br>" + \ str( totalCalls ) + " function calls (" + \ str( totalPrimitiveCalls ) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = QLabel( txt ) summary.setToolTip( txt ) summary.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Fixed ) summary.setFrameStyle( QFrame.StyledPanel ) summary.setAutoFillBackground( True ) summaryPalette = summary.palette() summaryBackground = summaryPalette.color( QPalette.Background ) summaryBackground.setRgb( min( summaryBackground.red() + 30, 255 ), min( summaryBackground.green() + 30, 255 ), min( summaryBackground.blue() + 30, 255 ) ) summaryPalette.setColor( QPalette.Background, summaryBackground ) summary.setPalette( summaryPalette ) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.escapePressed.connect( self.__onESC ) vLayout = QVBoxLayout() vLayout.setContentsMargins( 0, 0, 0, 0 ) vLayout.setSpacing( 0 ) vLayout.addWidget( summary ) vLayout.addWidget( self.__viewer ) self.setLayout( vLayout ) return
class ProfileGraphViewer(QWidget): """Profiling results as a graph""" sigEscapePressed = pyqtSignal() def __init__(self, scriptName, params, reportTime, dataFile, stats, parent=None): QWidget.__init__(self, parent) self.__dataFile = dataFile self.__script = scriptName self.__reportTime = reportTime self.__params = params self.__stats = stats project = GlobalData().project if project.isLoaded(): self.__projectPrefix = os.path.dirname(project.fileName) else: self.__projectPrefix = os.path.dirname(scriptName) if not self.__projectPrefix.endswith(os.path.sep): self.__projectPrefix += os.path.sep self.__createLayout() self.__getDiagramLayout() self.__viewer.setScene(self.__scene) def setFocus(self): """Sets the focus properly""" self.__viewer.setFocus() def __isOutsideItem(self, fileName): """Detects if the record should be shown as an outside one""" return not fileName.startswith(self.__projectPrefix) def __createLayout(self): """Creates the widget layout""" totalCalls = self.__stats.total_calls # The calls were not induced via recursion totalPrimitiveCalls = self.__stats.prim_calls totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + \ self.__params['arguments'] + "<br/>" \ "<b>Run at:</b> " + self.__reportTime + "<br/>" + \ str(totalCalls) + " function calls (" + \ str(totalPrimitiveCalls) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = HeaderFitLabel(self) summary.setText(txt) summary.setToolTip(txt) summary.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) summary.setMinimumWidth(10) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.sigEscapePressed.connect(self.__onESC) vLayout = QVBoxLayout() vLayout.setContentsMargins(0, 0, 0, 0) vLayout.setSpacing(0) vLayout.addWidget(summary) vLayout.addWidget(self.__viewer) self.setLayout(vLayout) @staticmethod def __getDotFont(parts): """Provides a QFont object if a font spec is found""" for part in parts: if 'fontname=' in part: fontName = part.replace('fontname=', '') fontName = fontName.replace('[', '') fontName = fontName.replace(']', '') fontName = fontName.replace(',', '') return QFont(fontName) return None def __postprocessFullDotSpec(self, dotSpec): """Removes the arrow size, extracts tooltips, extracts font info""" nodeFont = None edgeFont = None tooltips = {} processed = [] for line in dotSpec.splitlines(): parts = line.split() lineModified = False if parts: if parts[0] == 'node': # need to extract the fontname nodeFont = self.__getDotFont(parts) elif parts[0] == 'edge': # need to extract the fontname edgeFont = self.__getDotFont(parts) elif parts[0].isdigit(): if parts[1] == '->': # certain edge spec: replace arrowsize and font size for index, part in enumerate(parts): if part.startswith('[arrowsize='): modified = parts[:] modified[index] = '[arrowsize="0.0",' processed.append(' '.join(modified)) elif part.startswith('fontsize='): size = float(part.split('"')[1]) if edgeFont: edgeFont.setPointSize(size) lineModified = True elif parts[1].startswith('['): # certain node spec: pick the tooltip and font size lineno = None for part in parts: if part.startswith('tooltip='): nodePath = part.split('"')[1] pathLine = nodePath + ':' + str(lineno) tooltips[int(parts[0])] = pathLine elif part.startswith('fontsize='): size = float(part.split('"')[1]) if nodeFont: nodeFont.setPointSize(size) elif part.startswith('label='): try: lineno = int(part.split(':')[1]) except: pass if not lineModified: processed.append(line) return '\n'.join(processed), tooltips, nodeFont, edgeFont def __rungprof2dot(self): """Runs gprof2dot which produces a full dot spec""" nodeLimit = Settings().getProfilerSettings().nodeLimit edgeLimit = Settings().getProfilerSettings().edgeLimit with io.StringIO() as buf: gprofParser = gprof2dot.PstatsParser(self.__dataFile) profileData = gprofParser.parse() profileData.prune(nodeLimit / 100.0, edgeLimit / 100.0, False, False) dot = gprof2dot.DotWriter(buf) dot.strip = False dot.wrap = False dot.graph(profileData, gprof2dot.TEMPERATURE_COLORMAP) output = buf.getvalue() return self.__postprocessFullDotSpec(output) def __getDiagramLayout(self): """Runs external tools to get the diagram layout""" fullDotSpec, tooltips, nodeFont, edgeFont = self.__rungprof2dot() dotProc = Popen(["dot", "-Tplain"], stdin=PIPE, stdout=PIPE, bufsize=1) graphDescr = dotProc.communicate( fullDotSpec.encode('utf-8'))[0].decode('utf-8') graph = getGraphFromPlainDotData(graphDescr) graph.normalize(self.physicalDpiX(), self.physicalDpiY()) self.__scene.clear() self.__scene.setSceneRect(0, 0, graph.width, graph.height) for edge in graph.edges: self.__scene.addItem(FuncConnection(edge)) if edge.label != "": self.__scene.addItem(FuncConnectionLabel(edge, edgeFont)) for node in graph.nodes: fileName = "" lineNumber = 0 try: nodeNameAsInt = int(node.name) if nodeNameAsInt in tooltips: parts = tooltips[nodeNameAsInt].rsplit(':', 1) fileName = parts[0] if parts[1].isdigit(): lineNumber = int(parts[1]) except: pass self.__scene.addItem( Function(node, fileName, lineNumber, self.__isOutsideItem(fileName), nodeFont)) def __onESC(self): """Triggered when ESC is clicked""" self.sigEscapePressed.emit() def onCopy(self): """Copies the diagram to the exchange buffer""" self.__viewer.onCopy() def onSaveAs(self, fileName): """Saves the diagram to a file""" self.__viewer.onSaveAs(fileName) def zoomIn(self): """Triggered on the 'zoom in' button""" self.__viewer.zoomIn() def zoomOut(self): """Triggered on the 'zoom out' button""" self.__viewer.zoomOut() def resetZoom(self): """Triggered on the 'zoom reset' button""" self.__viewer.resetZoom()
class ProfileGraphViewer( QWidget ): " Profiling results as a graph " escapePressed = pyqtSignal() def __init__( self, scriptName, params, reportTime, dataFile, stats, parent = None ): QWidget.__init__( self, parent ) self.__dataFile = dataFile self.__script = scriptName self.__reportTime = reportTime self.__params = params self.__stats = stats project = GlobalData().project if project.isLoaded(): self.__projectPrefix = os.path.dirname( project.fileName ) else: self.__projectPrefix = os.path.dirname( scriptName ) if not self.__projectPrefix.endswith( os.path.sep ): self.__projectPrefix += os.path.sep self.__createLayout() self.__getDiagramLayout() self.__viewer.setScene( self.__scene ) return def setFocus( self ): " Sets the focus properly " self.__viewer.setFocus() return def __isOutsideItem( self, fileName ): " Detects if the record should be shown as an outside one " return not fileName.startswith( self.__projectPrefix ) def __createLayout( self ): " Creates the widget layout " totalCalls = self.__stats.total_calls totalPrimitiveCalls = self.__stats.prim_calls # The calls were not induced via recursion totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + self.__params.arguments + "<br>" \ "<b>Run at:</b> " + self.__reportTime + "<br>" + \ str( totalCalls ) + " function calls (" + \ str( totalPrimitiveCalls ) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = QLabel( txt ) summary.setToolTip( txt ) summary.setSizePolicy( QSizePolicy.Ignored, QSizePolicy.Fixed ) summary.setFrameStyle( QFrame.StyledPanel ) summary.setAutoFillBackground( True ) summaryPalette = summary.palette() summaryBackground = summaryPalette.color( QPalette.Background ) summaryBackground.setRgb( min( summaryBackground.red() + 30, 255 ), min( summaryBackground.green() + 30, 255 ), min( summaryBackground.blue() + 30, 255 ) ) summaryPalette.setColor( QPalette.Background, summaryBackground ) summary.setPalette( summaryPalette ) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.escapePressed.connect( self.__onESC ) vLayout = QVBoxLayout() vLayout.setContentsMargins( 0, 0, 0, 0 ) vLayout.setSpacing( 0 ) vLayout.addWidget( summary ) vLayout.addWidget( self.__viewer ) self.setLayout( vLayout ) return def __getDiagramLayout( self ): " Runs external tools to get the diagram layout " # Preparation: build a map of func ID -> fileName + line funcMap = {} index = 0 for func, props in self.__stats.stats.items(): funcMap[ index ] = ( func[ 0 ], func[ 1 ] ) index += 1 # First step is to run grpof2dot gprof2dot = thirdpartyDir + "gprof2dot" + os.path.sep + "gprof2dot.py" outputFile = self.__dataFile + ".dot" nodeLimit = Settings().profileNodeLimit edgeLimit = Settings().profileEdgeLimit dotSpec = safeRun( [ gprof2dot, '-n', str( nodeLimit ), '-e', str( edgeLimit ), '-f', 'pstats', '-o', outputFile, self.__dataFile ] ) graphDescr = safeRun( [ "dot", "-Tplain", outputFile ] ) graph = getGraphFromPlainDotData( graphDescr ) graph.normalize( self.physicalDpiX(), self.physicalDpiY() ) self.__scene.clear() self.__scene.setSceneRect( 0, 0, graph.width, graph.height ) for edge in graph.edges: self.__scene.addItem( FuncConnection( edge ) ) if edge.label != "": self.__scene.addItem( FuncConnectionLabel( edge ) ) for node in graph.nodes: fileName = "" lineNumber = 0 isOutside = True nodeID, newLabel = extractNodeID( node.label ) if nodeID != -1: node.label = newLabel # Now, detect the file name/line number and # if it belongs to the project ( fileName, lineNumber ) = funcMap[ nodeID ] self.__scene.addItem( Function( node, fileName, lineNumber, self.__isOutsideItem( fileName ) ) ) return def __onESC( self ): " Triggered when ESC is clicked " self.escapePressed.emit() return def onCopy( self ): " Copies the diagram to the exchange buffer " self.__viewer.onCopy() return def onSaveAs( self, fileName ): " Saves the diagram to a file " self.__viewer.onSaveAs( fileName ) return def zoomIn( self ): " Triggered on the 'zoom in' button " self.__viewer.zoomIn() return def zoomOut( self ): " Triggered on the 'zoom out' button " self.__viewer.zoomOut() return def resetZoom( self ): " Triggered on the 'zoom reset' button " self.__viewer.resetZoom() return
class ProfileGraphViewer(QWidget): " Profiling results as a graph " escapePressed = pyqtSignal() def __init__(self, scriptName, params, reportTime, dataFile, stats, parent=None): QWidget.__init__(self, parent) self.__dataFile = dataFile self.__script = scriptName self.__reportTime = reportTime self.__params = params self.__stats = stats project = GlobalData().project if project.isLoaded(): self.__projectPrefix = os.path.dirname(project.fileName) else: self.__projectPrefix = os.path.dirname(scriptName) if not self.__projectPrefix.endswith(os.path.sep): self.__projectPrefix += os.path.sep self.__createLayout() self.__getDiagramLayout() self.__viewer.setScene(self.__scene) return def setFocus(self): " Sets the focus properly " self.__viewer.setFocus() return def __isOutsideItem(self, fileName): " Detects if the record should be shown as an outside one " return not fileName.startswith(self.__projectPrefix) def __createLayout(self): " Creates the widget layout " totalCalls = self.__stats.total_calls totalPrimitiveCalls = self.__stats.prim_calls # The calls were not induced via recursion totalTime = self.__stats.total_tt txt = "<b>Script:</b> " + self.__script + " " + self.__params.arguments + "<br>" \ "<b>Run at:</b> " + self.__reportTime + "<br>" + \ str( totalCalls ) + " function calls (" + \ str( totalPrimitiveCalls ) + " primitive calls) in " + \ FLOAT_FORMAT % totalTime + " CPU seconds" summary = QLabel(txt) summary.setToolTip(txt) summary.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Fixed) summary.setFrameStyle(QFrame.StyledPanel) summary.setAutoFillBackground(True) summaryPalette = summary.palette() summaryBackground = summaryPalette.color(QPalette.Background) summaryBackground.setRgb(min(summaryBackground.red() + 30, 255), min(summaryBackground.green() + 30, 255), min(summaryBackground.blue() + 30, 255)) summaryPalette.setColor(QPalette.Background, summaryBackground) summary.setPalette(summaryPalette) self.__scene = QGraphicsScene() self.__viewer = DiagramWidget() self.__viewer.escapePressed.connect(self.__onESC) vLayout = QVBoxLayout() vLayout.setContentsMargins(0, 0, 0, 0) vLayout.setSpacing(0) vLayout.addWidget(summary) vLayout.addWidget(self.__viewer) self.setLayout(vLayout) return def __getDiagramLayout(self): " Runs external tools to get the diagram layout " # Preparation: build a map of func ID -> fileName + line funcMap = {} index = 0 for func, props in self.__stats.stats.items(): funcMap[index] = (func[0], func[1]) index += 1 # First step is to run grpof2dot gprof2dot = thirdpartyDir + "gprof2dot" + os.path.sep + "gprof2dot.py" outputFile = self.__dataFile + ".dot" nodeLimit = Settings().profileNodeLimit edgeLimit = Settings().profileEdgeLimit dotSpec = safeRun([ gprof2dot, '-n', str(nodeLimit), '-e', str(edgeLimit), '-f', 'pstats', '-o', outputFile, self.__dataFile ]) graphDescr = safeRun(["dot", "-Tplain", outputFile]) graph = getGraphFromPlainDotData(graphDescr) graph.normalize(self.physicalDpiX(), self.physicalDpiY()) self.__scene.clear() self.__scene.setSceneRect(0, 0, graph.width, graph.height) for edge in graph.edges: self.__scene.addItem(FuncConnection(edge)) if edge.label != "": self.__scene.addItem(FuncConnectionLabel(edge)) for node in graph.nodes: fileName = "" lineNumber = 0 isOutside = True nodeID, newLabel = extractNodeID(node.label) if nodeID != -1: node.label = newLabel # Now, detect the file name/line number and # if it belongs to the project (fileName, lineNumber) = funcMap[nodeID] self.__scene.addItem( Function(node, fileName, lineNumber, self.__isOutsideItem(fileName))) return def __onESC(self): " Triggered when ESC is clicked " self.escapePressed.emit() return def onCopy(self): " Copies the diagram to the exchange buffer " self.__viewer.onCopy() return def onSaveAs(self, fileName): " Saves the diagram to a file " self.__viewer.onSaveAs(fileName) return def zoomIn(self): " Triggered on the 'zoom in' button " self.__viewer.zoomIn() return def zoomOut(self): " Triggered on the 'zoom out' button " self.__viewer.zoomOut() return def resetZoom(self): " Triggered on the 'zoom reset' button " self.__viewer.resetZoom() return