Updated image, SVG, and print exporters. Image export works well; SVG and print still need work.

Added ability to run examples with a specific Qt graphics system
This commit is contained in:
Luke Campagnola 2012-12-26 16:29:29 -05:00
parent a157d9c4fa
commit 8899e8d858
13 changed files with 194 additions and 100 deletions

View File

@ -126,6 +126,9 @@ class ExampleLoader(QtGui.QMainWindow):
extra.append('pyqt') extra.append('pyqt')
elif self.ui.pysideCheck.isChecked(): elif self.ui.pysideCheck.isChecked():
extra.append('pyside') extra.append('pyside')
if self.ui.forceGraphicsCheck.isChecked():
extra.append(str(self.ui.forceGraphicsCombo.currentText()))
if fn is None: if fn is None:
return return
@ -163,22 +166,26 @@ def buildFileList(examples, files=None):
buildFileList(val, files) buildFileList(val, files)
return files return files
def testFile(name, f, exe, lib): def testFile(name, f, exe, lib, graphicsSystem):
global path global path
fn = os.path.join(path,f) fn = os.path.join(path,f)
#print "starting process: ", fn #print "starting process: ", fn
os.chdir(path)
sys.stdout.write(name) sys.stdout.write(name)
sys.stdout.flush() sys.stdout.flush()
import1 = "import %s" % lib if lib != '' else ''
import2 = os.path.splitext(os.path.split(fn)[1])[0]
graphicsSystem = '' if graphicsSystem is None else "pg.QtGui.QApplication.setGraphicsSystem('%s')" % graphicsSystem
code = """ code = """
try: try:
%s
import pyqtgraph as pg
%s %s
import %s import %s
import sys import sys
print("test complete") print("test complete")
sys.stdout.flush() sys.stdout.flush()
import pyqtgraph as pg
import time import time
while True: ## run a little event loop while True: ## run a little event loop
pg.QtGui.QApplication.processEvents() pg.QtGui.QApplication.processEvents()
@ -187,7 +194,7 @@ except:
print("test failed") print("test failed")
raise raise
""" % ("import %s" % lib if lib != '' else "", os.path.splitext(os.path.split(fn)[1])[0]) """ % (import1, import2, graphicsSystem)
#print code #print code
process = subprocess.Popen(['exec %s -i' % (exe)], shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 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.write(code.encode('UTF-8'))

View File

@ -57,6 +57,36 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="forceGraphicsCheck">
<property name="text">
<string>Force Graphics System:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="forceGraphicsCombo">
<item>
<property name="text">
<string>native</string>
</property>
</item>
<item>
<property name="text">
<string>raster</string>
</property>
</item>
<item>
<property name="text">
<string>opengl</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item> <item>
<widget class="QPushButton" name="loadBtn"> <widget class="QPushButton" name="loadBtn">
<property name="text"> <property name="text">

View File

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file './examples/exampleLoaderTemplate.ui' # Form implementation generated from reading ui file './examples/exampleLoaderTemplate.ui'
# #
# Created: Fri Oct 26 07:53:55 2012 # Created: Mon Dec 24 00:33:38 2012
# by: PyQt4 UI code generator 4.9.1 # by: PyQt4 UI code generator 4.9.1
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -44,6 +44,18 @@ class Ui_Form(object):
self.pysideCheck.setObjectName(_fromUtf8("pysideCheck")) self.pysideCheck.setObjectName(_fromUtf8("pysideCheck"))
self.horizontalLayout.addWidget(self.pysideCheck) self.horizontalLayout.addWidget(self.pysideCheck)
self.verticalLayout.addLayout(self.horizontalLayout) self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
self.forceGraphicsCheck = QtGui.QCheckBox(self.layoutWidget)
self.forceGraphicsCheck.setObjectName(_fromUtf8("forceGraphicsCheck"))
self.horizontalLayout_2.addWidget(self.forceGraphicsCheck)
self.forceGraphicsCombo = QtGui.QComboBox(self.layoutWidget)
self.forceGraphicsCombo.setObjectName(_fromUtf8("forceGraphicsCombo"))
self.forceGraphicsCombo.addItem(_fromUtf8(""))
self.forceGraphicsCombo.addItem(_fromUtf8(""))
self.forceGraphicsCombo.addItem(_fromUtf8(""))
self.horizontalLayout_2.addWidget(self.forceGraphicsCombo)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.loadBtn = QtGui.QPushButton(self.layoutWidget) self.loadBtn = QtGui.QPushButton(self.layoutWidget)
self.loadBtn.setObjectName(_fromUtf8("loadBtn")) self.loadBtn.setObjectName(_fromUtf8("loadBtn"))
self.verticalLayout.addWidget(self.loadBtn) self.verticalLayout.addWidget(self.loadBtn)
@ -62,5 +74,9 @@ class Ui_Form(object):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8)) Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.pyqtCheck.setText(QtGui.QApplication.translate("Form", "Force PyQt", None, QtGui.QApplication.UnicodeUTF8)) self.pyqtCheck.setText(QtGui.QApplication.translate("Form", "Force PyQt", None, QtGui.QApplication.UnicodeUTF8))
self.pysideCheck.setText(QtGui.QApplication.translate("Form", "Force PySide", None, QtGui.QApplication.UnicodeUTF8)) self.pysideCheck.setText(QtGui.QApplication.translate("Form", "Force PySide", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCheck.setText(QtGui.QApplication.translate("Form", "Force Graphics System:", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(0, QtGui.QApplication.translate("Form", "native", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(1, QtGui.QApplication.translate("Form", "raster", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(2, QtGui.QApplication.translate("Form", "opengl", None, QtGui.QApplication.UnicodeUTF8))
self.loadBtn.setText(QtGui.QApplication.translate("Form", "Load Example", None, QtGui.QApplication.UnicodeUTF8)) self.loadBtn.setText(QtGui.QApplication.translate("Form", "Load Example", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -2,8 +2,8 @@
# Form implementation generated from reading ui file './examples/exampleLoaderTemplate.ui' # Form implementation generated from reading ui file './examples/exampleLoaderTemplate.ui'
# #
# Created: Fri Oct 26 07:53:57 2012 # Created: Mon Dec 24 00:33:39 2012
# by: pyside-uic 0.2.13 running on PySide 1.1.0 # by: pyside-uic 0.2.13 running on PySide 1.1.2
# #
# WARNING! All changes made in this file will be lost! # WARNING! All changes made in this file will be lost!
@ -39,6 +39,18 @@ class Ui_Form(object):
self.pysideCheck.setObjectName("pysideCheck") self.pysideCheck.setObjectName("pysideCheck")
self.horizontalLayout.addWidget(self.pysideCheck) self.horizontalLayout.addWidget(self.pysideCheck)
self.verticalLayout.addLayout(self.horizontalLayout) self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.forceGraphicsCheck = QtGui.QCheckBox(self.layoutWidget)
self.forceGraphicsCheck.setObjectName("forceGraphicsCheck")
self.horizontalLayout_2.addWidget(self.forceGraphicsCheck)
self.forceGraphicsCombo = QtGui.QComboBox(self.layoutWidget)
self.forceGraphicsCombo.setObjectName("forceGraphicsCombo")
self.forceGraphicsCombo.addItem("")
self.forceGraphicsCombo.addItem("")
self.forceGraphicsCombo.addItem("")
self.horizontalLayout_2.addWidget(self.forceGraphicsCombo)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.loadBtn = QtGui.QPushButton(self.layoutWidget) self.loadBtn = QtGui.QPushButton(self.layoutWidget)
self.loadBtn.setObjectName("loadBtn") self.loadBtn.setObjectName("loadBtn")
self.verticalLayout.addWidget(self.loadBtn) self.verticalLayout.addWidget(self.loadBtn)
@ -57,5 +69,9 @@ class Ui_Form(object):
Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8)) Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8))
self.pyqtCheck.setText(QtGui.QApplication.translate("Form", "Force PyQt", None, QtGui.QApplication.UnicodeUTF8)) self.pyqtCheck.setText(QtGui.QApplication.translate("Form", "Force PyQt", None, QtGui.QApplication.UnicodeUTF8))
self.pysideCheck.setText(QtGui.QApplication.translate("Form", "Force PySide", None, QtGui.QApplication.UnicodeUTF8)) self.pysideCheck.setText(QtGui.QApplication.translate("Form", "Force PySide", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCheck.setText(QtGui.QApplication.translate("Form", "Force Graphics System:", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(0, QtGui.QApplication.translate("Form", "native", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(1, QtGui.QApplication.translate("Form", "raster", None, QtGui.QApplication.UnicodeUTF8))
self.forceGraphicsCombo.setItemText(2, QtGui.QApplication.translate("Form", "opengl", None, QtGui.QApplication.UnicodeUTF8))
self.loadBtn.setText(QtGui.QApplication.translate("Form", "Load Example", None, QtGui.QApplication.UnicodeUTF8)) self.loadBtn.setText(QtGui.QApplication.translate("Form", "Load Example", None, QtGui.QApplication.UnicodeUTF8))

View File

@ -3,6 +3,14 @@ import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))) sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
if 'pyside' in sys.argv: ## should force example to use PySide instead of PyQt if 'pyside' in sys.argv: ## should force example to use PySide instead of PyQt
import PySide from PySide import QtGui
elif 'pyqt' in sys.argv: elif 'pyqt' in sys.argv:
import PyQt4 from PyQt4 import QtGui
else:
from pyqtgraph.Qt import QtGui
for gs in ['raster', 'native', 'opengl']:
if gs in sys.argv:
QtGui.QApplication.setGraphicsSystem(gs)
break

View File

@ -27,12 +27,12 @@ class ImageExporter(Exporter):
def widthChanged(self): def widthChanged(self):
sr = self.getSourceRect() sr = self.getSourceRect()
ar = sr.height() / sr.width() ar = float(sr.height()) / sr.width()
self.params.param('height').setValue(self.params['width'] * ar, blockSignal=self.heightChanged) self.params.param('height').setValue(self.params['width'] * ar, blockSignal=self.heightChanged)
def heightChanged(self): def heightChanged(self):
sr = self.getSourceRect() sr = self.getSourceRect()
ar = sr.width() / sr.height() ar = float(sr.width()) / sr.height()
self.params.param('width').setValue(self.params['height'] * ar, blockSignal=self.widthChanged) self.params.param('width').setValue(self.params['height'] * ar, blockSignal=self.widthChanged)
def parameters(self): def parameters(self):
@ -51,6 +51,8 @@ class ImageExporter(Exporter):
targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height']) targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
sourceRect = self.getSourceRect() sourceRect = self.getSourceRect()
#self.png = QtGui.QImage(targetRect.size(), QtGui.QImage.Format_ARGB32) #self.png = QtGui.QImage(targetRect.size(), QtGui.QImage.Format_ARGB32)
#self.png.fill(pyqtgraph.mkColor(self.params['background'])) #self.png.fill(pyqtgraph.mkColor(self.params['background']))
bg = np.empty((self.params['width'], self.params['height'], 4), dtype=np.ubyte) bg = np.empty((self.params['width'], self.params['height'], 4), dtype=np.ubyte)
@ -60,11 +62,19 @@ class ImageExporter(Exporter):
bg[:,:,2] = color.red() bg[:,:,2] = color.red()
bg[:,:,3] = color.alpha() bg[:,:,3] = color.alpha()
self.png = pg.makeQImage(bg, alpha=True) self.png = pg.makeQImage(bg, alpha=True)
## set resolution of image:
origTargetRect = self.getTargetRect()
resolutionScale = targetRect.width() / origTargetRect.width()
#self.png.setDotsPerMeterX(self.png.dotsPerMeterX() * resolutionScale)
#self.png.setDotsPerMeterY(self.png.dotsPerMeterY() * resolutionScale)
painter = QtGui.QPainter(self.png) painter = QtGui.QPainter(self.png)
#dtr = painter.deviceTransform()
try: try:
self.setExportMode(True, {'antialias': self.params['antialias'], 'background': self.params['background']}) self.setExportMode(True, {'antialias': self.params['antialias'], 'background': self.params['background'], 'painter': painter, 'resolutionScale': resolutionScale})
painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params['antialias']) painter.setRenderHint(QtGui.QPainter.Antialiasing, self.params['antialias'])
self.getScene().render(painter, QtCore.QRectF(targetRect), sourceRect) self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
finally: finally:
self.setExportMode(False) self.setExportMode(False)
painter.end() painter.end()

View File

@ -3,8 +3,8 @@ from pyqtgraph.parametertree import Parameter
from pyqtgraph.Qt import QtGui, QtCore, QtSvg from pyqtgraph.Qt import QtGui, QtCore, QtSvg
import re import re
#__all__ = ['PrintExporter'] __all__ = ['PrintExporter']
__all__ = [] ## Printer is disabled for now--does not work very well. #__all__ = [] ## Printer is disabled for now--does not work very well.
class PrintExporter(Exporter): class PrintExporter(Exporter):
Name = "Printer" Name = "Printer"
@ -38,9 +38,15 @@ class PrintExporter(Exporter):
if dialog.exec_() != QtGui.QDialog.Accepted: if dialog.exec_() != QtGui.QDialog.Accepted:
return; return;
#dpi = QtGui.QDesktopWidget().physicalDpiX()
#self.svg.setSize(QtCore.QSize(100,100)) #self.svg.setSize(QtCore.QSize(100,100))
#self.svg.setResolution(600) #self.svg.setResolution(600)
res = printer.resolution() #res = printer.resolution()
sr = self.getSourceRect()
#res = sr.width() * .4 / (self.params['width'] * 100 / 2.54)
res = QtGui.QDesktopWidget().physicalDpiX()
printer.setResolution(res)
rect = printer.pageRect() rect = printer.pageRect()
center = rect.center() center = rect.center()
h = self.params['height'] * res * 100. / 2.54 h = self.params['height'] * res * 100. / 2.54
@ -52,8 +58,8 @@ class PrintExporter(Exporter):
sourceRect = self.getSourceRect() sourceRect = self.getSourceRect()
painter = QtGui.QPainter(printer) painter = QtGui.QPainter(printer)
try: try:
self.setExportMode(True) self.setExportMode(True, {'painter': painter})
self.getScene().render(painter, QtCore.QRectF(targetRect), sourceRect) self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
finally: finally:
self.setExportMode(False) self.setExportMode(False)
painter.end() painter.end()

View File

@ -46,7 +46,7 @@ class SVGExporter(Exporter):
painter = QtGui.QPainter(self.svg) painter = QtGui.QPainter(self.svg)
try: try:
self.setExportMode(True) self.setExportMode(True, {'painter': painter,})
self.render(painter, QtCore.QRectF(targetRect), sourceRect) self.render(painter, QtCore.QRectF(targetRect), sourceRect)
finally: finally:
self.setExportMode(False) self.setExportMode(False)

View File

@ -54,3 +54,8 @@ class ArrowItem(QtGui.QGraphicsPathItem):
def paint(self, p, *args): def paint(self, p, *args):
p.setRenderHint(QtGui.QPainter.Antialiasing) p.setRenderHint(QtGui.QPainter.Antialiasing)
QtGui.QGraphicsPathItem.paint(self, p, *args) QtGui.QGraphicsPathItem.paint(self, p, *args)
def shape(self):
#if not self.opts['pxMode']:
#return QtGui.QGraphicsPathItem.shape(self)
return self.path

View File

@ -28,10 +28,13 @@ class GraphicsItem(object):
self._viewWidget = None self._viewWidget = None
self._viewBox = None self._viewBox = None
self._connectedView = None self._connectedView = None
self._exportOpts = False ## If False, not currently exporting. Otherwise, contains dict of export options.
if register: if register:
GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items() GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items()
def getViewWidget(self): def getViewWidget(self):
""" """
Return the view widget for this item. If the scene has multiple views, only the first view is returned. Return the view widget for this item. If the scene has multiple views, only the first view is returned.
@ -82,6 +85,9 @@ class GraphicsItem(object):
Return the transform that converts local item coordinates to device coordinates (usually pixels). Return the transform that converts local item coordinates to device coordinates (usually pixels).
Extends deviceTransform to automatically determine the viewportTransform. Extends deviceTransform to automatically determine the viewportTransform.
""" """
if self._exportOpts is not False and 'painter' in self._exportOpts: ## currently exporting; device transform may be different.
return self._exportOpts['painter'].deviceTransform()
if viewportTransform is None: if viewportTransform is None:
view = self.getViewWidget() view = self.getViewWidget()
if view is None: if view is None:
@ -476,4 +482,16 @@ class GraphicsItem(object):
return tree return tree
def setExportMode(self, export, opts):
"""
This method is called by exporters to inform items that they are being drawn for export
with a specific set of options. Items access these via self._exportOptions.
When exporting is complete, _exportOptions is set to False.
"""
if export:
self._exportOpts = opts
#if 'antialias' not in opts:
#self._exportOpts['antialias'] = True
else:
self._exportOpts = False

View File

@ -52,7 +52,6 @@ class PlotCurveItem(GraphicsObject):
self.clear() self.clear()
self.path = None self.path = None
self.fillPath = None self.fillPath = None
self.exportOpts = False
## this is disastrous for performance. ## this is disastrous for performance.
@ -404,8 +403,8 @@ class PlotCurveItem(GraphicsObject):
path = self.path path = self.path
prof.mark('generate path') prof.mark('generate path')
if self.exportOpts is not False: if self._exportOpts is not False:
aa = self.exportOpts['antialias'] aa = self._exportOpts.get('antialias', True)
else: else:
aa = self.opts['antialias'] aa = self.opts['antialias']
@ -487,13 +486,6 @@ class PlotCurveItem(GraphicsObject):
ev.accept() ev.accept()
self.sigClicked.emit(self) self.sigClicked.emit(self)
def setExportMode(self, export, opts):
if export:
self.exportOpts = opts
if 'antialias' not in opts:
self.exportOpts['antialias'] = True
else:
self.exportOpts = False
class ROIPlotItem(PlotCurveItem): class ROIPlotItem(PlotCurveItem):
"""Plot curve that monitors an ROI and image for changes to automatically replot.""" """Plot curve that monitors an ROI and image for changes to automatically replot."""

View File

@ -239,7 +239,6 @@ class ScatterPlotItem(GraphicsObject):
'useCache': True, ## If useCache is False, symbols are re-drawn on every paint. 'useCache': True, ## If useCache is False, symbols are re-drawn on every paint.
'antialias': pg.getConfigOption('antialias'), 'antialias': pg.getConfigOption('antialias'),
} }
self.exportOpts = False
self.setPen(200,200,200, update=False) self.setPen(200,200,200, update=False)
self.setBrush(100,100,150, update=False) self.setBrush(100,100,150, update=False)
@ -546,7 +545,7 @@ class ScatterPlotItem(GraphicsObject):
if invalidate: if invalidate:
self.invalidate() self.invalidate()
def getSpotOpts(self, recs): def getSpotOpts(self, recs, scale=1.0):
if recs.ndim == 0: if recs.ndim == 0:
rec = recs rec = recs
symbol = rec['symbol'] symbol = rec['symbol']
@ -561,11 +560,12 @@ class ScatterPlotItem(GraphicsObject):
brush = rec['brush'] brush = rec['brush']
if brush is None: if brush is None:
brush = self.opts['brush'] brush = self.opts['brush']
return (symbol, size, fn.mkPen(pen), fn.mkBrush(brush)) return (symbol, size*scale, fn.mkPen(pen), fn.mkBrush(brush))
else: else:
recs = recs.copy() recs = recs.copy()
recs['symbol'][np.equal(recs['symbol'], None)] = self.opts['symbol'] recs['symbol'][np.equal(recs['symbol'], None)] = self.opts['symbol']
recs['size'][np.equal(recs['size'], -1)] = self.opts['size'] recs['size'][np.equal(recs['size'], -1)] = self.opts['size']
recs['size'] *= scale
recs['pen'][np.equal(recs['pen'], None)] = fn.mkPen(self.opts['pen']) recs['pen'][np.equal(recs['pen'], None)] = fn.mkPen(self.opts['pen'])
recs['brush'][np.equal(recs['brush'], None)] = fn.mkBrush(self.opts['brush']) recs['brush'][np.equal(recs['brush'], None)] = fn.mkBrush(self.opts['brush'])
return recs return recs
@ -675,18 +675,20 @@ class ScatterPlotItem(GraphicsObject):
rect = QtCore.QRectF(y, x, h, w) rect = QtCore.QRectF(y, x, h, w)
self.fragments.append(QtGui.QPainter.PixmapFragment.create(pos, rect)) self.fragments.append(QtGui.QPainter.PixmapFragment.create(pos, rect))
def setExportMode(self, export, opts): def setExportMode(self, *args, **kwds):
if export: GraphicsObject.setExportMode(self, *args, **kwds)
self.exportOpts = opts self.invalidate()
if 'antialias' not in opts:
self.exportOpts['antialias'] = True
else:
self.exportOpts = False
def paint(self, p, *args): def paint(self, p, *args):
#p.setPen(fn.mkPen('r')) #p.setPen(fn.mkPen('r'))
#p.drawRect(self.boundingRect()) #p.drawRect(self.boundingRect())
if self._exportOpts is not False:
aa = self._exportOpts.get('antialias', True)
scale = self._exportOpts.get('resolutionScale', 1.0) ## exporting to image; pixel resolution may have changed
else:
aa = self.opts['antialias']
scale = 1.0
if self.opts['pxMode'] is True: if self.opts['pxMode'] is True:
atlas = self.fragmentAtlas.getAtlas() atlas = self.fragmentAtlas.getAtlas()
#arr = fn.imageToArray(atlas.toImage(), copy=True) #arr = fn.imageToArray(atlas.toImage(), copy=True)
@ -701,13 +703,9 @@ class ScatterPlotItem(GraphicsObject):
p.resetTransform() p.resetTransform()
if not USE_PYSIDE and self.opts['useCache'] and self.exportOpts is False: if not USE_PYSIDE and self.opts['useCache'] and self._exportOpts is False:
p.drawPixmapFragments(self.fragments, atlas) p.drawPixmapFragments(self.fragments, atlas)
else: else:
if self.exportOpts is not False:
aa = self.exportOpts['antialias']
else:
aa = self.opts['antialias']
p.setRenderHint(p.Antialiasing, aa) p.setRenderHint(p.Antialiasing, aa)
for i in range(len(self.data)): for i in range(len(self.data)):
@ -715,23 +713,20 @@ class ScatterPlotItem(GraphicsObject):
frag = self.fragments[i] frag = self.fragments[i]
p.resetTransform() p.resetTransform()
p.translate(frag.x, frag.y) p.translate(frag.x, frag.y)
drawSymbol(p, *self.getSpotOpts(rec)) drawSymbol(p, *self.getSpotOpts(rec, scale))
else: else:
if self.picture is None: if self.picture is None:
self.picture = QtGui.QPicture() self.picture = QtGui.QPicture()
p2 = QtGui.QPainter(self.picture) p2 = QtGui.QPainter(self.picture)
for rec in self.data: for rec in self.data:
if scale != 1.0:
rec = rec.copy()
rec['size'] *= scale
p2.resetTransform() p2.resetTransform()
p2.translate(rec['x'], rec['y']) p2.translate(rec['x'], rec['y'])
drawSymbol(p2, *self.getSpotOpts(rec)) drawSymbol(p2, *self.getSpotOpts(rec, scale))
p2.end() p2.end()
if self.exportOpts is not False:
aa = self.exportOpts['antialias']
else:
aa = self.opts['antialias']
p.setRenderHint(p.Antialiasing, aa)
self.picture.play(p) self.picture.play(p)

View File

@ -27,25 +27,27 @@ class TextItem(UIGraphicsItem):
#*angle* Angle in degrees to rotate text (note that the rotation assigned in this item's #*angle* Angle in degrees to rotate text (note that the rotation assigned in this item's
#transformation will be ignored) #transformation will be ignored)
self.anchor = pg.Point(anchor)
#self.angle = 0
UIGraphicsItem.__init__(self) UIGraphicsItem.__init__(self)
self.textItem = QtGui.QGraphicsTextItem() self.textItem = QtGui.QGraphicsTextItem()
self.textItem.setParentItem(self)
self.lastTransform = None self.lastTransform = None
self._bounds = QtCore.QRectF() self._bounds = QtCore.QRectF()
if html is None: if html is None:
self.setText(text, color) self.setText(text, color)
else: else:
self.setHtml(html) self.setHtml(html)
self.anchor = pg.Point(anchor)
self.fill = pg.mkBrush(fill) self.fill = pg.mkBrush(fill)
self.border = pg.mkPen(border) self.border = pg.mkPen(border)
self.angle = angle self.rotate(angle)
#self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
def setText(self, text, color=(200,200,200)): def setText(self, text, color=(200,200,200)):
color = pg.mkColor(color) color = pg.mkColor(color)
self.textItem.setDefaultTextColor(color) self.textItem.setDefaultTextColor(color)
self.textItem.setPlainText(text) self.textItem.setPlainText(text)
self.updateText()
#html = '<span style="color: #%s; text-align: center;">%s</span>' % (color, text) #html = '<span style="color: #%s; text-align: center;">%s</span>' % (color, text)
#self.setHtml(html) #self.setHtml(html)
@ -70,38 +72,41 @@ class TextItem(UIGraphicsItem):
self.textItem.setFont(*args) self.textItem.setFont(*args)
self.updateText() self.updateText()
def updateText(self): #def setAngle(self, angle):
self.viewRangeChanged() #self.angle = angle
#self.updateText()
#def getImage(self):
#if self.img is None:
#br = self.textItem.boundingRect()
#img = QtGui.QImage(int(br.width()), int(br.height()), QtGui.QImage.Format_ARGB32)
#p = QtGui.QPainter(img)
#self.textItem.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
#p.end()
#self.img = img
#return self.img
def textBoundingRect(self):
## return the bounds of the text box in device coordinates def updateText(self):
pos = self.mapToDevice(QtCore.QPointF(0,0))
if pos is None: ## Needed to maintain font size when rendering to image with increased resolution
return None self.textItem.resetTransform()
tbr = self.textItem.boundingRect() #self.textItem.rotate(self.angle)
return QtCore.QRectF(pos.x() - tbr.width()*self.anchor.x(), pos.y() - tbr.height()*self.anchor.y(), tbr.width(), tbr.height()) if self._exportOpts is not False and 'resolutionScale' in self._exportOpts:
s = self._exportOpts['resolutionScale']
self.textItem.scale(s, s)
#br = self.textItem.mapRectToParent(self.textItem.boundingRect())
self.textItem.setPos(0,0)
br = self.textItem.boundingRect()
apos = self.textItem.mapToParent(pg.Point(br.width()*self.anchor.x(), br.height()*self.anchor.y()))
#print br, apos
self.textItem.setPos(-apos.x(), -apos.y())
#def textBoundingRect(self):
### return the bounds of the text box in device coordinates
#pos = self.mapToDevice(QtCore.QPointF(0,0))
#if pos is None:
#return None
#tbr = self.textItem.boundingRect()
#return QtCore.QRectF(pos.x() - tbr.width()*self.anchor.x(), pos.y() - tbr.height()*self.anchor.y(), tbr.width(), tbr.height())
def viewRangeChanged(self): def viewRangeChanged(self):
br = self.textBoundingRect() self.updateText()
if br is None:
return
self.prepareGeometryChange()
self._bounds = fn.invertQTransform(self.deviceTransform()).mapRect(br)
#print self._bounds
def boundingRect(self): def boundingRect(self):
return self._bounds return self.textItem.mapToParent(self.textItem.boundingRect()).boundingRect()
def paint(self, p, *args): def paint(self, p, *args):
tr = p.transform() tr = p.transform()
@ -110,23 +115,9 @@ class TextItem(UIGraphicsItem):
self.viewRangeChanged() self.viewRangeChanged()
self.lastTransform = tr self.lastTransform = tr
tbr = self.textBoundingRect()
#p.setPen(pg.mkPen('r'))
#p.drawRect(self.boundingRect())
p.setPen(self.border) p.setPen(self.border)
p.setBrush(self.fill) p.setBrush(self.fill)
p.setRenderHint(p.Antialiasing, True)
p.drawPolygon(self.textItem.mapToParent(self.textItem.boundingRect()))
#p.fillRect(tbr)
p.resetTransform()
#p.drawRect(tbr)
p.translate(tbr.left(), tbr.top())
p.rotate(self.angle)
p.drawRect(QtCore.QRectF(0, 0, tbr.width(), tbr.height()))
self.textItem.paint(p, QtGui.QStyleOptionGraphicsItem(), None)