From 51e88bd436b4af7f6ac7f424a8c5ef580b3aac3e Mon Sep 17 00:00:00 2001 From: Luke Campagnola <luke.campagnola@gmail.com> Date: Wed, 26 Dec 2012 13:48:12 -0500 Subject: [PATCH] SVG export fixes --- examples/__main__.py | 6 +- pyqtgraph/exporters/SVGExporter.py | 104 +++++++++++++++++++++-------- tests/svg.py | 58 +++++++++++----- 3 files changed, 121 insertions(+), 47 deletions(-) diff --git a/examples/__main__.py b/examples/__main__.py index 1dbe7b9a..bbba88f8 100644 --- a/examples/__main__.py +++ b/examples/__main__.py @@ -166,7 +166,7 @@ def buildFileList(examples, files=None): buildFileList(val, files) return files -def testFile(name, f, exe, lib, graphicsSystem): +def testFile(name, f, exe, lib, graphicsSystem=None): global path fn = os.path.join(path,f) #print "starting process: ", fn @@ -194,8 +194,8 @@ except: print("test failed") raise -""" % (import1, import2, graphicsSystem) - #print code +""" % (import1, graphicsSystem, import2) + process = subprocess.Popen(['exec %s -i' % (exe)], shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) process.stdin.write(code.encode('UTF-8')) #process.stdin.close() diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py index 9787de59..ce05b82d 100644 --- a/pyqtgraph/exporters/SVGExporter.py +++ b/pyqtgraph/exporters/SVGExporter.py @@ -153,7 +153,7 @@ def _generateItemSvg(item, nodes=None, root=None): if root is None: root = item - + ## Skip hidden items if hasattr(item, 'isVisible') and not item.isVisible(): return None @@ -171,10 +171,10 @@ def _generateItemSvg(item, nodes=None, root=None): doc = xml.parseString(xmlStr) else: childs = item.childItems() - if isinstance(root, QtGui.QGraphicsScene): - tr = item.sceneTransform() - else: - tr = item.itemTransform(root) + tr = itemTransform(item, root) + + #print item, pg.SRTTransform(tr) + #tr.translate(item.pos().x(), item.pos().y()) #tr = tr * item.transform() arr = QtCore.QByteArray() @@ -248,7 +248,7 @@ def _generateItemSvg(item, nodes=None, root=None): if isinstance(root, QtGui.QGraphicsScene): path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape())) else: - path = QtGui.QGraphicsPathItem(item.mapToItem(root, item.shape())) + path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape()))) pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0] ## and for the clipPath element clip = name + '_clip' @@ -306,6 +306,7 @@ def correctCoordinates(node, item): ch.setAttribute('d', newCoords) elif ch.tagName == 'text': removeTransform = False + ## leave text alone for now. Might need this later to correctly render text with outline. #c = np.array([ #[float(ch.getAttribute('x')), float(ch.getAttribute('y'))], #[float(ch.getAttribute('font-size')), 0], @@ -318,32 +319,82 @@ def correctCoordinates(node, item): #ch.setAttribute('font-size', str(fs)) else: print('warning: export not implemented for SVG tag %s (from item %s)' % (ch.tagName, item)) + + ## correct line widths if needed + if removeTransform and ch.getAttribute('vector-effect') != 'non-scaling-stroke': + w = float(grp.getAttribute('stroke-width')) + s = pg.transformCoordinates(tr, np.array([[w,0], [0,0]]), transpose=True) + w = ((s[0]-s[1])**2).sum()**0.5 + ch.setAttribute('stroke-width', str(w)) if removeTransform: grp.removeAttribute('transform') -def correctStroke(node, item, root, width=1): - #print "==============", item, node - if node.hasAttribute('stroke-width'): - width = float(node.getAttribute('stroke-width')) - if node.getAttribute('vector-effect') == 'non-scaling-stroke': - node.removeAttribute('vector-effect') - if isinstance(root, QtGui.QGraphicsScene): - w = item.mapFromScene(pg.Point(width,0)) - o = item.mapFromScene(pg.Point(0,0)) +def itemTransform(item, root): + ## Return the transformation mapping item to root + ## (actually to parent coordinate system of root) + + if item is root: + tr = QtGui.QTransform() + tr.translate(*item.pos()) + tr = tr * item.transform() + return tr + + + if int(item.flags() & item.ItemIgnoresTransformations) > 0: + pos = item.pos() + parent = item.parentItem() + if parent is not None: + pos = itemTransform(parent, root).map(pos) + tr = QtGui.QTransform() + tr.translate(pos.x(), pos.y()) + tr = item.transform() * tr + else: + ## find next parent that is either the root item or + ## an item that ignores its transformation + nextRoot = item + while True: + nextRoot = nextRoot.parentItem() + if nextRoot is None: + nextRoot = root + break + if nextRoot is root or int(nextRoot.flags() & nextRoot.ItemIgnoresTransformations) > 0: + break + + if isinstance(nextRoot, QtGui.QGraphicsScene): + tr = item.sceneTransform() else: - w = item.mapFromItem(root, pg.Point(width,0)) - o = item.mapFromItem(root, pg.Point(0,0)) - w = w-o - #print " ", w, o, w-o - w = (w.x()**2 + w.y()**2) ** 0.5 - #print " ", w - node.setAttribute('stroke-width', str(w)) + tr = itemTransform(nextRoot, root) * item.itemTransform(nextRoot)[0] + #pos = QtGui.QTransform() + #pos.translate(root.pos().x(), root.pos().y()) + #tr = pos * root.transform() * item.itemTransform(root)[0] + - for ch in node.childNodes: - if isinstance(ch, xml.Element): - correctStroke(ch, item, root, width) + return tr + + +#def correctStroke(node, item, root, width=1): + ##print "==============", item, node + #if node.hasAttribute('stroke-width'): + #width = float(node.getAttribute('stroke-width')) + #if node.getAttribute('vector-effect') == 'non-scaling-stroke': + #node.removeAttribute('vector-effect') + #if isinstance(root, QtGui.QGraphicsScene): + #w = item.mapFromScene(pg.Point(width,0)) + #o = item.mapFromScene(pg.Point(0,0)) + #else: + #w = item.mapFromItem(root, pg.Point(width,0)) + #o = item.mapFromItem(root, pg.Point(0,0)) + #w = w-o + ##print " ", w, o, w-o + #w = (w.x()**2 + w.y()**2) ** 0.5 + ##print " ", w + #node.setAttribute('stroke-width', str(w)) + + #for ch in node.childNodes: + #if isinstance(ch, xml.Element): + #correctStroke(ch, item, root, width) def cleanXml(node): ## remove extraneous text; let the xml library do the formatting. @@ -359,4 +410,5 @@ def cleanXml(node): if hasElement: for ch in nonElement: node.removeChild(ch) - + elif node.tagName == 'g': ## remove childless groups + node.parentNode.removeChild(node) diff --git a/tests/svg.py b/tests/svg.py index db54eb83..7c26833e 100644 --- a/tests/svg.py +++ b/tests/svg.py @@ -6,26 +6,40 @@ import pyqtgraph as pg app = pg.mkQApp() class SVGTest(test.TestCase): - def test_plotscene(self): - pg.setConfigOption('foreground', (0,0,0)) - w = pg.GraphicsWindow() - w.show() - p1 = w.addPlot() - p2 = w.addPlot() - p1.plot([1,3,2,3,1,6,9,8,4,2,3,5,3], pen={'color':'k'}) - p1.setXRange(0,5) - p2.plot([1,5,2,3,4,6,1,2,4,2,3,5,3], pen={'color':'k', 'cosmetic':False, 'width': 0.3}) - app.processEvents() - app.processEvents() - - ex = pg.exporters.SVGExporter.SVGExporter(w.scene()) - ex.export(fileName='test.svg') + #def test_plotscene(self): + #pg.setConfigOption('foreground', (0,0,0)) + #w = pg.GraphicsWindow() + #w.show() + #p1 = w.addPlot() + #p2 = w.addPlot() + #p1.plot([1,3,2,3,1,6,9,8,4,2,3,5,3], pen={'color':'k'}) + #p1.setXRange(0,5) + #p2.plot([1,5,2,3,4,6,1,2,4,2,3,5,3], pen={'color':'k', 'cosmetic':False, 'width': 0.3}) + #app.processEvents() + #app.processEvents() + + #ex = pg.exporters.SVGExporter.SVGExporter(w.scene()) + #ex.export(fileName='test.svg') - #def test_simple(self): + + def test_simple(self): + scene = pg.QtGui.QGraphicsScene() #rect = pg.QtGui.QGraphicsRectItem(0, 0, 100, 100) + #scene.addItem(rect) + #rect.setPos(20,20) #rect.translate(50, 50) #rect.rotate(30) - #grp = pg.ItemGroup() + #rect.scale(0.5, 0.5) + + #rect1 = pg.QtGui.QGraphicsRectItem(0, 0, 100, 100) + #rect1.setParentItem(rect) + #rect1.setFlag(rect1.ItemIgnoresTransformations) + #rect1.setPos(20, 20) + #rect1.scale(2,2) + + #el1 = pg.QtGui.QGraphicsEllipseItem(0, 0, 100, 100) + #el1.setParentItem(rect1) + ##grp = pg.ItemGroup() #grp.setParentItem(rect) #grp.translate(200,0) ##grp.rotate(30) @@ -40,8 +54,16 @@ class SVGTest(test.TestCase): #el.scale(0.5,2) #el.setParentItem(rect2) - #ex = pg.exporters.SVGExporter.SVGExporter(rect) - #ex.export(fileName='test.svg') + grp2 = pg.ItemGroup() + scene.addItem(grp2) + grp2.scale(100,100) + + rect3 = pg.QtGui.QGraphicsRectItem(0,0,2,2) + rect3.setPen(pg.mkPen(width=1, cosmetic=False)) + grp2.addItem(rect3) + + ex = pg.exporters.SVGExporter.SVGExporter(scene) + ex.export(fileName='test.svg') if __name__ == '__main__': -- GitLab