def __init__(self, initialValueStr, minValue, maxValue, dispDigitNo, tooltipStr): assert sorted(LengthEditor.UNITNAMES) == sorted(UsrParams.LENGTH_UNIT_DICT.keys()) assert sorted(LengthEditor.UNITTITLEDICT.keys()) == sorted(LengthEditor.UNITNAMES) assert isfinite(minValue) and isfinite(maxValue) assert minValue < maxValue r = UsrParams.parseLength(initialValueStr) assert r is not None initialValue, initialUnitName, initialValueInPx = r del r self.valueSpinner = None self.unitCombo = None adj = Gtk.Adjustment(value=initialValue, lower=minValue, upper=maxValue, step_increment=0.1, page_increment=1.0) self.valueSpinner = Gtk.SpinButton(adjustment=adj, climb_rate=0.0, digits=dispDigitNo) self.valueSpinner.set_numeric(True) self.valueSpinner.set_tooltip_text(tooltipStr) self.valueSpinner.show() self.unitCombo = Gtk.ComboBoxText() self.unitCombo.set_tooltip_text( u'Note: "px" means "user unit" in SVG terminology.\n' + u'Its size is a pixel width at a resolution of 90 dpi.') for unitName in LengthEditor.UNITNAMES: self.unitCombo.append_text(LengthEditor.UNITTITLEDICT[unitName]) self.unitCombo.set_active(LengthEditor.UNITNAMES.index(initialUnitName)) self.unitCombo.show() self.hbox = Gtk.HBox() self.hbox.pack_start(self.valueSpinner, True, True, 0) self.hbox.pack_start(self.unitCombo, False, False, 0)
def addSignalPath(self, pathIndex, verticesList, yScale, startPoint): """ Inserts a signal path element below this node, consisting of line segments according to the given vertices. verticesList is a (possibly) empty list of vertex lists. Each vertex list is a list of at least 2 tuples (x, y) of vertex positions. The y-coordinates are inverted (increase: towards the top of the canvas). startPoint is None or (startPointX, startPointY). If startPoint is None, all vertex positions are calculated as (x + startPointX, yScale * y + startPointY). If startPoint is not None, the first -- probably invisible -- point of the resulting path is startPoint. """ assert self._node is not None assert pathIndex >= 0 assert verticesList is not None assert startPoint is None or (isfinite(startPoint[0]) and isfinite(startPoint[1])) styleDict = {'stroke': 'black', 'fill': 'none'} p = PathElem.addCombinedLines(self._node, verticesList, yScale, startPoint, styleDict, False) p.setLabel(SignalGElem._LABEL_SIGNALPATH + str(pathIndex)) return p
def createRot(aDegree, c): cX, cY = c t = None if isfinite(cX) and isfinite(cY): t = PointTransf.createRot0(aDegree) t = PointTransf.createConcat(PointTransf.createTransl((cX, cY)), t) t = PointTransf.createConcat(t, PointTransf.createTransl( (-cX, -cY))) return t
def addCombinedLines(parentNode, verticesList, yScale, startPoint, styleDict, doClose): """ Inserts a 'path' element below parentNode, consisting of line segments according to the given vertices. verticesList is a (possibly empty) list of vertex lists. Each vertex list is a list of at least 2 tuples (x, y) of vertex positions. The y-coordinates are inverted (increase: towards the top of the canvas). startPoint is None or (startPointX, startPointY). If startPoint is None, all vertex positions are calculated as (x + startPointX, yScale * y + startPointY). If startPoint is not None, the first -- probably invisible -- point of the resulting path is startPoint. If doClose is True, all subpaths (built by element of verticesList) are closed. styleDict is a dictionary of properties for the "style" attribute. """ if startPoint is None: startPointX, startPointY = (0, 0) else: startPointX, startPointY = startPoint parentNode = Elem(parentNode).getNode() assert verticesList is not None assert isfinite(yScale) assert isfinite(startPointX) and isfinite(startPointY) pathSpecs = [] for vertices in verticesList: partPathSpec = None for x, y in vertices: if partPathSpec is None: partPathSpec = 'M' else: partPathSpec = partPathSpec + ' L' assert isfinite(x) and isfinite(y) partPathSpec = partPathSpec + ' {x},{y}'.format(x=startPointX + x, y=startPointY + yScale * y) if startPoint is not None and vertices[0] != (startPointX, startPointY): partPathSpec = 'M {x},{y} '.format(x=startPointX, y=startPointY) + partPathSpec if doClose: partPathSpec = partPathSpec + ' Z' pathSpecs.append(partPathSpec) pathSpec = ' '.join(pathSpecs) del pathSpecs attribs = { 'style': simplestyle.formatStyle(styleDict), 'd': pathSpec } return PathElem(etree.SubElement(parentNode, inkex.addNS('path','svg'), attribs))
def getLengthValue(lengthStr): value = float('nan') r = UsrParams.parseLength(lengthStr) if r is not None: x, unitStr, value = r assert isfinite(value) return value
def buildLength(x, unitStr): assert isfinite(x) assert unitStr is None or unitStr in UsrParams.LENGTH_UNIT_DICT lengthStr = str(x) if unitStr is not None: lengthStr = lengthStr + unitStr return lengthStr
def isValid(self): self.unitTimeWidth ok = UsrParams.getLengthValue(self.unitTimeWidth) > 0.0 ok = ok and UsrParams.getLengthValue(self.signalHeight) > 0.0 ok = ok and UsrParams.getLengthValue(self.edgeTimeWidth) >= 0.0 ok = ok and UsrParams.getLengthValue(self.breakTimeWidth) > 0.0 ok = ok and self.placementMethod in (u'homogeneous', u'individual') ok = ok and isfinite(UsrParams.getLengthValue(self.originDistY)) ok = ok and UsrParams.getLengthValue(self.originDistY) > 0.0 return ok
def createSkewY(aDegree): t = None try: aDegreeMod = PointTransf._mod360Degree(2 * aDegree) / 2 tanA = math.tan(math.radians(aDegreeMod)) if isfinite(tanA): m = (1, 0, tanA, 1) t = PointTransf(m, (0, 0)) except ValueError: pass return t
def completeOriginsHomog(incomplSignalOriginDict, n, originDiff): """ Returns a copy of a dictionary of signal origins, extended to a given number of signals. The first signal with an known? origin is used as "fix point" (not changed). The origins of all other signals are recalculated such that the difference of the origin of each signal to tje origin of the preceding signal is (originDiffX, originDiffY). incomplSignalOriginDict: non-empty dictionary (key: signal index 0 .. n - 1, value: signal origin). signal origin: (x, y) where x and y are not None. n: number of signals. (originDiffX, originDiffY): used as signal origin difference of adjacent signals, if len(incomplSignalOriginDict) < 2. Returns: signalOriginDict, an extended copy of incomplSignalOriginDict. For each signal index i with 0 <= i < n: signalOriginDict[i] is the signal origin """ (originDiffX, originDiffY) = originDiff assert incomplSignalOriginDict is not None assert len(incomplSignalOriginDict) > 0 assert None not in incomplSignalOriginDict.values() assert len(incomplSignalOriginDict) == 0 \ or (min(incomplSignalOriginDict) >= 0 and max(incomplSignalOriginDict) < n) assert isfinite(originDiffX) and isfinite(originDiffY) signalOriginDict = dict() refSignalIndex = min(incomplSignalOriginDict) xR, yR = incomplSignalOriginDict[refSignalIndex] for signalIndex in range(0, n): di = signalIndex - refSignalIndex signalOriginDict[signalIndex] = float( di * originDiffX) + xR, float(di * originDiffY) + yR return signalOriginDict
def parseLength(lengthStr): r = None if lengthStr is not None and isinstance(lengthStr, str): # http://www.w3.org/TR/2003/REC-SVG11-20030114/types.html lengthRegexp = re.compile( r'^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)( *([a-zA-Z]+))?$' ) m = lengthRegexp.match(lengthStr) if m is not None: try: x = float(m.group(1)) if m.group(6) is None: unitStr = u'px' else: unitStr = m.group(6) pxPerUnit = UsrParams.LENGTH_UNIT_DICT[unitStr] valueInPx = x * pxPerUnit if isfinite(valueInPx): r = (x, unitStr, valueInPx) except (ValueError, KeyError): pass return r
def effect(self): """Perform the effect: create/modify Timink object (signal cluster group).""" selectedSignalClusterGroup, sg, so = SignalClusterGElem.getSelected( self.svg.selected) try: if len(so) > 0: if len(so) == 1: msg = 'Element selected which is not part of a signal cluster.' else: msg = 'Elements selected which are not part of a signal cluster.' raise UserError( msg, 'Do select at most one signal cluster (and nothing else).') if len(sg) > 0 and selectedSignalClusterGroup is None: raise UserError( 'More than one signal cluster selected.', 'Do select at most one signal cluster (and nothing else).') del so, sg signalClusterSpecStr = '' versionJoint = None usrParams = UsrParams() if selectedSignalClusterGroup is not None: selectedSignalClusterGroup = SignalClusterGElem( selectedSignalClusterGroup) signalClusterSpecStr = selectedSignalClusterGroup.getSignalClusterSpec( ) if signalClusterSpecStr is None: signalClusterSpecStr = '' versionJoint = selectedSignalClusterGroup.getVersionJoint() r = UsrParams.parseStr( selectedSignalClusterGroup.getUsrParams()) if r is not None: usrParams, invalidKeys, unsupportedParamKeys = r if len(invalidKeys) > 0: s = ', '.join( map(escapeStringForUser, list(invalidKeys))) printInfo( 'Signal cluster (selected): Ignored unsupported attributes:\n' + s) if len(unsupportedParamKeys) > 0: s = ', '.join( map(escapeStringForUser, list(unsupportedParamKeys))) printInfo( 'Signal cluster (selected): Ignored attributes with invalid values:\n' + s) if selectedSignalClusterGroup is None: sgDict = None sgInfoDict, signalOriginDict, wasteElemDict = (dict(), dict(), dict()) else: sgDict = selectedSignalClusterGroup.getSignalGroups() sgInfoDict, signalOriginDict, wasteElemDict = self.analyzeExistingSignalGroups( sgDict) r = SignalClusterEditor(signalClusterSpecStr, usrParams, versionJoint, len(signalClusterSpecStr) == 0, len(signalOriginDict) > 1).run() if r is not None: signalClusterSpecStr, usrParams = r signalSpecStrs = SignalClusterSpecValidator.normalize( signalClusterSpecStr).split('\n') assert len(signalSpecStrs) > 0 if selectedSignalClusterGroup is None: # create new signal cluster group scg = SignalClusterGElem.addEmpty( self.svg.get_current_layer(), signalClusterSpecStr, usrParams) else: scg = selectedSignalClusterGroup removedAttribs = sorted( list(scg.setAttribs(signalClusterSpecStr, usrParams))) if len(removedAttribs) > 0: s = ', '.join( map(lambda o: escapeStringForUser(o), removedAttribs)) printInfo( 'Signal cluster (selected): Removed unsupported attributes: ' + s) del s sgInfoDict, signalOriginDict, wasteElemDict = \ self.cleanupExistingSignalGroups(len(signalSpecStrs), sgDict, sgInfoDict, signalOriginDict, wasteElemDict) del sgDict # set/complete signal origins centerOfView = (round(self.svg.namedview.center[0]), round(self.svg.namedview.center[1])) originDistY = UsrParams.getLengthValue(usrParams.originDistY) originDistX = UsrParams.getLengthValue(usrParams.originDistX) assert isfinite(originDistX) and isfinite(originDistY) if len(signalOriginDict) == 0: signalOriginDict[0] = centerOfView if len(signalOriginDict ) <= 1 or usrParams.placementMethod == 'homogeneous': signalOriginDict = TiminkEffect.completeOriginsHomog( signalOriginDict, len(signalSpecStrs), (originDistX, originDistY)) else: signalOriginDict = TiminkEffect.completeOriginsByInterp( signalOriginDict, len(signalSpecStrs)) del originDistX del originDistY unitTimeWidth = UsrParams.getLengthValue( usrParams.unitTimeWidth) assert isfinite(unitTimeWidth) signalHeight = UsrParams.getLengthValue(usrParams.signalHeight) assert isfinite(signalHeight) edgeTimeWidth = UsrParams.getLengthValue( usrParams.edgeTimeWidth) assert isfinite(edgeTimeWidth) breakTimeWidth = UsrParams.getLengthValue( usrParams.breakTimeWidth) assert isfinite(breakTimeWidth) # add / update signal groups according to sgInfoDict, signalOriginDict, signalSpecStrs for signalIndex in range(0, len(signalSpecStrs)): signalGroup, signalPaths, shading = sgInfoDict.get( signalIndex, (None, [], None)) signalOrigin = signalOriginDict[signalIndex] signalSpec = SignalSpec.createFromStr( signalSpecStrs[signalIndex], unitTimeWidth, breakTimeWidth) assert signalSpec is not None pathFragmentVerticesList, shading01VerticesList = signalSpec.getAllPathVerticesAndShading( edgeTimeWidth) pathNo = len(pathFragmentVerticesList) # remove 'transform' of signal path and shading path hadWasteTransf = False for signalPath in signalPaths: transf = signalPath.getTransform() if transf is None or not transf.isIdentity(): signalPath.setTransform(None) hadWasteTransf = True if shading is not None: transf = shading.getTransform() if transf is None or not transf.isIdentity(): signalPath.setTransform(None) hadWasteTransf = True if hadWasteTransf: printInfo( 'Signal {si}: Removed interfering \'transform\' attribute from \'path\' element.' .format(si=signalIndex)) del hadWasteTransf signalPaths = signalPaths + [None] * (pathNo - len(signalPaths)) assert len(signalPaths) >= pathNo if signalGroup is None: signalGroup = SignalGElem.addEmpty(scg, signalIndex) signalGroup.setTransform( PointTransf.createTransl(signalOrigin)) else: transf = signalGroup.getTransform() if transf is None: signalGroup.setTransform(None) printInfo( 'Signal {si}: Removed invalid \'transform\' attribute from \'g\' element.' .format(si=signalIndex)) else: oldSignalOrigin = transf.applyTo((0, 0)) originDiff = (signalOrigin[0] - oldSignalOrigin[0], signalOrigin[1] - oldSignalOrigin[1]) if originDiff != (0.0, 0.0): transf = PointTransf.createConcat( transf, PointTransf.createTransl(originDiff)) signalGroup.setTransform(transf) # determine signal group, whose style is to be used for a new signal group styleTmplSignalIndex = None if signalIndex > 0: styleTmplSignalIndex = signalIndex - 1 elif len(sgInfoDict) > 0: # first existing signal group styleTmplSignalIndex = min(sgInfoDict) assert styleTmplSignalIndex is None or styleTmplSignalIndex in sgInfoDict for pathIndex in range(0, pathNo): pathFragmentVertices = pathFragmentVerticesList[ pathIndex] oldSignalPath = signalPaths[pathIndex] if oldSignalPath is None: # no old signal 'path' available -> create new one newSignalPath = signalGroup.addSignalPath( pathIndex, pathFragmentVertices, -signalHeight, (0, 0)) if styleTmplSignalIndex is not None: tmplSignalGroup, signalPathsOfTmplSignalGroup, shadingOfTmplSignalGroup \ = sgInfoDict[styleTmplSignalIndex] if pathIndex < len( signalPathsOfTmplSignalGroup): # matching path in template signal group newSignalPath.copyStyleFrom( signalPathsOfTmplSignalGroup[pathIndex] ) else: # new matching path in template signal group # -> copy style from preceding path instead newSignalPath.copyStyleFrom( signalPaths[pathIndex - 1]) if pathIndex == 0: # create empty 'path' element to preserve the style for pi in range( pathNo, len(signalPathsOfTmplSignalGroup)): emptySignalPath = signalGroup.addSignalPath( pi, [], -signalHeight, (0, 0)) emptySignalPath.copyStyleFrom( signalPathsOfTmplSignalGroup[pi]) del emptySignalPath else: newSignalPath = signalGroup.addSignalPath( pathIndex, pathFragmentVertices, -signalHeight, (0, 0)) if pathIndex == 0: newSignalPath.copyTransformFrom(oldSignalPath) newSignalPath.copyIdFrom(oldSignalPath) newSignalPath.copyStyleFrom(oldSignalPath) # preserve position with respect to non-signal path elements in signal group: oldSignalPath.makeSiblingPredecessorOf( newSignalPath) oldSignalPath.remove() if newSignalPath.removeFill(): printInfo( 'Signal {si}: Did reset interfering fill style from \'path\' element to \'none\'.' .format(si=signalIndex)) if newSignalPath.forceVisibleStroke(): printInfo( 'Signal {si}: Did reset invisible stroke style from \'path\' element to \'black\'.' .format(si=signalIndex)) if scg.removeStyle(): # possible, because assigning a style to a group in Inkscape # sets the style of all contained elements (all levels below) printInfo( 'Signal {si}: Did remove interfering style from \'g\' element.' .format(si=signalIndex)) # replace all transforms by transform of signal path 0 if pathIndex > 0: newSignalPath.copyTransformFrom(signalPaths[0]) signalPaths[pathIndex] = newSignalPath # remove unused signal paths assert pathNo > 0 for pathIndex in range( len(signalPaths) - 1, pathNo - 1, -1): assert pathIndex > 0 oldSignalPath = signalPaths[pathIndex] if oldSignalPath is not None: if signalPaths[ pathIndex - 1] is not None and oldSignalPath.compareStyleWith( signalPaths[pathIndex - 1]): # style can be derived of preceding signal path oldSignalPath.remove() del signalPaths[pathIndex] else: # perserve (otherwise style information is lost) oldSignalPath.copyTransformFrom(signalPaths[0]) oldSignalPath.setToEmpty() newShading = signalGroup.addShadingPath( shading01VerticesList, -signalHeight, (0, 0)) if shading is not None: newShading.copyStyleFrom(shading) shading.remove() elif styleTmplSignalIndex is not None: tmplSignalGroup, signalPathsOfTmplSignalGroup, shadingOfTmplSignalGroup \ = sgInfoDict[styleTmplSignalIndex] if shadingOfTmplSignalGroup is not None: newShading.copyStyleFrom(shadingOfTmplSignalGroup) shading = newShading shading.copyTransformFrom(signalPaths[0]) if shading.removeStroke(): printInfo( 'Signal {si}: Did reset stroke style from shading \'path\' element to \'none\'.' .format(si=signalIndex)) # sort signal path elements (drawing order) existingSignalPaths = list( filter(lambda e: e is not None, signalPaths)) assert len(existingSignalPaths) > 0 for i in range(1, len(existingSignalPaths)): existingSignalPaths[i - 1].makeSiblingPredecessorOf( existingSignalPaths[i]) # the order of all other non-signal path elements remains unchanged with # respect to signal path 0 and to all other non-signal path elements # draw shading first (appears as bottom-most) existingSignalPaths[-1].makeSiblingPredecessorOf(shading) assert signalGroup is not None assert len(signalPaths) > 0 sgInfoDict[signalIndex] = (signalGroup, signalPaths, shading) except UserError as e: showErrorDlg(e.msg, e.hint) sys.exit(1)
def parseStr(paramStr): r = None def decode(s): return s paramDict = None if paramStr is not None: try: paramDict = dict() if paramStr != '': for pair in paramStr.split(';'): k, v = pair.split( ':') # ValueError, if wrong number of elements if len(k) == 0: raise ValueError paramDict[decode(k)] = decode(v) except (UnicodeDecodeError, ValueError): paramDict = None if paramDict is not None: params = UsrParams() invalidKeys = set() unsupportedParamKeys = set() for k, v in paramDict.items(): valueOk = False if k == u'unitTimeWidth': if UsrParams.getLengthValue(v) > 0.0: params.unitTimeWidth = v valueOk = True elif k == u'signalHeight': if UsrParams.getLengthValue(v) > 0.0: params.signalHeight = v valueOk = True elif k == u'edgeTimeWidth': if UsrParams.getLengthValue(v) >= 0.0: params.edgeTimeWidth = v valueOk = True elif k == u'breakTimeWidth': if UsrParams.getLengthValue(v) > 0.0: params.breakTimeWidth = v valueOk = True elif k == u'placementMethod': if v in (u'homogeneous', u'individual'): params.placementMethod = v valueOk = True elif k == u'originDistX': if isfinite(UsrParams.getLengthValue(v)): params.originDistX = v valueOk = True elif k == u'originDistY': if UsrParams.getLengthValue(v) > 0.0: params.originDistY = v valueOk = True else: invalidKeys.add(k) valueOk = True if not valueOk: unsupportedParamKeys.add(k) r = (params, invalidKeys, unsupportedParamKeys) return r
def createScale(sx, sy): t = None if isfinite(sx) and isfinite(sy): m = (sx, 0, 0, sy) t = PointTransf(m, (0, 0)) return t
def createTransl(t): tx, ty = t transf = None if isfinite(tx) and isfinite(ty): transf = PointTransf((1, 0, 0, 1), (tx, ty)) return transf