__init__.py 16.2 KB
Newer Older
Luke Campagnola's avatar
Luke Campagnola committed
1
# -*- coding: utf-8 -*-
2 3 4 5 6
"""
PyQtGraph - Scientific Graphics and GUI Library for Python
www.pyqtgraph.org
"""

Luke Campagnola's avatar
Luke Campagnola committed
7
__version__ = '0.9.10'
8

Luke Campagnola's avatar
Luke Campagnola committed
9 10
### import all the goodies and add some helper functions for easy CLI use

11 12
## 'Qt' is a local module; it is intended mainly to cover up the differences
## between PyQt4 and PySide.
13
from .Qt import QtGui
14

15
## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)
16 17 18
#if QtGui.QApplication.instance() is None:
    #app = QtGui.QApplication([])

19 20 21
import numpy  ## pyqtgraph requires numpy
              ## (import here to avoid massive error dump later on if numpy is not available)

22
import os, sys
23 24

## check python version
Luke Campagnola's avatar
Luke Campagnola committed
25
## Allow anything >= 2.7
26 27
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]))
28

29 30 31
## helpers for 2/3 compatibility
from . import python2_3

Luke Campagnola's avatar
Luke Campagnola committed
32
## install workarounds for numpy bugs
33
from . import numpy_fix
Luke Campagnola's avatar
Luke Campagnola committed
34

35
## in general openGL is poorly supported with Qt+GraphicsView.
36 37
## we only enable it where the performance benefit is critical.
## Note this only applies to 2D graphics; 3D graphics always use OpenGL.
Luke Campagnola's avatar
Luke Campagnola committed
38 39
if 'linux' in sys.platform:  ## linux has numerous bugs in opengl implementation
    useOpenGL = False
40
elif 'darwin' in sys.platform: ## openGL can have a major impact on mac, but also has serious bugs
41 42 43 44
    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).')
    QtGui.QApplication.setGraphicsSystem('raster')  ## work around a variety of bugs in the native graphics system 
Luke Campagnola's avatar
Luke Campagnola committed
45
else:
Luke Campagnola's avatar
Luke Campagnola committed
46
    useOpenGL = False  ## on windows there's a more even performance / bugginess tradeoff. 
Luke Campagnola's avatar
Luke Campagnola committed
47
                
48
CONFIG_OPTIONS = {
49
    'useOpenGL': useOpenGL, ## by default, this is platform-dependent (see widgets/GraphicsView). Set to True or False to explicitly enable/disable opengl.
50
    'leftButtonPan': True,  ## if false, left button drags a rubber band for zooming in viewbox
51 52
    'foreground': 'd',  ## default foreground color for axes, labels, etc.
    'background': 'k',        ## default background for GraphicsWidget
53
    'antialias': False,
54
    'editorCommand': None,  ## command used to invoke code editor from ConsoleWidgets
Luke Campagnola's avatar
Luke Campagnola committed
55
    'useWeave': False,       ## Use weave to speed up some operations, if it is available
Luke Campagnola's avatar
Luke Campagnola committed
56
    'weaveDebug': False,    ## Print full error message if weave compile fails
Luke Campagnola's avatar
Luke Campagnola committed
57
    'exitCleanup': True,    ## Attempt to work around some exit crash bugs in PyQt and PySide
58
    'enableExperimental': False, ## Enable experimental features (the curious can search for this key in the code)
Luke Campagnola's avatar
Luke Campagnola committed
59
    'crashWarning': False,  # If True, print warnings about situations that may result in a crash
60
} 
61

62

63 64 65
def setConfigOption(opt, value):
    CONFIG_OPTIONS[opt] = value

Luke Campagnola's avatar
Luke Campagnola committed
66
def setConfigOptions(**opts):
67 68
    CONFIG_OPTIONS.update(opts)

69 70 71
def getConfigOption(opt):
    return CONFIG_OPTIONS[opt]

72

73
def systemInfo():
74 75
    print("sys.platform: %s" % sys.platform)
    print("sys.version: %s" % sys.version)
76
    from .Qt import VERSION_INFO
77
    print("qt bindings: %s" % VERSION_INFO)
78
    
79 80 81 82
    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')
83
        if os.path.exists(lastRevFile):
84
            rev = open(lastRevFile, 'r').read().strip()
85
    
86
    print("pyqtgraph: %s; %s" % (__version__, rev))
87
    print("config:")
88 89 90
    import pprint
    pprint.pprint(CONFIG_OPTIONS)

91
## Rename orphaned .pyc files. This is *probably* safe :)
92 93
## We only do this if __version__ is None, indicating the code was probably pulled
## from the repository. 
94 95 96 97
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. 
98 99 100 101

    ### 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."
102 103 104 105
    
    printed = False
    startDir = os.path.abspath(startDir)
    for path, dirs, files in os.walk(startDir):
106 107
        if '__pycache__' in path:
            continue
108 109 110 111 112 113
        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:
114
                    print("NOTE: Renaming orphaned .pyc files:")
115 116 117 118 119 120 121
                    printed = True
                n = 1
                while True:
                    name2 = fileName + ".renamed%d" % n
                    if not os.path.exists(name2):
                        break
                    n += 1
122 123
                print("  " + fileName + "  ==>")
                print("  " + name2)
124 125 126
                os.rename(fileName, name2)
                
path = os.path.split(__file__)[0]
127
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.
128
    renamePyc(path)
129 130


131 132 133
## 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.
134 135 136
#from . import frozenSupport
#def importModules(path, globals, locals, excludes=()):
    #"""Import all modules residing within *path*, return a dict of name: module pairs.
137
    
138 139 140 141 142 143 144 145 146 147 148
    #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])
149
        
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    #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)
166
            
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
    #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 *
247

248 249 250
from .imageview import *
from .WidgetGroup import *
from .Point import Point
Luke Campagnola's avatar
Luke Campagnola committed
251
from .Vector import Vector
252
from .SRTTransform import SRTTransform
253
from .Transform3D import Transform3D
254
from .SRTTransform3D import SRTTransform3D
255 256 257
from .functions import *
from .graphicsWindows import *
from .SignalProxy import *
258
from .colormap import *
259
from .ptime import time
Luke Campagnola's avatar
Luke Campagnola committed
260
from .Qt import isQObjectAlive
261

262

Luke Campagnola's avatar
Luke Campagnola committed
263 264 265 266 267 268 269
##############################################################
## 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
270

Luke Campagnola's avatar
Luke Campagnola committed
271
## Attempts to work around exit crashes:
272
import atexit
273
_cleanupCalled = False
274
def cleanup():
275 276 277 278
    global _cleanupCalled
    if _cleanupCalled:
        return
    
Luke Campagnola's avatar
Luke Campagnola committed
279 280 281
    if not getConfigOption('exitCleanup'):
        return
    
Luke Campagnola's avatar
Luke Campagnola committed
282 283 284 285 286 287
    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..
288 289 290 291 292 293
    if QtGui.QApplication.instance() is None:
        return
    import gc
    s = QtGui.QGraphicsScene()
    for o in gc.get_objects():
        try:
294
            if isinstance(o, QtGui.QGraphicsItem) and isQObjectAlive(o) and o.scene() is None:
Luke Campagnola's avatar
Luke Campagnola committed
295 296 297 298
                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,))
299
                
300 301 302
                s.addItem(o)
        except RuntimeError:  ## occurs if a python wrapper no longer has its underlying C++ object
            continue
303 304
    _cleanupCalled = True

305
atexit.register(cleanup)
306

307 308 309 310 311 312 313 314 315 316 317 318
# 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

319

Luke Campagnola's avatar
Luke Campagnola committed
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
## 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
347 348 349 350 351 352 353
    if sys.platform == 'darwin':
        for fd in xrange(3, 4096):
            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..

Luke Campagnola's avatar
Luke Campagnola committed
354
    os._exit(0)
Luke Campagnola's avatar
Luke Campagnola committed
355 356
    

357

358
## Convenience functions for command-line use
Luke Campagnola's avatar
Luke Campagnola committed
359 360 361 362 363 364

plots = []
images = []
QAPP = None

def plot(*args, **kargs):
365
    """
Luke Campagnola's avatar
Luke Campagnola committed
366 367 368 369
    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>`)
370
    """
Luke Campagnola's avatar
Luke Campagnola committed
371
    mkQApp()
372 373 374 375 376 377 378
    #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)
Luke Campagnola's avatar
Luke Campagnola committed
379
        
380
    pwArgList = ['title', 'labels', 'name', 'left', 'right', 'top', 'bottom', 'background']
Luke Campagnola's avatar
Luke Campagnola committed
381 382 383 384 385 386 387 388 389
    pwArgs = {}
    dataArgs = {}
    for k in kargs:
        if k in pwArgList:
            pwArgs[k] = kargs[k]
        else:
            dataArgs[k] = kargs[k]
        
    w = PlotWindow(**pwArgs)
390 391
    if len(args) > 0 or len(dataArgs) > 0:
        w.plot(*args, **dataArgs)
Luke Campagnola's avatar
Luke Campagnola committed
392 393 394 395
    plots.append(w)
    w.show()
    return w
    
396 397
def image(*args, **kargs):
    """
Luke Campagnola's avatar
Luke Campagnola committed
398 399 400 401 402
    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 2D or 3D 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>`)
403
    """
Luke Campagnola's avatar
Luke Campagnola committed
404 405 406 407 408
    mkQApp()
    w = ImageWindow(*args, **kargs)
    images.append(w)
    w.show()
    return w
409
show = image  ## for backward compatibility
Luke Campagnola's avatar
Luke Campagnola committed
410

Luke Campagnola's avatar
Luke Campagnola committed
411
def dbg(*args, **kwds):
Luke Campagnola's avatar
Luke Campagnola committed
412 413
    """
    Create a console window and begin watching for exceptions.
Luke Campagnola's avatar
Luke Campagnola committed
414 415
    
    All arguments are passed to :func:`ConsoleWidget.__init__() <pyqtgraph.console.ConsoleWidget.__init__>`.
Luke Campagnola's avatar
Luke Campagnola committed
416 417
    """
    mkQApp()
Luke Campagnola's avatar
Luke Campagnola committed
418
    from . import console
Luke Campagnola's avatar
Luke Campagnola committed
419
    c = console.ConsoleWidget(*args, **kwds)
Luke Campagnola's avatar
Luke Campagnola committed
420 421 422 423 424 425 426
    c.catchAllExceptions()
    c.show()
    global consoles
    try:
        consoles.append(c)
    except NameError:
        consoles = [c]
427
    return c
428
    
Luke Campagnola's avatar
Luke Campagnola committed
429 430
    
def mkQApp():
431
    global QAPP
Luke Campagnola's avatar
Luke Campagnola committed
432 433
    inst = QtGui.QApplication.instance()
    if inst is None:
434
        QAPP = QtGui.QApplication([])
Luke Campagnola's avatar
Luke Campagnola committed
435 436
    else:
        QAPP = inst
437
    return QAPP
438