SVG export looks good.
This commit is contained in:
parent
b0e0781624
commit
49e2177623
@ -4,7 +4,7 @@ from pyqtgraph.Qt import QtGui, QtCore, QtSvg
|
|||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
import re
|
import re
|
||||||
import xml.dom.minidom as xml
|
import xml.dom.minidom as xml
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['SVGExporter']
|
__all__ = ['SVGExporter']
|
||||||
@ -121,13 +121,29 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
## 1) Qt SVG does not implement clipping paths. This is absurd.
|
## 1) Qt SVG does not implement clipping paths. This is absurd.
|
||||||
## The solution is to let Qt generate SVG for each item independently,
|
## The solution is to let Qt generate SVG for each item independently,
|
||||||
## then glue them together manually with clipping.
|
## then glue them together manually with clipping.
|
||||||
|
##
|
||||||
|
## The format Qt generates for all items looks like this:
|
||||||
|
##
|
||||||
|
## <g>
|
||||||
|
## <g transform="matrix(...)">
|
||||||
|
## one or more of: <path/> or <polyline/> or <text/>
|
||||||
|
## </g>
|
||||||
|
## <g transform="matrix(...)">
|
||||||
|
## one or more of: <path/> or <polyline/> or <text/>
|
||||||
|
## </g>
|
||||||
|
## . . .
|
||||||
|
## </g>
|
||||||
|
##
|
||||||
## 2) There seems to be wide disagreement over whether path strokes
|
## 2) There seems to be wide disagreement over whether path strokes
|
||||||
## should be scaled anisotropically.
|
## should be scaled anisotropically.
|
||||||
## see: http://web.mit.edu/jonas/www/anisotropy/
|
## see: http://web.mit.edu/jonas/www/anisotropy/
|
||||||
## Given that both inkscape and illustrator seem to prefer isotropic
|
## Given that both inkscape and illustrator seem to prefer isotropic
|
||||||
## scaling, we will optimize for those cases.
|
## scaling, we will optimize for those cases.
|
||||||
|
##
|
||||||
## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
|
## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
|
||||||
## inkscape only supports 1.1.
|
## inkscape only supports 1.1.
|
||||||
|
##
|
||||||
|
## Both 2 and 3 can be addressed by drawing all items in world coordinates.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -155,14 +171,12 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
doc = xml.parseString(xmlStr)
|
doc = xml.parseString(xmlStr)
|
||||||
else:
|
else:
|
||||||
childs = item.childItems()
|
childs = item.childItems()
|
||||||
tr.translate(item.pos().x(), item.pos().y())
|
if isinstance(root, QtGui.QGraphicsScene):
|
||||||
tr = tr * item.transform()
|
tr = item.sceneTransform()
|
||||||
#if not item.isVisible() or int(item.flags() & item.ItemHasNoContents) > 0:
|
else:
|
||||||
#m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32())
|
tr = item.itemTransform(root)
|
||||||
##print item, m
|
#tr.translate(item.pos().x(), item.pos().y())
|
||||||
#xmlStr = '<g><g transform="matrix(%f,%f,%f,%f,%f,%f)"></g></g>' % m # note: outer group is needed to separate clipping from transform
|
#tr = tr * item.transform()
|
||||||
#doc = xml.parseString(xmlStr)
|
|
||||||
#else:
|
|
||||||
arr = QtCore.QByteArray()
|
arr = QtCore.QByteArray()
|
||||||
buf = QtCore.QBuffer(arr)
|
buf = QtCore.QBuffer(arr)
|
||||||
svg = QtSvg.QSvgGenerator()
|
svg = QtSvg.QSvgGenerator()
|
||||||
@ -177,7 +191,7 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
if hasattr(item, 'setExportMode'):
|
if hasattr(item, 'setExportMode'):
|
||||||
item.setExportMode(True, {'painter': p})
|
item.setExportMode(True, {'painter': p})
|
||||||
try:
|
try:
|
||||||
#p.setTransform(tr)
|
p.setTransform(tr)
|
||||||
item.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
|
item.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
|
||||||
finally:
|
finally:
|
||||||
p.end()
|
p.end()
|
||||||
@ -198,16 +212,21 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
print doc.toxml()
|
print doc.toxml()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
## Get rid of group transformation matrices by applying
|
||||||
|
## transformation to inner coordinates
|
||||||
|
correctCoordinates(g1, item)
|
||||||
|
|
||||||
## make sure g1 has the transformation matrix
|
## make sure g1 has the transformation matrix
|
||||||
m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32())
|
#m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32())
|
||||||
g1.setAttribute('transform', "matrix(%f,%f,%f,%f,%f,%f)" % m)
|
#g1.setAttribute('transform', "matrix(%f,%f,%f,%f,%f,%f)" % m)
|
||||||
|
|
||||||
#print "=================",item,"====================="
|
#print "=================",item,"====================="
|
||||||
#print g1.toprettyxml(indent=" ", newl='')
|
#print g1.toprettyxml(indent=" ", newl='')
|
||||||
|
|
||||||
## Inkscape does not support non-scaling-stroke (this is SVG 1.2, inkscape supports 1.1)
|
## Inkscape does not support non-scaling-stroke (this is SVG 1.2, inkscape supports 1.1)
|
||||||
## So we need to correct anything attempting to use this.
|
## So we need to correct anything attempting to use this.
|
||||||
correctStroke(g1, item, root)
|
#correctStroke(g1, item, root)
|
||||||
|
|
||||||
## decide on a name for this item
|
## decide on a name for this item
|
||||||
baseName = item.__class__.__name__
|
baseName = item.__class__.__name__
|
||||||
@ -226,7 +245,10 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
## See if this item clips its children
|
## See if this item clips its children
|
||||||
if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
|
if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
|
||||||
## Generate svg for just the path
|
## Generate svg for just the path
|
||||||
path = QtGui.QGraphicsPathItem(item.shape())
|
if isinstance(root, QtGui.QGraphicsScene):
|
||||||
|
path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
|
||||||
|
else:
|
||||||
|
path = QtGui.QGraphicsPathItem(item.mapToItem(root, item.shape()))
|
||||||
pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
|
pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
|
||||||
## and for the clipPath element
|
## and for the clipPath element
|
||||||
clip = name + '_clip'
|
clip = name + '_clip'
|
||||||
@ -248,6 +270,57 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
|
|
||||||
return g1
|
return g1
|
||||||
|
|
||||||
|
def correctCoordinates(node, item):
|
||||||
|
## Remove transformation matrices from <g> tags by applying matrix to coordinates inside.
|
||||||
|
groups = node.getElementsByTagName('g')
|
||||||
|
for grp in groups:
|
||||||
|
matrix = grp.getAttribute('transform')
|
||||||
|
match = re.match(r'matrix\((.*)\)', matrix)
|
||||||
|
if match is None:
|
||||||
|
vals = [1,0,0,1,0,0]
|
||||||
|
else:
|
||||||
|
vals = map(float, match.groups()[0].split(','))
|
||||||
|
tr = np.array([[vals[0], vals[2], vals[4]], [vals[1], vals[3], vals[5]]])
|
||||||
|
|
||||||
|
removeTransform = False
|
||||||
|
for ch in grp.childNodes:
|
||||||
|
if not isinstance(ch, xml.Element):
|
||||||
|
continue
|
||||||
|
if ch.tagName == 'polyline':
|
||||||
|
removeTransform = True
|
||||||
|
coords = np.array([map(float, c.split(',')) for c in ch.getAttribute('points').strip().split(' ')])
|
||||||
|
coords = pg.transformCoordinates(tr, coords, transpose=True)
|
||||||
|
ch.setAttribute('points', ' '.join([','.join(map(str, c)) for c in coords]))
|
||||||
|
elif ch.tagName == 'path':
|
||||||
|
removeTransform = True
|
||||||
|
newCoords = ''
|
||||||
|
for c in ch.getAttribute('d').strip().split(' '):
|
||||||
|
x,y = c.split(',')
|
||||||
|
if x[0].isalpha():
|
||||||
|
t = x[0]
|
||||||
|
x = x[1:]
|
||||||
|
else:
|
||||||
|
t = ''
|
||||||
|
nc = pg.transformCoordinates(tr, np.array([[float(x),float(y)]]), transpose=True)
|
||||||
|
newCoords += t+str(nc[0,0])+','+str(nc[0,1])+' '
|
||||||
|
ch.setAttribute('d', newCoords)
|
||||||
|
elif ch.tagName == 'text':
|
||||||
|
removeTransform = False
|
||||||
|
#c = np.array([
|
||||||
|
#[float(ch.getAttribute('x')), float(ch.getAttribute('y'))],
|
||||||
|
#[float(ch.getAttribute('font-size')), 0],
|
||||||
|
#[0,0]])
|
||||||
|
#c = pg.transformCoordinates(tr, c, transpose=True)
|
||||||
|
#ch.setAttribute('x', str(c[0,0]))
|
||||||
|
#ch.setAttribute('y', str(c[0,1]))
|
||||||
|
#fs = c[1]-c[2]
|
||||||
|
#fs = (fs**2).sum()**0.5
|
||||||
|
#ch.setAttribute('font-size', str(fs))
|
||||||
|
else:
|
||||||
|
print('warning: export not implemented for SVG tag %s (from item %s)' % (ch.tagName, item))
|
||||||
|
|
||||||
|
if removeTransform:
|
||||||
|
grp.removeAttribute('transform')
|
||||||
|
|
||||||
|
|
||||||
def correctStroke(node, item, root, width=1):
|
def correctStroke(node, item, root, width=1):
|
||||||
|
@ -143,6 +143,7 @@ class LinearRegionItem(UIGraphicsItem):
|
|||||||
#prof = debug.Profiler('LinearRegionItem.paint')
|
#prof = debug.Profiler('LinearRegionItem.paint')
|
||||||
UIGraphicsItem.paint(self, p, *args)
|
UIGraphicsItem.paint(self, p, *args)
|
||||||
p.setBrush(self.currentBrush)
|
p.setBrush(self.currentBrush)
|
||||||
|
p.setPen(fn.mkPen(None))
|
||||||
p.drawRect(self.boundingRect())
|
p.drawRect(self.boundingRect())
|
||||||
#prof.finish()
|
#prof.finish()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user