Commit 2e79185d authored by Luke Campagnola's avatar Luke Campagnola
Browse files

Features:

  - Added GraphItem class for displaying networks/trees
  - Added ColorMap class for mapping linear gradients and generating lookup tables
    (Provides gradient editor functionality without the GUI)
  - Added ColorMapWidget for complex user-defined color mapping
  - Added ScatterPlotWidget for exploring relationships in multi-column tables
  - Added ErrorBarItem
  - SVG and image exporters can now copy to clipboard
  - PlotItem gets new methods: addLine, setLabels, and listDataItems
  - AxisItem gets setTickFont method
  - Added functions.arrayToQPath, shared between GraphItem and PlotCurveItem
  - Added gradient editors to parametertree
  - Expanded documentation, added beginning of Qt crash course

Bugfixes:
  - Fixed auto-ranging bugs: ViewBox now properly handles pixel-padding around data items
  - ViewBox ignores bounds of zoom-rect when auto ranging
  - Fixed AxisItem artifacts
  - Fixed GraphicsItem.pixelVector caching bugs and simplified workaround for fp-precision errors
  - LinearRegionItem.hoverEvent obeys 'movable' flag                                                                                                                                         
  - Fixed PlotDataItem nan masking bugs                                                                                                                                                      
  - Workaround for segmentation fault in QPainter.drawPixmapFragments                                                                                                                        
  - multiprocess and RemoteGraphicsView work correctly in Windows.                                                                                                                           
  - Expanded python 3 support                                                                                                                                                                
  - Silenced weave errors by default                                                                                                                                                         
  - Fixed " 'win' in sys.platform " occurrences matching 'darwin' (duh)
  - Workaround for change in QImage API (PyQt 4.9.6)
  - Fixed axis ordering bug in GLScatterPlotItem
parents 3a279970 81574689
......@@ -10,5 +10,7 @@ Contents:
graphicsItems/index
widgets/index
3dgraphics/index
colormap
parametertree/index
graphicsscene/index
flowchart/index
ColorMap
========
.. autoclass:: pyqtgraph.ColorMap
:members:
.. automethod:: pyqtgraph.ColorMap.__init__
......@@ -91,6 +91,8 @@ Mesh Generation Functions
Miscellaneous Functions
-----------------------
.. autofunction:: pyqtgraph.arrayToQPath
.. autofunction:: pyqtgraph.pseudoScatter
.. autofunction:: pyqtgraph.systemInfo
......
GraphItem
=========
.. autoclass:: pyqtgraph.GraphItem
:members:
.. automethod:: pyqtgraph.GraphItem.__init__
......@@ -12,6 +12,7 @@ Contents:
plotdataitem
plotitem
imageitem
graphitem
viewbox
linearregionitem
infiniteline
......
......@@ -15,6 +15,7 @@ Contents:
mouse_interaction
how_to_use
installation
qtcrashcourse
plotting
images
3dgraphics
......
......@@ -3,20 +3,76 @@ Qt Crash Course
Pyqtgraph makes extensive use of Qt for generating nearly all of its visual output and interfaces. Qt's documentation is very well written and we encourage all pyqtgraph developers to familiarize themselves with it. The purpose of this section is to provide an introduction to programming with Qt (using either PyQt or PySide) for the pyqtgraph developer.
QWidgets and Layouts
--------------------
A Qt GUI is almost always composed of a few basic components:
* A window. This is often provided by QMainWindow, but note that all QWidgets can be displayed in their window by simply calling widget.show() if the widget does not have a parent.
* Multiple QWidget instances such as QPushButton, QLabel, QComboBox, etc.
* QLayout instances (optional, but strongly encouraged) which automatically manage the positioning of widgets to allow the GUI to resize in a usable way.
Pyqtgraph fits into this scheme by providing its own QWidget subclasses to be inserted into your GUI.
Example::
from PyQt4 import QtGui # (the example applies equally well to PySide)
import pyqtgraph as pg
## Always start by initializing Qt (only once per application)
app = QtGui.QApplication([])
## Define a top-level widget to hold everything
w = QtGui.QWidget()
## Create some widgets to be placed inside
btn = QtGui.QPushButton('press me')
text = QtGui.QLineEdit('enter text')
listw = QtGui.QListWidget()
plot = pg.PlotWidget()
## Create a grid layout to manage the widgets size and position
layout = QtGui.QGridLayout()
w.setLayout(layout)
## Add widgets to the layout in their proper positions
layout.addWidget(btn, 0, 0) # button goes in upper-left
layout.addWidget(text, 1, 0) # text edit goes in middle-left
layout.addWidget(listw, 2, 0) # list widget goes in bottom-left
layout.addWidget(plot, 0, 1, 3, 1) # plot goes on right side, spanning 3 rows
## Display the widget as a new window
w.show()
## Start the Qt event loop
app.exec_()
More complex interfaces may be designed graphically using Qt Designer, which allows you to simply drag widgets into your window to define its appearance.
Naming Conventions
------------------
Virtually every class in pyqtgraph is an extension of base classes provided by Qt. When reading the documentation, remember that all of Qt's classes start with the letter 'Q', whereas pyqtgraph's classes do not. When reading through the methods for any class, it is often helpful to see which Qt base classes are used and look through the Qt documentation as well.
Most of Qt's classes define signals which can be difficult to tell apart from regular methods. Almost all signals explicity defined by pyqtgraph are named beginning with 'sig' to indicate that these signals are not defined at the Qt level.
In most cases, classes which end in 'Widget' are subclassed from QWidget and can therefore be used as a GUI element in a Qt window. Classes which end in 'Item' are subclasses of QGraphicsItem and can only be displayed within a QGraphicsView instance (such as GraphicsLayoutWidget or PlotWidget).
Signals, Slots, and Events
--------------------------
[ to be continued.. please post a request on the pyqtgraph forum if you'd like to read more ]
GraphicsView and GraphicsItems
------------------------------
Coordinate Systems
------------------
Coordinate Systems and Transformations
--------------------------------------
Mouse and Keyboard Input
......@@ -26,3 +82,7 @@ Mouse and Keyboard Input
QTimer, the Event Loop, and Multi-Threading
-------------------------------------------
Multi-threading vs Multi-processing in Qt
-----------------------------------------
ColorMapWidget
==============
.. autoclass:: pyqtgraph.ColorMapWidget
:members:
.. automethod:: pyqtgraph.ColorMapWidget.__init__
.. automethod:: pyqtgraph.widgets.ColorMapWidget.ColorMapParameter.setFields
.. automethod:: pyqtgraph.widgets.ColorMapWidget.ColorMapParameter.map
\ No newline at end of file
......@@ -17,6 +17,8 @@ Contents:
gradientwidget
histogramlutwidget
parametertree
colormapwidget
scatterplotwidget
graphicsview
rawimagewidget
datatreewidget
......
ScatterPlotWidget
=================
.. autoclass:: pyqtgraph.ScatterPlotWidget
:members:
.. automethod:: pyqtgraph.ScatterPlotWidget.__init__
# -*- coding: utf-8 -*-
"""
Demonstrates basic use of ErrorBarItem
"""
import initExample ## Add path to library (just for examples; you do not need this)
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
import numpy as np
import pyqtgraph as pg
import numpy as np
pg.setConfigOptions(antialias=True)
x = np.arange(10)
y = np.arange(10) %3
top = np.linspace(1.0, 3.0, 10)
bottom = np.linspace(2, 0.5, 10)
plt = pg.plot()
err = pg.ErrorBarItem(x=x, y=y, top=top, bottom=bottom, beam=0.5)
plt.addItem(err)
plt.plot(x, y, symbol='o', pen={'color': 0.8, 'width': 2})
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
......@@ -62,7 +62,7 @@ w.addItem(p3)
## Animated example
## compute surface vertex data
cols = 100
cols = 90
rows = 100
x = np.linspace(-8, 8, cols+1).reshape(cols+1,1)
y = np.linspace(-8, 8, rows+1).reshape(1,rows+1)
......
# -*- coding: utf-8 -*-
"""
Simple example of GridItem use.
"""
import initExample ## Add path to library (just for examples; you do not need this)
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
w = pg.GraphicsWindow()
v = w.addViewBox()
v.setAspectLocked()
g = pg.GraphItem()
v.addItem(g)
## Define positions of nodes
pos = np.array([
[0,0],
[10,0],
[0,10],
[10,10],
[5,5],
[15,5]
])
## Define the set of connections in the graph
adj = np.array([
[0,1],
[1,3],
[3,2],
[2,0],
[1,5],
[3,5],
])
## Define the symbol to use for each node (this is optional)
symbols = ['o','o','o','o','t','+']
## Define the line style for each connection (this is optional)
lines = np.array([
(255,0,0,255,1),
(255,0,255,255,2),
(255,0,255,255,3),
(255,255,0,255,2),
(255,0,0,255,1),
(255,255,255,255,4),
], dtype=[('red',np.ubyte),('green',np.ubyte),('blue',np.ubyte),('alpha',np.ubyte),('width',float)])
## Update the graph
g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
# -*- coding: utf-8 -*-
## This example demonstrates many of the 2D plotting capabilities
## in pyqtgraph. All of the plots may be panned/scaled by dragging with
## the left/right mouse buttons. Right click on any plot to show a context menu.
import initExample ## Add path to library (just for examples; you do not need this)
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
#QtGui.QApplication.setGraphicsSystem('raster')
app = QtGui.QApplication([])
#mw = QtGui.QMainWindow()
#mw.resize(800,800)
win = pg.GraphicsWindow(title="Basic plotting examples")
win.resize(1000,600)
p5 = win.addPlot(title="Scatter plot, axis labels, log scale")
x = np.random.normal(size=1000) * 1e-5
y = x*1000 + 0.005 * np.random.normal(size=1000)
y -= y.min()-1.0
mask = x > 1e-15
x = x[mask]
y = y[mask]
p5.plot(x, y, pen=None, symbol='t', symbolPen=None, symbolSize=10, symbolBrush=(100, 100, 255, 50))
p5.setLabel('left', "Y Axis", units='A')
p5.setLabel('bottom', "Y Axis", units='s')
p5.setLogMode(x=True, y=False)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
......@@ -59,7 +59,6 @@ pos = np.random.normal(size=(2,n), scale=1e-5)
spots = [{'pos': pos[:,i], 'data': 1, 'brush':pg.intColor(i, n), 'symbol': i%5, 'size': 5+i/10.} for i in range(n)]
s2.addPoints(spots)
w2.addItem(s2)
w2.setRange(s2.boundingRect())
s2.sigClicked.connect(clicked)
......@@ -71,7 +70,7 @@ s3 = pg.ScatterPlotItem(pxMode=False) ## Set pxMode=False to allow spots to tr
spots3 = []
for i in range(10):
for j in range(10):
spots3.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'brush':pg.intColor(i*10+j, 100)})
spots3.append({'pos': (1e-6*i, 1e-6*j), 'size': 1e-6, 'pen': {'color': 'w', 'width': 2}, 'brush':pg.intColor(i*10+j, 100)})
s3.addPoints(spots3)
w3.addItem(s3)
s3.sigClicked.connect(clicked)
......
import initExample ## Add path to library (just for examples; you do not need this)
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import numpy as np
pg.plot(np.random.normal(size=100000), title="Simplest possible plotting example")
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
pg.QtGui.QApplication.exec_()
......@@ -27,6 +27,8 @@ examples = OrderedDict([
('Scatter Plot', 'ScatterPlot.py'),
#('PlotItem', 'PlotItem.py'),
('IsocurveItem', 'isocurve.py'),
('GraphItem', 'GraphItem.py'),
('ErrorBarItem', 'ErrorBarItem.py'),
('ImageItem - video', 'ImageItem.py'),
('ImageItem - draw', 'Draw.py'),
('Region-of-Interest', 'ROIExamples.py'),
......
# -*- coding: utf-8 -*-
import initExample ## Add path to library (just for examples; you do not need this)
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
app = pg.mkQApp()
plt = pg.PlotWidget()
app.processEvents()
## Putting this at the beginning or end does not have much effect
plt.show()
## The auto-range is recomputed after each item is added,
## so disabling it before plotting helps
plt.enableAutoRange(False, False)
def plot():
start = pg.ptime.time()
n = 15
pts = 100
x = np.linspace(0, 0.8, pts)
y = np.random.random(size=pts)*0.8
for i in xrange(n):
for j in xrange(n):
## calling PlotWidget.plot() generates a PlotDataItem, which
## has a bit more overhead than PlotCurveItem, which is all
## we need here. This overhead adds up quickly and makes a big
## difference in speed.
#plt.plot(x=x+i, y=y+j)
plt.addItem(pg.PlotCurveItem(x=x+i, y=y+j))
#path = pg.arrayToQPath(x+i, y+j)
#item = QtGui.QGraphicsPathItem(path)
#item.setPen(pg.mkPen('w'))
#plt.addItem(item)
dt = pg.ptime.time() - start
print "Create plots took: %0.3fms" % (dt*1000)
## Plot and clear 5 times, printing the time it took
for i in range(5):
plt.clear()
plot()
app.processEvents()
plt.autoRange()
def fastPlot():
## Different approach: generate a single item with all data points.
## This runs about 20x faster.
start = pg.ptime.time()
n = 15
pts = 100
x = np.linspace(0, 0.8, pts)
y = np.random.random(size=pts)*0.8
xdata = np.empty((n, n, pts))
xdata[:] = x.reshape(1,1,pts) + np.arange(n).reshape(n,1,1)
ydata = np.empty((n, n, pts))
ydata[:] = y.reshape(1,1,pts) + np.arange(n).reshape(1,n,1)
conn = np.ones((n*n,pts))
conn[:,-1] = False # make sure plots are disconnected
path = pg.arrayToQPath(xdata.flatten(), ydata.flatten(), conn.flatten())
item = QtGui.QGraphicsPathItem(path)
item.setPen(pg.mkPen('w'))
plt.addItem(item)
dt = pg.ptime.time() - start
print "Create plots took: %0.3fms" % (dt*1000)
## Plot and clear 5 times, printing the time it took
if hasattr(pg, 'arrayToQPath'):
for i in range(5):
plt.clear()
fastPlot()
app.processEvents()
else:
print "Skipping fast tests--arrayToQPath function is missing."
plt.autoRange()
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
......@@ -8,32 +8,32 @@ import time
print "\n=================\nStart Process"
print("\n=================\nStart Process")
proc = mp.Process()
import os
print "parent:", os.getpid(), "child:", proc.proc.pid
print "started"
print("parent:", os.getpid(), "child:", proc.proc.pid)
print("started")
rnp = proc._import('numpy')
arr = rnp.array([1,2,3,4])
print repr(arr)
print str(arr)
print "return value:", repr(arr.mean(_returnType='value'))
print "return proxy:", repr(arr.mean(_returnType='proxy'))
print "return auto: ", repr(arr.mean(_returnType='auto'))
print(repr(arr))
print(str(arr))
print("return value:", repr(arr.mean(_returnType='value')))
print( "return proxy:", repr(arr.mean(_returnType='proxy')))
print( "return auto: ", repr(arr.mean(_returnType='auto')))
proc.join()
print "process finished"
print( "process finished")
print "\n=================\nStart ForkedProcess"
print( "\n=================\nStart ForkedProcess")
proc = mp.ForkedProcess()
rnp = proc._import('numpy')
arr = rnp.array([1,2,3,4])
print repr(arr)
print str(arr)
print repr(arr.mean())
print( repr(arr))
print( str(arr))
print( repr(arr.mean()))
proc.join()
print "process finished"
print( "process finished")
......@@ -42,10 +42,10 @@ import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
app = pg.QtGui.QApplication([])
print "\n=================\nStart QtProcess"
print( "\n=================\nStart QtProcess")
import sys
if (sys.flags.interactive != 1):
print " (not interactive; remote process will exit immediately.)"
print( " (not interactive; remote process will exit immediately.)")
proc = mp.QtProcess()
d1 = proc.transfer(np.random.normal(size=1000))
d2 = proc.transfer(np.random.normal(size=1000))
......
......@@ -5,7 +5,7 @@ import pyqtgraph.multiprocess as mp
import pyqtgraph as pg
import time
print "\n=================\nParallelize"
print( "\n=================\nParallelize")
## Do a simple task:
## for x in range(N):
......@@ -36,7 +36,7 @@ with pg.ProgressDialog('processing serially..', maximum=len(tasks)) as dlg:
dlg += 1
if dlg.wasCanceled():
raise Exception('processing canceled')
print "Serial time: %0.2f" % (time.time() - start)
print( "Serial time: %0.2f" % (time.time() - start))
### Use parallelize, but force a single worker
### (this simulates the behavior seen on windows, which lacks os.fork)
......@@ -47,8 +47,8 @@ with mp.Parallelize(enumerate(tasks), results=results2, workers=1, progressDialo
for j in xrange(size):
tot += j * x
tasker.results[i] = tot
print "\nParallel time, 1 worker: %0.2f" % (time.time() - start)
print "Results match serial: ", results2 == results
print( "\nParallel time, 1 worker: %0.2f" % (time.time() - start))
print( "Results match serial: %s" % str(results2 == results))
### Use parallelize with multiple workers
start = time.time()
......@@ -58,6 +58,6 @@ with mp.Parallelize(enumerate(tasks), results=results3, progressDialog='processi
for j in xrange(size):
tot += j * x
tasker.results[i] = tot
print "\nParallel time, %d workers: %0.2f" % (mp.Parallelize.suggestedWorkerCount(), time.time() - start)
print "Results match serial: ", results3 == results
print( "\nParallel time, %d workers: %0.2f" % (mp.Parallelize.suggestedWorkerCount(), time.time() - start))
print( "Results match serial: %s" % str(results3 == results))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment