Aluode/PerceptionLabPortable
0
1"""2PyQtGraph - Scientific Graphics and GUI Library for Python3www.pyqtgraph.org4"""5 6__version__ = '0.14.0'7 8### import all the goodies and add some helper functions for easy CLI use9 10import importlib11import os12import sys13 14import numpy # # pyqtgraph requires numpy15 16## 'Qt' is a local module; it is intended mainly to cover up the differences17## between PyQt and PySide.18from .colors import palette19from .Qt import QtCore, QtGui, QtWidgets20from .Qt import exec_ as exec21from .Qt import mkQApp22 23## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)24#if QtWidgets.QApplication.instance() is None:25 #app = QtWidgets.QApplication([])26 27 ## (import here to avoid massive error dump later on if numpy is not available)28 29 30CONFIG_OPTIONS = {31 'useOpenGL': False, ## Set to True or False to explicitly enable/disable opengl.32 'leftButtonPan': True, ## if false, left button drags a rubber band for zooming in viewbox33 # foreground/background take any arguments to the 'mkColor' in /pyqtgraph/functions.py34 'foreground': 'd', ## default foreground color for axes, labels, etc.35 'background': 'k', ## default background for GraphicsWidget36 'antialias': False,37 'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets38 'exitCleanup': True, ## Attempt to work around some exit crash bugs in PyQt and PySide39 'enableExperimental': False, ## Enable experimental features (the curious can search for this key in the code)40 'crashWarning': False, # If True, print warnings about situations that may result in a crash41 'mouseRateLimit': 100, # For ignoring frequent mouse events, max number of mouse move events per second, if <= 0, then it is switched off42 'imageAxisOrder': 'col-major', # For 'row-major', image data is expected in the standard (row, col) order.43 # For 'col-major', image data is expected in reversed (col, row) order.44 # The default is 'col-major' for backward compatibility, but this may45 # change in the future.46 'useCupy': False, # When True, attempt to use cupy ( currently only with ImageItem and related functions )47 'useNumba': False, # When True, use numba48 'segmentedLineMode': 'auto', # segmented line mode, controls if lines are plotted in segments or continuous49 # 'auto': whether lines are plotted in segments is automatically decided using pen properties and whether anti-aliasing is enabled50 # 'on' or True: lines are always plotted in segments51 # 'off' or False: lines are never plotted in segments52}53 54 55def setConfigOption(opt, value):56 if opt not in CONFIG_OPTIONS:57 raise KeyError('Unknown configuration option "%s"' % opt)58 if opt == 'imageAxisOrder' and value not in ('row-major', 'col-major'):59 raise ValueError('imageAxisOrder must be either "row-major" or "col-major"')60 if opt == 'segmentedLineMode' and value not in ('auto', 'on', 'off'):61 raise ValueError('segmentedLineMode must be "auto", "on" or "off"')62 CONFIG_OPTIONS[opt] = value63 64def setConfigOptions(**opts):65 """Set global configuration options.66 67 Each keyword argument sets one global option.68 """69 for k,v in opts.items():70 setConfigOption(k, v)71 72def getConfigOption(opt):73 """Return the value of a single global configuration option.74 """75 return CONFIG_OPTIONS[opt]76 77 78def systemInfo():79 print("sys.platform: %s" % sys.platform)80 print("sys.version: %s" % sys.version)81 from .Qt import VERSION_INFO82 print("qt bindings: %s" % VERSION_INFO)83 84 global __version__85 rev = None86 if __version__ is None: ## this code was probably checked out from bzr; look up the last-revision file87 lastRevFile = os.path.join(os.path.dirname(__file__), '..', '.bzr', 'branch', 'last-revision')88 if os.path.exists(lastRevFile):89 with open(lastRevFile, 'r') as fd:90 rev = fd.read().strip()91 92 print("pyqtgraph: %s; %s" % (__version__, rev))93 print("config:")94 import pprint95 pprint.pprint(CONFIG_OPTIONS)96 97## Rename orphaned .pyc files. This is *probably* safe :)98## We only do this if __version__ is None, indicating the code was probably pulled99## from the repository.100def renamePyc(startDir):101 ### Used to rename orphaned .pyc files102 ### When a python file changes its location in the repository, usually the .pyc file103 ### is left behind, possibly causing mysterious and difficult to track bugs.104 105 ### Note that this is no longer necessary for python 3.2; from PEP 3147:106 ### "If the py source file is missing, the pyc file inside __pycache__ will be ignored.107 ### This eliminates the problem of accidental stale pyc file imports."108 109 printed = False110 startDir = os.path.abspath(startDir)111 for path, dirs, files in os.walk(startDir):112 if '__pycache__' in path:113 continue114 for f in files:115 fileName = os.path.join(path, f)116 base, ext = os.path.splitext(fileName)117 py = base + ".py"118 if ext == '.pyc' and not os.path.isfile(py):119 if not printed:120 print("NOTE: Renaming orphaned .pyc files:")121 printed = True122 n = 1123 while True:124 name2 = fileName + ".renamed%d" % n125 if not os.path.exists(name2):126 break127 n += 1128 print(" " + fileName + " ==>")129 print(" " + name2)130 os.rename(fileName, name2)131 132path = os.path.split(__file__)[0]133 134# Attempts to work around exit crashes:135import atexit136 137from .colormap import *138from .functions import *139from .graphicsItems.ArrowItem import *140from .graphicsItems.AxisItem import *141from .graphicsItems.BarGraphItem import *142from .graphicsItems.BoxplotItem import *143from .graphicsItems.ButtonItem import *144from .graphicsItems.ColorBarItem import *145from .graphicsItems.CurvePoint import *146from .graphicsItems.DateAxisItem import *147from .graphicsItems.ErrorBarItem import *148from .graphicsItems.FillBetweenItem import *149from .graphicsItems.GradientEditorItem import *150from .graphicsItems.GradientLegend import *151from .graphicsItems.GraphicsItem import *152from .graphicsItems.GraphicsLayout import *153from .graphicsItems.GraphicsObject import *154from .graphicsItems.GraphicsWidget import *155from .graphicsItems.GraphicsWidgetAnchor import *156from .graphicsItems.GraphItem import *157from .graphicsItems.GridItem import *158from .graphicsItems.HistogramLUTItem import *159from .graphicsItems.ImageItem import *160from .graphicsItems.InfiniteLine import *161from .graphicsItems.IsocurveItem import *162from .graphicsItems.ItemGroup import *163from .graphicsItems.LabelItem import *164from .graphicsItems.LegendItem import *165from .graphicsItems.LinearRegionItem import *166from .graphicsItems.PColorMeshItem import *167from .graphicsItems.PlotCurveItem import *168from .graphicsItems.PlotDataItem import *169from .graphicsItems.PlotItem import *170from .graphicsItems.ROI import *171from .graphicsItems.ScaleBar import *172from .graphicsItems.ScatterPlotItem import *173from .graphicsItems.TargetItem import *174from .graphicsItems.TextItem import *175from .graphicsItems.UIGraphicsItem import *176from .graphicsItems.ViewBox import *177from .graphicsItems.VTickGroup import *178 179# indirect imports used within library180from .GraphicsScene import GraphicsScene181from .imageview import *182 183# indirect imports known to be used outside of the library184from .Point import Point185from .Qt import isQObjectAlive186from .SignalProxy import *187from .SRTTransform import SRTTransform188from .SRTTransform3D import SRTTransform3D189from .ThreadsafeTimer import *190from .Transform3D import Transform3D191from .util.cupy_helper import getCupy192from .Vector import Vector193from .WidgetGroup import *194from .widgets.BusyCursor import *195from .widgets.CheckTable import *196from .widgets.ColorButton import *197from .widgets.ColorMapMenu import ColorMapMenu198from .widgets.ColorMapWidget import *199from .widgets.ComboBox import *200from .widgets.DataFilterWidget import *201from .widgets.DataTreeWidget import *202from .widgets.DiffTreeWidget import *203from .widgets.FeedbackButton import *204from .widgets.FileDialog import *205from .widgets.GradientWidget import *206from .widgets.GraphicsLayoutWidget import *207from .widgets.GraphicsView import *208from .widgets.GroupBox import GroupBox209from .widgets.HistogramLUTWidget import *210from .widgets.JoystickButton import *211from .widgets.LayoutWidget import *212from .widgets.PathButton import *213from .widgets.PlotWidget import *214from .widgets.ProgressDialog import *215from .widgets.RawImageWidget import *216from .widgets.RemoteGraphicsView import RemoteGraphicsView217from .widgets.ScatterPlotWidget import *218from .widgets.SpinBox import *219from .widgets.TableWidget import *220from .widgets.TreeWidget import *221from .widgets.ValueLabel import *222from .widgets.VerticalLabel import *223 224##############################################################225## PyQt and PySide both are prone to crashing on exit.226## There are two general approaches to dealing with this:227## 1. Install atexit handlers that assist in tearing down to avoid crashes.228## This helps, but is never perfect.229## 2. Terminate the process before python starts tearing down230## This is potentially dangerous231 232_cleanupCalled = False233def cleanup():234 global _cleanupCalled235 if _cleanupCalled:236 return237 238 if not getConfigOption('exitCleanup'):239 return240 241 ViewBox.quit() ## tell ViewBox that it doesn't need to deregister views anymore.242 243 _cleanupCalled = True244 245atexit.register(cleanup)246 247# Call cleanup when QApplication quits. This is necessary because sometimes248# the QApplication will quit before the atexit callbacks are invoked.249# Note: cannot connect this function until QApplication has been created, so250# instead we have GraphicsView.__init__ call this for us.251_cleanupConnected = False252def _connectCleanup():253 global _cleanupConnected254 if _cleanupConnected:255 return256 QtWidgets.QApplication.instance().aboutToQuit.connect(cleanup)257 _cleanupConnected = True258 259 260## Optional function for exiting immediately (with some manual teardown)261def exit():262 """263 Causes python to exit without garbage-collecting any objects, and thus avoids264 calling object destructor methods. This is a sledgehammer workaround for265 a variety of bugs in PyQt and Pyside that cause crashes on exit.266 267 This function does the following in an attempt to 'safely' terminate268 the process:269 270 * Invoke atexit callbacks271 * Close all open file handles272 * os._exit()273 274 Note: there is some potential for causing damage with this function if you275 are using objects that _require_ their destructors to be called (for example,276 to properly terminate log files, disconnect from devices, etc). Situations277 like this are probably quite rare, but use at your own risk.278 """279 280 ## first disable our own cleanup function; won't be needing it.281 setConfigOptions(exitCleanup=False)282 283 ## invoke atexit callbacks284 atexit._run_exitfuncs()285 286 ## close file handles287 if sys.platform == 'darwin':288 for fd in range(3, 4096):289 if fd in [7]: # trying to close 7 produces an illegal instruction on the Mac.290 continue291 try:292 os.close(fd)293 except OSError:294 pass295 else:296 os.closerange(3, 4096) ## just guessing on the maximum descriptor count..297 298 os._exit(0)299 300 301## Convenience functions for command-line use302plots = []303images = []304QAPP = None305 306def plot(*args, **kargs):307 """308 Create and return a :class:`PlotWidget <pyqtgraph.PlotWidget>`309 Accepts a *title* argument to set the title of the window.310 All other arguments are used to plot data. (see :func:`PlotItem.plot() <pyqtgraph.PlotItem.plot>`)311 """312 mkQApp()313 pwArgList = ['title', 'labels', 'name', 'left', 'right', 'top', 'bottom', 'background']314 pwArgs = {}315 dataArgs = {}316 for k in kargs:317 if k in pwArgList:318 pwArgs[k] = kargs[k]319 else:320 dataArgs[k] = kargs[k]321 windowTitle = pwArgs.pop("title", "PlotWidget")322 w = PlotWidget(**pwArgs)323 w.setWindowTitle(windowTitle)324 if len(args) > 0 or len(dataArgs) > 0:325 w.plot(*args, **dataArgs)326 plots.append(w)327 w.show()328 return w329 330def image(*args, **kargs):331 """332 Create and return an :class:`ImageView <pyqtgraph.ImageView>`333 Will show 2D or 3D image data.334 Accepts a *title* argument to set the title of the window.335 All other arguments are used to show data. (see :func:`ImageView.setImage() <pyqtgraph.ImageView.setImage>`)336 """337 mkQApp()338 w = ImageView()339 windowTitle = kargs.pop("title", "ImageView")340 w.setWindowTitle(windowTitle)341 w.setImage(*args, **kargs)342 images.append(w)343 w.show()344 return w345show = image ## for backward compatibility346 347 348def dbg(*args, **kwds):349 """350 Create a console window and begin watching for exceptions.351 352 All arguments are passed to :func:`ConsoleWidget.__init__() <pyqtgraph.console.ConsoleWidget.__init__>`.353 """354 mkQApp()355 from . import console356 c = console.ConsoleWidget(*args, **kwds)357 c.catchAllExceptions()358 c.show()359 global consoles360 try:361 consoles.append(c)362 except NameError:363 consoles = [c]364 return c365 366 367def stack(*args, **kwds):368 """369 Create a console window and show the current stack trace.370 371 All arguments are passed to :func:`ConsoleWidget.__init__() <pyqtgraph.console.ConsoleWidget.__init__>`.372 """373 mkQApp()374 from . import console375 c = console.ConsoleWidget(*args, **kwds)376 c.setStack()377 c.show()378 global consoles379 try:380 consoles.append(c)381 except NameError:382 consoles = [c]383 return c384 385 386def setPalette(app, style):387 if isinstance(style, str):388 style = style.lower()389 if style == 'qdarkstylelight':390 p = palette.getQDarkStyleLightQPalette()391 elif style in ['qdarkstyle','qdarkstyledark']:392 p = palette.getQDarkStyleDarkQPalette()393 else:394 raise ValueError(f'no palette by the name {style} exists')395 elif isinstance(style, QtGui.QPalette):396 p = style397 else:398 raise TypeError('style either be a string or QPalette')399 app.setPalette(p)400 