Bugfixes:

- SVG export text elements use generic font-family as backup, corrected item transformation issues
- Fixed RuntimeError caused when clearing item hierarchies from ViewBox
- Fixed example execution bug

Packaging maintenance:
- Added missing files to MANIFEST.in, fixed setup.py package detection
- Added debian control files for building source packages
- Fixed version numbering in doc, __init__.py
This commit is contained in:
Luke Campagnola 2012-12-29 02:04:04 -05:00
commit 906468996a
18 changed files with 144 additions and 38 deletions

8
MANIFEST.in Normal file
View File

@ -0,0 +1,8 @@
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 *
recursive-include tools *
include doc/Makefile doc/make.bat README.txt LICENSE.txt

View File

@ -17,8 +17,7 @@ import sys, os
# add these directories to sys.path here. If the directory is relative to the # 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. # documentation root, use os.path.abspath to make it absolute, like shown here.
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(path, '..', '..', '..')) sys.path.insert(0, os.path.join(path, '..', '..'))
print sys.path
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
@ -50,9 +49,9 @@ copyright = '2011, Luke Campagnola'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '1.8' version = ''
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '1.8' release = ''
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -3,8 +3,8 @@
You can adapt this file completely to your liking, but it should at least You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive. contain the root `toctree` directive.
Welcome to the documentation for pyqtgraph 1.8 Welcome to the documentation for pyqtgraph
============================================== ==========================================
Contents: Contents:

View File

@ -2,10 +2,10 @@
import sys, os import sys, os
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
path.rstrip(os.path.sep) path.rstrip(os.path.sep)
if path.endswith('pyqtgraph'): if 'pyqtgraph' in os.listdir(path):
sys.path.insert(0, os.path.join(path, '..')) ## examples installed inside pyqtgraph package
elif 'pyqtgraph' in os.listdir(path):
sys.path.insert(0, path) ## examples adjacent to pyqtgraph (as in source) 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 ## should force example to use PySide instead of PyQt
if 'pyside' in sys.argv: if 'pyside' in sys.argv:

View File

@ -1,5 +1,10 @@
# -*- coding: utf-8 -*- # -*- 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 ### import all the goodies and add some helper functions for easy CLI use
@ -63,19 +68,21 @@ def systemInfo():
from .Qt import VERSION_INFO from .Qt import VERSION_INFO
print("qt bindings: %s" % VERSION_INFO) print("qt bindings: %s" % VERSION_INFO)
global REVISION global __version__
if REVISION is None: ## this code was probably checked out from bzr; look up the last-revision file rev = None
lastRevFile = os.path.join(os.path.dirname(__file__), '.bzr', 'branch', 'last-revision') 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): 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:") print("config:")
import pprint import pprint
pprint.pprint(CONFIG_OPTIONS) pprint.pprint(CONFIG_OPTIONS)
## Rename orphaned .pyc files. This is *probably* safe :) ## 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): def renamePyc(startDir):
### Used to rename orphaned .pyc files ### Used to rename orphaned .pyc files
### When a python file changes its location in the repository, usually the .pyc file ### When a python file changes its location in the repository, usually the .pyc file
@ -108,9 +115,8 @@ def renamePyc(startDir):
print(" " + name2) print(" " + name2)
os.rename(fileName, name2) os.rename(fileName, name2)
import os
path = os.path.split(__file__)[0] 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) renamePyc(path)

View File

@ -17,6 +17,9 @@ class SVGExporter(Exporter):
self.params = Parameter(name='params', type='group', children=[ self.params = Parameter(name='params', type='group', children=[
#{'name': 'width', 'type': 'float', 'value': tr.width(), 'limits': (0, None)}, #{'name': 'width', 'type': 'float', 'value': tr.width(), 'limits': (0, None)},
#{'name': 'height', 'type': 'float', 'value': tr.height(), '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('width').sigValueChanged.connect(self.widthChanged)
#self.params.param('height').sigValueChanged.connect(self.heightChanged) #self.params.param('height').sigValueChanged.connect(self.heightChanged)
@ -83,7 +86,7 @@ class SVGExporter(Exporter):
return bytes(xml) return bytes(xml)
else: else:
with open(fileName, 'w') as fh: with open(fileName, 'w') as fh:
fh.write(xml) fh.write(xml.encode('UTF-8'))
xmlHeader = """\ xmlHeader = """\
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -171,8 +174,16 @@ def _generateItemSvg(item, nodes=None, root=None):
doc = xml.parseString(xmlStr) doc = xml.parseString(xmlStr)
else: else:
childs = item.childItems() 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) #print item, pg.SRTTransform(tr)
#tr.translate(item.pos().x(), item.pos().y()) #tr.translate(item.pos().x(), item.pos().y())
@ -239,17 +250,23 @@ def _generateItemSvg(item, nodes=None, root=None):
nodes[name] = g1 nodes[name] = g1
g1.setAttribute('id', name) 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 childGroup = g1 ## add children directly to this node unless we are clipping
if not isinstance(item, QtGui.QGraphicsScene): if not isinstance(item, QtGui.QGraphicsScene):
## 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
if isinstance(root, QtGui.QGraphicsScene): #if isinstance(root, QtGui.QGraphicsScene):
path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape())) #path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
else: #else:
path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape()))) #path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape())))
pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0] 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 ## and for the clipPath element
clip = name + '_clip' clip = name + '_clip'
clipNode = g1.ownerDocument.createElement('clipPath') clipNode = g1.ownerDocument.createElement('clipPath')
@ -294,7 +311,10 @@ def correctCoordinates(node, item):
elif ch.tagName == 'path': elif ch.tagName == 'path':
removeTransform = True removeTransform = True
newCoords = '' 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(',') x,y = c.split(',')
if x[0].isalpha(): if x[0].isalpha():
t = x[0] t = x[0]
@ -317,8 +337,18 @@ def correctCoordinates(node, item):
#fs = c[1]-c[2] #fs = c[1]-c[2]
#fs = (fs**2).sum()**0.5 #fs = (fs**2).sum()**0.5
#ch.setAttribute('font-size', str(fs)) #ch.setAttribute('font-size', str(fs))
else:
print('warning: export not implemented for SVG tag %s (from item %s)' % (ch.tagName, item)) ## 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 ## correct line widths if needed
if removeTransform and ch.getAttribute('vector-effect') != 'non-scaling-stroke': if removeTransform and ch.getAttribute('vector-effect') != 'non-scaling-stroke':

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

@ -63,7 +63,10 @@ class GraphicsItem(object):
if self._viewBox is None: if self._viewBox is None:
p = self p = self
while True: 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: if p is None:
vb = self.getViewWidget() vb = self.getViewWidget()
if vb is None: if vb is None:
@ -496,4 +499,4 @@ class GraphicsItem(object):
#self._exportOpts['antialias'] = True #self._exportOpts['antialias'] = True
else: else:
self._exportOpts = False self._exportOpts = False

View File

@ -115,9 +115,10 @@ class TextItem(UIGraphicsItem):
self.viewRangeChanged() self.viewRangeChanged()
self.lastTransform = tr self.lastTransform = tr
p.setPen(self.border) if self.border.style() != QtCore.Qt.NoPen or self.fill.style() != QtCore.Qt.NoBrush:
p.setBrush(self.fill) p.setPen(self.border)
p.setRenderHint(p.Antialiasing, True) p.setBrush(self.fill)
p.drawPolygon(self.textItem.mapToParent(self.textItem.boundingRect())) p.setRenderHint(p.Antialiasing, True)
p.drawPolygon(self.textItem.mapToParent(self.textItem.boundingRect()))

View File

@ -1,11 +1,19 @@
from distutils.core import setup from distutils.core import setup
import distutils.dir_util
import os import os
## generate list of all sub-packages ## 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]] path = os.path.abspath(os.path.dirname(__file__))
subdirs = filter(lambda p: len(p) == 1 or p[1] != 'build', subdirs) 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'] 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', setup(name='pyqtgraph',
version='', version='',
description='Scientific Graphics and GUI Library for Python', 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']}, #package_data={'pyqtgraph': ['graphicsItems/PlotItem/*.png']},
classifiers = [ classifiers = [
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Development Status :: 4 - Beta", "Development Status :: 4 - Beta",
"Environment :: Other Environment", "Environment :: Other Environment",
@ -33,7 +44,7 @@ It is intended for use in mathematics / scientific / engineering applications. D
"Topic :: Scientific/Engineering :: Visualization", "Topic :: Scientific/Engineering :: Visualization",
"Topic :: Software Development :: User Interfaces", "Topic :: Software Development :: User Interfaces",
], ],
requires = [ install_requires = [
'numpy', 'numpy',
'scipy', 'scipy',
], ],

5
tools/debian/changelog Normal file
View File

@ -0,0 +1,5 @@
python-pyqtgraph (0.9.1-1) UNRELEASED; urgency=low
* Initial release.
-- Luke <luke.campagnola@gmail.com> Sat, 29 Dec 2012 01:07:23 -0500

1
tools/debian/compat Normal file
View File

@ -0,0 +1 @@
8

18
tools/debian/control Normal file
View File

@ -0,0 +1,18 @@
Source: python-pyqtgraph
Maintainer: Luke Campagnola <luke.campagnola@gmail.com>
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.

10
tools/debian/copyright Normal file
View File

@ -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.

1
tools/debian/files Normal file
View File

@ -0,0 +1 @@
python-pyqtgraph_0.9.1-1_all.deb python optional

3
tools/debian/postrm Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh -e
#DEBHELPER#
rm -rf /usr/lib/python2.7/dist-packages/pyqtgraph

4
tools/debian/rules Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/make -f
%:
dh $@

View File

@ -0,0 +1 @@
3.0 (quilt)