Merge pull request #1846 from pijyoi/import_hygiene

reduce pollution of pg namespace
This commit is contained in:
Ogi Moore 2021-06-25 23:26:26 -07:00 committed by GitHub
commit 93fa8664cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 115 additions and 147 deletions

View File

View File

View File

View File

@ -12,7 +12,7 @@ This module exists to smooth out some of the differences between PySide and PyQt
import os, sys, re, time, subprocess, warnings import os, sys, re, time, subprocess, warnings
from .python2_3 import asUnicode from ..python2_3 import asUnicode
PYSIDE = 'PySide' PYSIDE = 'PySide'
PYSIDE2 = 'PySide2' PYSIDE2 = 'PySide2'
@ -130,11 +130,30 @@ def _loadUiType(uiFile):
return form_class, base_class return form_class, base_class
# For historical reasons, pyqtgraph maintains a Qt4-ish interface back when
# there wasn't a QtWidgets module. This _was_ done by monkey-patching all of
# QtWidgets into the QtGui module. This monkey-patching modifies QtGui at a
# global level.
# To avoid this, we now maintain a local "mirror" of QtCore, QtGui and QtWidgets.
# Thus, when monkey-patching happens later on in this file, they will only affect
# the local modules and not the global modules.
def _copy_attrs(src, dst):
for o in dir(src):
if not hasattr(dst, o):
setattr(dst, o, getattr(src, o))
from . import QtCore, QtGui, QtWidgets
if QT_LIB == PYQT5: if QT_LIB == PYQT5:
# We're using PyQt5 which has a different structure so we're going to use a shim to # We're using PyQt5 which has a different structure so we're going to use a shim to
# recreate the Qt4 structure for Qt5 # recreate the Qt4 structure for Qt5
from PyQt5 import QtGui, QtCore, QtWidgets, sip, uic import PyQt5.QtCore, PyQt5.QtGui, PyQt5.QtWidgets
_copy_attrs(PyQt5.QtCore, QtCore)
_copy_attrs(PyQt5.QtGui, QtGui)
_copy_attrs(PyQt5.QtWidgets, QtWidgets)
from PyQt5 import sip, uic
try: try:
from PyQt5 import QtSvg from PyQt5 import QtSvg
except ImportError as err: except ImportError as err:
@ -147,7 +166,12 @@ if QT_LIB == PYQT5:
VERSION_INFO = 'PyQt5 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR VERSION_INFO = 'PyQt5 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR
elif QT_LIB == PYQT6: elif QT_LIB == PYQT6:
from PyQt6 import QtGui, QtCore, QtWidgets, sip, uic import PyQt6.QtCore, PyQt6.QtGui, PyQt6.QtWidgets
_copy_attrs(PyQt6.QtCore, QtCore)
_copy_attrs(PyQt6.QtGui, QtGui)
_copy_attrs(PyQt6.QtWidgets, QtWidgets)
from PyQt6 import sip, uic
try: try:
from PyQt6 import QtSvg from PyQt6 import QtSvg
@ -165,7 +189,10 @@ elif QT_LIB == PYQT6:
VERSION_INFO = 'PyQt6 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR VERSION_INFO = 'PyQt6 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR
elif QT_LIB == PYSIDE2: elif QT_LIB == PYSIDE2:
from PySide2 import QtGui, QtCore, QtWidgets import PySide2.QtCore, PySide2.QtGui, PySide2.QtWidgets
_copy_attrs(PySide2.QtCore, QtCore)
_copy_attrs(PySide2.QtGui, QtGui)
_copy_attrs(PySide2.QtWidgets, QtWidgets)
try: try:
from PySide2 import QtSvg from PySide2 import QtSvg
@ -182,7 +209,10 @@ elif QT_LIB == PYSIDE2:
VERSION_INFO = 'PySide2 ' + PySide2.__version__ + ' Qt ' + QtCore.__version__ VERSION_INFO = 'PySide2 ' + PySide2.__version__ + ' Qt ' + QtCore.__version__
elif QT_LIB == PYSIDE6: elif QT_LIB == PYSIDE6:
from PySide6 import QtGui, QtCore, QtWidgets import PySide6.QtCore, PySide6.QtGui, PySide6.QtWidgets
_copy_attrs(PySide6.QtCore, QtCore)
_copy_attrs(PySide6.QtGui, QtGui)
_copy_attrs(PySide6.QtWidgets, QtWidgets)
try: try:
from PySide6 import QtSvg from PySide6 import QtSvg

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .Qt import QtCore, QtGui from .Qt import QtCore, QtGui
__all__ = ['ThreadsafeTimer']
class ThreadsafeTimer(QtCore.QObject): class ThreadsafeTimer(QtCore.QObject):
""" """
Thread-safe replacement for QTimer. Thread-safe replacement for QTimer.

View File

@ -10,7 +10,7 @@ __version__ = '0.12.1'
## 'Qt' is a local module; it is intended mainly to cover up the differences ## 'Qt' is a local module; it is intended mainly to cover up the differences
## between PyQt4 and PySide. ## between PyQt4 and PySide.
from .Qt import QtGui, mkQApp from .Qt import QtCore, QtGui, mkQApp
from .Qt import exec_ as exec from .Qt import exec_ as exec
## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause) ## not really safe--If we accidentally create another QApplication, the process hangs (and it is very difficult to trace the cause)
@ -280,6 +280,15 @@ from .ptime import time
from .Qt import isQObjectAlive from .Qt import isQObjectAlive
from .ThreadsafeTimer import * from .ThreadsafeTimer import *
# indirect imports used within library
from .GraphicsScene import GraphicsScene
from .python2_3 import asUnicode
from .util.cupy_helper import getCupy
# indirect imports known to be used outside of the library
from .metaarray import MetaArray
from .ordereddict import OrderedDict
############################################################## ##############################################################
## PyQt and PySide both are prone to crashing on exit. ## PyQt and PySide both are prone to crashing on exit.

View File

@ -26,6 +26,31 @@ from .metaarray import MetaArray
from collections import OrderedDict from collections import OrderedDict
from .python2_3 import asUnicode, basestring from .python2_3 import asUnicode, basestring
# in order of appearance in this file.
# add new functions to this list only if they are to reside in pg namespace.
__all__ = [
'siScale', 'siFormat', 'siParse', 'siEval', 'siApply',
'Color', 'mkColor', 'mkBrush', 'mkPen', 'hsvColor',
'CIELabColor', 'colorCIELab', 'colorDistance',
'colorTuple', 'colorStr', 'intColor', 'glColor',
'makeArrowPath', 'eq',
'affineSliceCoords', 'affineSlice',
'interweaveArrays', 'interpolateArray', 'subArray',
'transformToArray', 'transformCoordinates',
'solve3DTransform', 'solveBilinearTransform',
'clip_scalar', 'clip_array', 'rescaleData', 'applyLookupTable',
'makeRGBA', 'makeARGB',
# 'try_fastpath_argb', 'ndarray_to_qimage',
'makeQImage',
# 'qimage_to_ndarray',
'imageToArray', 'colorToAlpha',
'gaussianFilter', 'downsample', 'arrayToQPath',
# 'ndarray_from_qpolygonf', 'create_qpolygonf', 'arrayToQPolygonF',
'isocurve', 'traceImage', 'isosurface',
'invertQTransform',
'pseudoScatter', 'toposort', 'disconnect', 'SignalBlock']
Colors = { Colors = {
'b': QtGui.QColor(0,0,255,255), 'b': QtGui.QColor(0,0,255,255),
'g': QtGui.QColor(0,255,0,255), 'g': QtGui.QColor(0,255,0,255),

View File

@ -3,6 +3,8 @@ from .. import functions as fn
from .PlotDataItem import PlotDataItem from .PlotDataItem import PlotDataItem
from .PlotCurveItem import PlotCurveItem from .PlotCurveItem import PlotCurveItem
__all__ = ['FillBetweenItem']
class FillBetweenItem(QtGui.QGraphicsPathItem): class FillBetweenItem(QtGui.QGraphicsPathItem):
""" """
GraphicsItem filling the space between two PlotDataItems. GraphicsItem filling the space between two PlotDataItems.

View File

@ -9,6 +9,7 @@ from .. import functions as fn
import weakref import weakref
import operator import operator
__all__ = ['GraphicsItem']
# Recipe from https://docs.python.org/3.8/library/collections.html#collections.OrderedDict # Recipe from https://docs.python.org/3.8/library/collections.html#collections.OrderedDict
# slightly adapted for Python 3.7 compatibility # slightly adapted for Python 3.7 compatibility

View File

@ -1,6 +1,7 @@
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
from ..Point import Point from ..Point import Point
__all__ = ['GraphicsWidgetAnchor']
class GraphicsWidgetAnchor(object): class GraphicsWidgetAnchor(object):
""" """

View File

@ -12,6 +12,7 @@ from .ViewBox import *
from .GradientEditorItem import * from .GradientEditorItem import *
from .LinearRegionItem import * from .LinearRegionItem import *
from .PlotDataItem import * from .PlotDataItem import *
from .PlotCurveItem import *
from .AxisItem import * from .AxisItem import *
from .GridItem import * from .GridItem import *
from ..Point import Point from ..Point import Point

View File

@ -3,6 +3,7 @@ from .GraphicsObject import *
from .. import functions as fn from .. import functions as fn
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
__all__ = ['IsocurveItem']
class IsocurveItem(GraphicsObject): class IsocurveItem(GraphicsObject):
""" """

View File

@ -6,6 +6,7 @@ from .GraphicsObject import GraphicsObject
from .. import mkBrush, mkPen from .. import mkBrush, mkPen
from .. import functions as fn from .. import functions as fn
__all__ = ['NonUniformImage']
class NonUniformImage(GraphicsObject): class NonUniformImage(GraphicsObject):
""" """

View File

@ -11,6 +11,7 @@ from .. import functions as fn
from .. import debug as debug from .. import debug as debug
from .. import getConfigOption from .. import getConfigOption
__all__ = ['PlotDataItem']
class PlotDataItem(GraphicsObject): class PlotDataItem(GraphicsObject):
""" """

View File

@ -14,9 +14,11 @@ from ..InfiniteLine import InfiniteLine
from ..LabelItem import LabelItem from ..LabelItem import LabelItem
from ..LegendItem import LegendItem from ..LegendItem import LegendItem
from ..PlotDataItem import PlotDataItem from ..PlotDataItem import PlotDataItem
from ..PlotCurveItem import PlotCurveItem
from ..ScatterPlotItem import ScatterPlotItem
from ..ViewBox import ViewBox from ..ViewBox import ViewBox
from ... import functions as fn from ... import functions as fn
from ... import icons, PlotCurveItem, ScatterPlotItem from ... import icons
from ...Qt import QtGui, QtCore, QT_LIB from ...Qt import QtGui, QtCore, QT_LIB
from ...WidgetGroup import WidgetGroup from ...WidgetGroup import WidgetGroup
from ...python2_3 import basestring from ...python2_3 import basestring

View File

@ -1 +1,3 @@
from .PlotItem import PlotItem from .PlotItem import PlotItem
__all__ = ['PlotItem']

View File

@ -10,6 +10,7 @@ from .ViewBox import ViewBox
import string import string
import warnings import warnings
__all__ = ['TargetItem', 'TargetLabel']
class TargetItem(UIGraphicsItem): class TargetItem(UIGraphicsItem):
"""Draws a draggable target symbol (circle plus crosshair). """Draws a draggable target symbol (circle plus crosshair).

View File

@ -4,6 +4,7 @@ from ..Point import Point
from .. import functions as fn from .. import functions as fn
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
__all__ = ['TextItem']
class TextItem(GraphicsObject): class TextItem(GraphicsObject):
""" """

View File

@ -1 +1,3 @@
from .ViewBox import ViewBox from .ViewBox import ViewBox
__all__ = ['ViewBox']

View File

@ -6,6 +6,8 @@ it is possible to place any widget into its own window by simply calling its
show() method. show() method.
""" """
__all__ = ['GraphicsWindow', 'TabWindow', 'PlotWindow', 'ImageWindow']
from .Qt import QtCore, QtGui, mkQApp from .Qt import QtCore, QtGui, mkQApp
from .widgets.PlotWidget import * from .widgets.PlotWidget import *
from .imageview import * from .imageview import *

View File

@ -4,3 +4,5 @@ Includes ROI plotting over time and image normalization.
""" """
from .ImageView import ImageView from .ImageView import ImageView
__all__ = ['ImageView']

View File

@ -1,137 +1,12 @@
# Copyright (c) 2009 Raymond Hettinger import collections
#
# 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.
import warnings import warnings
warnings.warn(
"OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13",
DeprecationWarning, stacklevel=2
)
import sys
if sys.version[0] > '2':
from collections import OrderedDict
else:
from UserDict import DictMixin
class OrderedDict(dict, DictMixin): class OrderedDict(collections.OrderedDict):
def __init__(self, *args, **kwds):
def __init__(self, *args, **kwds): warnings.warn(
if len(args) > 1: "OrderedDict is in the standard library for supported versions of Python. Will be removed in 0.13",
raise TypeError('expected at most 1 arguments, got %d' % len(args)) DeprecationWarning,
try: stacklevel=2,
self.__end )
except AttributeError: super().__init__(*args, **kwds)
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.__map = {} # key --> [key, prev, next]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
end = self.__end
curr = end[1]
curr[2] = end[1] = self.__map[key] = [key, curr, end]
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
key, prev, next = self.__map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.__end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.__end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def popitem(self, last=True):
if not self:
raise KeyError('dictionary is empty')
if last:
key = reversed(self).next()
else:
key = iter(self).next()
value = self.pop(key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
tmp = self.__map, self.__end
del self.__map, self.__end
inst_dict = vars(self).copy()
self.__map, self.__end = tmp
if inst_dict:
return (self.__class__, (items,), inst_dict)
return self.__class__, (items,)
def keys(self):
return list(self)
setdefault = DictMixin.setdefault
update = DictMixin.update
pop = DictMixin.pop
values = DictMixin.values
items = DictMixin.items
iterkeys = DictMixin.iterkeys
itervalues = DictMixin.itervalues
iteritems = DictMixin.iteritems
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, self.items())
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __eq__(self, other):
if isinstance(other, OrderedDict):
if len(self) != len(other):
return False
for p, q in zip(self.items(), other.items()):
if p != q:
return False
return True
return dict.__eq__(self, other)
def __ne__(self, other):
return not self == other

View File

@ -4,6 +4,7 @@ from ..SignalProxy import SignalProxy
from collections import OrderedDict from collections import OrderedDict
from ..python2_3 import asUnicode, basestring from ..python2_3 import asUnicode, basestring
__all__ = ['ComboBox']
class ComboBox(QtGui.QComboBox): class ComboBox(QtGui.QComboBox):
"""Extends QComboBox to add extra functionality. """Extends QComboBox to add extra functionality.

View File

@ -2,6 +2,7 @@ from ..Qt import QtGui, QtCore
from .PathButton import PathButton from .PathButton import PathButton
from ..python2_3 import basestring from ..python2_3 import basestring
__all__ = ['GroupBox']
class GroupBox(QtGui.QGroupBox): class GroupBox(QtGui.QGroupBox):
"""Subclass of QGroupBox that implements collapse handle. """Subclass of QGroupBox that implements collapse handle.

View File

@ -6,6 +6,8 @@ from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as Navigatio
from matplotlib.figure import Figure from matplotlib.figure import Figure
__all__ = ['MatplotlibWidget']
class MatplotlibWidget(QtGui.QWidget): class MatplotlibWidget(QtGui.QWidget):
""" """
Implements a Matplotlib figure inside a QWidget. Implements a Matplotlib figure inside a QWidget.

View File

@ -18,6 +18,7 @@ except (ImportError, AttributeError):
# AttributeError upon import # AttributeError upon import
HAVE_OPENGL = False HAVE_OPENGL = False
__all__ = ['RawImageWidget']
class RawImageWidget(QtGui.QWidget): class RawImageWidget(QtGui.QWidget):
""" """
@ -78,6 +79,7 @@ class RawImageWidget(QtGui.QWidget):
if HAVE_OPENGL: if HAVE_OPENGL:
__all__.append('RawImageGLWidget')
class RawImageGLWidget(QOpenGLWidget): class RawImageGLWidget(QOpenGLWidget):
""" """
Similar to RawImageWidget, but uses a GL widget to do all drawing. Similar to RawImageWidget, but uses a GL widget to do all drawing.

View File

@ -2,6 +2,7 @@
CSV export test CSV export test
""" """
from __future__ import division, print_function, absolute_import from __future__ import division, print_function, absolute_import
import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
import csv import csv
import tempfile import tempfile
@ -19,11 +20,11 @@ def test_CSVExporter():
plt.plot(y=y1, name='myPlot') plt.plot(y=y1, name='myPlot')
y2 = [3,4,6,1,2,4,2,3,5,3,5,1,3] y2 = [3,4,6,1,2,4,2,3,5,3,5,1,3]
x2 = pg.np.linspace(0, 1.0, len(y2)) x2 = np.linspace(0, 1.0, len(y2))
plt.plot(x=x2, y=y2) plt.plot(x=x2, y=y2)
y3 = [1,5,2,3,4,6,1,2,4,2,3,5,3] y3 = [1,5,2,3,4,6,1,2,4,2,3,5,3]
x3 = pg.np.linspace(0, 1.0, len(y3)+1) x3 = np.linspace(0, 1.0, len(y3)+1)
plt.plot(x=x3, y=y3, stepMode="center") plt.plot(x=x3, y=y3, stepMode="center")
ex = pg.exporters.CSVExporter(plt.plotItem) ex = pg.exporters.CSVExporter(plt.plotItem)

View File

@ -7,7 +7,7 @@ pytest.importorskip("matplotlib")
app = pg.mkQApp() app = pg.mkQApp()
skip_qt6 = pytest.mark.skipif( skip_qt6 = pytest.mark.skipif(
pg.QT_LIB in ["PySide6", "PyQt6"], pg.Qt.QT_LIB in ["PySide6", "PyQt6"],
reason= ( reason= (
"Matplotlib has no Qt6 support yet, " "Matplotlib has no Qt6 support yet, "
"see https://github.com/matplotlib/matplotlib/pull/19255" "see https://github.com/matplotlib/matplotlib/pull/19255"

View File

@ -229,7 +229,7 @@ def test_setRect():
def test_dividebyzero(): def test_dividebyzero():
im = pg.image(pg.np.random.normal(size=(100,100))) im = pg.image(np.random.normal(size=(100,100)))
im.imageItem.setAutoDownsample(True) im.imageItem.setAutoDownsample(True)
im.view.setRange(xRange=[-5+25, 5e+25],yRange=[-5e+25, 5e+25]) im.view.setRange(xRange=[-5+25, 5e+25],yRange=[-5e+25, 5e+25])
app.processEvents() app.processEvents()

View File

@ -37,7 +37,7 @@ def test_PlotWidget():
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
w = pg.PlotWidget(*args, **kwds) w = pg.PlotWidget(*args, **kwds)
data = pg.np.array([1,5,2,4,3]) data = np.array([1,5,2,4,3])
c = w.plot(data, name='stuff') c = w.plot(data, name='stuff')
w.addLegend() w.addLegend()