# -*- coding: utf-8 -*- import time import sys from PyQt5 import QtCore, QtGui, uic, Qt, QtWidgets import math import random from db.SymbolAttr import UIAttr, RefAttr from ui.SymbolUIItem import SymbolUIItem class RefData(object): REF_CALL = 0 def __init__(self, type): self.type = type class LineCache(object): N_POINTS = 14 COLOR_TABLE = [] SRC_COLOR = QtGui.QColor(237,6,6) MID_COLOR = QtGui.QColor(200,200,200) DST_COLOR = QtGui.QColor(0,209,192) def __init__(self, start, end): self.startPnt = start self.endPnt = end self.path = QtGui.QPainterPath() self.path.moveTo(self.startPnt) self.path.cubicTo(self.startPnt* 0.5, self.endPnt*0.5, self.endPnt) self.isVisible = True self.weight = 1 # nPnts = LineCache.N_POINTS # self.pntList = [None] * nPnts # for i in range(nPnts): # t = i / float(nPnts-1) # self.pntList[i] = QtCore.QPointF(self.path.pointAtPercent(t)) # # if not self.COLOR_TABLE: # srcClr = (93,195,187) # tarClr = (255,104,104) # for i in range(nPnts-1): # t = i / float(nPnts-2) # c = [0,0,0] # for j in range(3): # c[j] = srcClr[j] * (1-t) + tarClr[j] * t # self.COLOR_TABLE.append(QtGui.QColor(c[0],c[1],c[2],50)) def setVisible(self, visible): self.isVisible = visible def paint(self, qPainter, alpha = 50, width = 1): if not self.isVisible: return gradient = QtWidgets.QLinearGradient(self.startPnt, self.endPnt) self.SRC_COLOR.setAlpha(alpha) self.MID_COLOR.setAlpha(alpha) self.DST_COLOR.setAlpha(alpha) gradient.setColorAt(0.0, self.SRC_COLOR) gradient.setColorAt(0.5, self.MID_COLOR) gradient.setColorAt(1.0, self.DST_COLOR) qPainter.setPen(QtGui.QPen(QtGui.QBrush(gradient), width)) #for i in range(LineCache.N_POINTS-1): # qPainter.setPen(self.COLOR_TABLE[i]) # qPainter.drawLine(self.pntList[i], self.pntList[i+1]) qPainter.drawPath(self.path) #qPainter.drawLine(self.startPnt, self.endPnt) class SymbolScene(QtWidgets.QGraphicsScene): def __init__(self, *args): super(SymbolScene, self).__init__(*args) self.symbolRoot = None self.symbolDict = {} self.baseRadius = 200 self.totalRadius = 20 self.unpinnedAngle = 1 self.pinnedAngle = 1 self.highPosList = [] self.normalPosList = [] self.lowPosList = [] self.callRef = {} from db.SymbolNode import SymbolNode self.widthDict = {SymbolNode.KIND_NAMESPACE: 10, SymbolNode.KIND_FUNCTION: 30, SymbolNode.KIND_VARIABLE: 10, SymbolNode.KIND_CLASS: 10, SymbolNode.KIND_UNKNOWN: 10} self.setItemIndexMethod(QtWidgets.QGraphicsScene.BspTreeIndex) def getBaseRadius(self): return self.baseRadius def buildScene(self): from db.DBManager import DBManager dbObj = DBManager.instance().getDB() self.callRef = {} self.symbolRoot, self.symbolDict = dbObj.buildSymbolTree() if not self.symbolRoot or not self.symbolDict: return self._buildRef() self._buildUI() def refreshUI(self): self._buildUI() def _buildUI(self): if self.symbolRoot is None: return self._buildRefAttr() depth, nLeaf, nPinnedLeaf, maxR = self._layoutButtonUp(self.symbolRoot) rootAttr = self.symbolRoot.getAttr(UIAttr.ATTR_UI) rootAttr.maxR = rootAttr.minR + 1 self.totalRadius = rootAttr.maxR idealPinnedAngle = 5.0 pinnedScore = 1.0 * nPinnedLeaf unpinnedScore = (360.0 / idealPinnedAngle - 1) / (nLeaf-1) * (nLeaf - nPinnedLeaf) if nPinnedLeaf > 0: self.pinnedAngle = math.pi * 2.0 * pinnedScore / (pinnedScore+unpinnedScore) / nPinnedLeaf if nLeaf - nPinnedLeaf > 0: self.unpinnedAngle = math.pi * 2.0 * unpinnedScore / (pinnedScore+unpinnedScore) / (nLeaf-nPinnedLeaf) self._layoutTopDown(self.symbolRoot, 0, math.pi * 2.0, False) self._buildLines() def getHighPosList(self): return self.highPosList def getNormalPosList(self): return self.normalPosList def getLowPosList(self): return self.lowPosList def _buildLines(self): from db.SymbolAttr import SymbolAttr scene = self refDict = scene.getCallDict() self.highPosList = [] self.normalPosList = [] self.lowPosList = [] classCallDict = {} for key, ref in refDict.items(): callerNode = scene.getNode(key[0]) calleeNode = scene.getNode(key[1]) if not callerNode or not calleeNode: continue callerAttr = callerNode.getAttr(SymbolAttr.ATTR_UI) calleeAttr = calleeNode.getAttr(SymbolAttr.ATTR_UI) callerItem = callerAttr.getUIItem() calleeItem = calleeAttr.getUIItem() callerPnt = callerItem.getCurveSlot() calleePnt = calleeItem.getCurveSlot() # hide member functions when unpinned visible = True if callerNode.parent == calleeNode.parent and callerNode.parent and callerNode.parent.getKind() == callerNode.KIND_CLASS: parentAttr = callerNode.parent.getAttr(UIAttr.ATTR_UI) if parentAttr and parentAttr.isPinned == False: continue line = LineCache(QtCore.QPointF(callerPnt[0], callerPnt[1]), QtCore.QPointF(calleePnt[0], calleePnt[1])) if callerAttr.isAncestorPinned or calleeAttr.isAncestorPinned or callerAttr.isPinned or calleeAttr.isPinned: self.highPosList.append(line) elif callerNode.parent and calleeNode.parent and\ callerNode.parent.getKind() == callerNode.KIND_CLASS and calleeNode.parent.getKind() == calleeNode.KIND_CLASS: key = (callerNode.parent.uniqueName, calleeNode.parent.uniqueName) callLine = classCallDict.get(key) if not callLine: callerAttr = callerNode.parent.getAttr(SymbolAttr.ATTR_UI) calleeAttr = calleeNode.parent.getAttr(SymbolAttr.ATTR_UI) callerItem = callerAttr.getUIItem() calleeItem = calleeAttr.getUIItem() callerPnt = callerItem.getCurveSlot() calleePnt = calleeItem.getCurveSlot() line = LineCache( QtCore.QPointF(callerPnt[0], callerPnt[1]), QtCore.QPointF(calleePnt[0], calleePnt[1])) classCallDict[key] = line line.weight = 1 else: classCallDict[key].weight += 1 else: # elif callerAttr.isIgnored or calleeAttr.isIgnored: # self.lowPosList.append(line) # else: self.normalPosList.append(line) self.normalPosList.extend(classCallDict.values()) def _buildRefAttr(self): for uname, node in self.symbolDict.items(): refAttr = node.getOrAddAttr(RefAttr.ATTR_REF) refAttr.nCall = 0 refAttr.nCalled = 0 for srcName, tarName in self.callRef: srcNode = self.symbolDict.get(srcName) tarNode = self.symbolDict.get(tarName) if not srcNode or not tarNode: continue srcRef = srcNode.getAttr(RefAttr.ATTR_REF) tarRef = tarNode.getAttr(RefAttr.ATTR_REF) srcRef.nCall +=1 tarRef.nCalled +=1 def _layoutButtonUp(self, node): depth = 0 nLeaf = 0 nPinnedLeaf = 0 minR = 0 for uname, child in node.getChildDict().items(): childDepth, leafCount, pinnedLeafCount, childR = self._layoutButtonUp(child) depth = max(depth, childDepth) nLeaf += leafCount nPinnedLeaf += pinnedLeafCount minR = max(minR, childR) if len(node.getChildDict()) == 0: nLeaf = 1 minR = self.baseRadius depth += 1 layoutAttr = node.getOrAddAttr(UIAttr.ATTR_UI) if layoutAttr.isPinned: nPinnedLeaf = nLeaf layoutAttr.subtreeDepth = depth layoutAttr.subtreeNLeaf = nLeaf layoutAttr.subtreeNPinnedLeaf = nPinnedLeaf layoutAttr.minR = minR maxR = minR + self.widthDict[node.getKind()] return depth, nLeaf, nPinnedLeaf, maxR def _layoutTopDown(self, node, minTheta, maxTheta, isPinned): nodeAttr = node.getAttr(UIAttr.ATTR_UI) nodeAttr.minTheta = minTheta nodeAttr.maxTheta = maxTheta nodeAttr.isAncestorPinned = isPinned uiItem = nodeAttr.uiItem if not uiItem: uiItem = SymbolUIItem(node) nodeAttr.uiItem = uiItem uiItem.buildUI(nodeAttr, self) self.addItem(uiItem) else: uiItem.buildUI(nodeAttr, self) # dTheta = float(maxTheta - minTheta) / nodeAttr.subtreeNLeaf begTheta = minTheta childList = list(node.getChildDict().values()) childList.sort(key=lambda x:(x.defineFile, x.kind, x.getAttr(RefAttr.ATTR_REF).getCallerCalleeDiff())) for child in childList: childAttr = child.getAttr(UIAttr.ATTR_UI) childAttr.maxR = nodeAttr.minR newBegTheta = begTheta pinned = isPinned or childAttr.isPinned if pinned: newBegTheta += childAttr.subtreeNLeaf * self.pinnedAngle else: newBegTheta += childAttr.subtreeNPinnedLeaf * self.pinnedAngle + (childAttr.subtreeNLeaf - childAttr.subtreeNPinnedLeaf) * self.unpinnedAngle self._layoutTopDown(child, begTheta, newBegTheta, pinned) begTheta = newBegTheta def _buildRef(self): from db.DBManager import DBManager dbObj = DBManager.instance().getDB() for uname, symbol in self.symbolDict.items(): entityList, refList = dbObj.searchRefEntity(uname, 'call', '*', True) for ent in entityList: tarUname = ent.uniquename() self.callRef[(uname, tarUname)] = RefData(RefData.REF_CALL) def pinSymbol(self, isPinned): itemList = self.selectedItems() if not itemList: return from db.SymbolAttr import SymbolAttr for item in itemList: node = item.getNode() attr = node.getOrAddAttr(SymbolAttr.ATTR_UI) attr.setPinned(isPinned) self.refreshUI() def ignoreSymbol(self, isIgnored): itemList = self.selectedItems() if not itemList: return from db.SymbolAttr import SymbolAttr for item in itemList: node = item.getNode() attr = node.getOrAddAttr(SymbolAttr.ATTR_UI) attr.setIgnored(isIgnored) self.refreshUI() def getCallDict(self): return self.callRef def getNode(self, uniqueName): return self.symbolDict.get(uniqueName, None) def updateNodeVisibility(self, lod): t0 = time.clock() r = self.baseRadius * lod for item in self.items(): al = r * (item.theta[1] - item.theta[0]) item.setVisible(al > 2) t1 = time.clock()