From 8899e8d858d0e4740f9a339a40647ca6808202c1 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Wed, 26 Dec 2012 16:29:29 -0500
Subject: [PATCH 01/11] 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
---
examples/__main__.py | 15 +++--
examples/exampleLoaderTemplate.ui | 30 +++++++++
examples/exampleLoaderTemplate_pyqt.py | 18 ++++-
examples/exampleLoaderTemplate_pyside.py | 20 +++++-
examples/initExample.py | 12 +++-
exporters/ImageExporter.py | 18 +++--
exporters/PrintExporter.py | 16 +++--
exporters/SVGExporter.py | 2 +-
graphicsItems/ArrowItem.py | 5 ++
graphicsItems/GraphicsItem.py | 20 +++++-
graphicsItems/PlotCurveItem.py | 12 +---
graphicsItems/ScatterPlotItem.py | 43 ++++++------
graphicsItems/TextItem.py | 83 +++++++++++-------------
13 files changed, 194 insertions(+), 100 deletions(-)
diff --git a/examples/__main__.py b/examples/__main__.py
index 6f4bf138..1dbe7b9a 100644
--- a/examples/__main__.py
+++ b/examples/__main__.py
@@ -126,6 +126,9 @@ class ExampleLoader(QtGui.QMainWindow):
extra.append('pyqt')
elif self.ui.pysideCheck.isChecked():
extra.append('pyside')
+
+ if self.ui.forceGraphicsCheck.isChecked():
+ extra.append(str(self.ui.forceGraphicsCombo.currentText()))
if fn is None:
return
@@ -163,22 +166,26 @@ def buildFileList(examples, files=None):
buildFileList(val, files)
return files
-def testFile(name, f, exe, lib):
+def testFile(name, f, exe, lib, graphicsSystem):
global path
fn = os.path.join(path,f)
#print "starting process: ", fn
-
+ os.chdir(path)
sys.stdout.write(name)
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 = """
try:
+ %s
+ import pyqtgraph as pg
%s
import %s
import sys
print("test complete")
sys.stdout.flush()
- import pyqtgraph as pg
import time
while True: ## run a little event loop
pg.QtGui.QApplication.processEvents()
@@ -187,7 +194,7 @@ except:
print("test failed")
raise
-""" % ("import %s" % lib if lib != '' else "", os.path.splitext(os.path.split(fn)[1])[0])
+""" % (import1, import2, graphicsSystem)
#print code
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'))
diff --git a/examples/exampleLoaderTemplate.ui b/examples/exampleLoaderTemplate.ui
index 1453240c..cd5ce921 100644
--- a/examples/exampleLoaderTemplate.ui
+++ b/examples/exampleLoaderTemplate.ui
@@ -57,6 +57,36 @@
+ -
+
+
-
+
+
+ Force Graphics System:
+
+
+
+ -
+
+
-
+
+ native
+
+
+ -
+
+ raster
+
+
+ -
+
+ opengl
+
+
+
+
+
+
-
diff --git a/examples/exampleLoaderTemplate_pyqt.py b/examples/exampleLoaderTemplate_pyqt.py
index 26e55a44..f359cc32 100644
--- a/examples/exampleLoaderTemplate_pyqt.py
+++ b/examples/exampleLoaderTemplate_pyqt.py
@@ -2,7 +2,7 @@
# 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
#
# WARNING! All changes made in this file will be lost!
@@ -44,6 +44,18 @@ class Ui_Form(object):
self.pysideCheck.setObjectName(_fromUtf8("pysideCheck"))
self.horizontalLayout.addWidget(self.pysideCheck)
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.setObjectName(_fromUtf8("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))
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.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))
diff --git a/examples/exampleLoaderTemplate_pyside.py b/examples/exampleLoaderTemplate_pyside.py
index a81f7299..113c1654 100644
--- a/examples/exampleLoaderTemplate_pyside.py
+++ b/examples/exampleLoaderTemplate_pyside.py
@@ -2,8 +2,8 @@
# Form implementation generated from reading ui file './examples/exampleLoaderTemplate.ui'
#
-# Created: Fri Oct 26 07:53:57 2012
-# by: pyside-uic 0.2.13 running on PySide 1.1.0
+# Created: Mon Dec 24 00:33:39 2012
+# by: pyside-uic 0.2.13 running on PySide 1.1.2
#
# WARNING! All changes made in this file will be lost!
@@ -39,6 +39,18 @@ class Ui_Form(object):
self.pysideCheck.setObjectName("pysideCheck")
self.horizontalLayout.addWidget(self.pysideCheck)
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.setObjectName("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))
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.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))
diff --git a/examples/initExample.py b/examples/initExample.py
index 1b38363b..908f5fb0 100644
--- a/examples/initExample.py
+++ b/examples/initExample.py
@@ -3,6 +3,14 @@ import sys, os
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
- import PySide
+ from PySide import QtGui
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
+
diff --git a/exporters/ImageExporter.py b/exporters/ImageExporter.py
index 763b615d..cb6cf396 100644
--- a/exporters/ImageExporter.py
+++ b/exporters/ImageExporter.py
@@ -27,12 +27,12 @@ class ImageExporter(Exporter):
def widthChanged(self):
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)
def heightChanged(self):
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)
def parameters(self):
@@ -51,6 +51,8 @@ class ImageExporter(Exporter):
targetRect = QtCore.QRect(0, 0, self.params['width'], self.params['height'])
sourceRect = self.getSourceRect()
+
+
#self.png = QtGui.QImage(targetRect.size(), QtGui.QImage.Format_ARGB32)
#self.png.fill(pyqtgraph.mkColor(self.params['background']))
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[:,:,3] = color.alpha()
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)
+ #dtr = painter.deviceTransform()
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'])
- self.getScene().render(painter, QtCore.QRectF(targetRect), sourceRect)
+ self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
finally:
self.setExportMode(False)
painter.end()
diff --git a/exporters/PrintExporter.py b/exporters/PrintExporter.py
index 4a47af2e..5b31b45d 100644
--- a/exporters/PrintExporter.py
+++ b/exporters/PrintExporter.py
@@ -3,8 +3,8 @@ from pyqtgraph.parametertree import Parameter
from pyqtgraph.Qt import QtGui, QtCore, QtSvg
import re
-#__all__ = ['PrintExporter']
-__all__ = [] ## Printer is disabled for now--does not work very well.
+__all__ = ['PrintExporter']
+#__all__ = [] ## Printer is disabled for now--does not work very well.
class PrintExporter(Exporter):
Name = "Printer"
@@ -38,9 +38,15 @@ class PrintExporter(Exporter):
if dialog.exec_() != QtGui.QDialog.Accepted:
return;
+ #dpi = QtGui.QDesktopWidget().physicalDpiX()
+
#self.svg.setSize(QtCore.QSize(100,100))
#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()
center = rect.center()
h = self.params['height'] * res * 100. / 2.54
@@ -52,8 +58,8 @@ class PrintExporter(Exporter):
sourceRect = self.getSourceRect()
painter = QtGui.QPainter(printer)
try:
- self.setExportMode(True)
- self.getScene().render(painter, QtCore.QRectF(targetRect), sourceRect)
+ self.setExportMode(True, {'painter': painter})
+ self.getScene().render(painter, QtCore.QRectF(targetRect), QtCore.QRectF(sourceRect))
finally:
self.setExportMode(False)
painter.end()
diff --git a/exporters/SVGExporter.py b/exporters/SVGExporter.py
index 2d040282..134330d4 100644
--- a/exporters/SVGExporter.py
+++ b/exporters/SVGExporter.py
@@ -46,7 +46,7 @@ class SVGExporter(Exporter):
painter = QtGui.QPainter(self.svg)
try:
- self.setExportMode(True)
+ self.setExportMode(True, {'painter': painter,})
self.render(painter, QtCore.QRectF(targetRect), sourceRect)
finally:
self.setExportMode(False)
diff --git a/graphicsItems/ArrowItem.py b/graphicsItems/ArrowItem.py
index 153ea712..22d0065b 100644
--- a/graphicsItems/ArrowItem.py
+++ b/graphicsItems/ArrowItem.py
@@ -54,3 +54,8 @@ class ArrowItem(QtGui.QGraphicsPathItem):
def paint(self, p, *args):
p.setRenderHint(QtGui.QPainter.Antialiasing)
QtGui.QGraphicsPathItem.paint(self, p, *args)
+
+ def shape(self):
+ #if not self.opts['pxMode']:
+ #return QtGui.QGraphicsPathItem.shape(self)
+ return self.path
\ No newline at end of file
diff --git a/graphicsItems/GraphicsItem.py b/graphicsItems/GraphicsItem.py
index 43b8148c..d3021b75 100644
--- a/graphicsItems/GraphicsItem.py
+++ b/graphicsItems/GraphicsItem.py
@@ -28,10 +28,13 @@ class GraphicsItem(object):
self._viewWidget = None
self._viewBox = None
self._connectedView = None
+ self._exportOpts = False ## If False, not currently exporting. Otherwise, contains dict of export options.
if register:
GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items()
-
+
+
+
def getViewWidget(self):
"""
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).
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:
view = self.getViewWidget()
if view is None:
@@ -476,4 +482,16 @@ class GraphicsItem(object):
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
\ No newline at end of file
diff --git a/graphicsItems/PlotCurveItem.py b/graphicsItems/PlotCurveItem.py
index c49ce30b..dbbeb077 100644
--- a/graphicsItems/PlotCurveItem.py
+++ b/graphicsItems/PlotCurveItem.py
@@ -52,7 +52,6 @@ class PlotCurveItem(GraphicsObject):
self.clear()
self.path = None
self.fillPath = None
- self.exportOpts = False
## this is disastrous for performance.
@@ -404,8 +403,8 @@ class PlotCurveItem(GraphicsObject):
path = self.path
prof.mark('generate path')
- if self.exportOpts is not False:
- aa = self.exportOpts['antialias']
+ if self._exportOpts is not False:
+ aa = self._exportOpts.get('antialias', True)
else:
aa = self.opts['antialias']
@@ -487,13 +486,6 @@ class PlotCurveItem(GraphicsObject):
ev.accept()
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):
"""Plot curve that monitors an ROI and image for changes to automatically replot."""
diff --git a/graphicsItems/ScatterPlotItem.py b/graphicsItems/ScatterPlotItem.py
index 2528d35b..2e41cb7c 100644
--- a/graphicsItems/ScatterPlotItem.py
+++ b/graphicsItems/ScatterPlotItem.py
@@ -239,7 +239,6 @@ class ScatterPlotItem(GraphicsObject):
'useCache': True, ## If useCache is False, symbols are re-drawn on every paint.
'antialias': pg.getConfigOption('antialias'),
}
- self.exportOpts = False
self.setPen(200,200,200, update=False)
self.setBrush(100,100,150, update=False)
@@ -546,7 +545,7 @@ class ScatterPlotItem(GraphicsObject):
if invalidate:
self.invalidate()
- def getSpotOpts(self, recs):
+ def getSpotOpts(self, recs, scale=1.0):
if recs.ndim == 0:
rec = recs
symbol = rec['symbol']
@@ -561,11 +560,12 @@ class ScatterPlotItem(GraphicsObject):
brush = rec['brush']
if brush is None:
brush = self.opts['brush']
- return (symbol, size, fn.mkPen(pen), fn.mkBrush(brush))
+ return (symbol, size*scale, fn.mkPen(pen), fn.mkBrush(brush))
else:
recs = recs.copy()
recs['symbol'][np.equal(recs['symbol'], None)] = self.opts['symbol']
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['brush'][np.equal(recs['brush'], None)] = fn.mkBrush(self.opts['brush'])
return recs
@@ -675,18 +675,20 @@ class ScatterPlotItem(GraphicsObject):
rect = QtCore.QRectF(y, x, h, w)
self.fragments.append(QtGui.QPainter.PixmapFragment.create(pos, rect))
- def setExportMode(self, export, opts):
- if export:
- self.exportOpts = opts
- if 'antialias' not in opts:
- self.exportOpts['antialias'] = True
- else:
- self.exportOpts = False
-
+ def setExportMode(self, *args, **kwds):
+ GraphicsObject.setExportMode(self, *args, **kwds)
+ self.invalidate()
def paint(self, p, *args):
#p.setPen(fn.mkPen('r'))
#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:
atlas = self.fragmentAtlas.getAtlas()
#arr = fn.imageToArray(atlas.toImage(), copy=True)
@@ -701,13 +703,9 @@ class ScatterPlotItem(GraphicsObject):
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)
else:
- if self.exportOpts is not False:
- aa = self.exportOpts['antialias']
- else:
- aa = self.opts['antialias']
p.setRenderHint(p.Antialiasing, aa)
for i in range(len(self.data)):
@@ -715,23 +713,20 @@ class ScatterPlotItem(GraphicsObject):
frag = self.fragments[i]
p.resetTransform()
p.translate(frag.x, frag.y)
- drawSymbol(p, *self.getSpotOpts(rec))
+ drawSymbol(p, *self.getSpotOpts(rec, scale))
else:
if self.picture is None:
self.picture = QtGui.QPicture()
p2 = QtGui.QPainter(self.picture)
for rec in self.data:
-
+ if scale != 1.0:
+ rec = rec.copy()
+ rec['size'] *= scale
p2.resetTransform()
p2.translate(rec['x'], rec['y'])
- drawSymbol(p2, *self.getSpotOpts(rec))
+ drawSymbol(p2, *self.getSpotOpts(rec, scale))
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)
diff --git a/graphicsItems/TextItem.py b/graphicsItems/TextItem.py
index a85e919d..b5666f6e 100644
--- a/graphicsItems/TextItem.py
+++ b/graphicsItems/TextItem.py
@@ -27,25 +27,27 @@ class TextItem(UIGraphicsItem):
#*angle* Angle in degrees to rotate text (note that the rotation assigned in this item's
#transformation will be ignored)
-
+ self.anchor = pg.Point(anchor)
+ #self.angle = 0
UIGraphicsItem.__init__(self)
self.textItem = QtGui.QGraphicsTextItem()
+ self.textItem.setParentItem(self)
self.lastTransform = None
self._bounds = QtCore.QRectF()
if html is None:
self.setText(text, color)
else:
self.setHtml(html)
- self.anchor = pg.Point(anchor)
self.fill = pg.mkBrush(fill)
self.border = pg.mkPen(border)
- self.angle = angle
- #self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
+ self.rotate(angle)
+ self.setFlag(self.ItemIgnoresTransformations) ## This is required to keep the text unscaled inside the viewport
def setText(self, text, color=(200,200,200)):
color = pg.mkColor(color)
self.textItem.setDefaultTextColor(color)
self.textItem.setPlainText(text)
+ self.updateText()
#html = '%s' % (color, text)
#self.setHtml(html)
@@ -70,38 +72,41 @@ class TextItem(UIGraphicsItem):
self.textItem.setFont(*args)
self.updateText()
- def updateText(self):
- self.viewRangeChanged()
-
- #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 setAngle(self, angle):
+ #self.angle = angle
+ #self.updateText()
- 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 updateText(self):
+
+ ## Needed to maintain font size when rendering to image with increased resolution
+ self.textItem.resetTransform()
+ #self.textItem.rotate(self.angle)
+ 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):
- br = self.textBoundingRect()
- if br is None:
- return
- self.prepareGeometryChange()
- self._bounds = fn.invertQTransform(self.deviceTransform()).mapRect(br)
- #print self._bounds
+ self.updateText()
def boundingRect(self):
- return self._bounds
+ return self.textItem.mapToParent(self.textItem.boundingRect()).boundingRect()
def paint(self, p, *args):
tr = p.transform()
@@ -110,23 +115,9 @@ class TextItem(UIGraphicsItem):
self.viewRangeChanged()
self.lastTransform = tr
-
- tbr = self.textBoundingRect()
-
- #p.setPen(pg.mkPen('r'))
- #p.drawRect(self.boundingRect())
-
p.setPen(self.border)
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)
\ No newline at end of file
From 6931eacffd2809abc74d30e4fae59151ce5088f6 Mon Sep 17 00:00:00 2001
From: Luke Campagnola
Date: Wed, 26 Dec 2012 21:02:36 -0500
Subject: [PATCH 02/11] Fixed doc build to work with new package structure.
---
doc/source/conf.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 2fd718e4..97d4b35c 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -17,8 +17,7 @@ import sys, os
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
path = os.path.dirname(os.path.abspath(__file__))
-sys.path.insert(0, os.path.join(path, '..', '..', '..'))
-print sys.path
+sys.path.insert(0, os.path.join(path, '..', '..'))
# -- General configuration -----------------------------------------------------
From 19d7bc56054a3c29c1a18f281e41e83896721ab8 Mon Sep 17 00:00:00 2001
From: Luke Campagnola
Date: Thu, 27 Dec 2012 04:35:23 +0000
Subject: [PATCH 03/11] bugfixes for new package structure
---
examples/initExample.py | 6 +++---
setup.py | 15 +++++++++++++--
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/examples/initExample.py b/examples/initExample.py
index f95a0cb0..38dd3edc 100644
--- a/examples/initExample.py
+++ b/examples/initExample.py
@@ -2,10 +2,10 @@
import sys, os
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
path.rstrip(os.path.sep)
-if path.endswith('pyqtgraph'):
- sys.path.insert(0, os.path.join(path, '..')) ## examples installed inside pyqtgraph package
-elif 'pyqtgraph' in os.listdir(path):
+if 'pyqtgraph' in os.listdir(path):
sys.path.insert(0, path) ## examples adjacent to pyqtgraph (as in source)
+elif path.endswith('pyqtgraph'):
+ sys.path.insert(0, os.path.abspath(os.path.join(path, '..'))) ## examples installed inside pyqtgraph package
## should force example to use PySide instead of PyQt
if 'pyside' in sys.argv:
diff --git a/setup.py b/setup.py
index e4dc07cf..64d22ba2 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,19 @@
from distutils.core import setup
+import distutils.dir_util
import os
## generate list of all sub-packages
-subdirs = [i[0].split(os.path.sep)[1:] for i in os.walk('./pyqtgraph') if '__init__.py' in i[2]]
-subdirs = filter(lambda p: len(p) == 1 or p[1] != 'build', subdirs)
+path = os.path.abspath(os.path.dirname(__file__))
+n = len(path.split(os.path.sep))
+subdirs = [i[0].split(os.path.sep)[n:] for i in os.walk(os.path.join(path, 'pyqtgraph')) if '__init__.py' in i[2]]
all_packages = ['.'.join(p) for p in subdirs] + ['pyqtgraph.examples']
+
+## Make sure build directory is clean before installing
+buildPath = os.path.join(path, 'build')
+if os.path.isdir(buildPath):
+ distutils.dir_util.remove_tree(buildPath)
+
setup(name='pyqtgraph',
version='',
description='Scientific Graphics and GUI Library for Python',
@@ -23,6 +31,9 @@ It is intended for use in mathematics / scientific / engineering applications. D
#package_data={'pyqtgraph': ['graphicsItems/PlotItem/*.png']},
classifiers = [
"Programming Language :: Python",
+ "Programming Language :: Python :: 2",
+ "Programming Language :: Python :: 2.6",
+ "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
"Environment :: Other Environment",
From 7f51813c2c0a101f1783a4af4acadb587b95e556 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Thu, 27 Dec 2012 01:52:32 -0500
Subject: [PATCH 04/11] Added MANIFEST.in for generating cleaner source
distributions updated versioning system
---
MANIFEST.in | 7 +++++++
pyqtgraph/__init__.py | 24 +++++++++++++++---------
2 files changed, 22 insertions(+), 9 deletions(-)
create mode 100644 MANIFEST.in
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..fb08e7e5
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+recursive-include pyqtgraph *.py *.ui *.m README *.txt
+recursive-include tests *.py *.ui
+recursive-include examples *.py *.ui
+recursive-include doc *.rst *.py *.svg *.png *.jpg
+recursive-include doc/build/html *
+include doc/Makefile doc/make.bat
+
diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py
index 6e950770..93d9f7b8 100644
--- a/pyqtgraph/__init__.py
+++ b/pyqtgraph/__init__.py
@@ -1,5 +1,10 @@
# -*- coding: utf-8 -*-
-REVISION = None
+"""
+PyQtGraph - Scientific Graphics and GUI Library for Python
+www.pyqtgraph.org
+"""
+
+__version__ = None
### import all the goodies and add some helper functions for easy CLI use
@@ -63,19 +68,21 @@ def systemInfo():
from .Qt import VERSION_INFO
print("qt bindings: %s" % VERSION_INFO)
- global REVISION
- if REVISION is None: ## this code was probably checked out from bzr; look up the last-revision file
- lastRevFile = os.path.join(os.path.dirname(__file__), '.bzr', 'branch', 'last-revision')
+ global __version__
+ rev = None
+ if __version__ is None: ## this code was probably checked out from bzr; look up the last-revision file
+ lastRevFile = os.path.join(os.path.dirname(__file__), '..', '.bzr', 'branch', 'last-revision')
if os.path.exists(lastRevFile):
- REVISION = open(lastRevFile, 'r').read().strip()
+ rev = open(lastRevFile, 'r').read().strip()
- print("pyqtgraph: %s" % REVISION)
+ print("pyqtgraph: %s; %s" % (__version__, rev))
print("config:")
import pprint
pprint.pprint(CONFIG_OPTIONS)
## Rename orphaned .pyc files. This is *probably* safe :)
-
+## We only do this if __version__ is None, indicating the code was probably pulled
+## from the repository.
def renamePyc(startDir):
### Used to rename orphaned .pyc files
### When a python file changes its location in the repository, usually the .pyc file
@@ -108,9 +115,8 @@ def renamePyc(startDir):
print(" " + name2)
os.rename(fileName, name2)
-import os
path = os.path.split(__file__)[0]
-if not hasattr(sys, 'frozen'): ## If we are frozen, there's a good chance we don't have the original .py files anymore.
+if __version__ is None and not hasattr(sys, 'frozen') and sys.version_info[0] == 2: ## If we are frozen, there's a good chance we don't have the original .py files anymore.
renamePyc(path)
From 000354ac2189078bf3dff3f3dfc01e8fd6fda975 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Thu, 27 Dec 2012 02:21:34 -0500
Subject: [PATCH 05/11] Fixed documentation version numbers
---
doc/source/conf.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 97d4b35c..236cb807 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -49,9 +49,9 @@ copyright = '2011, Luke Campagnola'
# built documents.
#
# The short X.Y version.
-version = '1.8'
+version = ''
# The full version, including alpha/beta/rc tags.
-release = '1.8'
+release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
From b9822b1d10e2820cb4a62cf47869130b1eeb4def Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Thu, 27 Dec 2012 03:13:35 -0500
Subject: [PATCH 06/11] Fixed doc version (again) Added debian control files
---
doc/source/conf.py | 4 ++--
doc/source/index.rst | 4 ++--
pyqtgraph/__init__.py | 2 +-
setup.py | 2 +-
tools/DEBIAN/control | 13 +++++++++++++
tools/DEBIAN/postrm | 2 ++
6 files changed, 21 insertions(+), 6 deletions(-)
create mode 100644 tools/DEBIAN/control
create mode 100755 tools/DEBIAN/postrm
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 236cb807..4a275cd1 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -49,9 +49,9 @@ copyright = '2011, Luke Campagnola'
# built documents.
#
# The short X.Y version.
-version = ''
+version = '0.9.0'
# The full version, including alpha/beta/rc tags.
-release = ''
+release = '0.9.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 5d606061..cc89f3d8 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -3,8 +3,8 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to the documentation for pyqtgraph 1.8
-==============================================
+Welcome to the documentation for pyqtgraph
+==========================================
Contents:
diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py
index 93d9f7b8..2998be79 100644
--- a/pyqtgraph/__init__.py
+++ b/pyqtgraph/__init__.py
@@ -4,7 +4,7 @@ PyQtGraph - Scientific Graphics and GUI Library for Python
www.pyqtgraph.org
"""
-__version__ = None
+__version__ = '0.9.0'
### import all the goodies and add some helper functions for easy CLI use
diff --git a/setup.py b/setup.py
index 64d22ba2..5b608eda 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ if os.path.isdir(buildPath):
distutils.dir_util.remove_tree(buildPath)
setup(name='pyqtgraph',
- version='',
+ version='0.9.0',
description='Scientific Graphics and GUI Library for Python',
long_description="""\
PyQtGraph is a pure-python graphics and GUI library built on PyQt4/PySide and numpy.
diff --git a/tools/DEBIAN/control b/tools/DEBIAN/control
new file mode 100644
index 00000000..d7c74bc7
--- /dev/null
+++ b/tools/DEBIAN/control
@@ -0,0 +1,13 @@
+Package: python-pyqtgraph
+Version: 0.9.0
+Section: python
+Priority: optional
+Architecture: all
+Essential: no
+Installed-Size: 5048
+Maintainer: Luke Campagnola
+Homepage: http://luke.campagnola.me/code/pyqtgraph
+Depends: python (>= 2.6), python-qt4 | python-pyside, python-scipy, python-numpy
+Suggests: python-opengl, python-qt4-gl
+Description: Scientific Graphics and GUI Library for Python
+ PyQtGraph is a pure-python graphics and GUI library built on PyQt4 and numpy. It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display.
diff --git a/tools/DEBIAN/postrm b/tools/DEBIAN/postrm
new file mode 100755
index 00000000..35a4685c
--- /dev/null
+++ b/tools/DEBIAN/postrm
@@ -0,0 +1,2 @@
+#!/bin/sh
+rm -rf /usr/lib/python2.7/dist-packages/pyqtgraph
From 87ea160a2360aac41a48f1218554a163cff25171 Mon Sep 17 00:00:00 2001
From: Luke Campagnola
Date: Thu, 27 Dec 2012 10:31:08 -0500
Subject: [PATCH 07/11] Correction to setup.py - use install_requires to inform
pip of dependencies.
---
setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index 5b608eda..8833b1d8 100644
--- a/setup.py
+++ b/setup.py
@@ -44,7 +44,7 @@ It is intended for use in mathematics / scientific / engineering applications. D
"Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: User Interfaces",
],
- requires = [
+ install_requires = [
'numpy',
'scipy',
],
From 8d5e24c8fdb46e14f496d6d136a8d044eea7ef53 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Thu, 27 Dec 2012 11:53:22 -0500
Subject: [PATCH 08/11] Removed incorrect version numbers
---
doc/source/conf.py | 4 ++--
pyqtgraph/__init__.py | 2 +-
setup.py | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 4a275cd1..236cb807 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -49,9 +49,9 @@ copyright = '2011, Luke Campagnola'
# built documents.
#
# The short X.Y version.
-version = '0.9.0'
+version = ''
# The full version, including alpha/beta/rc tags.
-release = '0.9.0'
+release = ''
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py
index 2998be79..93d9f7b8 100644
--- a/pyqtgraph/__init__.py
+++ b/pyqtgraph/__init__.py
@@ -4,7 +4,7 @@ PyQtGraph - Scientific Graphics and GUI Library for Python
www.pyqtgraph.org
"""
-__version__ = '0.9.0'
+__version__ = None
### import all the goodies and add some helper functions for easy CLI use
diff --git a/setup.py b/setup.py
index 8833b1d8..8128a851 100644
--- a/setup.py
+++ b/setup.py
@@ -15,7 +15,7 @@ if os.path.isdir(buildPath):
distutils.dir_util.remove_tree(buildPath)
setup(name='pyqtgraph',
- version='0.9.0',
+ version='',
description='Scientific Graphics and GUI Library for Python',
long_description="""\
PyQtGraph is a pure-python graphics and GUI library built on PyQt4/PySide and numpy.
From f32a04a43314af335538930ce565be01bcfea345 Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Fri, 28 Dec 2012 16:23:28 -0500
Subject: [PATCH 09/11] SVG export fixes: - unicode support for text objects
- always export in scene coordinates with offset from root item
---
pyqtgraph/exporters/SVGExporter.py | 37 ++++++++++++++++++++---------
pyqtgraph/graphicsItems/TextItem.py | 9 +++----
2 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py
index ce05b82d..321c311f 100644
--- a/pyqtgraph/exporters/SVGExporter.py
+++ b/pyqtgraph/exporters/SVGExporter.py
@@ -83,7 +83,7 @@ class SVGExporter(Exporter):
return bytes(xml)
else:
with open(fileName, 'w') as fh:
- fh.write(xml)
+ fh.write(xml.encode('UTF-8'))
xmlHeader = """\
@@ -171,8 +171,16 @@ def _generateItemSvg(item, nodes=None, root=None):
doc = xml.parseString(xmlStr)
else:
childs = item.childItems()
- tr = itemTransform(item, root)
+ tr = itemTransform(item, item.scene())
+ ## offset to corner of root item
+ if isinstance(root, QtGui.QGraphicsScene):
+ rootPos = QtCore.QPoint(0,0)
+ else:
+ rootPos = root.scenePos()
+ tr2 = QtGui.QTransform()
+ tr2.translate(-rootPos.x(), -rootPos.y())
+ tr = tr * tr2
#print item, pg.SRTTransform(tr)
#tr.translate(item.pos().x(), item.pos().y())
@@ -239,17 +247,23 @@ def _generateItemSvg(item, nodes=None, root=None):
nodes[name] = g1
g1.setAttribute('id', name)
- ## If this item clips its children, we need to take car of that.
+ ## If this item clips its children, we need to take care of that.
childGroup = g1 ## add children directly to this node unless we are clipping
if not isinstance(item, QtGui.QGraphicsScene):
## See if this item clips its children
if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
## Generate svg for just the path
- if isinstance(root, QtGui.QGraphicsScene):
- path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
- else:
- path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape())))
- pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
+ #if isinstance(root, QtGui.QGraphicsScene):
+ #path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
+ #else:
+ #path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape())))
+ path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
+ item.scene().addItem(path)
+ try:
+ pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
+ finally:
+ item.scene().removeItem(path)
+
## and for the clipPath element
clip = name + '_clip'
clipNode = g1.ownerDocument.createElement('clipPath')
@@ -294,7 +308,10 @@ def correctCoordinates(node, item):
elif ch.tagName == 'path':
removeTransform = True
newCoords = ''
- for c in ch.getAttribute('d').strip().split(' '):
+ oldCoords = ch.getAttribute('d').strip()
+ if oldCoords == '':
+ continue
+ for c in oldCoords.split(' '):
x,y = c.split(',')
if x[0].isalpha():
t = x[0]
@@ -317,8 +334,6 @@ def correctCoordinates(node, item):
#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))
## correct line widths if needed
if removeTransform and ch.getAttribute('vector-effect') != 'non-scaling-stroke':
diff --git a/pyqtgraph/graphicsItems/TextItem.py b/pyqtgraph/graphicsItems/TextItem.py
index b5666f6e..911057f4 100644
--- a/pyqtgraph/graphicsItems/TextItem.py
+++ b/pyqtgraph/graphicsItems/TextItem.py
@@ -115,9 +115,10 @@ class TextItem(UIGraphicsItem):
self.viewRangeChanged()
self.lastTransform = tr
- p.setPen(self.border)
- p.setBrush(self.fill)
- p.setRenderHint(p.Antialiasing, True)
- p.drawPolygon(self.textItem.mapToParent(self.textItem.boundingRect()))
+ if self.border.style() != QtCore.Qt.NoPen or self.fill.style() != QtCore.Qt.NoBrush:
+ p.setPen(self.border)
+ p.setBrush(self.fill)
+ p.setRenderHint(p.Antialiasing, True)
+ p.drawPolygon(self.textItem.mapToParent(self.textItem.boundingRect()))
\ No newline at end of file
From b0030e1a49ca06509960d40f0bb2b8a7a50cabca Mon Sep 17 00:00:00 2001
From: Luke Campagnola <>
Date: Sat, 29 Dec 2012 02:35:45 -0500
Subject: [PATCH 10/11] Bugfixes: - Fixed RuntimeError when clearing items from
ViewBox - SVG exporter adds generic font-family names to text items
---
pyqtgraph/exporters/SVGExporter.py | 15 +++++++++++++++
pyqtgraph/graphicsItems/GraphicsItem.py | 5 ++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py
index 321c311f..70f1f632 100644
--- a/pyqtgraph/exporters/SVGExporter.py
+++ b/pyqtgraph/exporters/SVGExporter.py
@@ -17,6 +17,9 @@ class SVGExporter(Exporter):
self.params = Parameter(name='params', type='group', children=[
#{'name': 'width', 'type': 'float', 'value': tr.width(), 'limits': (0, None)},
#{'name': 'height', 'type': 'float', 'value': tr.height(), 'limits': (0, None)},
+ #{'name': 'viewbox clipping', 'type': 'bool', 'value': True},
+ #{'name': 'normalize coordinates', 'type': 'bool', 'value': True},
+ #{'name': 'normalize line width', 'type': 'bool', 'value': True},
])
#self.params.param('width').sigValueChanged.connect(self.widthChanged)
#self.params.param('height').sigValueChanged.connect(self.heightChanged)
@@ -335,6 +338,18 @@ def correctCoordinates(node, item):
#fs = (fs**2).sum()**0.5
#ch.setAttribute('font-size', str(fs))
+ ## Correct some font information
+ families = ch.getAttribute('font-family').split(',')
+ if len(families) == 1:
+ font = QtGui.QFont(families[0].strip('" '))
+ if font.style() == font.SansSerif:
+ families.append('sans-serif')
+ elif font.style() == font.Serif:
+ families.append('serif')
+ elif font.style() == font.Courier:
+ families.append('monospace')
+ ch.setAttribute('font-family', ', '.join([f if ' ' not in f else '"%s"'%f for f in families]))
+
## correct line widths if needed
if removeTransform and ch.getAttribute('vector-effect') != 'non-scaling-stroke':
w = float(grp.getAttribute('stroke-width'))
diff --git a/pyqtgraph/graphicsItems/GraphicsItem.py b/pyqtgraph/graphicsItems/GraphicsItem.py
index 34fd4bd7..2018fb4c 100644
--- a/pyqtgraph/graphicsItems/GraphicsItem.py
+++ b/pyqtgraph/graphicsItems/GraphicsItem.py
@@ -63,7 +63,10 @@ class GraphicsItem(object):
if self._viewBox is None:
p = self
while True:
- p = p.parentItem()
+ try:
+ p = p.parentItem()
+ except RuntimeError: ## sometimes happens as items are being removed from a scene and collected.
+ return None
if p is None:
vb = self.getViewWidget()
if vb is None:
From 927f032f19ed8e24126a2bbca0413c6c81b9cabb Mon Sep 17 00:00:00 2001
From: Luke Campagnola
Date: Sat, 29 Dec 2012 01:40:41 -0500
Subject: [PATCH 11/11] MANIFEST.in includes some missing files: tools, README,
LICENSE updated debian control structure for building source packages
---
MANIFEST.in | 3 ++-
tools/DEBIAN/control | 13 -------------
tools/debian/changelog | 5 +++++
tools/debian/compat | 1 +
tools/debian/control | 18 ++++++++++++++++++
tools/debian/copyright | 10 ++++++++++
tools/debian/files | 1 +
tools/{DEBIAN => debian}/postrm | 3 ++-
tools/debian/rules | 4 ++++
tools/debian/source/format | 1 +
10 files changed, 44 insertions(+), 15 deletions(-)
delete mode 100644 tools/DEBIAN/control
create mode 100644 tools/debian/changelog
create mode 100644 tools/debian/compat
create mode 100644 tools/debian/control
create mode 100644 tools/debian/copyright
create mode 100644 tools/debian/files
rename tools/{DEBIAN => debian}/postrm (66%)
create mode 100755 tools/debian/rules
create mode 100644 tools/debian/source/format
diff --git a/MANIFEST.in b/MANIFEST.in
index fb08e7e5..02d67f6f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -3,5 +3,6 @@ recursive-include tests *.py *.ui
recursive-include examples *.py *.ui
recursive-include doc *.rst *.py *.svg *.png *.jpg
recursive-include doc/build/html *
-include doc/Makefile doc/make.bat
+recursive-include tools *
+include doc/Makefile doc/make.bat README.txt LICENSE.txt
diff --git a/tools/DEBIAN/control b/tools/DEBIAN/control
deleted file mode 100644
index d7c74bc7..00000000
--- a/tools/DEBIAN/control
+++ /dev/null
@@ -1,13 +0,0 @@
-Package: python-pyqtgraph
-Version: 0.9.0
-Section: python
-Priority: optional
-Architecture: all
-Essential: no
-Installed-Size: 5048
-Maintainer: Luke Campagnola
-Homepage: http://luke.campagnola.me/code/pyqtgraph
-Depends: python (>= 2.6), python-qt4 | python-pyside, python-scipy, python-numpy
-Suggests: python-opengl, python-qt4-gl
-Description: Scientific Graphics and GUI Library for Python
- PyQtGraph is a pure-python graphics and GUI library built on PyQt4 and numpy. It is intended for use in mathematics / scientific / engineering applications. Despite being written entirely in python, the library is very fast due to its heavy leverage of numpy for number crunching and Qt's GraphicsView framework for fast display.
diff --git a/tools/debian/changelog b/tools/debian/changelog
new file mode 100644
index 00000000..1edf45f3
--- /dev/null
+++ b/tools/debian/changelog
@@ -0,0 +1,5 @@
+python-pyqtgraph (0.9.1-1) UNRELEASED; urgency=low
+
+ * Initial release.
+
+ -- Luke Sat, 29 Dec 2012 01:07:23 -0500
diff --git a/tools/debian/compat b/tools/debian/compat
new file mode 100644
index 00000000..45a4fb75
--- /dev/null
+++ b/tools/debian/compat
@@ -0,0 +1 @@
+8
diff --git a/tools/debian/control b/tools/debian/control
new file mode 100644
index 00000000..7ab6f28a
--- /dev/null
+++ b/tools/debian/control
@@ -0,0 +1,18 @@
+Source: python-pyqtgraph
+Maintainer: Luke Campagnola
+Section: python
+Priority: optional
+Standards-Version: 3.9.3
+Build-Depends: debhelper (>= 8)
+
+Package: python-pyqtgraph
+Architecture: all
+Homepage: http://luke.campagnola.me/code/pyqtgraph
+Depends: python (>= 2.6), python-support (>= 0.90), python-qt4 | python-pyside, python-scipy, python-numpy, ${misc:Depends}
+Suggests: python-opengl, python-qt4-gl
+Description: Scientific Graphics and GUI Library for Python
+ PyQtGraph is a pure-python graphics and GUI library built on PyQt4 and numpy.
+ It is intended for use in mathematics / scientific / engineering applications.
+ Despite being written entirely in python, the library is very fast due to its
+ heavy leverage of numpy for number crunching and Qt's GraphicsView framework
+ for fast display.
diff --git a/tools/debian/copyright b/tools/debian/copyright
new file mode 100644
index 00000000..22791ae3
--- /dev/null
+++ b/tools/debian/copyright
@@ -0,0 +1,10 @@
+Copyright (c) 2012 University of North Carolina at Chapel Hill
+Luke Campagnola ('luke.campagnola@%s.com' % 'gmail')
+
+The MIT License
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/tools/debian/files b/tools/debian/files
new file mode 100644
index 00000000..4af05533
--- /dev/null
+++ b/tools/debian/files
@@ -0,0 +1 @@
+python-pyqtgraph_0.9.1-1_all.deb python optional
diff --git a/tools/DEBIAN/postrm b/tools/debian/postrm
similarity index 66%
rename from tools/DEBIAN/postrm
rename to tools/debian/postrm
index 35a4685c..e1eae9f2 100755
--- a/tools/DEBIAN/postrm
+++ b/tools/debian/postrm
@@ -1,2 +1,3 @@
-#!/bin/sh
+#!/bin/sh -e
+#DEBHELPER#
rm -rf /usr/lib/python2.7/dist-packages/pyqtgraph
diff --git a/tools/debian/rules b/tools/debian/rules
new file mode 100755
index 00000000..2d33f6ac
--- /dev/null
+++ b/tools/debian/rules
@@ -0,0 +1,4 @@
+#!/usr/bin/make -f
+
+%:
+ dh $@
diff --git a/tools/debian/source/format b/tools/debian/source/format
new file mode 100644
index 00000000..163aaf8d
--- /dev/null
+++ b/tools/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)