diff --git a/examples/GLIsosurface.py b/examples/GLIsosurface.py index 16045a78..1afe797c 100644 --- a/examples/GLIsosurface.py +++ b/examples/GLIsosurface.py @@ -7,7 +7,7 @@ This example uses the isosurface function to convert a scalar field ## Add path to library (just for examples; you do not need this) import initExample -from pyqtgraph.Qt import QtCore, QtGui +import numpy as np import pyqtgraph as pg import pyqtgraph.opengl as gl @@ -22,23 +22,16 @@ g = gl.GLGridItem() g.scale(2,2,1) w.addItem(g) -import numpy as np - ## Define a scalar field from which we will generate an isosurface def psi(i, j, k, offset=(25, 25, 50)): x = i-offset[0] y = j-offset[1] z = k-offset[2] - th = np.arctan2(z, (x**2+y**2)**0.5) - phi = np.arctan2(y, x) - r = (x**2 + y**2 + z **2)**0.5 + th = np.arctan2(z, np.hypot(x, y)) + r = np.sqrt(x**2 + y**2 + z **2) a0 = 1 - #ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th) ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1) - return ps - - #return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2 print("Generating scalar field..") diff --git a/examples/GLLinePlotItem.py b/examples/GLLinePlotItem.py index b375c1c1..82e1f6fb 100644 --- a/examples/GLLinePlotItem.py +++ b/examples/GLLinePlotItem.py @@ -29,19 +29,16 @@ gz = gl.GLGridItem() gz.translate(0, 0, -10) w.addItem(gz) -def fn(x, y): - return np.cos((x**2 + y**2)**0.5) - n = 51 y = np.linspace(-10,10,n) x = np.linspace(-10,10,100) for i in range(n): - yi = np.array([y[i]]*100) - d = (x**2 + yi**2)**0.5 + yi = y[i] + d = np.hypot(x, yi) z = 10 * np.cos(d) / (d+1) - pts = np.vstack([x,yi,z]).transpose() + pts = np.column_stack([x, np.full_like(x, yi), z]) plt = gl.GLLinePlotItem(pos=pts, color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True) w.addItem(plt) - + if __name__ == '__main__': pg.mkQApp().exec_() diff --git a/examples/GLScatterPlotItem.py b/examples/GLScatterPlotItem.py index c279eadf..1d8d5e06 100644 --- a/examples/GLScatterPlotItem.py +++ b/examples/GLScatterPlotItem.py @@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots. import initExample import pyqtgraph as pg +from pyqtgraph import functions as fn from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph.opengl as gl import numpy as np @@ -84,10 +85,10 @@ def update(): global phase, sp2, d2 s = -np.cos(d2*2+phase) color = np.empty((len(d2),4), dtype=np.float32) - color[:,3] = np.clip(s * 0.1, 0, 1) - color[:,0] = np.clip(s * 3.0, 0, 1) - color[:,1] = np.clip(s * 1.0, 0, 1) - color[:,2] = np.clip(s ** 3, 0, 1) + color[:,3] = fn.clip_array(s * 0.1, 0., 1.) + color[:,0] = fn.clip_array(s * 3.0, 0., 1.) + color[:,1] = fn.clip_array(s * 1.0, 0., 1.) + color[:,2] = fn.clip_array(s ** 3, 0., 1.) sp2.setData(color=color) phase -= 0.1 diff --git a/examples/GLVolumeItem.py b/examples/GLVolumeItem.py index 5d7e6601..ba67514b 100644 --- a/examples/GLVolumeItem.py +++ b/examples/GLVolumeItem.py @@ -7,9 +7,10 @@ Demonstrates GLVolumeItem for displaying volumetric data. ## Add path to library (just for examples; you do not need this) import initExample +import numpy as np import pyqtgraph as pg -from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph.opengl as gl +from pyqtgraph import functions as fn app = pg.mkQApp("GLVolumeItem Example") w = gl.GLViewWidget() @@ -23,27 +24,21 @@ g = gl.GLGridItem() g.scale(10, 10, 1) w.addItem(g) -import numpy as np ## Hydrogen electron probability density def psi(i, j, k, offset=(50,50,100)): x = i-offset[0] y = j-offset[1] z = k-offset[2] - th = np.arctan2(z, (x**2+y**2)**0.5) - phi = np.arctan2(y, x) - r = (x**2 + y**2 + z **2)**0.5 + th = np.arctan2(z, np.hypot(x, y)) + r = np.sqrt(x**2 + y**2 + z **2) a0 = 2 - #ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th) ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1) - return ps - - #return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2 data = np.fromfunction(psi, (100,100,200)) -positive = np.log(np.clip(data, 0, data.max())**2) -negative = np.log(np.clip(-data, 0, -data.min())**2) +positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**2) +negative = np.log(fn.clip_array(-data, -np.finfo(data.dtype).eps, -data.min())**2) d2 = np.empty(data.shape + (4,), dtype=np.ubyte) d2[..., 0] = positive * (255./positive.max()) diff --git a/examples/GLshaders.py b/examples/GLshaders.py index 4e1acc51..38e6988d 100644 --- a/examples/GLshaders.py +++ b/examples/GLshaders.py @@ -9,6 +9,7 @@ used to affect the appearance of a surface. ## Add path to library (just for examples; you do not need this) import initExample +import numpy as np from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg import pyqtgraph.opengl as gl @@ -23,9 +24,6 @@ g = gl.GLGridItem() g.scale(2,2,1) w.addItem(g) -import numpy as np - - md = gl.MeshData.sphere(rows=10, cols=20) x = np.linspace(-8, 8, 6) diff --git a/examples/optics/pyoptic.py b/examples/optics/pyoptic.py index f70edd2a..99221bd7 100644 --- a/examples/optics/pyoptic.py +++ b/examples/optics/pyoptic.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, asin, sin, cos, degrees, sqrt, hypot import pyqtgraph as pg from pyqtgraph.Qt import QtGui, QtCore import numpy as np @@ -49,7 +50,7 @@ class GlassDB: B = list(map(float, [info['B1'], info['B2'], info['B3']])) C = list(map(float, [info['C1'], info['C2'], info['C3']])) w2 = (wl/1000.)**2 - n = np.sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2]))) + n = sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2]))) cache[wl] = n return cache[wl] @@ -249,10 +250,14 @@ class Lens(Optic): p1 = surface.mapToItem(ray, p1) rd = ray['dir'] - a1 = np.arctan2(rd[1], rd[0]) - ar = a1 - ai + np.arcsin((np.sin(ai) * ray['ior'] / ior)) + + a1 = atan2(rd[1], rd[0]) + try: + ar = a1 - ai + asin((sin(ai) * ray['ior'] / ior)) + except ValueError: + ar = np.nan ray.setEnd(p1) - dp = Point(np.cos(ar), np.sin(ar)) + dp = Point(cos(ar), sin(ar)) ray = Ray(parent=ray, ior=ior, dir=dp) return [ray] @@ -279,10 +284,10 @@ class Mirror(Optic): if p1 is not None: p1 = surface.mapToItem(ray, p1) rd = ray['dir'] - a1 = np.arctan2(rd[1], rd[0]) - ar = a1 + np.pi - 2*ai + a1 = atan2(rd[1], rd[0]) + ar = a1 + np.pi - 2 * ai ray.setEnd(p1) - dp = Point(np.cos(ar), np.sin(ar)) + dp = Point(cos(ar), sin(ar)) ray = Ray(parent=ray, dir=dp) else: ray.setEnd(None) @@ -374,7 +379,7 @@ class CircleSurface(pg.GraphicsObject): ## half-height of surface can't be larger than radius h2 = min(h2, abs(r)) arc = QtCore.QRectF(0, -r, r*2, r*2) - a1 = np.arcsin(h2/r) * 180. / np.pi + a1 = degrees(asin(h2/r)) a2 = -2*a1 a1 += 180. self.path.arcMoveTo(arc, a1) @@ -406,13 +411,13 @@ class CircleSurface(pg.GraphicsObject): if abs(y) > h: return None, None else: - return (Point(0, y), np.arctan2(dir[1], dir[0])) + return (Point(0, y), atan2(dir[1], dir[0])) else: #print " curve" ## find intersection of circle and line (quadratic formula) dx = dir[0] dy = dir[1] - dr = (dx**2 + dy**2) ** 0.5 + dr = hypot(dx, dy) # length D = p[0] * (p[1]+dy) - (p[0]+dx) * p[1] idr2 = 1.0 / dr**2 disc = r**2 * dr**2 - D**2 @@ -423,8 +428,7 @@ class CircleSurface(pg.GraphicsObject): sgn = -1 else: sgn = 1 - - + br = self.path.boundingRect() x1 = (D*dy + sgn*dx*disc2) * idr2 y1 = (-D*dx + abs(dy)*disc2) * idr2 @@ -436,19 +440,12 @@ class CircleSurface(pg.GraphicsObject): pt = Point(x2, y2) if not br.contains(x2+r, y2): return None, None - raise Exception("No intersection!") - norm = np.arctan2(pt[1], pt[0]) + norm = atan2(pt[1], pt[0]) if r < 0: norm += np.pi - #print " norm:", norm*180/3.1415 dp = p - pt - #print " dp:", dp - ang = np.arctan2(dp[1], dp[0]) - #print " ang:", ang*180/3.1415 - #print " ai:", (ang-norm)*180/3.1415 - - #print " intersection:", pt + ang = atan2(dp[1], dp[0]) return pt + Point(r, 0), ang-norm diff --git a/pyqtgraph/Point.py b/pyqtgraph/Point.py index fea37dda..bdd8bc33 100644 --- a/pyqtgraph/Point.py +++ b/pyqtgraph/Point.py @@ -6,14 +6,8 @@ Distributed under MIT/X11 license. See license.txt for more information. """ from .Qt import QtCore -import numpy as np +from math import atan2, hypot, degrees -def clip(x, mn, mx): - if x > mx: - return mx - if x < mn: - return mn - return x class Point(QtCore.QPointF): """Extension of QPointF which adds a few missing methods.""" @@ -93,42 +87,21 @@ class Point(QtCore.QPointF): return self._math_('__pow__', a) def _math_(self, op, x): - #print "point math:", op - #try: - #fn = getattr(QtCore.QPointF, op) - #pt = fn(self, x) - #print fn, pt, self, x - #return Point(pt) - #except AttributeError: x = Point(x) return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1])) def length(self): """Returns the vector length of this Point.""" - try: - return (self[0]**2 + self[1]**2) ** 0.5 - except OverflowError: - try: - return self[1] / np.sin(np.arctan2(self[1], self[0])) - except OverflowError: - return np.inf - + return hypot(self[0], self[1]) # length + def norm(self): """Returns a vector in the same direction with unit length.""" return self / self.length() def angle(self, a): """Returns the angle in degrees between this vector and the vector a.""" - n1 = self.length() - n2 = a.length() - if n1 == 0. or n2 == 0.: - return None - ## Probably this should be done with arctan2 instead.. - ang = np.arccos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians - c = self.cross(a) - if c > 0: - ang *= -1. - return ang * 180. / np.pi + rads = atan2(self.y(), self.x()) - atan2(a.y(), a.x()) + return degrees(rads) def dot(self, a): """Returns the dot product of a and this Point.""" @@ -146,8 +119,7 @@ class Point(QtCore.QPointF): def __repr__(self): return "Point(%f, %f)" % (self[0], self[1]) - - + def min(self): return min(self[0], self[1]) diff --git a/pyqtgraph/SRTTransform.py b/pyqtgraph/SRTTransform.py index 35ec0625..a340f8b6 100644 --- a/pyqtgraph/SRTTransform.py +++ b/pyqtgraph/SRTTransform.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from .Qt import QtCore, QtGui from .Point import Point import numpy as np @@ -76,7 +77,7 @@ class SRTTransform(QtGui.QTransform): self._state = { 'pos': Point(p1), 'scale': Point(dp2.length(), dp3.length() * sy), - 'angle': (np.arctan2(dp2[1], dp2[0]) * 180. / np.pi) + da + 'angle': degrees(atan2(dp2[1], dp2[0])) + da } self.update() diff --git a/pyqtgraph/SRTTransform3D.py b/pyqtgraph/SRTTransform3D.py index 7d458edd..2fd3ff87 100644 --- a/pyqtgraph/SRTTransform3D.py +++ b/pyqtgraph/SRTTransform3D.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from .Qt import QtCore, QtGui from .Vector import Vector from .Transform3D import Transform3D @@ -164,7 +165,7 @@ class SRTTransform3D(Transform3D): sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd]) ## finally, we get the complete angle from arctan(sin/cos) - self._state['angle'] = np.arctan2(sin, cos) * 180 / np.pi + self._state['angle'] = degrees(atan2(sin, cos)) if self._state['angle'] == 0: self._state['axis'] = (0,0,1) diff --git a/pyqtgraph/Vector.py b/pyqtgraph/Vector.py index a9d28e41..a64e8968 100644 --- a/pyqtgraph/Vector.py +++ b/pyqtgraph/Vector.py @@ -4,9 +4,9 @@ Vector.py - Extension of QVector3D which adds a few missing methods. Copyright 2010 Luke Campagnola Distributed under MIT/X11 license. See license.txt for more information. """ - +from math import acos, degrees from .Qt import QtGui, QtCore, QT_LIB -import numpy as np +from . import functions as fn class Vector(QtGui.QVector3D): """Extension of QVector3D which adds a few helpful methods.""" @@ -88,11 +88,11 @@ class Vector(QtGui.QVector3D): if n1 == 0. or n2 == 0.: return None ## Probably this should be done with arctan2 instead.. - ang = np.arccos(np.clip(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians + rads = acos(fn.clip_scalar(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians # c = self.crossProduct(a) # if c > 0: # ang *= -1. - return ang * 180. / np.pi + return degrees(rads) def __abs__(self): return Vector(abs(self.x()), abs(self.y()), abs(self.z())) diff --git a/pyqtgraph/canvas/CanvasItem.py b/pyqtgraph/canvas/CanvasItem.py index 88612055..3883919e 100644 --- a/pyqtgraph/canvas/CanvasItem.py +++ b/pyqtgraph/canvas/CanvasItem.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import numpy as np -from ..Qt import QtGui, QtCore, QtSvg, QT_LIB +from ..Qt import QtGui, QtCore, QT_LIB +from .. import functions as fn from ..graphicsItems.ROI import ROI from .. import SRTTransform, ItemGroup import importlib @@ -240,7 +240,7 @@ class CanvasItem(QtCore.QObject): self._graphicsItem.setOpacity(alpha) def setAlpha(self, alpha): - self.alphaSlider.setValue(int(np.clip(alpha * 1023, 0, 1023))) + self.alphaSlider.setValue(int(fn.clip_scalar(alpha * 1023, 0, 1023))) def alpha(self): return self.alphaSlider.value() / 1023. diff --git a/pyqtgraph/debug.py b/pyqtgraph/debug.py index 15d1fb3c..37b612cd 100644 --- a/pyqtgraph/debug.py +++ b/pyqtgraph/debug.py @@ -202,7 +202,7 @@ def findRefPath(startObj, endObj, maxLen=8, restart=True, seen={}, path=None, ig #print prefix+" FRAME" continue try: - if any([r is x for x in path]): + if any(r is x for x in path): #print prefix+" LOOP", objChainString([r]+path) continue except: @@ -282,7 +282,7 @@ def refPathString(chain): o2 = chain[i] cont = False if isinstance(o1, list) or isinstance(o1, tuple): - if any([o2 is x for x in o1]): + if any(o2 is x for x in o1): s += "[%d]" % o1.index(o2) continue #print " not list" diff --git a/pyqtgraph/dockarea/Container.py b/pyqtgraph/dockarea/Container.py index 04b775f9..34f16288 100644 --- a/pyqtgraph/dockarea/Container.py +++ b/pyqtgraph/dockarea/Container.py @@ -126,7 +126,7 @@ class SplitContainer(Container, QtGui.QSplitter): def saveState(self): sizes = self.sizes() - if all([x == 0 for x in sizes]): + if all(x == 0 for x in sizes): sizes = [10] * len(sizes) return {'sizes': sizes} diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 6b9916c1..45043a4c 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -60,19 +60,15 @@ def siScale(x, minVal=1e-25, allowUnicode=True): if isinstance(x, decimal.Decimal): x = float(x) - try: - if np.isnan(x) or np.isinf(x): + if not math.isfinite(x): return(1, '') except: - print(x, type(x)) raise if abs(x) < minVal: m = 0 - x = 0 else: - m = int(np.clip(np.floor(np.log(abs(x))/np.log(1000)), -9.0, 9.0)) - + m = int(clip_scalar(math.floor(math.log(abs(x))/math.log(1000)), -9.0, 9.0)) if m == 0: pref = '' elif m < -8 or m > 8: @@ -84,7 +80,6 @@ def siScale(x, minVal=1e-25, allowUnicode=True): pref = SI_PREFIXES_ASCII[m+8] m1 = -3*m p = 10.**m1 - return (p, pref) @@ -268,6 +263,8 @@ def mkColor(*args): g = int(c[2:4], 16) b = int(c[4:6], 16) a = int(c[6:8], 16) + else: + raise ValueError(f"Unknown how to convert string {c} to color") elif isinstance(args[0], QtGui.QColor): return QtGui.QColor(args[0]) elif np.issubdtype(type(args[0]), np.floating): @@ -275,10 +272,10 @@ def mkColor(*args): a = 255 elif hasattr(args[0], '__len__'): if len(args[0]) == 3: - (r, g, b) = args[0] + r, g, b = args[0] a = 255 elif len(args[0]) == 4: - (r, g, b, a) = args[0] + r, g, b, a = args[0] elif len(args[0]) == 2: return intColor(*args[0]) else: @@ -288,16 +285,13 @@ def mkColor(*args): else: raise TypeError(err) elif len(args) == 3: - (r, g, b) = args + r, g, b = args a = 255 elif len(args) == 4: - (r, g, b, a) = args + r, g, b, a = args else: raise TypeError(err) - - args = [r,g,b,a] - args = [0 if np.isnan(a) or np.isinf(a) else a for a in args] - args = list(map(int, args)) + args = [int(a) if np.isfinite(a) else 0 for a in (r, g, b, a)] return QtGui.QColor(*args) @@ -432,16 +426,16 @@ def makeArrowPath(headLen=20, headWidth=None, tipAngle=20, tailLen=20, tailWidth If *tailLen* is None, no tail will be drawn. """ if headWidth is None: - headWidth = headLen * np.tan(tipAngle * 0.5 * np.pi/180.) + headWidth = headLen * math.tan(math.radians(tipAngle * 0.5)) path = QtGui.QPainterPath() path.moveTo(0,0) path.lineTo(headLen, -headWidth) if tailLen is None: - innerY = headLen - headWidth * np.tan(baseAngle*np.pi/180.) + innerY = headLen - headWidth * math.tan(math.radians(baseAngle)) path.lineTo(innerY, 0) else: tailWidth *= 0.5 - innerY = headLen - (headWidth-tailWidth) * np.tan(baseAngle*np.pi/180.) + innerY = headLen - (headWidth-tailWidth) * math.tan(math.radians(baseAngle)) path.lineTo(innerY, -tailWidth) path.lineTo(headLen + tailLen, -tailWidth) path.lineTo(headLen + tailLen, tailWidth) @@ -458,7 +452,7 @@ def eq(a, b): 1. Returns True if a IS b, even if a==b still evaluates to False. 2. While a is b will catch the case with np.nan values, special handling is done for distinct - float('nan') instances using np.isnan. + float('nan') instances using math.isnan. 3. Tests for equivalence using ==, but silently ignores some common exceptions that can occur (AtrtibuteError, ValueError). 4. When comparing arrays, returns False if the array shapes are not the same. @@ -472,7 +466,7 @@ def eq(a, b): # The above catches np.nan, but not float('nan') if isinstance(a, float) and isinstance(b, float): - if np.isnan(a) and np.isnan(b): + if math.isnan(a) and math.isnan(b): return True # Avoid comparing large arrays against scalars; this is expensive and we know it should return False. diff --git a/pyqtgraph/graphicsItems/ArrowItem.py b/pyqtgraph/graphicsItems/ArrowItem.py index 77b6c44c..2d79d360 100644 --- a/pyqtgraph/graphicsItems/ArrowItem.py +++ b/pyqtgraph/graphicsItems/ArrowItem.py @@ -1,3 +1,4 @@ +from math import hypot from ..Qt import QtGui, QtCore from .. import functions as fn import numpy as np @@ -135,7 +136,7 @@ class ArrowItem(QtGui.QGraphicsPathItem): pad = 0 if self.opts['pxMode']: br = self.boundingRect() - pad += (br.width()**2 + br.height()**2) ** 0.5 + pad += hypot(br.width(), br.height()) pen = self.pen() if pen.isCosmetic(): pad += max(1, pen.width()) * 0.7072 diff --git a/pyqtgraph/graphicsItems/AxisItem.py b/pyqtgraph/graphicsItems/AxisItem.py index 37490d9d..28459923 100644 --- a/pyqtgraph/graphicsItems/AxisItem.py +++ b/pyqtgraph/graphicsItems/AxisItem.py @@ -4,6 +4,7 @@ from ..python2_3 import asUnicode import numpy as np from ..Point import Point from .. import debug as debug +from math import ceil, floor, log, log10, isfinite import sys import weakref from .. import functions as fn @@ -511,7 +512,7 @@ class AxisItem(GraphicsWidget): def setRange(self, mn, mx): """Set the range of values displayed by the axis. Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView `""" - if any(np.isinf((mn, mx))) or any(np.isnan((mn, mx))): + if not isfinite(mn) or not isfinite(mx): raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx))) self.range = [mn, mx] if self.autoSIPrefix: @@ -680,13 +681,13 @@ class AxisItem(GraphicsWidget): return [] ## decide optimal minor tick spacing in pixels (this is just aesthetics) - optimalTickCount = max(2., np.log(size)) + optimalTickCount = max(2., log(size)) ## optimal minor tick spacing optimalSpacing = dif / optimalTickCount ## the largest power-of-10 spacing which is smaller than optimal - p10unit = 10 ** np.floor(np.log10(optimalSpacing)) + p10unit = 10 ** floor(log10(optimalSpacing)) ## Determine major/minor tick spacings which flank the optimal spacing. intervals = np.array([1., 2., 10., 20., 100.]) * p10unit @@ -758,7 +759,7 @@ class AxisItem(GraphicsWidget): spacing, offset = tickLevels[i] ## determine starting tick - start = (np.ceil((minVal-offset) / spacing) * spacing) + offset + start = (ceil((minVal-offset) / spacing) * spacing) + offset ## determine number of ticks num = int((maxVal-start) / spacing) + 1 @@ -766,7 +767,7 @@ class AxisItem(GraphicsWidget): ## remove any ticks that were present in higher levels ## we assume here that if the difference between a tick value and a previously seen tick value ## is less than spacing/100, then they are 'equal' and we can ignore the new tick. - values = list(filter(lambda x: all(np.abs(allValues-x) > spacing/self.scale*0.01), values)) + values = list(filter(lambda x: np.all(np.abs(allValues-x) > spacing/self.scale*0.01), values)) allValues = np.concatenate([allValues, values]) ticks.append((spacing/self.scale, values)) @@ -795,8 +796,8 @@ class AxisItem(GraphicsWidget): ticks.append((spacing, t)) if len(ticks) < 3: - v1 = int(np.floor(minVal)) - v2 = int(np.ceil(maxVal)) + v1 = int(floor(minVal)) + v2 = int(ceil(maxVal)) #major = list(range(v1+1, v2)) minor = [] @@ -822,7 +823,7 @@ class AxisItem(GraphicsWidget): if self.logMode: return self.logTickStrings(values, scale, spacing) - places = max(0, np.ceil(-np.log10(spacing*scale))) + places = max(0, ceil(-log10(spacing*scale))) strings = [] for v in values: vs = v * scale @@ -969,7 +970,7 @@ class AxisItem(GraphicsWidget): if lineAlpha is None: lineAlpha = 255 / (i+1) if self.grid is not False: - lineAlpha *= self.grid/255. * np.clip((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.) + lineAlpha *= self.grid/255. * fn.clip_scalar((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.) elif isinstance(lineAlpha, float): lineAlpha *= 255 lineAlpha = max(0, int(round(lineAlpha))) diff --git a/pyqtgraph/graphicsItems/CurvePoint.py b/pyqtgraph/graphicsItems/CurvePoint.py index 368a0a82..c7052ddb 100644 --- a/pyqtgraph/graphicsItems/CurvePoint.py +++ b/pyqtgraph/graphicsItems/CurvePoint.py @@ -1,6 +1,7 @@ +from math import atan2, degrees from ..Qt import QtGui, QtCore from . import ArrowItem -import numpy as np +from ..functions import clip_scalar from ..Point import Point import weakref from .GraphicsObject import GraphicsObject @@ -61,26 +62,26 @@ class CurvePoint(GraphicsObject): pos = self.property('position') if 'QVariant' in repr(pos): ## need to support 2 APIs :( pos = pos.toDouble()[0] - index = (len(x)-1) * np.clip(pos, 0.0, 1.0) + index = (len(x)-1) * clip_scalar(pos, 0.0, 1.0) if index != int(index): ## interpolate floating-point values i1 = int(index) - i2 = np.clip(i1+1, 0, len(x)-1) + i2 = clip_scalar(i1+1, 0, len(x)-1) s2 = index-i1 s1 = 1.0-s2 newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2) else: index = int(index) - i1 = np.clip(index-1, 0, len(x)-1) - i2 = np.clip(index+1, 0, len(x)-1) + i1 = clip_scalar(index-1, 0, len(x)-1) + i2 = clip_scalar(index+1, 0, len(x)-1) newPos = (x[index], y[index]) p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1])) p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2])) - ang = np.arctan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians + rads = atan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians self.resetTransform() if self._rotate: - self.setRotation(180 + np.rad2deg(ang)) ## takes degrees + self.setRotation(180 + degrees(rads)) QtGui.QGraphicsItem.setPos(self, *newPos) return True diff --git a/pyqtgraph/graphicsItems/GraphicsItem.py b/pyqtgraph/graphicsItems/GraphicsItem.py index 61f48a45..273d419f 100644 --- a/pyqtgraph/graphicsItems/GraphicsItem.py +++ b/pyqtgraph/graphicsItems/GraphicsItem.py @@ -1,4 +1,5 @@ import warnings +from math import hypot from collections import OrderedDict from functools import reduce from ..Qt import QtGui, QtCore, isQObjectAlive @@ -308,7 +309,7 @@ class GraphicsItem(object): v = self.pixelVectors() if v == (None, None): return None, None - return (v[0].x()**2+v[0].y()**2)**0.5, (v[1].x()**2+v[1].y()**2)**0.5 + return (hypot(v[0].x(), v[0].y()), hypot(v[1].x(), v[1].y())) # lengths def pixelWidth(self): ## deprecated diff --git a/pyqtgraph/graphicsItems/GridItem.py b/pyqtgraph/graphicsItems/GridItem.py index 4cb30bf8..312d7b62 100644 --- a/pyqtgraph/graphicsItems/GridItem.py +++ b/pyqtgraph/graphicsItems/GridItem.py @@ -126,8 +126,7 @@ class GridItem(UIGraphicsItem): for i in range(self.grid_depth - 1, -1, -1): dist = br-ul nlTarget = 10.**i - - d = 10. ** np.floor(np.log10(abs(dist/nlTarget))+0.5) + d = 10. ** np.floor(np.log10(np.abs(dist/nlTarget))+0.5) for ax in range(0,2): ts = self.opts['tickSpacing'][ax] try: @@ -141,11 +140,6 @@ class GridItem(UIGraphicsItem): br1 = np.ceil(br / d) * d dist = br1-ul1 nl = (dist / d) + 0.5 - #print "level", i - #print " dim", dim - #print " dist", dist - #print " d", d - #print " nl", nl for ax in range(0,2): ## Draw grid for both axes if i >= len(self.opts['tickSpacing'][ax]): continue @@ -153,7 +147,7 @@ class GridItem(UIGraphicsItem): continue ppl = dim[ax] / nl[ax] - c = np.clip(5 * (ppl-3), 0., 50.).astype(int) + c = int(fn.clip_scalar(5 * (ppl-3), 0, 50)) linePen = self.opts['pen'] lineColor = self.opts['pen'].color() diff --git a/pyqtgraph/graphicsItems/InfiniteLine.py b/pyqtgraph/graphicsItems/InfiniteLine.py index 74a8da95..c64a4684 100644 --- a/pyqtgraph/graphicsItems/InfiniteLine.py +++ b/pyqtgraph/graphicsItems/InfiniteLine.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from ..Qt import QtGui, QtCore from ..Point import Point from .GraphicsObject import GraphicsObject @@ -359,7 +360,7 @@ class InfiniteLine(GraphicsObject): up = tr.map(Point(left, 1)) dif = end - start length = Point(dif).length() - angle = np.arctan2(dif.y(), dif.x()) * 180 / np.pi + angle = degrees(atan2(dif.y(), dif.x())) p.translate(start) p.rotate(angle) @@ -592,7 +593,7 @@ class InfLineLabel(TextItem): return rel = self._posToRel(ev.pos()) - self.orthoPos = np.clip(self._startPosition + rel - self._cursorOffset, 0, 1) + self.orthoPos = fn.clip_scalar(self._startPosition + rel - self._cursorOffset, 0., 1.) self.updatePosition() if ev.isFinish(): self._moving = False diff --git a/pyqtgraph/graphicsItems/NonUniformImage.py b/pyqtgraph/graphicsItems/NonUniformImage.py index 2b697767..fc42bf27 100644 --- a/pyqtgraph/graphicsItems/NonUniformImage.py +++ b/pyqtgraph/graphicsItems/NonUniformImage.py @@ -1,4 +1,5 @@ from ..Qt import QtGui, QtCore +import math import numpy as np from ..colormap import ColorMap from .GraphicsObject import GraphicsObject @@ -94,7 +95,7 @@ class NonUniformImage(GraphicsObject): value = 0.0 elif np.isposinf(value): value = 1.0 - elif np.isnan(value): + elif math.isnan(value): continue # ignore NaN else: value = (value - mn) / (mx - mn) # normalize diff --git a/pyqtgraph/graphicsItems/PlotCurveItem.py b/pyqtgraph/graphicsItems/PlotCurveItem.py index 39003813..06f9bdee 100644 --- a/pyqtgraph/graphicsItems/PlotCurveItem.py +++ b/pyqtgraph/graphicsItems/PlotCurveItem.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from ..Qt import QtCore, QtGui, QtWidgets HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget') - +import math import warnings import numpy as np from .GraphicsObject import GraphicsObject @@ -131,6 +131,8 @@ class PlotCurveItem(GraphicsObject): elif ax == 1: d = y d2 = x + else: + raise ValueError("Invalid axis value") ## If an orthogonal range is specified, mask the data now if orthoRange is not None: @@ -149,7 +151,7 @@ class PlotCurveItem(GraphicsObject): # All-NaN data is acceptable; Explicit numpy warning is not needed. warnings.simplefilter("ignore") b = (np.nanmin(d), np.nanmax(d)) - if any(np.isinf(b)): + if math.isinf(b[0]) or math.isinf(b[1]): mask = np.isfinite(d) d = d[mask] if len(d) == 0: diff --git a/pyqtgraph/graphicsItems/PlotDataItem.py b/pyqtgraph/graphicsItems/PlotDataItem.py index 2c07798b..49d8ef61 100644 --- a/pyqtgraph/graphicsItems/PlotDataItem.py +++ b/pyqtgraph/graphicsItems/PlotDataItem.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import warnings +import math import numpy as np from .. import metaarray as metaarray from ..Qt import QtCore @@ -660,7 +661,7 @@ class PlotDataItem(GraphicsObject): eps = np.finfo(y.dtype).eps else: eps = 1 - y = np.sign(y) * np.log10(np.abs(y)+eps) + y = np.copysign(np.log10(np.abs(y)+eps), y) ds = self.opts['downsample'] if not isinstance(ds, int): @@ -799,10 +800,10 @@ class PlotDataItem(GraphicsObject): # All-NaN data is handled by returning None; Explicit numpy warning is not needed. warnings.simplefilter("ignore") ymin = np.nanmin(self.yData) - if np.isnan( ymin ): + if math.isnan( ymin ): return None # most likely case for all-NaN data xmin = np.nanmin(self.xData) - if np.isnan( xmin ): + if math.isnan( xmin ): return None # less likely case for all-NaN data ymax = np.nanmax(self.yData) xmax = np.nanmax(self.xData) diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py index a2affcf7..8da9ba39 100644 --- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py +++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py @@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget): if y is not None: self.ctrl.yGridCheck.setChecked(y) if alpha is not None: - v = np.clip(alpha, 0, 1)*self.ctrl.gridAlphaSlider.maximum() + v = fn.clip_scalar(alpha, 0., 1.)*self.ctrl.gridAlphaSlider.maximum() self.ctrl.gridAlphaSlider.setValue(v) def close(self): diff --git a/pyqtgraph/graphicsItems/ROI.py b/pyqtgraph/graphicsItems/ROI.py index c2cb49ed..6c5f8418 100644 --- a/pyqtgraph/graphicsItems/ROI.py +++ b/pyqtgraph/graphicsItems/ROI.py @@ -15,9 +15,9 @@ of how to build an ROI at the bottom of the file. from ..Qt import QtCore, QtGui import numpy as np #from numpy.linalg import norm -from ..Point import * +from ..Point import Point from ..SRTTransform import SRTTransform -from math import cos, sin +from math import atan2, cos, sin, hypot, radians from .. import functions as fn from .GraphicsObject import GraphicsObject from .UIGraphicsItem import UIGraphicsItem @@ -141,12 +141,13 @@ class ROI(GraphicsObject): maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None, pen=None, hoverPen=None, handlePen=None, handleHoverPen=None, - movable=True, rotatable=True, resizable=True, removable=False): + movable=True, rotatable=True, resizable=True, removable=False, + aspectLocked=False): GraphicsObject.__init__(self, parent) self.setAcceptedMouseButtons(QtCore.Qt.NoButton) pos = Point(pos) size = Point(size) - self.aspectLocked = False + self.aspectLocked = aspectLocked self.translatable = movable self.rotatable = rotatable self.resizable = resizable @@ -678,7 +679,7 @@ class ROI(GraphicsObject): The format returned is a list of (name, pos) tuples. """ - if index == None: + if index is None: positions = [] for h in self.handles: positions.append((h['name'], h['pos'])) @@ -691,7 +692,7 @@ class ROI(GraphicsObject): The format returned is a list of (name, pos) tuples. """ - if index == None: + if index is None: positions = [] for h in self.handles: positions.append((h['name'], h['item'].scenePos())) @@ -1247,8 +1248,8 @@ class ROI(GraphicsObject): vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin - lvx = np.sqrt(vx.x()**2 + vx.y()**2) - lvy = np.sqrt(vy.x()**2 + vy.y()**2) + lvx = hypot(vx.x(), vx.y()) # length + lvy = hypot(vy.x(), vy.y()) # length ##img.width is number of pixels, not width of item. ##need pxWidth and pxHeight instead of pxLen ? sx = 1.0 / lvx @@ -1298,7 +1299,7 @@ class ROI(GraphicsObject): """Return global transformation (rotation angle+translation) required to move from relative state to current state. If relative state isn't specified, then we use the state of the ROI when mouse is pressed.""" - if relativeTo == None: + if relativeTo is None: relativeTo = self.preMoveState st = self.getState() @@ -1425,7 +1426,7 @@ class Handle(UIGraphicsItem): menu = self.scene().addParentContextMenus(self, self.getMenu(), ev) ## Make sure it is still ok to remove this handle - removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois]) + removeAllowed = all(r.checkRemoveHandle(self) for r in self.rois) self.removeAction.setEnabled(removeAllowed) pos = ev.screenPos() menu.popup(QtCore.QPoint(pos.x(), pos.y())) @@ -1481,7 +1482,7 @@ class Handle(UIGraphicsItem): size = self.radius self.path = QtGui.QPainterPath() ang = self.startAng - dt = 2*np.pi / self.sides + dt = 2 * np.pi / self.sides for i in range(0, self.sides+1): x = size * cos(ang) y = size * sin(ang) @@ -1518,13 +1519,13 @@ class Handle(UIGraphicsItem): return None v = dt.map(QtCore.QPointF(1, 0)) - dt.map(QtCore.QPointF(0, 0)) - va = np.arctan2(v.y(), v.x()) + va = atan2(v.y(), v.x()) dti = fn.invertQTransform(dt) devPos = dt.map(QtCore.QPointF(0,0)) tr = QtGui.QTransform() tr.translate(devPos.x(), devPos.y()) - tr.rotate(va * 180. / 3.1415926) + tr.rotateRadians(va) return dti.map(tr.map(self.path)) @@ -1663,7 +1664,7 @@ class LineROI(ROI): d = pos2-pos1 l = d.length() ang = Point(1, 0).angle(d) - ra = ang * np.pi / 180. + ra = radians(ang if ang is not None else 0.) c = Point(-width/2. * sin(ra), -width/2. * cos(ra)) pos1 = pos1 + c @@ -1671,9 +1672,6 @@ class LineROI(ROI): self.addScaleRotateHandle([0, 0.5], [1, 0.5]) self.addScaleRotateHandle([1, 0.5], [0, 0.5]) self.addScaleHandle([0.5, 1], [0.5, 0.5]) - - - class MultiRectROI(QtGui.QGraphicsObject): @@ -1887,7 +1885,7 @@ class EllipseROI(ROI): h = arr.shape[axes[1]] ## generate an ellipsoidal mask - mask = np.fromfunction(lambda x,y: (((x+0.5)/(w/2.)-1)**2+ ((y+0.5)/(h/2.)-1)**2)**0.5 < 1, (w, h)) + mask = np.fromfunction(lambda x,y: np.hypot(((x+0.5)/(w/2.)-1), ((y+0.5)/(h/2.)-1)) < 1, (w, h)) # reshape to match array axes if axes[0] > axes[1]: @@ -1914,7 +1912,7 @@ class EllipseROI(ROI): center = br.center() r1 = br.width() / 2. r2 = br.height() / 2. - theta = np.linspace(0, 2*np.pi, 24) + theta = np.linspace(0, 2 * np.pi, 24) x = center.x() + r1 * np.cos(theta) y = center.y() + r2 * np.sin(theta) path.moveTo(x[0], y[0]) @@ -1944,8 +1942,7 @@ class CircleROI(EllipseROI): if radius is None: raise TypeError("Must provide either size or radius.") size = (radius*2, radius*2) - EllipseROI.__init__(self, pos, size, **args) - self.aspectLocked = True + EllipseROI.__init__(self, pos, size, aspectLocked=True, **args) def _addHandles(self): self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5]) @@ -2310,16 +2307,15 @@ class CrosshairROI(ROI): """A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable.""" def __init__(self, pos=None, size=None, **kargs): - if size == None: + if size is None: size=[1,1] - if pos == None: + if pos is None: pos = [0,0] self._shape = None - ROI.__init__(self, pos, size, **kargs) + ROI.__init__(self, pos, size, aspectLocked=True, **kargs) self.sigRegionChanged.connect(self.invalidate) self.addScaleRotateHandle(Point(1, 0), Point(0, 0)) - self.aspectLocked = True def invalidate(self): self._shape = None @@ -2394,8 +2390,7 @@ class TriangleROI(ROI): """ def __init__(self, pos, size, **args): - ROI.__init__(self, pos, [size, size], **args) - self.aspectLocked = True + ROI.__init__(self, pos, [size, size], aspectLocked=True, **args) angles = np.linspace(0, np.pi * 4 / 3, 3) verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0 self.poly = QtGui.QPolygonF() diff --git a/pyqtgraph/graphicsItems/ScatterPlotItem.py b/pyqtgraph/graphicsItems/ScatterPlotItem.py index 3d35f8fc..080a8b24 100644 --- a/pyqtgraph/graphicsItems/ScatterPlotItem.py +++ b/pyqtgraph/graphicsItems/ScatterPlotItem.py @@ -6,6 +6,7 @@ try: except ImportError: imap = map import itertools +import math import numpy as np import weakref from ..Qt import QtGui, QtCore, QT_LIB @@ -116,7 +117,7 @@ def renderSymbol(symbol, size, pen, brush, device=None): for more information). """ ## Render a spot with the given parameters to a pixmap - penPxWidth = max(np.ceil(pen.widthF()), 1) + penPxWidth = max(math.ceil(pen.widthF()), 1) if device is None: device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32) device.fill(0) @@ -950,6 +951,8 @@ class ScatterPlotItem(GraphicsObject): elif ax == 1: d = self.data['y'] d2 = self.data['x'] + else: + raise ValueError("Invalid axis value") if orthoRange is not None: mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1]) @@ -1073,7 +1076,7 @@ class ScatterPlotItem(GraphicsObject): # Map points using painter's world transform so they are drawn with pixel-valued sizes pts = np.vstack([self.data['x'], self.data['y']]) pts = fn.transformCoordinates(p.transform(), pts) - pts = np.clip(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault. + pts = fn.clip_array(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault. p.resetTransform() if self.opts['useCache'] and self._exportOpts is False: diff --git a/pyqtgraph/graphicsItems/TargetItem.py b/pyqtgraph/graphicsItems/TargetItem.py index 26f40b9b..1a4d5144 100644 --- a/pyqtgraph/graphicsItems/TargetItem.py +++ b/pyqtgraph/graphicsItems/TargetItem.py @@ -1,5 +1,4 @@ -from math import atan2, pi - +from math import atan2 from ..Qt import QtGui, QtCore from ..Point import Point from .. import functions as fn @@ -243,7 +242,7 @@ class TargetItem(UIGraphicsItem): tr = QtGui.QTransform() tr.translate(devPos.x(), devPos.y()) va = atan2(v.y(), v.x()) - tr.rotate(va * 180.0 / pi) + tr.rotateRadians(va) tr.scale(self.scale, self.scale) return dti.map(tr.map(self._path)) diff --git a/pyqtgraph/graphicsItems/TextItem.py b/pyqtgraph/graphicsItems/TextItem.py index 8dcb04f6..2af87d5d 100644 --- a/pyqtgraph/graphicsItems/TextItem.py +++ b/pyqtgraph/graphicsItems/TextItem.py @@ -1,4 +1,4 @@ -import numpy as np +from math import atan2, degrees from ..Qt import QtCore, QtGui from ..Point import Point from .. import functions as fn @@ -208,7 +208,7 @@ class TextItem(GraphicsObject): angle = -self.angle if self.rotateAxis is not None: d = pt.map(self.rotateAxis) - pt.map(Point(0, 0)) - a = np.arctan2(d.y(), d.x()) * 180 / np.pi + a = degrees(atan2(d.y(), d.x())) angle += a t.rotate(angle) self.setTransform(t) diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index 6725d78b..8d60c577 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import weakref import sys +import math from copy import deepcopy import numpy as np from ...Qt import QtGui, QtCore @@ -549,10 +550,9 @@ class ViewBox(GraphicsWidget): dy = 1 mn -= dy*0.5 mx += dy*0.5 - xpad = 0.0 # Make sure no nan/inf get through - if not all(np.isfinite([mn, mx])): + if not math.isfinite(mn) or not math.isfinite(mx): raise Exception("Cannot set range [%s, %s]" % (str(mn), str(mx))) # Apply padding @@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget): def suggestPadding(self, axis): l = self.width() if axis==0 else self.height() if l > 0: - padding = np.clip(1./(l**0.5), 0.02, 0.1) + padding = fn.clip_scalar(1./(l**0.5), 0.02, 0.1) else: padding = 0.02 return padding @@ -903,11 +903,11 @@ class ViewBox(GraphicsWidget): targetRect[ax] = childRange[ax] args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] - # check for and ignore bad ranges + # check for and ignore bad ranges for k in ['xRange', 'yRange']: if k in args: - if not np.all(np.isfinite(args[k])): - r = args.pop(k) + if not math.isfinite(args[k][0]) or not math.isfinite(args[k][1]): + _ = args.pop(k) #print("Warning: %s is invalid: %s" % (k, str(r)) if len(args) == 0: @@ -1369,10 +1369,20 @@ class ViewBox(GraphicsWidget): xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0]) yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1]) pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding() - if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any(): + if ( + xr is None or + (xr[0] is None and xr[1] is None) or + not math.isfinite(xr[0]) or + not math.isfinite(xr[1]) + ): useX = False xr = (0,0) - if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any(): + if ( + yr is None or + (yr[0] is None and yr[1] is None) or + not math.isfinite(yr[0]) or + not math.isfinite(yr[1]) + ): useY = False yr = (0,0) diff --git a/pyqtgraph/imageview/ImageView.py b/pyqtgraph/imageview/ImageView.py index 62c030a4..f934b36d 100644 --- a/pyqtgraph/imageview/ImageView.py +++ b/pyqtgraph/imageview/ImageView.py @@ -12,14 +12,16 @@ Widget used for displaying 2D or 3D data. Features: - ROI plotting - Image normalization through a variety of methods """ -import os, sys +import os +from math import log10 import numpy as np from ..Qt import QtCore, QtGui, QT_LIB +from .. import functions as fn import importlib ui_template = importlib.import_module( f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__) - + from ..graphicsItems.ImageItem import * from ..graphicsItems.ROI import * from ..graphicsItems.LinearRegionItem import * @@ -272,7 +274,7 @@ class ImageView(QtGui.QWidget): if not isinstance(img, np.ndarray): required = ['dtype', 'max', 'min', 'ndim', 'shape', 'size'] - if not all([hasattr(img, attr) for attr in required]): + if not all(hasattr(img, attr) for attr in required): raise TypeError("Image must be NumPy array or any object " "that provides compatible attributes/methods:\n" " %s" % str(required)) @@ -512,7 +514,7 @@ class ImageView(QtGui.QWidget): def setCurrentIndex(self, ind): """Set the currently displayed frame index.""" - index = np.clip(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1) + index = fn.clip_scalar(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1) self.ignorePlaying = True # Implicitly call timeLineChanged self.timeLine.setValue(self.tVals[index]) @@ -807,7 +809,7 @@ class ImageView(QtGui.QWidget): img = self.getProcessedImage() if self.hasTimeAxis(): base, ext = os.path.splitext(fileName) - fmt = "%%s%%0%dd%%s" % int(np.log10(img.shape[0])+1) + fmt = "%%s%%0%dd%%s" % int(log10(img.shape[0])+1) for i in range(img.shape[0]): self.imageItem.setImage(img[i], autoLevels=False) self.imageItem.save(fmt % (base, i, ext)) diff --git a/pyqtgraph/metaarray/MetaArray.py b/pyqtgraph/metaarray/MetaArray.py index 169ff43c..b07f66ab 100644 --- a/pyqtgraph/metaarray/MetaArray.py +++ b/pyqtgraph/metaarray/MetaArray.py @@ -124,7 +124,7 @@ class MetaArray(object): nameTypes = [basestring, tuple] @staticmethod def isNameType(var): - return any([isinstance(var, t) for t in MetaArray.nameTypes]) + return any(isinstance(var, t) for t in MetaArray.nameTypes) ## methods to wrap from embedded ndarray / HDF5 diff --git a/pyqtgraph/opengl/GLViewWidget.py b/pyqtgraph/opengl/GLViewWidget.py index 2dace1fa..098d7ce9 100644 --- a/pyqtgraph/opengl/GLViewWidget.py +++ b/pyqtgraph/opengl/GLViewWidget.py @@ -5,6 +5,7 @@ import numpy as np from .. import Vector from .. import functions as fn import warnings +from math import cos, sin, tan, radians ##Vector = QtGui.QVector3D ShareWidget = None @@ -174,7 +175,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): nearClip = dist * 0.001 farClip = dist * 1000. - r = nearClip * np.tan(fov * 0.5 * np.pi / 180.) + r = nearClip * tan(0.5 * radians(fov)) t = r * h / w ## Note that X0 and width in these equations must be the values used in viewport @@ -316,12 +317,12 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): pos = center - self.opts['rotation'].rotatedVector( Vector(0,0,dist) ) else: # using 'euler' rotation method - elev = self.opts['elevation'] * np.pi / 180 - azim = self.opts['azimuth'] * np.pi / 180 + elev = radians(self.opts['elevation']) + azim = radians(self.opts['azimuth']) pos = Vector( - center.x() + dist * np.cos(elev) * np.cos(azim), - center.y() + dist * np.cos(elev) * np.sin(azim), - center.z() + dist * np.sin(elev) + center.x() + dist * cos(elev) * cos(azim), + center.y() + dist * cos(elev) * sin(azim), + center.z() + dist * sin(elev) ) return pos @@ -335,7 +336,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): self.opts['rotation'] = q else: # default euler rotation method self.opts['azimuth'] += azim - self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90) + self.opts['elevation'] = fn.clip_scalar(self.opts['elevation'] + elev, -90., 90.) self.update() def pan(self, dx, dy, dz, relative='global'): @@ -378,7 +379,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): cPos = self.cameraPosition() cVec = self.opts['center'] - cPos dist = cVec.length() ## distance from camera to center - xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point + xDist = dist * 2. * tan(0.5 * radians(self.opts['fov'])) ## approx. width of view at distance of center point xScale = xDist / self.width() zVec = QtGui.QVector3D(0,0,1) xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized() @@ -399,15 +400,15 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): # apply translation self.opts['center'] += scale_factor * (xv*-dx + yv*dy + zv*dz) else: # use default euler rotation method - elev = np.radians(self.opts['elevation']) - azim = np.radians(self.opts['azimuth']) - fov = np.radians(self.opts['fov']) + elev = radians(self.opts['elevation']) + azim = radians(self.opts['azimuth']) + fov = radians(self.opts['fov']) dist = (self.opts['center'] - self.cameraPosition()).length() - fov_factor = np.tan(fov / 2) * 2 + fov_factor = tan(fov / 2) * 2 scale_factor = dist * fov_factor / self.width() - z = scale_factor * np.cos(elev) * dy - x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy) - y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy) + z = scale_factor * cos(elev) * dy + x = scale_factor * (sin(azim) * dx - sin(elev) * cos(azim) * dy) + y = scale_factor * (cos(azim) * dx + sin(elev) * sin(azim) * dy) self.opts['center'] += QtGui.QVector3D(x, -y, z) else: raise ValueError("relative argument must be global, view, or view-upright") @@ -425,7 +426,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): dist = ((pos-cam)**2).sum(axis=-1)**0.5 else: dist = (pos-cam).length() - xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) + xDist = dist * 2. * tan(0.5 * radians(self.opts['fov'])) return xDist / self.width() def mousePressEvent(self, ev): diff --git a/pyqtgraph/opengl/items/GLScatterPlotItem.py b/pyqtgraph/opengl/items/GLScatterPlotItem.py index 5d81515b..834ede0b 100644 --- a/pyqtgraph/opengl/items/GLScatterPlotItem.py +++ b/pyqtgraph/opengl/items/GLScatterPlotItem.py @@ -3,7 +3,8 @@ from OpenGL.GL import * from OpenGL.arrays import vbo from .. GLGraphicsItem import GLGraphicsItem from .. import shaders -from ... import QtGui +from ... import functions as fn +from ...Qt import QtGui import numpy as np __all__ = ['GLScatterPlotItem'] @@ -61,13 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem): ## Generate texture for rendering points w = 64 - def fn(x,y): - r = ((x-(w-1)/2.)**2 + (y-(w-1)/2.)**2) ** 0.5 - return 255 * (w/2. - np.clip(r, w/2.-1.0, w/2.)) + def genTexture(x,y): + r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.)) + return 255 * (w / 2 - fn.clip_array(r, w / 2 - 1, w / 2)) pData = np.empty((w, w, 4)) pData[:] = 255 - pData[:,:,3] = np.fromfunction(fn, pData.shape[:2]) - #print pData.shape, pData.min(), pData.max() + pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2]) pData = pData.astype(np.ubyte) if getattr(self, "pointTexture", None) is None: diff --git a/pyqtgraph/parametertree/SystemSolver.py b/pyqtgraph/parametertree/SystemSolver.py index cac42483..d7b6bef8 100644 --- a/pyqtgraph/parametertree/SystemSolver.py +++ b/pyqtgraph/parametertree/SystemSolver.py @@ -1,6 +1,8 @@ from collections import OrderedDict import numpy as np import copy +from math import log2 +from .. import functions as fn class SystemSolver(object): @@ -390,15 +392,12 @@ if __name__ == '__main__': sh = self.shutter # this raises RuntimeError if shutter has not # been specified ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light) - ap = np.clip(ap, 2.0, 16.0) + ap = fn.clip_scalar(ap, 2.0, 16.0) except RuntimeError: # program mode; we can select a suitable shutter # value at the same time. sh = (1./60.) raise - - - return ap def _balance(self): @@ -406,10 +405,8 @@ if __name__ == '__main__': light = self.lightMeter sh = self.shutter ap = self.aperture - fl = self.flash - bal = (4.0 / ap) * (sh / (1./60.)) * (iso / 100.) * (2 ** light) - return np.log2(bal) + return log2(bal) camera = Camera() diff --git a/pyqtgraph/widgets/ColorMapWidget.py b/pyqtgraph/widgets/ColorMapWidget.py index 5d1e5681..9f2fafe8 100644 --- a/pyqtgraph/widgets/ColorMapWidget.py +++ b/pyqtgraph/widgets/ColorMapWidget.py @@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter): elif op == 'Set': colors[mask] = colors2[mask] - colors = np.clip(colors, 0, 1) + colors = fn.clip_array(colors, 0., 1.) if mode == 'byte': colors = (colors * 255).astype(np.ubyte) @@ -210,11 +210,11 @@ class RangeColorMapItem(ptree.types.SimpleParameter): def map(self, data): data = data[self.fieldName] - scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1) + scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1) cmap = self.value() colors = cmap.map(scaled, mode='float') - mask = np.isnan(data) | np.isinf(data) + mask = np.invert(np.isfinite(data)) nanColor = self['NaN'] nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.) colors[mask] = nanColor diff --git a/pyqtgraph/widgets/GraphicsView.py b/pyqtgraph/widgets/GraphicsView.py index 3054f0e7..d2b0f553 100644 --- a/pyqtgraph/widgets/GraphicsView.py +++ b/pyqtgraph/widgets/GraphicsView.py @@ -7,10 +7,7 @@ Distributed under MIT/X11 license. See license.txt for more information. from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB from ..Point import Point -import sys, os -from .FileDialog import FileDialog from ..GraphicsScene import GraphicsScene -import numpy as np from .. import functions as fn from .. import debug as debug from .. import getConfigOption @@ -378,7 +375,7 @@ class GraphicsView(QtGui.QGraphicsView): return if ev.buttons() == QtCore.Qt.RightButton: - delta = Point(np.clip(delta[0], -50, 50), np.clip(-delta[1], -50, 50)) + delta = Point(fn.clip_scalar(delta[0], -50, 50), fn.clip_scalar(-delta[1], -50, 50)) scale = 1.01 ** delta self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos)) self.sigDeviceRangeChanged.emit(self, self.range) diff --git a/pyqtgraph/widgets/JoystickButton.py b/pyqtgraph/widgets/JoystickButton.py index 4e4a335f..53674df2 100644 --- a/pyqtgraph/widgets/JoystickButton.py +++ b/pyqtgraph/widgets/JoystickButton.py @@ -1,4 +1,5 @@ -from ..Qt import QtGui, QtCore +from math import hypot +from ..Qt import QtGui, QtCore, mkQApp __all__ = ['JoystickButton'] @@ -41,7 +42,7 @@ class JoystickButton(QtGui.QPushButton): def setState(self, *xy): xy = list(xy) - d = (xy[0]**2 + xy[1]**2)**0.5 + d = hypot(xy[0], xy[1]) # length nxy = [0, 0] for i in [0,1]: if xy[i] == 0: @@ -84,7 +85,7 @@ class JoystickButton(QtGui.QPushButton): if __name__ == '__main__': - app = pg.mkQApp() + app = mkQApp() w = QtGui.QMainWindow() b = JoystickButton() w.setCentralWidget(b)