From 86447f36df323be5ce8e7ba6fdbfa244452e6196 Mon Sep 17 00:00:00 2001 From: Robert Wiesner Date: Fri, 21 Feb 2025 18:58:51 +0000 Subject: [PATCH 1/5] Update 14 files - /src/sdfvisio/cscomponent.py - /src/sdfvisio/debugstatemachine.py - /src/sdfvisio/devicehierarchy.py - /src/sdfvisio/jtagtab.py - /src/sdfvisio/layouttopology.py - /src/sdfvisio/link.py - /src/sdfvisio/qtcolorscheme.py - /src/sdfvisio/qtdaplog.py - /src/sdfvisio/qtdisplay.py - /src/sdfvisio/qtprimitives.py - /src/sdfvisio/qtsdfmain.py - /src/sdfvisio/qtsettings.py - /src/sdfvisio/sdfvisio.py - /src/sdfvisio/statemachine.py --- src/sdfvisio/cscomponent.py | 34 +++------ src/sdfvisio/debugstatemachine.py | 13 ++-- src/sdfvisio/devicehierarchy.py | 24 ++++--- src/sdfvisio/jtagtab.py | 6 +- src/sdfvisio/layouttopology.py | 114 +++++++++++++++--------------- src/sdfvisio/link.py | 21 +++--- src/sdfvisio/qtcolorscheme.py | 64 ++++++++--------- src/sdfvisio/qtdaplog.py | 80 +++++++++------------ src/sdfvisio/qtdisplay.py | 60 ++++++---------- src/sdfvisio/qtprimitives.py | 9 +-- src/sdfvisio/qtsdfmain.py | 19 ++--- src/sdfvisio/qtsettings.py | 1 + src/sdfvisio/sdfvisio.py | 61 +++++++++++----- src/sdfvisio/statemachine.py | 14 ++-- 14 files changed, 245 insertions(+), 275 deletions(-) diff --git a/src/sdfvisio/cscomponent.py b/src/sdfvisio/cscomponent.py index 1f99a7d..6ce7eba 100644 --- a/src/sdfvisio/cscomponent.py +++ b/src/sdfvisio/cscomponent.py @@ -44,17 +44,11 @@ class cTrackRegister: def getAllValues(self) ->list: """Return an array with all of the values for the entire file""" - aRet = [] - for l in self.aAccess: - aRet.append(l[2]) - return aRet + return [l[2] for l in self.aAccess] def getAllRows(self) -> list: """Return an array with all rows changin the file""" - aRet = [] - for l in self.aAccess: - aRet.append(l[0]) - return aRet + return [l[0] for l in self.aAccess] def getFirstValue(self): """Get the first register value""" @@ -106,22 +100,21 @@ class cCSGeneric: def updateRegister(self, row : int, regName : str, aReg : list, val : int, dapLogTab : cDapLogTableWidget) -> bool: """track register value changes and return True, in case the register values is bad""" self.dapLogTab = dapLogTab - if regName not in self.dRegValue: - self.dRegValue[regName] = cTrackRegister(regName) + self.dRegValue.setdefault(regName, cTrackRegister(regName)) #print(f"{self.device.name}:{regName} {aReg} {val}") self.dRegValue[regName].updateRegister(row, aReg[0], val) if self.useCID is None: - if regName[:4] == "CIDR": + if regName.startswith("CIDR"): self.useCID = self.aCIDR - elif regName[:6] == "EDCIDR": + elif regName.startswith("EDCIDR"): self.useCID = self.aEDCIDR if self.usePID is None: - if regName[:4] == "PIDR": + if regName.startswith("PIDR"): self.usePID = self.aPIDR - elif regName[:6] == "EDPIDR": + elif regName.startswith("EDPIDR"): self.usePID = self.aEDPIDR ret = (self.usePID is not None) and (regName in self.usePID) and ((val & 0xffffff00) != 0) @@ -139,17 +132,8 @@ class cCSGeneric: def getCombineRegisters(self, regList : list, shiftWidth : int) -> int: """combine multiple registers to a single value""" - ret = None - aRow = [] - shift = 0 - if regList is not None: - for name in regList: - if name in self.dRegValue: - val = self.dRegValue[name].getLastValue() << shift - aRow += self.dRegValue[name].getAllRows() - ret = val if ret is None else (val + ret) - shift += shiftWidth - return ret, aRow + values = [(self.dRegValue[name].getLastValue() << (i * shift_width)) for i, name in enumerate(reg_list) if name in self.dRegValue] + return sum(values) if values else None, [self.dRegValue[name].getAllRows() for name in reg_list if name in self.dRegValue] def getPID(self): """Combine the last PID register to a current value or XX""" diff --git a/src/sdfvisio/debugstatemachine.py b/src/sdfvisio/debugstatemachine.py index 1cd9fae..eb7b04b 100644 --- a/src/sdfvisio/debugstatemachine.py +++ b/src/sdfvisio/debugstatemachine.py @@ -147,11 +147,15 @@ class cArmv8DebugStateMachine(cTrackData): def setRXOerror(self, component, regname : str, access : str, value): self.errorState = "EDSCR.RXO[27] set" + def setDebugRegister(self, regname, value): + self.dRegVal[regname] = int(value[:10], 16) + def setDBGDTRTX_EL0(self, component, regname : str, access : str, value): - self.dRegVal["DBGDTRTX_EL0"] = int(value[:10], 16) + self.setDebugRegister("DBGDTRTX_EL0", value) def setDBGDTRTX_EL0andUpdate(self, component, regname : str, access : str, value): - self.dRegVal["DBGDTRTX_EL0"] = int(value[:10], 16) + self.setDebugRegister("DBGDTRTX_EL0", value) + val = (self.dRegVal["DBGDTRRX_EL0"] << 32) + self.dRegVal["DBGDTRTX_EL0"] self.dRegVal[self.readRegisterName] = val @@ -160,9 +164,10 @@ class cArmv8DebugStateMachine(cTrackData): else: self.addData(f"{self.readRegisterName:>20}: 0x{val:016X}") self.readRegisterName = None - + def setDBGDTRRX_EL0(self, component, regname : str, access : str, value): - self.dRegVal["DBGDTRRX_EL0"] = int(value[:10], 16) + self.setDebugRegister("DBGDTRRX_EL0", value) + def readRegisterValue(self, component, regname : str, access : str, value): """MSR DBGDTR_EL0, """ diff --git a/src/sdfvisio/devicehierarchy.py b/src/sdfvisio/devicehierarchy.py index 05afad6..bd0f790 100644 --- a/src/sdfvisio/devicehierarchy.py +++ b/src/sdfvisio/devicehierarchy.py @@ -60,7 +60,7 @@ class cDevice: """ def __init__(self, dapName : str, name : str, devType : str, dev): """Setup variables on initialize, initialized not set values with None""" - self.dpName = dapName + self.dapName = dapName self.name = name self.devType = devType self.parent = None @@ -73,30 +73,32 @@ class cDevice: self.toolTip = None for item in dev.findall(".//config_item"): - if item.attrib["name"] == "CORESIGHT_BASE_ADDRESS": # Should be in each device + name = item.attrib.get("name", "") + if name == "CORESIGHT_BASE_ADDRESS": # Should be in each device self.address = str(item.text) # Find AP_INDEX for CSMEMAP categorisation - elif item.attrib["name"] == "CORESIGHT_AP_INDEX": # Should be in each device and AP + elif name == "CORESIGHT_AP_INDEX": # Should be in each device and AP self.index = getInt(item.text) # Check for "PARENT_AP_INDEX" for nested CSMEMAPs - elif item.attrib["name"] == "PARENT_AP_INDEX": # Only for nested APs + elif name == "PARENT_AP_INDEX": # Only for nested APs self.parent = getInt(item.text) else: - self.info[item.attrib["name"]] = str(item.text) + self.info[name] = str(item.text) for item in dev.findall(".//device_info_item"): - if "name" in item.attrib: - if item.attrib["name"] == "EXCLUDE_FROM_DTSL": # Only for nested APs + name = item.attrib.get("name", None) + if name is not None: + if name == "EXCLUDE_FROM_DTSL": # Only for nested APs self.isExcluded = str(item.text).lower() != "false" else: - self.info[item.attrib["name"]] = str(item.text) + self.info[name] = str(item.text) if self.index is None: self.index = 0 # use the default AP index self.info["++IMPLICIT_AP_INDEX++"] = "True" def updateRegister(self, reg : str, value : str, row : int, dapLogTab : cDapLogTableWidget) -> None: - """Trak device specific register values""" + """Track device specific register values""" val = int(value.split()[0], 16) aReg = reg.split(":") regName = aReg[-1].split()[0] @@ -109,7 +111,7 @@ class cDevice: def __str__(self) -> str: """Return a string representation""" - return f"{self.dpName}:{self.devType}:{self.name} " + return f"{self.dapName}:{self.devType}:{self.name} " def setDepth(self, depth : int) -> None: """The the level in the topology structure""" @@ -124,7 +126,7 @@ class cDevice: """Create a device specific tool tip used for the boxes and dap log eater table""" if self.toolTip is None: aRet = [] - self.addToolTipPart(aRet, "DP", self.dpName) + self.addToolTipPart(aRet, "DAP", self.dapName) self.addToolTipPart(aRet, " AP_Index", self.index) self.addToolTipPart(aRet, " Address ", self.address) self.addToolTipPart(aRet, " Device type", self.devType) diff --git a/src/sdfvisio/jtagtab.py b/src/sdfvisio/jtagtab.py index dafcc50..163b671 100644 --- a/src/sdfvisio/jtagtab.py +++ b/src/sdfvisio/jtagtab.py @@ -32,12 +32,10 @@ class cJTAGTAB: self.isSWJEn = isSWJEn self.dDevInfo = {} - for diis in dev.findall("device_info_items"): - for dii in diis.findall("device_info_item"): - self.dDevInfo[dii.attrib["name"]] = dii.text + self.dDevInfo = { dii.attrib["name"]: dii.text for diis in dev.findall("device_info_items") for dii in diis.findall("device_info_item") if self.irLen == 0 and not isSWJEn: - # try to determin the IR length + # try to determine the IR length if self.type[:8] == "UNKNOWN_": self.irLen = int(self.type[8:]) elif self.type in ["ARMCS-DP", "ETB", "ETM"]: diff --git a/src/sdfvisio/layouttopology.py b/src/sdfvisio/layouttopology.py index 505356f..9743a10 100644 --- a/src/sdfvisio/layouttopology.py +++ b/src/sdfvisio/layouttopology.py @@ -27,6 +27,7 @@ cTopoLink: create the links for each topology block, while the SDF file descripe cTopo: Layout a topologgy, after the layout we have a list of boxes and lines to be drawn """ +import bisect from .link import cLink, cArtEntry from .qtsettings import settings @@ -37,7 +38,7 @@ class cBox: self.y = y self.dx = dx # display width self.dy = dy - self.ldx = None # logical based on the sum od all inputs width + self.ldx = None # logical based on the sum of all input width self.highlight = False self.position = False @@ -102,10 +103,7 @@ class cInOut: self.maxi = idx item = cInOutElement(name, idx) - i = 0; - while i < len(self.list) and self.list[i] < item: - i += 1 - self.list.insert(i, item) + bisect.insort(self.list, item) def linksTo(self, name): for item in self.list: @@ -162,7 +160,7 @@ class cTopoLink: return self.output.maxi def getMaxInOutIdx(self) -> int: - """Return the maximum index of receiver and sender to determin the max mox size""" + """Return the maximum index of receiver and sender to determine the max mox size""" return max(self.getMaxMi(), self.getMaxSi()) class cTopo: @@ -194,7 +192,7 @@ class cTopo: 2.4.2: start layouting the "x" offset of the boxes with the entire line beinging cernterare at 0 2.4.3: calculate the max line width for all rows (deep) 2.5: for all boxes adjust the "x" value by the widest marking - 2.6: createLines: for each final item start recustilfy creating line information for the from to and collect this information for later use + 2.6: createAllLines: generated all lines for this graph getBoxByIdx: return the next index or -1 and the boxes and text to be drawn for each component """ @@ -376,7 +374,7 @@ class cTopo: again = False for s in self.aEntry: setIdx = None - if s.level == -1: + if s.level is None: for m in self.aEntry: if s.dAttr["master"] == m.dAttr["slave"]: setIdx = m.level @@ -387,28 +385,7 @@ class cTopo: s.level = setIdx + 1 again = True - def layoutTopology(self): - # self.assignLevel() - - # for all entries, create a link - for m in self.aEntry: - self.createLink(m) - - # identify all final entries, these have no desintation / ouput - for nm in self.dLink: - if self.dLink[nm].output.getCount() == 0: - self.dFinal[nm] = self.dLink[nm] - - finalCount = 0 - self.aBoxes = [] - self.maxDepth = 0 - - # for all final entries - for nm in self.dFinal: - finalCount += 1 - d = self.setDeep(nm) - self.maxDepth = self.maxDepth if self.maxDepth > d else d - + def moveBoxesUpward(self): # Move the final boxes upward as needed for nm in self.dFinal: minDepth = None @@ -419,6 +396,29 @@ class cTopo: if minDepth is not None and 2 < minDepth: self.dLink[nm].deep = minDepth - 1 + def assignFinalBoxesAndDepthLevel(self): + # identify all final entries, these have no desintation / ouput + self.dFinal = { nm : self.dLink[nm] for nm in self.dLink if self.dLink[nm].output.getCount() == 0} + + # for all final entries + for nm in self.dFinal: + d = self.setDeep(nm) + self.maxDepth = max(self.maxDepth, d) + return len(self.dFinal) + + def layoutTopology(self): + self.aBoxes = [] + self.maxDepth = 0 + + # for all entries, create a link + for m in self.aEntry: + self.createLink(m) + + finalCount = self.assignFinalBoxesAndDepthLevel(); + + # improvide the box layout to reduce the back link + self.moveBoxesUpward() + # if there are too many final boxes move them to maxDepth + 1 if -1 == self.name.find("CoreTrace/ATB") and 15 < finalCount: cnt = 0 @@ -439,9 +439,7 @@ class cTopo: self.adjustBoxLocation() # create lines between the boxes - self.aLines = [] - for nm in self.dFinal: - self.createLines(nm) + self.aLines = self.createAllLines(nm) self.updateHighlightLines(self.opt["highlight"]["path"]) @@ -613,24 +611,30 @@ class cTopo: y = self.dLink[nm].box.y + (0 if self.sinkFirst else self.dLink[nm].box.dy) return x, y, mi - def createLines(self, nm): - if not self.dLink[nm].lines: - self.dLink[nm].lines = True - sx = self.dLink[nm].box.x + (self.dLink[nm].box.dx - self.SIZE_IN_OUT * self.dLink[nm].getMaxSi()) // 2 - sy = self.dLink[nm].box.y + (self.dLink[nm].box.dy if self.sinkFirst else 0) + def createAllLines(self, nm): + """General all lines between the boxes itterative and the return the array""" + aRet = [] + for nm, src in self.dLink.items(): + src.lines = True + sx = src.box.x + (src.box.dx - self.SIZE_IN_OUT * src.getMaxSi()) // 2 + sy = src.box.y + (src.box.dy if self.sinkFirst else 0) + + dSkip = {} - for input in self.dLink[nm].input: + for input in src.input: N = input.name if N != "": - dSkip[N] = dSkip[N] + 1 if N in dSkip else 0 - ex = (self.dLink[N].box.dx - self.SIZE_IN_OUT * self.dLink[N].getMaxMi()) // 2 - ex += self.dLink[N].box.x + dSkip[N] = dSkip.get(N, 0) + 1 + dst = self.dLink[N] + ex = (dst.box.dx - self.SIZE_IN_OUT * dst.getMaxMi()) // 2 + ex += dst.box.x + ex, ey, mi = self.getEndXYandTXT(N, nm, dSkip) dx = 0 if input.idx is None else self.SIZE_IN_OUT * input.idx - self.aLines.append(cLine(sx + dx, sy, ex, ey, input.idx, mi, nm, N)) - self.createLines(N) + aRet.append(cLine(sx + dx, sy, ex, ey, input.idx, mi, nm, N)) + return aRet def updateHighlightLines(self, pairs): aHighlightLines = self.createAllPaths(pairs) @@ -711,23 +715,19 @@ class cTopo: def getAllBoxes(self): """get all boxes to be drawn, return one by one""" + isTrace = self.name == "CoreTrace/ATB" + for aBox in self.aBoxes: for boxText in aBox: - if boxText != "": - c = "defaultBorder" - border = None - if self.name == "CoreTrace/ATB": - if self.dLink[boxText].output.getCount() == 0: - c = "traceSink" - border = "tracesink" - elif self.dLink[boxText].input.getCount() == 0: - c = "traceSource" - border = "tracesource" + currBox = self.dLink(boxText, None) + if currBox is not None and boxText != "": + border = "tracesink" if isTrace and currBox.output.getCount() == 0 else + ("tracesource" if isTrace and currBox.input.getCount() == 0 else None) highlight = False - B = boxText.upper() + boxTxt = boxText.upper() for n in self.opt["highlight"]["box"]: - highlight |= 0 <= B.find(n) - yield ( ( self.dLink[boxText].box, boxText, self.dLink[boxText].device, border, highlight) ) + highlight |= 0 <= boxTxt.find(n) + yield ( ( currBox.box, boxText, currBox.device, border, highlight) ) def centerBoxes(self, aBox): dx = -self.BOX_DX_DIST diff --git a/src/sdfvisio/link.py b/src/sdfvisio/link.py index 17e67fe..cb5f37f 100644 --- a/src/sdfvisio/link.py +++ b/src/sdfvisio/link.py @@ -25,11 +25,8 @@ cArtEntry: create an artificial entry for CS Funnels or Replicators """ class cLink: def __init__(self, attrib): - self.dAttr = {} - if attrib != None: - for i in attrib: - self.dAttr[i] = attrib[i] - self.level = -1 # level not yet set + self.dAttr = {k: v for k, v in attrib.items() } if attrib else {} + self.level = None # level not yet set def setMS(self, m, s): self.dAttr["master"] = m @@ -38,15 +35,13 @@ class cLink: def __repr__(self): return self.__str__() + def getStrPart(self, prefix, name): + nameVal = self.dAttr.get(name, None) + return '' if nameVal is None else f' {prefix}[{nameVal}]' + def __str__(self): - misi = "" - if "master_interface" in self.dAttr: - misi += " MI[" + self.dAttr["master_interface"] + "]" - if "slave_interface" in self.dAttr: - misi += " SI[" + self.dAttr["slave_interface"] + "]" - if "trigger" in self.dAttr: - misi += " TR[" + self.dAttr["trigger"] + "]" - return self.dAttr["master"] + ":" + str(self.level) + " -> " + self.dAttr["slave"] + misi + misi = self.getStrPart("MI", "master_interface") + self.getStrPart("SI", "slave_interface") + self.getStrPart("TR", "trigger") + return f'{self.dAttr["master"]}:{self.level} -> {self.dAttr["slave"]}{misi}' class cArtEntry(cLink): """Create an artificial funnel or replicator and simulate a XML ElementTree.Element of the SDF XML file""" diff --git a/src/sdfvisio/qtcolorscheme.py b/src/sdfvisio/qtcolorscheme.py index 7dc447c..de34b90 100644 --- a/src/sdfvisio/qtcolorscheme.py +++ b/src/sdfvisio/qtcolorscheme.py @@ -18,12 +18,11 @@ ### """ -Handle the various colo schema +Handle the various color schema """ from PySide6.QtWidgets import QLineEdit, QLabel, QColorDialog from PySide6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout, QPushButton, QVBoxLayout -from PySide6.QtGui import QPen, QFont, QColor, QBrush, QMouseEvent -from PySide6.QtCore import QObject +from PySide6.QtGui import QPen, QFont, QColor from .qtsettings import settings # Entries are a tuple of attribute name, color [, pen width] @@ -243,6 +242,29 @@ class cSceneColor(): self.brush[colorName] = [QBrush(c) for c in color] else: print("Skip:", self.group, self.name, entry, type(color)) + self.updateColorModeFromSettings() + + def updateColorModeFromSettings(self): + prefix = self.getColorPath("pen") + for key in settings.getGroupKeys(prefix): + # update all pens + penColor = settings.getStr(prefix + "/" + key) + if penColor: + value = [ int(part) for part in penColor.split(",") if part.isnumeric()] + pen = self.pen.get(key) + if pen and 4 == len(value): + pen.setColor(QColor(value[0], value[1], value[2])) + pen.setWidth(value[3]) + brush = self.brush.get(key) + if brush: + brush.setColor(QColor(value[0], value[1], value[2])) + else: + print("Key not found:", key) + + prefix = self.getColorPath("brush") + for key in settings.getGroupKeys(prefix): + print("Brush: ", prefix + "/" + key) + def getColorPath(self, suffix : str): return f"Color_{self.group}_{self.name}/{suffix}" @@ -483,26 +505,6 @@ class cEditColorSchema(QDialog): self.exec() return self.ret and self.colorChanged -def updateColorModeFromSettings(color): - prefix = color.getColorPath("pen") - for key in settings.getGroupKeys(prefix): - # update all pens - penColor = settings.getStr(prefix + "/" + key) - if penColor: - value = [ int(part) for part in penColor.split(",") if part.isnumeric()] - pen = color.pen.get(key) - if pen and 4 == len(value): - pen.setColor(QColor(value[0], value[1], value[2])) - pen.setWidth(value[3]) - brush = color.brush.get(key) - if brush: - brush.setColor(QColor(value[0], value[1], value[2])) - else: - print("Key not found:", key) - - prefix = color.getColorPath("brush") - for key in settings.getGroupKeys(prefix): - print("Brush: ", prefix + "/" + key) def updateSettingsFromColorMode(): for colorList in colorModeList: @@ -526,20 +528,12 @@ def updateSettingsFromColorMode(): settings.updateStr(f"{brushPrefix}{idx}", rgb, None) def generateColorList(defaultColors): - ret = [] - for idx, colorList in enumerate(defaultColors): - ret.append([]) - for subIdx, colorInfo in enumerate(colorList[1]): - ret[-1].append(cSceneColor(colorList[0], colorInfo[0], colorInfo[1])) - updateColorModeFromSettings(ret[-1][-1]) - return ret + return [ [cSceneColor(colorList[0], colorInfo[0], colorInfo[1]) for subIdx, colorInfo in enumerate(colorList[1])] + for idx, colorList in enumerate(defaultColors) ] + colorModeList = generateColorList(defaultColors) updateSettingsFromColorMode() def getColorModeList(): - aRet = [] - for colorList in colorModeList: - for color in colorList: - aRet.append(f"{color.group}/{color.name}") - return aRet + return [f"{color.group}/{color.name}" for colorList in colorModeList for color in colorList ] diff --git a/src/sdfvisio/qtdaplog.py b/src/sdfvisio/qtdaplog.py index 498b5d3..aef0a5c 100644 --- a/src/sdfvisio/qtdaplog.py +++ b/src/sdfvisio/qtdaplog.py @@ -150,7 +150,7 @@ class cDapLogTableWidget(QTableWidget): def exportAsCSV(self): """Export the dap log with filter as a comma separated file, a multi line content creates multiple output lines""" fileNames = QFileDialog.getSaveFileName(self, 'Save content as CSV', '.', "CSV File (*.csv)") - if fileNames != None and 0 < len(fileNames[0]): + if fileNames is not None and 0 < len(fileNames[0]): aCol = [] aOut = ["Index"] for col in range(len(self.aHeader)): @@ -158,35 +158,33 @@ class cDapLogTableWidget(QTableWidget): aCol.append(col) aOut.append(self.aHeader[col]) - fOut = open(fileNames[0], "w") - print(",".join(aOut), file = fOut) - - for row in range(self.rowCount()): - if not self.isRowHidden(row): - aOut = [self.verticalHeaderItem(row).text()] - - for col in aCol: - txt = self.getItemText(row, col) - if txt is None: - aOut.append("") - else: - aOut.append(txt.strip()) - again = True - - while again: - again = False - aLineOut = [] - for idx in range(len(aOut)): - aS = aOut[idx].split("\n", 1) - aLineOut.append(f'"{aS[0].strip()}"') - if 2 == len(aS): - again = True - aOut[idx] = aS[1] - else: - aOut[idx] = "" - print(",".join(aLineOut), file = fOut) - - fOut.close() + with open(fileNames[0], "w") as fOut: + print(",".join(aOut), file = fOut) + + for row in range(self.rowCount()): + if not self.isRowHidden(row): + # create the CSV file but the column header + aOut = [self.getItemText(row, col, "").strip() for col in aCol] + + # add the column header at the beginning + aOut.insert(0, self.verticalHeaderItem(row).text()) + + again = True + + while again: + again = False + aLineOut = [] + for idx in range(len(aOut)): + aS = aOut[idx].split("\n", 1) + aLineOut.append(f'"{aS[0].strip()}"') + if 2 == len(aS): + again = True + aOut[idx] = aS[1] + else: + aOut[idx] = "" + print(",".join(aLineOut), file = fOut) + + fOut.close() def contextMenuEvent(self, event): aCopyToClip = ["Python Object", "CSV"] @@ -331,7 +329,7 @@ class cDapLogTableWidget(QTableWidget): twi = self.cellWidget(row, col) if twi is None else twi twi.setHighlight(highlight) - def highlightItems(self, part): + def highlightItems(self, part : str): self.aFoundRow = [] self.foundIdx = 0 @@ -342,16 +340,11 @@ class cDapLogTableWidget(QTableWidget): for col in range(len(self.aHeader)): text = self.getItemText(row, col) if text is not None: - state = self.dStatus[self.aHeader[col]] and not (part is None) - if state: - m = prog.search(text) - state = m != None + state = self.dStatus[self.aHeader[col]] and not (part is None) and (prog.search(text) is not None) + self.updateColors(row, col, state) if state: - self.updateColors(row, col, True) self.aFoundRow.append(row) - else: - self.updateColors(row, col, False) def doSearchAndHighlight(self, searchString): self.highlightItems(searchString) @@ -429,14 +422,11 @@ class cDapLogTableWidget(QTableWidget): twi.setTextAlignment(Qt.AlignRight) self.setVerticalHeaderItem(row, twi); - def getItemText(self, row, col): + def getItemText(self, row, col, dflt = None): twi = self.item(row, col) if twi is None: textEdit = self.cellWidget(row, col) - if textEdit is None: - return None - print(row, col, textEdit.toPlainText()) - return textEdit.toPlainText() + return dflt if textEdit is None else textEdit.toPlainText() return twi.text() @@ -530,7 +520,7 @@ class cDapLogWidget(QMainWindow): toolbar.addWidget(ret) return ret - def exportAsCSV(self): + def actionExportAsCSV(self): self.table.exportAsCSV() def __init__(self, clipboard): @@ -539,7 +529,7 @@ class cDapLogWidget(QMainWindow): self.table = cDapLogTableWidget(self) toolbar = QToolBar(self) - self.exportBtn = self.createButton(toolbar, "Export", self.exportAsCSV, None) + self.exportBtn = self.createButton(toolbar, "Export", self.actionExportAsCSV, None) self.gotoFirst = self.createButton(toolbar, "First", self.actionGotoFirst, None) self.gotoPrev = self.createButton(toolbar, "Previous", self.actionGotoPrev, QKeySequence("Ctrl+p")) diff --git a/src/sdfvisio/qtdisplay.py b/src/sdfvisio/qtdisplay.py index 0588334..e8c7464 100644 --- a/src/sdfvisio/qtdisplay.py +++ b/src/sdfvisio/qtdisplay.py @@ -42,26 +42,22 @@ Helper Function """ # Imports - +import os import sys import subprocess import traceback from pathlib import Path from .__init__ import __version__, __revision__, __description__, __copyright__ -from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QTabWidget, QToolBar, QStyle -from PySide6.QtWidgets import QLabel, QStyleFactory -from PySide6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout, QPushButton, QVBoxLayout -from PySide6.QtWidgets import QCheckBox -from PySide6.QtWidgets import QGraphicsDropShadowEffect -from PySide6.QtWidgets import QTableWidget, QTableWidgetItem -from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QLabel, QComboBox -from PySide6.QtGui import QPen, QFont, QColor, QBrush, QPixmap, QPainter, QImage, QCursor, QWheelEvent, QFontMetrics, QMouseEvent -from PySide6.QtGui import QAction, QPolygonF, QPainterPath, QPalette +from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget +from PySide6.QtWidgets import QStyleFactory +from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem, QComboBox +from PySide6.QtGui import QFont, QColor, QBrush, QPixmap, QPainter, QCursor +from PySide6.QtGui import QAction from PySide6.QtWidgets import QWidgetAction -from PySide6.QtCore import QSize, QRect, QTimer, Qt, QPointF, Signal, Slot, QObject +from PySide6.QtCore import QSize, QRect, Qt, Signal, Slot, QObject from PySide6.QtSvg import QSvgGenerator -from PySide6.QtWidgets import QMenuBar, QMenu, QScrollBar, QGraphicsItem +from PySide6.QtWidgets import QMenu from PySide6.QtWidgets import QFileDialog, QMessageBox from PySide6.QtGui import QFontDatabase from .qtcolorscheme import colorModeList, cEditColorSchema, getColorModeList @@ -72,9 +68,7 @@ from .qtsdfmain import cAddDapLogEater from .qtsdfmain import cGraphicsView from .qtsdfmain import cGraphicsScene from .qtprimitives import cGraphicsTextItem, cGraphicsPathItem, cGraphicsPolygonItem -from .qtprimitives import cSearchDialog from .qtdaplog import cDapLogWidget -from PySide6.QtWidgets import QAbstractItemView from PySide6.QtWidgets import QProgressBar clipboard = None @@ -165,7 +159,7 @@ class cContext: keyList.sort() dAddrCnt20 = {} dAddrCnt24 = {} - # determin the address prefix list + # determine the address prefix list for key in keyList: idx = int(key, 16) // 0x100000 dAddrCnt20[idx] = dAddrCnt20.get(idx, 0) + len(self.dAddr[key]) @@ -232,7 +226,6 @@ class cDisplaySDFContent(QObject): self.dColorByAP = { "UNKNOWN_UNKNOWN" : 0} self.dBoxes = {} - self.aByAddr = [] self.context = cContext() self.aLines = [] self.aTexts = [] @@ -330,7 +323,7 @@ class cDisplaySDFContent(QObject): if pair is not None: self.performUpdateHighlighLines([ [pair[0].upper(), pair[1].upper()] ] ) elif action == addDapLog: - baseName = self.sdf.fileName.split("/")[-1].split("\\")[-1] + baseName = os.path.basename(self.sdf.fileName) dialog = cAddDapLogEater(self.view, settings.getStr(f"{baseName}/DapLogFile")) value = dialog.run() if not value is None: @@ -402,8 +395,8 @@ class cDisplaySDFContent(QObject): return None def getBrushColor(self, dev, dfltName): - if dev is not None and dev.dpName is not None and dev.index is not None: - key = f"{dev.dpName}_{dev.index}" + if dev is not None and dev.dapName is not None and dev.index is not None: + key = f"{dev.dapName}_{dev.index}" if key not in self.dColorByAP: self.dColorByAP[key] = self.colorIdx @@ -451,13 +444,13 @@ class cDisplaySDFContent(QObject): else: self.updateHighlighList = None - def addBox(self, sx, sy, dx, dy, name, dev = None, - markRed = False, border = None, toolTipPrefix = "" , + def addBox(self, sx : int, sy : int, dx : int, dy : int, name : str, + dev = None, canBeMarkedRed = False, border = None, toolTipPrefix = "" , toolTip = None, highlight = None): """ sx, sy, dx, sy: create a box starting at (sx, sy) with the size (dx, dy) name: this is the instribe names of the box dev: options device description of the box - markRed: if true, this box can be marked red in case of excluded device or none device + canBeMarkedRed: if true, this box can be marked red in case of excluded device or none device border: default border color toolTipPrefix / toolTip: create a tool tip and add it to the box as needed """ @@ -476,7 +469,7 @@ class cDisplaySDFContent(QObject): if noIdxName not in self.dBoxesHighlight: self.dBoxesHighlight[noIdxName] = [] - toolTip = self.getToolTipText(name, dev, markRed) if toolTip is None else toolTip + toolTip = self.getToolTipText(name, dev, canBeMarkedRed) if toolTip is None else toolTip devType = "unknown" if toolTip is not None: for val in toolTip.split("\n"): @@ -491,12 +484,16 @@ class cDisplaySDFContent(QObject): brushName, brushIdx = self.getBrushColor(dev, "artrepl") else: boxType = "default" - if (dev is None or dev.isExcluded) and markRed: + if canBeMarkedRed and (dev is None or dev.isExcluded): brushName, brushIdx = self.getBrushColor(None, "red") else: brushName, brushIdx = self.getBrushColor(dev, "background") item = cGraphicsPolygonItem(boxType, sx-3, sy-3, dx+6, dy+6, "highlight", "background", None, None) + + if (highlightVisible): + item.setVisible(True) + self.dBoxesHighlight[noIdxName].append((name, item)) self.image.addItem(item) @@ -514,21 +511,6 @@ class cDisplaySDFContent(QObject): highIdx = None self.context.addItem(addr, item, sx, sy, dx, dy, name, apIdx) - for idx in range(len(self.aByAddr)): - if addr == self.aByAddr[idx][0][0]: - # two components at the same address - self.aByAddr[idx].append((addr, item, sx, sy, dx, dy, name)) - addr = None - break - elif addr < self.aByAddr[idx][0][0]: - # insert component here - self.aByAddr.insert(idx, [(addr, item, sx, sy, dx, dy, name)]) - addr = None - break - if addr is not None: - self.aByAddr.append([(addr, item, sx, sy, dx, dy, name)]) - - if boxType == "funnel": dy -= dy // 5 elif boxType == "replicator": diff --git a/src/sdfvisio/qtprimitives.py b/src/sdfvisio/qtprimitives.py index 0247dfd..d7d2a40 100644 --- a/src/sdfvisio/qtprimitives.py +++ b/src/sdfvisio/qtprimitives.py @@ -29,19 +29,15 @@ cGraphicsTextItem: create a text item to the graphics scene; based on the widthD base class: QGraphicsTextItem """ from PySide6.QtCore import QPointF -from PySide6.QtCore import QSettings -from PySide6.QtGui import QColor from PySide6.QtGui import QPolygonF from PySide6.QtGui import QPainterPath -from PySide6.QtGui import QFont, QFontMetrics +from PySide6.QtGui import QFontMetrics from PySide6.QtWidgets import QGraphicsPathItem from PySide6.QtWidgets import QGraphicsPolygonItem from PySide6.QtWidgets import QGraphicsTextItem from PySide6.QtWidgets import QGraphicsItem -from PySide6.QtWidgets import QGraphicsDropShadowEffect from PySide6.QtWidgets import QLineEdit, QLabel from PySide6.QtWidgets import QDialog, QDialogButtonBox -from PySide6.QtWidgets import QGridLayout, QPushButton from PySide6.QtWidgets import QHBoxLayout, QVBoxLayout class cSearchDialog(QDialog): @@ -158,7 +154,7 @@ class cGraphicsPolygonItem(QGraphicsPolygonItem): self.setVisible(penName != "highlight") class cGraphicsTextItem(QGraphicsTextItem): - """create a graphis text item and center it, if necessary""" + """create a graphics text item and center it, if necessary""" defaultFont = None def __init__(self, mx : int, my : int, text : str, widthDX = None, widthDY = None): """mx, my: top left corner or mid coordinate coordinate @@ -173,6 +169,7 @@ class cGraphicsTextItem(QGraphicsTextItem): else use as DY for the mid point """ super().__init__(text) + self.setFont(cGraphicsTextItem.defaultFont) fm = QFontMetrics(cGraphicsTextItem.defaultFont) diff --git a/src/sdfvisio/qtsdfmain.py b/src/sdfvisio/qtsdfmain.py index dd62426..62efaad 100644 --- a/src/sdfvisio/qtsdfmain.py +++ b/src/sdfvisio/qtsdfmain.py @@ -176,6 +176,7 @@ class cSelectSrcDst(QDialog): return self.ret class cGraphicsScene(QGraphicsScene): + """Subclass the QGraphicsScene to enable item selection see commente out code line""" def __init__(self, sdfContent): super().__init__() # self.selectionChanged.connect(self.handleSelection) @@ -209,7 +210,10 @@ class cGraphicsView(QGraphicsView): item.setPen(self.schema.getPen(item.penName)) elif isinstance(item, cGraphicsPolygonItem): item.setPen(self.schema.getPen(item.penName)) - item.setBrush(self.schema.getBrush(item.brushName, item.brushIdx)) + if self.dimmBrush and item.isSelected(): + item.setBrush(self.dimmBrush) + else: + item.setBrush(self.schema.getBrush(item.brushName, item.brushIdx)) elif isinstance(item, cGraphicsTextItem): item.setDefaultTextColor(self.schema.getPen("foreground").color()) else: @@ -219,21 +223,12 @@ class cGraphicsView(QGraphicsView): def doBlinkBox(self, flag): """Blink the current box""" - brush = self.lastBox.brush() - # brush.setStyle(Qt.Dense3Pattern if flag else Qt.SolidPattern) - if flag: - self.lastBox.setBrush(self.dimmBrush) - else: - self.lastBox.setBrush(self.normalBrush) - - self.lastBox.update(self.lastBox.boundingRect()) + self.update(self.lastBox.boundingRect()) self.lastBox.setSelected(flag) def doTimer(self): if self.lastBox != None and self.countDown != None: self.countDown -= 1 - brush = self.lastBox.brush() - style = brush.style() if self.countDown > 0: self.doBlinkBox(not self.lastBox.isSelected()) QTimer.singleShot(500, self.doTimer) @@ -248,7 +243,7 @@ class cGraphicsView(QGraphicsView): def startBlinkBox(self, box): if (self.lastBox != None): - self.lastBox.setSelect(False) + self.doBlinkBox(False) self.lastBox = None self.lastBox = box self.countDown = 2*3+1 diff --git a/src/sdfvisio/qtsettings.py b/src/sdfvisio/qtsettings.py index 069bc6c..004c9fa 100644 --- a/src/sdfvisio/qtsettings.py +++ b/src/sdfvisio/qtsettings.py @@ -83,6 +83,7 @@ class cSettings(QSettings): return ret.toInt() if ret != None else None def getStr(self, name : str): + """Return None if the key does no exist, otherwise the string""" ret = self.value(name, None) if type(ret) == str: return ret diff --git a/src/sdfvisio/sdfvisio.py b/src/sdfvisio/sdfvisio.py index 65c2967..2390505 100644 --- a/src/sdfvisio/sdfvisio.py +++ b/src/sdfvisio/sdfvisio.py @@ -43,6 +43,22 @@ from .qtdisplay import cDisplaySDFContent, cDisplaySDF from .qtsettings import settings from .debugstatemachine import cDebugStateMachine +class cFromToPoint: + """Create a hashable object for a line start and end point + The assumption is that the X/Y coordinates are 0 - 0xffffffff + """ + def __init__(self, x : int, y : int): + self.x = x & 0xffffffff + self.y = y & 0xffffffff + self.hash = (x << 32) + y + + def __eq__(self, other): + return self.x == other.x and self.y == other.y + def __hash__(self): + return self.hash + def __str__(self): + return f"({self.x},{self.y})" + # one class per SDF file to handle all created display tabs class cSDF: """Handle the user interface for a single SDF file create the SDF specific tables and menue""" @@ -64,6 +80,7 @@ class cSDF: self.dlePrefix = None self.dCluster = {} self.dTraceDev = {} + self.aMarkedLines = [] self.debugStateMachine = cDebugStateMachine(self.dDevByName, self.addDapLogLine) @@ -251,11 +268,15 @@ class cSDF: self.updateImages() def addProblematicConfig(self): - """Create table and add questional information about the topology""" + """Create table and add questionable information about the topology""" self.loopTree = self.sdfWindow.addTreeView("Trace Checks", ["Information", "Description"]) - count = 0 + + count = len(self.aMarkedLines) + for item in self.aMarkedLines: + self.loopTree.addTreeItem(item) + for i, val in self.dLinkList.items(): - if i[:9] == "CoreTrace": + if i.startswith("CoreTrace"): for item in val.getTopologyCheck(): count += 1 self.loopTree.addTreeItem(item) @@ -286,18 +307,25 @@ class cSDF: # Draw the highlighted lines for l in val.aLines: - linStart = f"{l.sx}_{l.sy}" - linEnd = f"{l.ex}_{l.ey}" + linStart = cFromToPoint(l.sx, l.sy) + linEnd = cFromToPoint(l.ex, l.ey) - if linStart in dCoord and dCoord[linStart] == linEnd: + if linStart in dCoord and dCoord[linStart][0] == linEnd: print(f"WARNING: Duplicated line for {name}: {linStart} -> {linEnd}") - elif linEnd in dCoord and dCoord[linEnd] == linStart: + elif linEnd in dCoord and dCoord[linEnd][0] == linStart: print(f"WARNING: Duplicated line for {name}: {linStart} -> {linEnd}") - color = "seconds" if (linStart in dCoord) or (linEnd in dCoord) else "first" + if linStart in dCoord: + color = "seconds" + self.aMarkedLines.append(["Link Duplicates", "Source", (fromText, f"{toText} and {dCoord[linStart][1]}")]) + elif linEnd in dCoord: + color = "seconds" + self.aMarkedLines.append(["Link Duplicates", "Destination", (toText, f"{fromText} and {dCoord[linEnd][1]}")]) + else: + color = "first" - dCoord[linStart] = linEnd - dCoord[linEnd] = linStart + dCoord[linStart] = (linEnd, toText) + dCoord[linEnd] = (linStart, fromText) fromText = f"{l.inName}" if len(l.startTxt) == 0 else f"{l.inName}[{l.startTxt}]" toText = f"{l.outName}" if len(l.endTxt) == 0 else f"{l.outName}[{l.endTxt}]" @@ -306,10 +334,9 @@ class cSDF: topology.addLine(l.sx, l.sy, l.ex, l.ey, color, f"{fromText} -> {toText}") # Draw the boxes on top to override the end portions - for boxInfo in val.getAllBoxes(): - topology.addBox(boxInfo[0].x, boxInfo[0].y, boxInfo[0].dx, boxInfo[0].dy, - boxInfo[1], dev = boxInfo[2], markRed = True, border = boxInfo[3], - highlight = boxInfo[4]) + for box, boxText, device, border, highlightVisible in val.getAllBoxes(): + topology.addBox(box.x, box.y, box.dx, box.dy, boxText, + dev = device, canBeMarkedRed = True, border = border, highlightVisible = highlightVisible) # Draw in / out numbers for l in val.aLines: @@ -445,11 +472,11 @@ class cSDFMain(cDisplaySDF): # A similar loop from the hierarchy test can be used to draw the diagram. # Display the block hierarchy for the MEMAP, JTAG-AP and alike - for dpName in list(dict.fromkeys([device.dpName for device in sdf.list])): - sdf.APbus.hierarchyBlock(column, row, height, width, dpName, device=None) + for dapName in list(dict.fromkeys([device.dapName for device in sdf.list])): + sdf.APbus.hierarchyBlock(column, row, height, width, dapName, device=None) row += spacing # Used for vertical spacing maxCol = column # Used for depth based indenting - for dev in list(device for device in sdf.list if device.dpName == dpName): + for dev in list(device for device in sdf.list if device.dapName == dapName): curCol = column + (indent * dev.depth) # Current box column from depth maxCol = max(curCol, maxCol) # Finds the maximum depth of the hierarchy sdf.addDevice(curCol, row, height, width, dev) diff --git a/src/sdfvisio/statemachine.py b/src/sdfvisio/statemachine.py index a62c79a..06b3ebf 100644 --- a/src/sdfvisio/statemachine.py +++ b/src/sdfvisio/statemachine.py @@ -150,17 +150,17 @@ class cStateMachine: return self.currentState == self.startState def selectState(self, component, regname : str, access : str, value): - for state in self.stateList: - if state.check(self.currentState, component, regname, access, value): - self.currentState = state.nextIndex; + if any(state.check(self.currentState, component, regname, access, value) and + (setattr(self, 'currentState', state.nextIndex) or True) for state in self.stateList): return True if not self.inResetState(): - for state in self.specialList: - if state.check(self.currentState, component, regname, access, value): - if state.isIgnore: # ignore this state and check the next line + """check all of the special state to handle ignore and reset""" + matched_state = next((state for state in self.specialList if state.check(self.currentState, component, regname, access, value)), None) + if matched_state: + if matched_state.isIgnore: # ignore this state and check the next line return True - if state.isReset: # force state back to start state + if matched_state.isReset: # force state back to start state self.currentState = self.startState; return False -- GitLab From bd3af36b04b50cb47432a39ec228d07f9a9a1645 Mon Sep 17 00:00:00 2001 From: Robert Wiesner Date: Fri, 21 Feb 2025 19:03:39 +0000 Subject: [PATCH 2/5] Update file jtagtab.py --- src/sdfvisio/jtagtab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdfvisio/jtagtab.py b/src/sdfvisio/jtagtab.py index 163b671..87fa304 100644 --- a/src/sdfvisio/jtagtab.py +++ b/src/sdfvisio/jtagtab.py @@ -32,7 +32,7 @@ class cJTAGTAB: self.isSWJEn = isSWJEn self.dDevInfo = {} - self.dDevInfo = { dii.attrib["name"]: dii.text for diis in dev.findall("device_info_items") for dii in diis.findall("device_info_item") + self.dDevInfo = { dii.attrib["name"]: dii.text for diis in dev.findall("device_info_items") for dii in diis.findall("device_info_item") } if self.irLen == 0 and not isSWJEn: # try to determine the IR length -- GitLab From 069796e83dce322cf8203a99b32de33747102e89 Mon Sep 17 00:00:00 2001 From: Robert Wiesner Date: Fri, 21 Feb 2025 19:55:11 +0000 Subject: [PATCH 3/5] Fix issues with merge --- src/sdfvisio/__init__.py | 5 ++-- src/sdfvisio/layouttopology.py | 10 ++++---- src/sdfvisio/qtcolorscheme.py | 2 +- src/sdfvisio/qtdisplay.py | 2 +- src/sdfvisio/qtsdfmain.py | 3 ++- src/sdfvisio/sdfvisio.py | 44 +++++++++++++++++----------------- 6 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/sdfvisio/__init__.py b/src/sdfvisio/__init__.py index 8b4a364..637b830 100644 --- a/src/sdfvisio/__init__.py +++ b/src/sdfvisio/__init__.py @@ -22,7 +22,7 @@ """ Declare the version, copyright, and registion information """ -__version__= "1.0.19" +__version__= "1.0.19.1" __copyright__=""" sdfvisio: Arm Ltd, Apache 2.0, https://opensource.org/licenses/Apache-2.0 @@ -45,7 +45,8 @@ In the topology window, the connection lines indiacte (for light color theme): Note: the color change isn't enirely tested """ __revision__=""" -1.0.19: improvide the sources be removing dependency and simplify the topology generation +1.0.19.1: improvide source and rename symbols +1.0.19: improve the sources be removing dependency and simplify the topology generation 1.0.18: change minimum python version to 3.11 1.0.17: make the dap log earter reaable, but not dark mode aware 1.0.16: fix the single core, SMP and multi cluster treeview and improvide DapLogEater compatibillity handling diff --git a/src/sdfvisio/layouttopology.py b/src/sdfvisio/layouttopology.py index 9743a10..cd51bc2 100644 --- a/src/sdfvisio/layouttopology.py +++ b/src/sdfvisio/layouttopology.py @@ -427,7 +427,7 @@ class cTopo: maxCnt = cnt // 16 delta = maxCnt * self.maxDepth self.addDeep(nm, delta) - cnt += d + cnt += 1 self.maxDepth = self.maxDepth * (maxCnt + 1) # calculate the box sized and sort the box sizes by final box @@ -719,15 +719,15 @@ class cTopo: for aBox in self.aBoxes: for boxText in aBox: - currBox = self.dLink(boxText, None) + currBox = self.dLink.get(boxText, None) if currBox is not None and boxText != "": - border = "tracesink" if isTrace and currBox.output.getCount() == 0 else - ("tracesource" if isTrace and currBox.input.getCount() == 0 else None) + border = "tracesink" if isTrace and currBox.output.getCount() == 0 else + ("tracesource" if isTrace and currBox.input.getCount() == 0 else None) highlight = False boxTxt = boxText.upper() for n in self.opt["highlight"]["box"]: highlight |= 0 <= boxTxt.find(n) - yield ( ( currBox.box, boxText, currBox.device, border, highlight) ) + yield ( ( currBox.box, boxText, currBox.device, border, highlight ) ) def centerBoxes(self, aBox): dx = -self.BOX_DX_DIST diff --git a/src/sdfvisio/qtcolorscheme.py b/src/sdfvisio/qtcolorscheme.py index de34b90..1250896 100644 --- a/src/sdfvisio/qtcolorscheme.py +++ b/src/sdfvisio/qtcolorscheme.py @@ -22,7 +22,7 @@ Handle the various color schema """ from PySide6.QtWidgets import QLineEdit, QLabel, QColorDialog from PySide6.QtWidgets import QDialog, QDialogButtonBox, QGridLayout, QPushButton, QVBoxLayout -from PySide6.QtGui import QPen, QFont, QColor +from PySide6.QtGui import QPen, QFont, QColor, QBrush from .qtsettings import settings # Entries are a tuple of attribute name, color [, pen width] diff --git a/src/sdfvisio/qtdisplay.py b/src/sdfvisio/qtdisplay.py index e8c7464..d20c65b 100644 --- a/src/sdfvisio/qtdisplay.py +++ b/src/sdfvisio/qtdisplay.py @@ -446,7 +446,7 @@ class cDisplaySDFContent(QObject): def addBox(self, sx : int, sy : int, dx : int, dy : int, name : str, dev = None, canBeMarkedRed = False, border = None, toolTipPrefix = "" , - toolTip = None, highlight = None): + toolTip = None, highlightVisible = False): """ sx, sy, dx, sy: create a box starting at (sx, sy) with the size (dx, dy) name: this is the instribe names of the box dev: options device description of the box diff --git a/src/sdfvisio/qtsdfmain.py b/src/sdfvisio/qtsdfmain.py index 62efaad..63bc892 100644 --- a/src/sdfvisio/qtsdfmain.py +++ b/src/sdfvisio/qtsdfmain.py @@ -204,6 +204,7 @@ class cGraphicsView(QGraphicsView): self.setStyleSheet(schema.getStyleSheet()) def paintEvent(self, event): + """In the Paint event, we set the pen and brush color feoat each object""" if event.type() == QEvent.Paint: for item in self.items(): if isinstance(item, cGraphicsPathItem): @@ -223,7 +224,7 @@ class cGraphicsView(QGraphicsView): def doBlinkBox(self, flag): """Blink the current box""" - self.update(self.lastBox.boundingRect()) + self.lastBox.update(self.lastBox.boundingRect()) self.lastBox.setSelected(flag) def doTimer(self): diff --git a/src/sdfvisio/sdfvisio.py b/src/sdfvisio/sdfvisio.py index 2390505..c8fc592 100644 --- a/src/sdfvisio/sdfvisio.py +++ b/src/sdfvisio/sdfvisio.py @@ -68,7 +68,7 @@ class cSDF: self.fileName = fileName self.APbus = None self.list = None - self.topology = {} + self.dTopoDisp = {} self.dLinkList = {} self.debugActivity = None self.dDevByName = {} @@ -255,7 +255,7 @@ class cSDF: def updateImages(self): """Update and size all created images and diplay them""" - for name, item in self.topology.items(): + for name, item in self.dTopoDisp.items(): item.updateImage() self.APbus.updateImage() @@ -292,21 +292,21 @@ class cSDF: self.coreTree.addTreeItem(i) self.coreTree.resizeColumnToContents(0) - def updateHighlightLines(self, name : str, val : cTopo): + def updateHighlightLines(self, name : str, topo : cTopo): """Update highlights for a topology """ - topology = self.topology[name] - for l in val.aLines: + topoDisp = self.dTopoDisp[name] + for l in topo.aLines: if l.highlight: - topology.enableLineHighlight(l.sx, l.sy, l.ex, l.ey) + topoDisp.enableLineHighlight(l.sx, l.sy, l.ex, l.ey) - def updateTabImage(self, name : str, val : cTopo): + def updateTabImage(self, name : str, topo : cTopo): """Update a table image""" - topology = self.topology[name] + topoDisp = self.dTopoDisp[name] dCoord = {} # used to check for multiple start and endpoints used and duplicated lines # Draw the highlighted lines - for l in val.aLines: + for l in topo.aLines: linStart = cFromToPoint(l.sx, l.sy) linEnd = cFromToPoint(l.ex, l.ey) @@ -330,17 +330,17 @@ class cSDF: fromText = f"{l.inName}" if len(l.startTxt) == 0 else f"{l.inName}[{l.startTxt}]" toText = f"{l.outName}" if len(l.endTxt) == 0 else f"{l.outName}[{l.endTxt}]" - topology.addLineHighlight(l.sx, l.sy, l.ex, l.ey) - topology.addLine(l.sx, l.sy, l.ex, l.ey, color, f"{fromText} -> {toText}") + topoDisp.addLineHighlight(l.sx, l.sy, l.ex, l.ey) + topoDisp.addLine(l.sx, l.sy, l.ex, l.ey, color, f"{fromText} -> {toText}") # Draw the boxes on top to override the end portions - for box, boxText, device, border, highlightVisible in val.getAllBoxes(): - topology.addBox(box.x, box.y, box.dx, box.dy, boxText, + for box, boxText, device, border, highlightVisible in topo.getAllBoxes(): + topoltopoDispogy.addBox(box.x, box.y, box.dx, box.dy, boxText, dev = device, canBeMarkedRed = True, border = border, highlightVisible = highlightVisible) # Draw in / out numbers - for l in val.aLines: - topology.addLineText(l.sx, l.sy, l.ex, l.ey, l.startTxt, l.endTxt) + for l in topo.aLines: + topoDisp.addLineText(l.sx, l.sy, l.ex, l.ey, l.startTxt, l.endTxt) def createAllTopologyTabs(self): """Create all topology tabs with the network connection""" @@ -348,9 +348,9 @@ class cSDF: text = f"{i} (Comp: {val.countBoxes()}" text += f", Conn: {len(val.aLines)})" if 0 < len(val.aLines) else ")" # create the new tab - topology = cDisplaySDFContent(text, self.sdfWindow, apBus = self.APbus, sdf = self, topo = val) - topology.updateCurveFlag(False) - self.topology[i] = topology + topoDisp = cDisplaySDFContent(text, self.sdfWindow, apBus = self.APbus, sdf = self, topo = val) + topoDisp.updateCurveFlag(False) + self.dTopoDisp[i] = topoDisp self.updateTabImage(i, val) @@ -568,13 +568,13 @@ class cSDFMain(cDisplaySDF): """create the additional tabs if the named option is enabled""" if name in self.opt: title += f" ({len(self.opt[name])})" - topology = cDisplaySDFContent(title, self.mainWindow, sdf.APbus) - topology.updateCurveFlag(False) - sdf.topology[name] = topology + topoDisp = cDisplaySDFContent(title, self.mainWindow, sdf.APbus) + topoDisp.updateCurveFlag(False) + sdf.dTopoDisp[name] = topoDisp x = 50 y = 50 for i in self.opt[name]: - topology.addBox(x, y, 100, 50, i) + topoDisp.addBox(x, y, 100, 50, i) if x < 640: x += 100 + 20 else: -- GitLab From cbb1a4d166565f1085b40bd056ce41ae2649c808 Mon Sep 17 00:00:00 2001 From: Robert Wiesner Date: Fri, 21 Feb 2025 20:09:23 +0000 Subject: [PATCH 4/5] Fix variable names --- src/sdfvisio/layouttopology.py | 8 ++++++-- src/sdfvisio/sdfvisio.py | 12 ++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/sdfvisio/layouttopology.py b/src/sdfvisio/layouttopology.py index cd51bc2..3d11292 100644 --- a/src/sdfvisio/layouttopology.py +++ b/src/sdfvisio/layouttopology.py @@ -721,8 +721,12 @@ class cTopo: for boxText in aBox: currBox = self.dLink.get(boxText, None) if currBox is not None and boxText != "": - border = "tracesink" if isTrace and currBox.output.getCount() == 0 else - ("tracesource" if isTrace and currBox.input.getCount() == 0 else None) + if isTrace: + outCnt = currBox.output.getCount() + inCnt = currBox.input.getCount() + border = "tracesink" if outCnt == 0 else ("tracesource" if inCnt == 0 else None) + else: + border = None highlight = False boxTxt = boxText.upper() for n in self.opt["highlight"]["box"]: diff --git a/src/sdfvisio/sdfvisio.py b/src/sdfvisio/sdfvisio.py index c8fc592..c9f6524 100644 --- a/src/sdfvisio/sdfvisio.py +++ b/src/sdfvisio/sdfvisio.py @@ -310,6 +310,9 @@ class cSDF: linStart = cFromToPoint(l.sx, l.sy) linEnd = cFromToPoint(l.ex, l.ey) + fromText = f"{l.inName}" if len(l.startTxt) == 0 else f"{l.inName}[{l.startTxt}]" + toText = f"{l.outName}" if len(l.endTxt) == 0 else f"{l.outName}[{l.endTxt}]" + if linStart in dCoord and dCoord[linStart][0] == linEnd: print(f"WARNING: Duplicated line for {name}: {linStart} -> {linEnd}") elif linEnd in dCoord and dCoord[linEnd][0] == linStart: @@ -317,25 +320,22 @@ class cSDF: if linStart in dCoord: color = "seconds" - self.aMarkedLines.append(["Link Duplicates", "Source", (fromText, f"{toText} and {dCoord[linStart][1]}")]) + self.aMarkedLines.append(["Link Duplicates", "Destination", (fromText, f"{toText} and {dCoord[linStart][1]}")]) elif linEnd in dCoord: color = "seconds" - self.aMarkedLines.append(["Link Duplicates", "Destination", (toText, f"{fromText} and {dCoord[linEnd][1]}")]) + self.aMarkedLines.append(["Link Duplicates", "Source", (toText, f"{fromText} and {dCoord[linEnd][1]}")]) else: color = "first" dCoord[linStart] = (linEnd, toText) dCoord[linEnd] = (linStart, fromText) - fromText = f"{l.inName}" if len(l.startTxt) == 0 else f"{l.inName}[{l.startTxt}]" - toText = f"{l.outName}" if len(l.endTxt) == 0 else f"{l.outName}[{l.endTxt}]" - topoDisp.addLineHighlight(l.sx, l.sy, l.ex, l.ey) topoDisp.addLine(l.sx, l.sy, l.ex, l.ey, color, f"{fromText} -> {toText}") # Draw the boxes on top to override the end portions for box, boxText, device, border, highlightVisible in topo.getAllBoxes(): - topoltopoDispogy.addBox(box.x, box.y, box.dx, box.dy, boxText, + topoDisp.addBox(box.x, box.y, box.dx, box.dy, boxText, dev = device, canBeMarkedRed = True, border = border, highlightVisible = highlightVisible) # Draw in / out numbers -- GitLab From 9b8ac22f322a3503c157bb9a47d08c1cd41a241f Mon Sep 17 00:00:00 2001 From: Robert Wiesner Date: Fri, 21 Feb 2025 20:12:36 +0000 Subject: [PATCH 5/5] Update version number --- src/sdfvisio/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdfvisio/__init__.py b/src/sdfvisio/__init__.py index 637b830..27eb4c3 100644 --- a/src/sdfvisio/__init__.py +++ b/src/sdfvisio/__init__.py @@ -22,7 +22,7 @@ """ Declare the version, copyright, and registion information """ -__version__= "1.0.19.1" +__version__= "1.0.20" __copyright__=""" sdfvisio: Arm Ltd, Apache 2.0, https://opensource.org/licenses/Apache-2.0 @@ -45,7 +45,7 @@ In the topology window, the connection lines indiacte (for light color theme): Note: the color change isn't enirely tested """ __revision__=""" -1.0.19.1: improvide source and rename symbols +1.0.20: improvide source and rename symbols 1.0.19: improve the sources be removing dependency and simplify the topology generation 1.0.18: change minimum python version to 3.11 1.0.17: make the dap log earter reaable, but not dark mode aware -- GitLab