2011-02-08 00:40:38 +00:00
# -*- coding: utf-8 -*-
2012-12-27 06:52:32 +00:00
"""
PyQtGraph - Scientific Graphics and GUI Library for Python
www . pyqtgraph . org
"""
2016-11-05 05:47:56 +00:00
__version__ = ' 0.10.0 '
2012-06-29 18:39:27 +00:00
2011-02-08 00:40:38 +00:00
### import all the goodies and add some helper functions for easy CLI use
2012-03-02 02:55:32 +00:00
## 'Qt' is a local module; it is intended mainly to cover up the differences
## between PyQt4 and PySide.
2012-06-29 18:39:27 +00:00
from . Qt import QtGui
2012-03-02 02:55:32 +00:00
2012-05-11 22:05:41 +00:00
## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)
2012-03-02 02:55:32 +00:00
#if QtGui.QApplication.instance() is None:
#app = QtGui.QApplication([])
2013-01-12 19:39:23 +00:00
import numpy ## pyqtgraph requires numpy
## (import here to avoid massive error dump later on if numpy is not available)
2012-06-29 18:53:32 +00:00
import os , sys
2012-04-04 18:45:12 +00:00
## check python version
2012-07-12 19:35:58 +00:00
## Allow anything >= 2.7
2012-09-13 14:12:59 +00:00
if sys . version_info [ 0 ] < 2 or ( sys . version_info [ 0 ] == 2 and sys . version_info [ 1 ] < 6 ) :
raise Exception ( " Pyqtgraph requires Python version 2.6 or greater (this is %d . %d ) " % ( sys . version_info [ 0 ] , sys . version_info [ 1 ] ) )
2012-04-04 18:45:12 +00:00
2012-05-11 22:05:41 +00:00
## helpers for 2/3 compatibility
from . import python2_3
2012-10-03 01:23:59 +00:00
## install workarounds for numpy bugs
2012-12-05 05:25:45 +00:00
from . import numpy_fix
2012-10-03 01:23:59 +00:00
2012-05-31 20:22:50 +00:00
## in general openGL is poorly supported with Qt+GraphicsView.
2012-05-11 22:05:41 +00:00
## we only enable it where the performance benefit is critical.
## Note this only applies to 2D graphics; 3D graphics always use OpenGL.
2012-03-27 16:30:51 +00:00
if ' linux ' in sys . platform : ## linux has numerous bugs in opengl implementation
useOpenGL = False
2012-05-31 20:22:50 +00:00
elif ' darwin ' in sys . platform : ## openGL can have a major impact on mac, but also has serious bugs
2012-12-23 02:49:40 +00:00
useOpenGL = False
if QtGui . QApplication . instance ( ) is not None :
print ( ' Warning: QApplication was created before pyqtgraph was imported; there may be problems (to avoid bugs, call QApplication.setGraphicsSystem( " raster " ) before the QApplication is created). ' )
2014-02-14 09:29:33 +00:00
if QtGui . QApplication . setGraphicsSystem :
QtGui . QApplication . setGraphicsSystem ( ' raster ' ) ## work around a variety of bugs in the native graphics system
2012-03-27 16:30:51 +00:00
else :
2012-04-04 13:29:35 +00:00
useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff.
2012-03-27 16:30:51 +00:00
2012-03-02 02:55:32 +00:00
CONFIG_OPTIONS = {
2012-06-29 18:39:27 +00:00
' useOpenGL ' : useOpenGL , ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
2012-05-11 22:05:41 +00:00
' leftButtonPan ' : True , ## if false, left button drags a rubber band for zooming in viewbox
2015-07-12 21:43:05 +00:00
# foreground/background take any arguments to the 'mkColor' in /pyqtgraph/functions.py
2013-09-20 07:46:10 +00:00
' foreground ' : ' d ' , ## default foreground color for axes, labels, etc.
' background ' : ' k ' , ## default background for GraphicsWidget
2012-05-11 22:05:41 +00:00
' antialias ' : False ,
2012-06-29 18:39:27 +00:00
' editorCommand ' : None , ## command used to invoke code editor from ConsoleWidgets
2014-03-24 12:27:52 +00:00
' useWeave ' : False , ## Use weave to speed up some operations, if it is available
2013-01-12 19:31:49 +00:00
' weaveDebug ' : False , ## Print full error message if weave compile fails
2013-03-07 20:29:56 +00:00
' exitCleanup ' : True , ## Attempt to work around some exit crash bugs in PyQt and PySide
2013-03-05 00:43:51 +00:00
' enableExperimental ' : False , ## Enable experimental features (the curious can search for this key in the code)
2014-03-24 19:47:32 +00:00
' crashWarning ' : False , # If True, print warnings about situations that may result in a crash
2016-06-16 15:54:52 +00:00
' imageAxisOrder ' : ' col-major ' , # For 'row-major', image data is expected in the standard (row, col) order.
# For 'col-major', image data is expected in reversed (col, row) order.
# The default is 'col-major' for backward compatibility, but this may
2016-04-28 05:33:51 +00:00
# change in the future.
2012-05-11 22:05:41 +00:00
}
2012-03-02 02:55:32 +00:00
2012-06-29 18:39:27 +00:00
2012-03-02 02:55:32 +00:00
def setConfigOption ( opt , value ) :
2016-08-31 22:15:03 +00:00
global CONFIG_OPTIONS
if opt not in CONFIG_OPTIONS :
raise KeyError ( ' Unknown configuration option " %s " ' % opt )
if opt == ' imageAxisOrder ' and value not in ( ' row-major ' , ' col-major ' ) :
raise ValueError ( ' imageAxisOrder must be either " row-major " or " col-major " ' )
2012-03-02 02:55:32 +00:00
CONFIG_OPTIONS [ opt ] = value
2012-12-26 03:20:31 +00:00
def setConfigOptions ( * * opts ) :
2016-04-28 05:33:51 +00:00
""" Set global configuration options.
Each keyword argument sets one global option .
"""
2016-08-31 22:15:03 +00:00
for k , v in opts . items ( ) :
setConfigOption ( k , v )
2012-12-25 05:43:31 +00:00
2012-03-02 02:55:32 +00:00
def getConfigOption ( opt ) :
2016-04-28 05:33:51 +00:00
""" Return the value of a single global configuration option.
"""
2012-03-02 02:55:32 +00:00
return CONFIG_OPTIONS [ opt ]
2012-03-11 15:59:45 +00:00
2012-06-29 18:39:27 +00:00
def systemInfo ( ) :
2012-07-09 12:38:30 +00:00
print ( " sys.platform: %s " % sys . platform )
print ( " sys.version: %s " % sys . version )
2012-06-29 18:39:27 +00:00
from . Qt import VERSION_INFO
2012-07-09 12:38:30 +00:00
print ( " qt bindings: %s " % VERSION_INFO )
2012-06-29 18:53:32 +00:00
2012-12-27 06:52:32 +00:00
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 ' )
2012-06-29 18:53:32 +00:00
if os . path . exists ( lastRevFile ) :
2012-12-27 06:52:32 +00:00
rev = open ( lastRevFile , ' r ' ) . read ( ) . strip ( )
2012-06-29 18:53:32 +00:00
2012-12-27 06:52:32 +00:00
print ( " pyqtgraph: %s ; %s " % ( __version__ , rev ) )
2012-07-09 12:38:30 +00:00
print ( " config: " )
2012-06-29 18:39:27 +00:00
import pprint
pprint . pprint ( CONFIG_OPTIONS )
2012-03-11 15:59:45 +00:00
## Rename orphaned .pyc files. This is *probably* safe :)
2012-12-27 06:52:32 +00:00
## We only do this if __version__ is None, indicating the code was probably pulled
## from the repository.
2012-03-11 15:59:45 +00:00
def renamePyc ( startDir ) :
### Used to rename orphaned .pyc files
### When a python file changes its location in the repository, usually the .pyc file
### is left behind, possibly causing mysterious and difficult to track bugs.
2012-09-09 23:07:36 +00:00
### Note that this is no longer necessary for python 3.2; from PEP 3147:
### "If the py source file is missing, the pyc file inside __pycache__ will be ignored.
### This eliminates the problem of accidental stale pyc file imports."
2012-03-11 15:59:45 +00:00
printed = False
startDir = os . path . abspath ( startDir )
for path , dirs , files in os . walk ( startDir ) :
2012-05-11 22:05:41 +00:00
if ' __pycache__ ' in path :
continue
2012-03-11 15:59:45 +00:00
for f in files :
fileName = os . path . join ( path , f )
base , ext = os . path . splitext ( fileName )
py = base + " .py "
if ext == ' .pyc ' and not os . path . isfile ( py ) :
if not printed :
2012-05-11 22:05:41 +00:00
print ( " NOTE: Renaming orphaned .pyc files: " )
2012-03-11 15:59:45 +00:00
printed = True
n = 1
while True :
name2 = fileName + " .renamed %d " % n
if not os . path . exists ( name2 ) :
break
n + = 1
2012-05-11 22:05:41 +00:00
print ( " " + fileName + " ==> " )
print ( " " + name2 )
2012-03-11 15:59:45 +00:00
os . rename ( fileName , name2 )
path = os . path . split ( __file__ ) [ 0 ]
2012-12-27 06:52:32 +00:00
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.
2012-10-11 04:57:24 +00:00
renamePyc ( path )
2012-03-11 15:59:45 +00:00
2012-03-02 02:55:32 +00:00
## Import almost everything to make it available from a single namespace
## don't import the more complex systems--canvas, parametertree, flowchart, dockarea
## these must be imported separately.
2013-11-23 00:52:40 +00:00
#from . import frozenSupport
#def importModules(path, globals, locals, excludes=()):
#"""Import all modules residing within *path*, return a dict of name: module pairs.
2012-10-11 04:57:24 +00:00
2013-11-23 00:52:40 +00:00
#Note that *path* MUST be relative to the module doing the import.
#"""
#d = os.path.join(os.path.split(globals['__file__'])[0], path)
#files = set()
#for f in frozenSupport.listdir(d):
#if frozenSupport.isdir(os.path.join(d, f)) and f not in ['__pycache__', 'tests']:
#files.add(f)
#elif f[-3:] == '.py' and f != '__init__.py':
#files.add(f[:-3])
#elif f[-4:] == '.pyc' and f != '__init__.pyc':
#files.add(f[:-4])
2012-03-02 02:55:32 +00:00
2013-11-23 00:52:40 +00:00
#mods = {}
#path = path.replace(os.sep, '.')
#for modName in files:
#if modName in excludes:
#continue
#try:
#if len(path) > 0:
#modName = path + '.' + modName
#print( "from .%s import * " % modName)
#mod = __import__(modName, globals, locals, ['*'], 1)
#mods[modName] = mod
#except:
#import traceback
#traceback.print_stack()
#sys.excepthook(*sys.exc_info())
#print("[Error importing module: %s]" % modName)
2012-10-11 04:57:24 +00:00
2013-11-23 00:52:40 +00:00
#return mods
#def importAll(path, globals, locals, excludes=()):
#"""Given a list of modules, import all names from each module into the global namespace."""
#mods = importModules(path, globals, locals, excludes)
#for mod in mods.values():
#if hasattr(mod, '__all__'):
#names = mod.__all__
#else:
#names = [n for n in dir(mod) if n[0] != '_']
#for k in names:
#if hasattr(mod, k):
#globals[k] = getattr(mod, k)
# Dynamic imports are disabled. This causes too many problems.
#importAll('graphicsItems', globals(), locals())
#importAll('widgets', globals(), locals(),
#excludes=['MatplotlibWidget', 'RawImageWidget', 'RemoteGraphicsView'])
from . graphicsItems . VTickGroup import *
from . graphicsItems . GraphicsWidget import *
from . graphicsItems . ScaleBar import *
from . graphicsItems . PlotDataItem import *
from . graphicsItems . GraphItem import *
from . graphicsItems . TextItem import *
from . graphicsItems . GraphicsLayout import *
from . graphicsItems . UIGraphicsItem import *
from . graphicsItems . GraphicsObject import *
from . graphicsItems . PlotItem import *
from . graphicsItems . ROI import *
from . graphicsItems . InfiniteLine import *
from . graphicsItems . HistogramLUTItem import *
from . graphicsItems . GridItem import *
from . graphicsItems . GradientLegend import *
from . graphicsItems . GraphicsItem import *
from . graphicsItems . BarGraphItem import *
from . graphicsItems . ViewBox import *
from . graphicsItems . ArrowItem import *
from . graphicsItems . ImageItem import *
from . graphicsItems . AxisItem import *
from . graphicsItems . LabelItem import *
from . graphicsItems . CurvePoint import *
from . graphicsItems . GraphicsWidgetAnchor import *
from . graphicsItems . PlotCurveItem import *
from . graphicsItems . ButtonItem import *
from . graphicsItems . GradientEditorItem import *
from . graphicsItems . MultiPlotItem import *
from . graphicsItems . ErrorBarItem import *
from . graphicsItems . IsocurveItem import *
from . graphicsItems . LinearRegionItem import *
from . graphicsItems . FillBetweenItem import *
from . graphicsItems . LegendItem import *
from . graphicsItems . ScatterPlotItem import *
from . graphicsItems . ItemGroup import *
from . widgets . MultiPlotWidget import *
from . widgets . ScatterPlotWidget import *
from . widgets . ColorMapWidget import *
from . widgets . FileDialog import *
from . widgets . ValueLabel import *
from . widgets . HistogramLUTWidget import *
from . widgets . CheckTable import *
from . widgets . BusyCursor import *
from . widgets . PlotWidget import *
from . widgets . ComboBox import *
from . widgets . GradientWidget import *
from . widgets . DataFilterWidget import *
from . widgets . SpinBox import *
from . widgets . JoystickButton import *
from . widgets . GraphicsLayoutWidget import *
from . widgets . TreeWidget import *
from . widgets . PathButton import *
from . widgets . VerticalLabel import *
from . widgets . FeedbackButton import *
from . widgets . ColorButton import *
from . widgets . DataTreeWidget import *
from . widgets . GraphicsView import *
from . widgets . LayoutWidget import *
from . widgets . TableWidget import *
from . widgets . ProgressDialog import *
2012-03-02 02:55:32 +00:00
2012-05-11 22:05:41 +00:00
from . imageview import *
from . WidgetGroup import *
from . Point import Point
2012-05-30 03:18:34 +00:00
from . Vector import Vector
2012-05-31 20:22:50 +00:00
from . SRTTransform import SRTTransform
2012-10-27 01:47:45 +00:00
from . Transform3D import Transform3D
2012-05-31 20:22:50 +00:00
from . SRTTransform3D import SRTTransform3D
2012-05-11 22:05:41 +00:00
from . functions import *
from . graphicsWindows import *
from . SignalProxy import *
2013-02-10 22:45:16 +00:00
from . colormap import *
2012-05-11 22:05:41 +00:00
from . ptime import time
2014-04-15 19:49:17 +00:00
from . Qt import isQObjectAlive
2012-03-02 02:55:32 +00:00
2013-11-23 00:52:40 +00:00
2013-03-07 20:29:56 +00:00
##############################################################
## PyQt and PySide both are prone to crashing on exit.
## There are two general approaches to dealing with this:
## 1. Install atexit handlers that assist in tearing down to avoid crashes.
## This helps, but is never perfect.
## 2. Terminate the process before python starts tearing down
## This is potentially dangerous
2012-03-02 02:55:32 +00:00
2013-03-07 20:29:56 +00:00
## Attempts to work around exit crashes:
2012-06-18 18:01:51 +00:00
import atexit
2014-11-27 02:25:17 +00:00
_cleanupCalled = False
2012-06-18 18:01:51 +00:00
def cleanup ( ) :
2014-11-27 02:25:17 +00:00
global _cleanupCalled
if _cleanupCalled :
return
2013-03-07 20:29:56 +00:00
if not getConfigOption ( ' exitCleanup ' ) :
return
2012-12-05 02:02:05 +00:00
ViewBox . quit ( ) ## tell ViewBox that it doesn't need to deregister views anymore.
## Workaround for Qt exit crash:
## ALL QGraphicsItems must have a scene before they are deleted.
## This is potentially very expensive, but preferred over crashing.
## Note: this appears to be fixed in PySide as of 2012.12, but it should be left in for a while longer..
2016-11-28 14:41:56 +00:00
app = QtGui . QApplication . instance ( )
if app is None or not isinstance ( app , QtGui . QApplication ) :
# app was never constructed is already deleted or is an
# QCoreApplication/QGuiApplication and not a full QApplication
2012-06-18 18:01:51 +00:00
return
import gc
s = QtGui . QGraphicsScene ( )
for o in gc . get_objects ( ) :
try :
2014-03-13 20:38:50 +00:00
if isinstance ( o , QtGui . QGraphicsItem ) and isQObjectAlive ( o ) and o . scene ( ) is None :
2014-03-24 19:47:32 +00:00
if getConfigOption ( ' crashWarning ' ) :
sys . stderr . write ( ' Error: graphics item without scene. '
' Make sure ViewBox.close() and GraphicsView.close() '
' are properly called before app shutdown ( %s ) \n ' % ( o , ) )
2014-03-13 20:38:50 +00:00
2012-06-18 18:01:51 +00:00
s . addItem ( o )
except RuntimeError : ## occurs if a python wrapper no longer has its underlying C++ object
continue
2014-11-27 02:25:17 +00:00
_cleanupCalled = True
2012-06-18 18:01:51 +00:00
atexit . register ( cleanup )
2012-03-02 02:55:32 +00:00
2014-11-27 02:25:17 +00:00
# Call cleanup when QApplication quits. This is necessary because sometimes
# the QApplication will quit before the atexit callbacks are invoked.
# Note: cannot connect this function until QApplication has been created, so
# instead we have GraphicsView.__init__ call this for us.
_cleanupConnected = False
def _connectCleanup ( ) :
global _cleanupConnected
if _cleanupConnected :
return
QtGui . QApplication . instance ( ) . aboutToQuit . connect ( cleanup )
_cleanupConnected = True
2012-03-02 02:55:32 +00:00
2013-03-07 20:29:56 +00:00
## Optional function for exiting immediately (with some manual teardown)
def exit ( ) :
"""
Causes python to exit without garbage - collecting any objects , and thus avoids
calling object destructor methods . This is a sledgehammer workaround for
a variety of bugs in PyQt and Pyside that cause crashes on exit .
This function does the following in an attempt to ' safely ' terminate
the process :
* Invoke atexit callbacks
* Close all open file handles
* os . _exit ( )
Note : there is some potential for causing damage with this function if you
are using objects that _require_ their destructors to be called ( for example ,
to properly terminate log files , disconnect from devices , etc ) . Situations
like this are probably quite rare , but use at your own risk .
"""
## first disable our own cleanup function; won't be needing it.
setConfigOptions ( exitCleanup = False )
## invoke atexit callbacks
atexit . _run_exitfuncs ( )
## close file handles
2014-08-07 13:03:26 +00:00
if sys . platform == ' darwin ' :
2015-05-19 13:29:55 +00:00
for fd in range ( 3 , 4096 ) :
2014-08-07 13:03:26 +00:00
if fd not in [ 7 ] : # trying to close 7 produces an illegal instruction on the Mac.
os . close ( fd )
else :
os . closerange ( 3 , 4096 ) ## just guessing on the maximum descriptor count..
2013-07-10 04:02:16 +00:00
os . _exit ( 0 )
2013-03-07 20:29:56 +00:00
2012-03-02 02:55:32 +00:00
2012-06-18 18:01:51 +00:00
## Convenience functions for command-line use
2011-02-08 00:40:38 +00:00
plots = [ ]
images = [ ]
QAPP = None
def plot ( * args , * * kargs ) :
2012-03-02 02:55:32 +00:00
"""
2012-04-18 04:02:15 +00:00
Create and return a : class : ` PlotWindow < pyqtgraph . PlotWindow > `
( this is just a window with : class : ` PlotWidget < pyqtgraph . PlotWidget > ` inside ) , plot data in it .
Accepts a * title * argument to set the title of the window .
All other arguments are used to plot data . ( see : func : ` PlotItem . plot ( ) < pyqtgraph . PlotItem . plot > ` )
2012-03-02 02:55:32 +00:00
"""
2011-02-08 00:40:38 +00:00
mkQApp ( )
2012-03-23 06:45:11 +00:00
#if 'title' in kargs:
#w = PlotWindow(title=kargs['title'])
#del kargs['title']
#else:
#w = PlotWindow()
#if len(args)+len(kargs) > 0:
#w.plot(*args, **kargs)
2012-03-23 07:21:04 +00:00
2013-08-24 04:27:09 +00:00
pwArgList = [ ' title ' , ' labels ' , ' name ' , ' left ' , ' right ' , ' top ' , ' bottom ' , ' background ' ]
2012-03-23 07:21:04 +00:00
pwArgs = { }
dataArgs = { }
for k in kargs :
if k in pwArgList :
pwArgs [ k ] = kargs [ k ]
else :
dataArgs [ k ] = kargs [ k ]
w = PlotWindow ( * * pwArgs )
2013-12-19 17:30:00 +00:00
if len ( args ) > 0 or len ( dataArgs ) > 0 :
w . plot ( * args , * * dataArgs )
2011-02-08 00:40:38 +00:00
plots . append ( w )
w . show ( )
return w
2012-03-02 02:55:32 +00:00
def image ( * args , * * kargs ) :
"""
2012-04-18 04:02:15 +00:00
Create and return an : class : ` ImageWindow < pyqtgraph . ImageWindow > `
( this is just a window with : class : ` ImageView < pyqtgraph . ImageView > ` widget inside ) , show image data inside .
Will show 2 D or 3 D image data .
Accepts a * title * argument to set the title of the window .
All other arguments are used to show data . ( see : func : ` ImageView . setImage ( ) < pyqtgraph . ImageView . setImage > ` )
2012-03-02 02:55:32 +00:00
"""
2011-02-08 00:40:38 +00:00
mkQApp ( )
w = ImageWindow ( * args , * * kargs )
images . append ( w )
w . show ( )
return w
2012-03-02 02:55:32 +00:00
show = image ## for backward compatibility
2012-10-03 01:23:59 +00:00
2013-11-07 04:14:27 +00:00
def dbg ( * args , * * kwds ) :
2012-10-03 01:23:59 +00:00
"""
Create a console window and begin watching for exceptions .
2013-11-07 04:14:27 +00:00
All arguments are passed to : func : ` ConsoleWidget . __init__ ( ) < pyqtgraph . console . ConsoleWidget . __init__ > ` .
2012-10-03 01:23:59 +00:00
"""
mkQApp ( )
2013-09-10 04:31:43 +00:00
from . import console
2013-11-07 04:14:27 +00:00
c = console . ConsoleWidget ( * args , * * kwds )
2012-10-03 01:23:59 +00:00
c . catchAllExceptions ( )
c . show ( )
global consoles
try :
consoles . append ( c )
except NameError :
consoles = [ c ]
2014-02-18 01:02:42 +00:00
return c
2012-03-02 02:55:32 +00:00
2011-02-08 00:40:38 +00:00
def mkQApp ( ) :
2012-06-18 18:01:51 +00:00
global QAPP
2012-06-18 23:40:15 +00:00
inst = QtGui . QApplication . instance ( )
if inst is None :
2012-03-11 15:59:45 +00:00
QAPP = QtGui . QApplication ( [ ] )
2012-06-18 23:40:15 +00:00
else :
QAPP = inst
2012-06-18 18:01:51 +00:00
return QAPP
2012-03-11 15:59:45 +00:00