Merge pull request #1724 from j9ac9k/use-math-module-for-single-values

Use math module methods for scalars
This commit is contained in:
Ogi Moore 2021-04-23 23:10:17 -07:00 committed by GitHub
commit 6a708dc203
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 201 additions and 244 deletions

View File

@ -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) ## Add path to library (just for examples; you do not need this)
import initExample import initExample
from pyqtgraph.Qt import QtCore, QtGui import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
import pyqtgraph.opengl as gl import pyqtgraph.opengl as gl
@ -22,24 +22,17 @@ g = gl.GLGridItem()
g.scale(2,2,1) g.scale(2,2,1)
w.addItem(g) w.addItem(g)
import numpy as np
## Define a scalar field from which we will generate an isosurface ## Define a scalar field from which we will generate an isosurface
def psi(i, j, k, offset=(25, 25, 50)): def psi(i, j, k, offset=(25, 25, 50)):
x = i-offset[0] x = i-offset[0]
y = j-offset[1] y = j-offset[1]
z = k-offset[2] z = k-offset[2]
th = np.arctan2(z, (x**2+y**2)**0.5) th = np.arctan2(z, np.hypot(x, y))
phi = np.arctan2(y, x) r = np.sqrt(x**2 + y**2 + z **2)
r = (x**2 + y**2 + z **2)**0.5
a0 = 1 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) 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 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..") print("Generating scalar field..")
data = np.abs(np.fromfunction(psi, (50,50,100))) data = np.abs(np.fromfunction(psi, (50,50,100)))

View File

@ -29,17 +29,14 @@ gz = gl.GLGridItem()
gz.translate(0, 0, -10) gz.translate(0, 0, -10)
w.addItem(gz) w.addItem(gz)
def fn(x, y):
return np.cos((x**2 + y**2)**0.5)
n = 51 n = 51
y = np.linspace(-10,10,n) y = np.linspace(-10,10,n)
x = np.linspace(-10,10,100) x = np.linspace(-10,10,100)
for i in range(n): for i in range(n):
yi = np.array([y[i]]*100) yi = y[i]
d = (x**2 + yi**2)**0.5 d = np.hypot(x, yi)
z = 10 * np.cos(d) / (d+1) 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) plt = gl.GLLinePlotItem(pos=pts, color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True)
w.addItem(plt) w.addItem(plt)

View File

@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
import initExample import initExample
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph import functions as fn
from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl import pyqtgraph.opengl as gl
import numpy as np import numpy as np
@ -84,10 +85,10 @@ def update():
global phase, sp2, d2 global phase, sp2, d2
s = -np.cos(d2*2+phase) s = -np.cos(d2*2+phase)
color = np.empty((len(d2),4), dtype=np.float32) color = np.empty((len(d2),4), dtype=np.float32)
color[:,3] = np.clip(s * 0.1, 0, 1) color[:,3] = fn.clip_array(s * 0.1, 0., 1.)
color[:,0] = np.clip(s * 3.0, 0, 1) color[:,0] = fn.clip_array(s * 3.0, 0., 1.)
color[:,1] = np.clip(s * 1.0, 0, 1) color[:,1] = fn.clip_array(s * 1.0, 0., 1.)
color[:,2] = np.clip(s ** 3, 0, 1) color[:,2] = fn.clip_array(s ** 3, 0., 1.)
sp2.setData(color=color) sp2.setData(color=color)
phase -= 0.1 phase -= 0.1

View File

@ -7,9 +7,10 @@ Demonstrates GLVolumeItem for displaying volumetric data.
## Add path to library (just for examples; you do not need this) ## Add path to library (just for examples; you do not need this)
import initExample import initExample
import numpy as np
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl import pyqtgraph.opengl as gl
from pyqtgraph import functions as fn
app = pg.mkQApp("GLVolumeItem Example") app = pg.mkQApp("GLVolumeItem Example")
w = gl.GLViewWidget() w = gl.GLViewWidget()
@ -23,27 +24,21 @@ g = gl.GLGridItem()
g.scale(10, 10, 1) g.scale(10, 10, 1)
w.addItem(g) w.addItem(g)
import numpy as np
## Hydrogen electron probability density ## Hydrogen electron probability density
def psi(i, j, k, offset=(50,50,100)): def psi(i, j, k, offset=(50,50,100)):
x = i-offset[0] x = i-offset[0]
y = j-offset[1] y = j-offset[1]
z = k-offset[2] z = k-offset[2]
th = np.arctan2(z, (x**2+y**2)**0.5) th = np.arctan2(z, np.hypot(x, y))
phi = np.arctan2(y, x) r = np.sqrt(x**2 + y**2 + z **2)
r = (x**2 + y**2 + z **2)**0.5
a0 = 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) 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 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)) data = np.fromfunction(psi, (100,100,200))
positive = np.log(np.clip(data, 0, data.max())**2) positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**2)
negative = np.log(np.clip(-data, 0, -data.min())**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 = np.empty(data.shape + (4,), dtype=np.ubyte)
d2[..., 0] = positive * (255./positive.max()) d2[..., 0] = positive * (255./positive.max())

View File

@ -9,6 +9,7 @@ used to affect the appearance of a surface.
## Add path to library (just for examples; you do not need this) ## Add path to library (just for examples; you do not need this)
import initExample import initExample
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg import pyqtgraph as pg
import pyqtgraph.opengl as gl import pyqtgraph.opengl as gl
@ -23,9 +24,6 @@ g = gl.GLGridItem()
g.scale(2,2,1) g.scale(2,2,1)
w.addItem(g) w.addItem(g)
import numpy as np
md = gl.MeshData.sphere(rows=10, cols=20) md = gl.MeshData.sphere(rows=10, cols=20)
x = np.linspace(-8, 8, 6) x = np.linspace(-8, 8, 6)

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from math import atan2, asin, sin, cos, degrees, sqrt, hypot
import pyqtgraph as pg import pyqtgraph as pg
from pyqtgraph.Qt import QtGui, QtCore from pyqtgraph.Qt import QtGui, QtCore
import numpy as np import numpy as np
@ -49,7 +50,7 @@ class GlassDB:
B = list(map(float, [info['B1'], info['B2'], info['B3']])) B = list(map(float, [info['B1'], info['B2'], info['B3']]))
C = list(map(float, [info['C1'], info['C2'], info['C3']])) C = list(map(float, [info['C1'], info['C2'], info['C3']]))
w2 = (wl/1000.)**2 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 cache[wl] = n
return cache[wl] return cache[wl]
@ -249,10 +250,14 @@ class Lens(Optic):
p1 = surface.mapToItem(ray, p1) p1 = surface.mapToItem(ray, p1)
rd = ray['dir'] 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) 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) ray = Ray(parent=ray, ior=ior, dir=dp)
return [ray] return [ray]
@ -279,10 +284,10 @@ class Mirror(Optic):
if p1 is not None: if p1 is not None:
p1 = surface.mapToItem(ray, p1) p1 = surface.mapToItem(ray, p1)
rd = ray['dir'] rd = ray['dir']
a1 = np.arctan2(rd[1], rd[0]) a1 = atan2(rd[1], rd[0])
ar = a1 + np.pi - 2 * ai ar = a1 + np.pi - 2 * ai
ray.setEnd(p1) ray.setEnd(p1)
dp = Point(np.cos(ar), np.sin(ar)) dp = Point(cos(ar), sin(ar))
ray = Ray(parent=ray, dir=dp) ray = Ray(parent=ray, dir=dp)
else: else:
ray.setEnd(None) ray.setEnd(None)
@ -374,7 +379,7 @@ class CircleSurface(pg.GraphicsObject):
## half-height of surface can't be larger than radius ## half-height of surface can't be larger than radius
h2 = min(h2, abs(r)) h2 = min(h2, abs(r))
arc = QtCore.QRectF(0, -r, r*2, r*2) 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 a2 = -2*a1
a1 += 180. a1 += 180.
self.path.arcMoveTo(arc, a1) self.path.arcMoveTo(arc, a1)
@ -406,13 +411,13 @@ class CircleSurface(pg.GraphicsObject):
if abs(y) > h: if abs(y) > h:
return None, None return None, None
else: else:
return (Point(0, y), np.arctan2(dir[1], dir[0])) return (Point(0, y), atan2(dir[1], dir[0]))
else: else:
#print " curve" #print " curve"
## find intersection of circle and line (quadratic formula) ## find intersection of circle and line (quadratic formula)
dx = dir[0] dx = dir[0]
dy = dir[1] 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] D = p[0] * (p[1]+dy) - (p[0]+dx) * p[1]
idr2 = 1.0 / dr**2 idr2 = 1.0 / dr**2
disc = r**2 * dr**2 - D**2 disc = r**2 * dr**2 - D**2
@ -424,7 +429,6 @@ class CircleSurface(pg.GraphicsObject):
else: else:
sgn = 1 sgn = 1
br = self.path.boundingRect() br = self.path.boundingRect()
x1 = (D*dy + sgn*dx*disc2) * idr2 x1 = (D*dy + sgn*dx*disc2) * idr2
y1 = (-D*dx + abs(dy)*disc2) * idr2 y1 = (-D*dx + abs(dy)*disc2) * idr2
@ -436,19 +440,12 @@ class CircleSurface(pg.GraphicsObject):
pt = Point(x2, y2) pt = Point(x2, y2)
if not br.contains(x2+r, y2): if not br.contains(x2+r, y2):
return None, None return None, None
raise Exception("No intersection!")
norm = np.arctan2(pt[1], pt[0]) norm = atan2(pt[1], pt[0])
if r < 0: if r < 0:
norm += np.pi norm += np.pi
#print " norm:", norm*180/3.1415
dp = p - pt dp = p - pt
#print " dp:", dp ang = atan2(dp[1], dp[0])
ang = np.arctan2(dp[1], dp[0])
#print " ang:", ang*180/3.1415
#print " ai:", (ang-norm)*180/3.1415
#print " intersection:", pt
return pt + Point(r, 0), ang-norm return pt + Point(r, 0), ang-norm

View File

@ -6,14 +6,8 @@ Distributed under MIT/X11 license. See license.txt for more information.
""" """
from .Qt import QtCore 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): class Point(QtCore.QPointF):
"""Extension of QPointF which adds a few missing methods.""" """Extension of QPointF which adds a few missing methods."""
@ -93,25 +87,12 @@ class Point(QtCore.QPointF):
return self._math_('__pow__', a) return self._math_('__pow__', a)
def _math_(self, op, x): 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) x = Point(x)
return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1])) return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1]))
def length(self): def length(self):
"""Returns the vector length of this Point.""" """Returns the vector length of this Point."""
try: return hypot(self[0], self[1]) # length
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
def norm(self): def norm(self):
"""Returns a vector in the same direction with unit length.""" """Returns a vector in the same direction with unit length."""
@ -119,16 +100,8 @@ class Point(QtCore.QPointF):
def angle(self, a): def angle(self, a):
"""Returns the angle in degrees between this vector and the vector a.""" """Returns the angle in degrees between this vector and the vector a."""
n1 = self.length() rads = atan2(self.y(), self.x()) - atan2(a.y(), a.x())
n2 = a.length() return degrees(rads)
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
def dot(self, a): def dot(self, a):
"""Returns the dot product of a and this Point.""" """Returns the dot product of a and this Point."""
@ -147,7 +120,6 @@ class Point(QtCore.QPointF):
def __repr__(self): def __repr__(self):
return "Point(%f, %f)" % (self[0], self[1]) return "Point(%f, %f)" % (self[0], self[1])
def min(self): def min(self):
return min(self[0], self[1]) return min(self[0], self[1])

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from math import atan2, degrees
from .Qt import QtCore, QtGui from .Qt import QtCore, QtGui
from .Point import Point from .Point import Point
import numpy as np import numpy as np
@ -76,7 +77,7 @@ class SRTTransform(QtGui.QTransform):
self._state = { self._state = {
'pos': Point(p1), 'pos': Point(p1),
'scale': Point(dp2.length(), dp3.length() * sy), '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() self.update()

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from math import atan2, degrees
from .Qt import QtCore, QtGui from .Qt import QtCore, QtGui
from .Vector import Vector from .Vector import Vector
from .Transform3D import Transform3D from .Transform3D import Transform3D
@ -164,7 +165,7 @@ class SRTTransform3D(Transform3D):
sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd]) sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd])
## finally, we get the complete angle from arctan(sin/cos) ## 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: if self._state['angle'] == 0:
self._state['axis'] = (0,0,1) self._state['axis'] = (0,0,1)

View File

@ -4,9 +4,9 @@ Vector.py - Extension of QVector3D which adds a few missing methods.
Copyright 2010 Luke Campagnola Copyright 2010 Luke Campagnola
Distributed under MIT/X11 license. See license.txt for more information. Distributed under MIT/X11 license. See license.txt for more information.
""" """
from math import acos, degrees
from .Qt import QtGui, QtCore, QT_LIB from .Qt import QtGui, QtCore, QT_LIB
import numpy as np from . import functions as fn
class Vector(QtGui.QVector3D): class Vector(QtGui.QVector3D):
"""Extension of QVector3D which adds a few helpful methods.""" """Extension of QVector3D which adds a few helpful methods."""
@ -88,11 +88,11 @@ class Vector(QtGui.QVector3D):
if n1 == 0. or n2 == 0.: if n1 == 0. or n2 == 0.:
return None return None
## Probably this should be done with arctan2 instead.. ## 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) # c = self.crossProduct(a)
# if c > 0: # if c > 0:
# ang *= -1. # ang *= -1.
return ang * 180. / np.pi return degrees(rads)
def __abs__(self): def __abs__(self):
return Vector(abs(self.x()), abs(self.y()), abs(self.z())) return Vector(abs(self.x()), abs(self.y()), abs(self.z()))

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import numpy as np from ..Qt import QtGui, QtCore, QT_LIB
from ..Qt import QtGui, QtCore, QtSvg, QT_LIB from .. import functions as fn
from ..graphicsItems.ROI import ROI from ..graphicsItems.ROI import ROI
from .. import SRTTransform, ItemGroup from .. import SRTTransform, ItemGroup
import importlib import importlib
@ -240,7 +240,7 @@ class CanvasItem(QtCore.QObject):
self._graphicsItem.setOpacity(alpha) self._graphicsItem.setOpacity(alpha)
def setAlpha(self, 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): def alpha(self):
return self.alphaSlider.value() / 1023. return self.alphaSlider.value() / 1023.

View File

@ -202,7 +202,7 @@ def findRefPath(startObj, endObj, maxLen=8, restart=True, seen={}, path=None, ig
#print prefix+" FRAME" #print prefix+" FRAME"
continue continue
try: 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) #print prefix+" LOOP", objChainString([r]+path)
continue continue
except: except:
@ -282,7 +282,7 @@ def refPathString(chain):
o2 = chain[i] o2 = chain[i]
cont = False cont = False
if isinstance(o1, list) or isinstance(o1, tuple): 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) s += "[%d]" % o1.index(o2)
continue continue
#print " not list" #print " not list"

View File

@ -126,7 +126,7 @@ class SplitContainer(Container, QtGui.QSplitter):
def saveState(self): def saveState(self):
sizes = self.sizes() sizes = self.sizes()
if all([x == 0 for x in sizes]): if all(x == 0 for x in sizes):
sizes = [10] * len(sizes) sizes = [10] * len(sizes)
return {'sizes': sizes} return {'sizes': sizes}

View File

@ -60,19 +60,15 @@ def siScale(x, minVal=1e-25, allowUnicode=True):
if isinstance(x, decimal.Decimal): if isinstance(x, decimal.Decimal):
x = float(x) x = float(x)
try: try:
if np.isnan(x) or np.isinf(x): if not math.isfinite(x):
return(1, '') return(1, '')
except: except:
print(x, type(x))
raise raise
if abs(x) < minVal: if abs(x) < minVal:
m = 0 m = 0
x = 0
else: 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: if m == 0:
pref = '' pref = ''
elif m < -8 or m > 8: elif m < -8 or m > 8:
@ -84,7 +80,6 @@ def siScale(x, minVal=1e-25, allowUnicode=True):
pref = SI_PREFIXES_ASCII[m+8] pref = SI_PREFIXES_ASCII[m+8]
m1 = -3*m m1 = -3*m
p = 10.**m1 p = 10.**m1
return (p, pref) return (p, pref)
@ -268,6 +263,8 @@ def mkColor(*args):
g = int(c[2:4], 16) g = int(c[2:4], 16)
b = int(c[4:6], 16) b = int(c[4:6], 16)
a = int(c[6:8], 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): elif isinstance(args[0], QtGui.QColor):
return QtGui.QColor(args[0]) return QtGui.QColor(args[0])
elif np.issubdtype(type(args[0]), np.floating): elif np.issubdtype(type(args[0]), np.floating):
@ -275,10 +272,10 @@ def mkColor(*args):
a = 255 a = 255
elif hasattr(args[0], '__len__'): elif hasattr(args[0], '__len__'):
if len(args[0]) == 3: if len(args[0]) == 3:
(r, g, b) = args[0] r, g, b = args[0]
a = 255 a = 255
elif len(args[0]) == 4: elif len(args[0]) == 4:
(r, g, b, a) = args[0] r, g, b, a = args[0]
elif len(args[0]) == 2: elif len(args[0]) == 2:
return intColor(*args[0]) return intColor(*args[0])
else: else:
@ -288,16 +285,13 @@ def mkColor(*args):
else: else:
raise TypeError(err) raise TypeError(err)
elif len(args) == 3: elif len(args) == 3:
(r, g, b) = args r, g, b = args
a = 255 a = 255
elif len(args) == 4: elif len(args) == 4:
(r, g, b, a) = args r, g, b, a = args
else: else:
raise TypeError(err) raise TypeError(err)
args = [int(a) if np.isfinite(a) else 0 for a in (r, g, b, a)]
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))
return QtGui.QColor(*args) 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 *tailLen* is None, no tail will be drawn.
""" """
if headWidth is None: 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 = QtGui.QPainterPath()
path.moveTo(0,0) path.moveTo(0,0)
path.lineTo(headLen, -headWidth) path.lineTo(headLen, -headWidth)
if tailLen is None: 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) path.lineTo(innerY, 0)
else: else:
tailWidth *= 0.5 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(innerY, -tailWidth)
path.lineTo(headLen + tailLen, -tailWidth) path.lineTo(headLen + tailLen, -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. 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 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 3. Tests for equivalence using ==, but silently ignores some common exceptions that can occur
(AtrtibuteError, ValueError). (AtrtibuteError, ValueError).
4. When comparing arrays, returns False if the array shapes are not the same. 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') # The above catches np.nan, but not float('nan')
if isinstance(a, float) and isinstance(b, float): 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 return True
# Avoid comparing large arrays against scalars; this is expensive and we know it should return False. # Avoid comparing large arrays against scalars; this is expensive and we know it should return False.

View File

@ -1,3 +1,4 @@
from math import hypot
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
from .. import functions as fn from .. import functions as fn
import numpy as np import numpy as np
@ -135,7 +136,7 @@ class ArrowItem(QtGui.QGraphicsPathItem):
pad = 0 pad = 0
if self.opts['pxMode']: if self.opts['pxMode']:
br = self.boundingRect() br = self.boundingRect()
pad += (br.width()**2 + br.height()**2) ** 0.5 pad += hypot(br.width(), br.height())
pen = self.pen() pen = self.pen()
if pen.isCosmetic(): if pen.isCosmetic():
pad += max(1, pen.width()) * 0.7072 pad += max(1, pen.width()) * 0.7072

View File

@ -4,6 +4,7 @@ from ..python2_3 import asUnicode
import numpy as np import numpy as np
from ..Point import Point from ..Point import Point
from .. import debug as debug from .. import debug as debug
from math import ceil, floor, log, log10, isfinite
import sys import sys
import weakref import weakref
from .. import functions as fn from .. import functions as fn
@ -511,7 +512,7 @@ class AxisItem(GraphicsWidget):
def setRange(self, mn, mx): def setRange(self, mn, mx):
"""Set the range of values displayed by the axis. """Set the range of values displayed by the axis.
Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView <pyqtgraph.AxisItem.linkToView>`""" Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView <pyqtgraph.AxisItem.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))) raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx)))
self.range = [mn, mx] self.range = [mn, mx]
if self.autoSIPrefix: if self.autoSIPrefix:
@ -680,13 +681,13 @@ class AxisItem(GraphicsWidget):
return [] return []
## decide optimal minor tick spacing in pixels (this is just aesthetics) ## 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 ## optimal minor tick spacing
optimalSpacing = dif / optimalTickCount optimalSpacing = dif / optimalTickCount
## the largest power-of-10 spacing which is smaller than optimal ## 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. ## Determine major/minor tick spacings which flank the optimal spacing.
intervals = np.array([1., 2., 10., 20., 100.]) * p10unit intervals = np.array([1., 2., 10., 20., 100.]) * p10unit
@ -758,7 +759,7 @@ class AxisItem(GraphicsWidget):
spacing, offset = tickLevels[i] spacing, offset = tickLevels[i]
## determine starting tick ## determine starting tick
start = (np.ceil((minVal-offset) / spacing) * spacing) + offset start = (ceil((minVal-offset) / spacing) * spacing) + offset
## determine number of ticks ## determine number of ticks
num = int((maxVal-start) / spacing) + 1 num = int((maxVal-start) / spacing) + 1
@ -766,7 +767,7 @@ class AxisItem(GraphicsWidget):
## remove any ticks that were present in higher levels ## 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 ## 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. ## 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]) allValues = np.concatenate([allValues, values])
ticks.append((spacing/self.scale, values)) ticks.append((spacing/self.scale, values))
@ -795,8 +796,8 @@ class AxisItem(GraphicsWidget):
ticks.append((spacing, t)) ticks.append((spacing, t))
if len(ticks) < 3: if len(ticks) < 3:
v1 = int(np.floor(minVal)) v1 = int(floor(minVal))
v2 = int(np.ceil(maxVal)) v2 = int(ceil(maxVal))
#major = list(range(v1+1, v2)) #major = list(range(v1+1, v2))
minor = [] minor = []
@ -822,7 +823,7 @@ class AxisItem(GraphicsWidget):
if self.logMode: if self.logMode:
return self.logTickStrings(values, scale, spacing) return self.logTickStrings(values, scale, spacing)
places = max(0, np.ceil(-np.log10(spacing*scale))) places = max(0, ceil(-log10(spacing*scale)))
strings = [] strings = []
for v in values: for v in values:
vs = v * scale vs = v * scale
@ -969,7 +970,7 @@ class AxisItem(GraphicsWidget):
if lineAlpha is None: if lineAlpha is None:
lineAlpha = 255 / (i+1) lineAlpha = 255 / (i+1)
if self.grid is not False: 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): elif isinstance(lineAlpha, float):
lineAlpha *= 255 lineAlpha *= 255
lineAlpha = max(0, int(round(lineAlpha))) lineAlpha = max(0, int(round(lineAlpha)))

View File

@ -1,6 +1,7 @@
from math import atan2, degrees
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
from . import ArrowItem from . import ArrowItem
import numpy as np from ..functions import clip_scalar
from ..Point import Point from ..Point import Point
import weakref import weakref
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
@ -61,26 +62,26 @@ class CurvePoint(GraphicsObject):
pos = self.property('position') pos = self.property('position')
if 'QVariant' in repr(pos): ## need to support 2 APIs :( if 'QVariant' in repr(pos): ## need to support 2 APIs :(
pos = pos.toDouble()[0] 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 if index != int(index): ## interpolate floating-point values
i1 = int(index) i1 = int(index)
i2 = np.clip(i1+1, 0, len(x)-1) i2 = clip_scalar(i1+1, 0, len(x)-1)
s2 = index-i1 s2 = index-i1
s1 = 1.0-s2 s1 = 1.0-s2
newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2) newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2)
else: else:
index = int(index) index = int(index)
i1 = np.clip(index-1, 0, len(x)-1) i1 = clip_scalar(index-1, 0, len(x)-1)
i2 = np.clip(index+1, 0, len(x)-1) i2 = clip_scalar(index+1, 0, len(x)-1)
newPos = (x[index], y[index]) newPos = (x[index], y[index])
p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1])) p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1]))
p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2])) 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() self.resetTransform()
if self._rotate: if self._rotate:
self.setRotation(180 + np.rad2deg(ang)) ## takes degrees self.setRotation(180 + degrees(rads))
QtGui.QGraphicsItem.setPos(self, *newPos) QtGui.QGraphicsItem.setPos(self, *newPos)
return True return True

View File

@ -1,4 +1,5 @@
import warnings import warnings
from math import hypot
from collections import OrderedDict from collections import OrderedDict
from functools import reduce from functools import reduce
from ..Qt import QtGui, QtCore, isQObjectAlive from ..Qt import QtGui, QtCore, isQObjectAlive
@ -308,7 +309,7 @@ class GraphicsItem(object):
v = self.pixelVectors() v = self.pixelVectors()
if v == (None, None): if v == (None, None):
return 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): def pixelWidth(self):
## deprecated ## deprecated

View File

@ -126,8 +126,7 @@ class GridItem(UIGraphicsItem):
for i in range(self.grid_depth - 1, -1, -1): for i in range(self.grid_depth - 1, -1, -1):
dist = br-ul dist = br-ul
nlTarget = 10.**i nlTarget = 10.**i
d = 10. ** np.floor(np.log10(np.abs(dist/nlTarget))+0.5)
d = 10. ** np.floor(np.log10(abs(dist/nlTarget))+0.5)
for ax in range(0,2): for ax in range(0,2):
ts = self.opts['tickSpacing'][ax] ts = self.opts['tickSpacing'][ax]
try: try:
@ -141,11 +140,6 @@ class GridItem(UIGraphicsItem):
br1 = np.ceil(br / d) * d br1 = np.ceil(br / d) * d
dist = br1-ul1 dist = br1-ul1
nl = (dist / d) + 0.5 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 for ax in range(0,2): ## Draw grid for both axes
if i >= len(self.opts['tickSpacing'][ax]): if i >= len(self.opts['tickSpacing'][ax]):
continue continue
@ -153,7 +147,7 @@ class GridItem(UIGraphicsItem):
continue continue
ppl = dim[ax] / nl[ax] 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'] linePen = self.opts['pen']
lineColor = self.opts['pen'].color() lineColor = self.opts['pen'].color()

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from math import atan2, degrees
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
from ..Point import Point from ..Point import Point
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
@ -359,7 +360,7 @@ class InfiniteLine(GraphicsObject):
up = tr.map(Point(left, 1)) up = tr.map(Point(left, 1))
dif = end - start dif = end - start
length = Point(dif).length() 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.translate(start)
p.rotate(angle) p.rotate(angle)
@ -592,7 +593,7 @@ class InfLineLabel(TextItem):
return return
rel = self._posToRel(ev.pos()) 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() self.updatePosition()
if ev.isFinish(): if ev.isFinish():
self._moving = False self._moving = False

View File

@ -1,4 +1,5 @@
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
import math
import numpy as np import numpy as np
from ..colormap import ColorMap from ..colormap import ColorMap
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
@ -94,7 +95,7 @@ class NonUniformImage(GraphicsObject):
value = 0.0 value = 0.0
elif np.isposinf(value): elif np.isposinf(value):
value = 1.0 value = 1.0
elif np.isnan(value): elif math.isnan(value):
continue # ignore NaN continue # ignore NaN
else: else:
value = (value - mn) / (mx - mn) # normalize value = (value - mn) / (mx - mn) # normalize

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from ..Qt import QtCore, QtGui, QtWidgets from ..Qt import QtCore, QtGui, QtWidgets
HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget') HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget')
import math
import warnings import warnings
import numpy as np import numpy as np
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
@ -131,6 +131,8 @@ class PlotCurveItem(GraphicsObject):
elif ax == 1: elif ax == 1:
d = y d = y
d2 = x d2 = x
else:
raise ValueError("Invalid axis value")
## If an orthogonal range is specified, mask the data now ## If an orthogonal range is specified, mask the data now
if orthoRange is not None: if orthoRange is not None:
@ -149,7 +151,7 @@ class PlotCurveItem(GraphicsObject):
# All-NaN data is acceptable; Explicit numpy warning is not needed. # All-NaN data is acceptable; Explicit numpy warning is not needed.
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
b = (np.nanmin(d), np.nanmax(d)) 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) mask = np.isfinite(d)
d = d[mask] d = d[mask]
if len(d) == 0: if len(d) == 0:

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import warnings import warnings
import math
import numpy as np import numpy as np
from .. import metaarray as metaarray from .. import metaarray as metaarray
from ..Qt import QtCore from ..Qt import QtCore
@ -660,7 +661,7 @@ class PlotDataItem(GraphicsObject):
eps = np.finfo(y.dtype).eps eps = np.finfo(y.dtype).eps
else: else:
eps = 1 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'] ds = self.opts['downsample']
if not isinstance(ds, int): 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. # All-NaN data is handled by returning None; Explicit numpy warning is not needed.
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
ymin = np.nanmin(self.yData) ymin = np.nanmin(self.yData)
if np.isnan( ymin ): if math.isnan( ymin ):
return None # most likely case for all-NaN data return None # most likely case for all-NaN data
xmin = np.nanmin(self.xData) xmin = np.nanmin(self.xData)
if np.isnan( xmin ): if math.isnan( xmin ):
return None # less likely case for all-NaN data return None # less likely case for all-NaN data
ymax = np.nanmax(self.yData) ymax = np.nanmax(self.yData)
xmax = np.nanmax(self.xData) xmax = np.nanmax(self.xData)

View File

@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget):
if y is not None: if y is not None:
self.ctrl.yGridCheck.setChecked(y) self.ctrl.yGridCheck.setChecked(y)
if alpha is not None: 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) self.ctrl.gridAlphaSlider.setValue(v)
def close(self): def close(self):

View File

@ -15,9 +15,9 @@ of how to build an ROI at the bottom of the file.
from ..Qt import QtCore, QtGui from ..Qt import QtCore, QtGui
import numpy as np import numpy as np
#from numpy.linalg import norm #from numpy.linalg import norm
from ..Point import * from ..Point import Point
from ..SRTTransform import SRTTransform from ..SRTTransform import SRTTransform
from math import cos, sin from math import atan2, cos, sin, hypot, radians
from .. import functions as fn from .. import functions as fn
from .GraphicsObject import GraphicsObject from .GraphicsObject import GraphicsObject
from .UIGraphicsItem import UIGraphicsItem from .UIGraphicsItem import UIGraphicsItem
@ -141,12 +141,13 @@ class ROI(GraphicsObject):
maxBounds=None, snapSize=1.0, scaleSnap=False, maxBounds=None, snapSize=1.0, scaleSnap=False,
translateSnap=False, rotateSnap=False, parent=None, pen=None, translateSnap=False, rotateSnap=False, parent=None, pen=None,
hoverPen=None, handlePen=None, handleHoverPen=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) GraphicsObject.__init__(self, parent)
self.setAcceptedMouseButtons(QtCore.Qt.NoButton) self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
pos = Point(pos) pos = Point(pos)
size = Point(size) size = Point(size)
self.aspectLocked = False self.aspectLocked = aspectLocked
self.translatable = movable self.translatable = movable
self.rotatable = rotatable self.rotatable = rotatable
self.resizable = resizable self.resizable = resizable
@ -678,7 +679,7 @@ class ROI(GraphicsObject):
The format returned is a list of (name, pos) tuples. The format returned is a list of (name, pos) tuples.
""" """
if index == None: if index is None:
positions = [] positions = []
for h in self.handles: for h in self.handles:
positions.append((h['name'], h['pos'])) positions.append((h['name'], h['pos']))
@ -691,7 +692,7 @@ class ROI(GraphicsObject):
The format returned is a list of (name, pos) tuples. The format returned is a list of (name, pos) tuples.
""" """
if index == None: if index is None:
positions = [] positions = []
for h in self.handles: for h in self.handles:
positions.append((h['name'], h['item'].scenePos())) 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 vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin
vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin
lvx = np.sqrt(vx.x()**2 + vx.y()**2) lvx = hypot(vx.x(), vx.y()) # length
lvy = np.sqrt(vy.x()**2 + vy.y()**2) lvy = hypot(vy.x(), vy.y()) # length
##img.width is number of pixels, not width of item. ##img.width is number of pixels, not width of item.
##need pxWidth and pxHeight instead of pxLen ? ##need pxWidth and pxHeight instead of pxLen ?
sx = 1.0 / lvx sx = 1.0 / lvx
@ -1298,7 +1299,7 @@ class ROI(GraphicsObject):
"""Return global transformation (rotation angle+translation) required to move """Return global transformation (rotation angle+translation) required to move
from relative state to current state. If relative state isn't specified, from relative state to current state. If relative state isn't specified,
then we use the state of the ROI when mouse is pressed.""" then we use the state of the ROI when mouse is pressed."""
if relativeTo == None: if relativeTo is None:
relativeTo = self.preMoveState relativeTo = self.preMoveState
st = self.getState() st = self.getState()
@ -1425,7 +1426,7 @@ class Handle(UIGraphicsItem):
menu = self.scene().addParentContextMenus(self, self.getMenu(), ev) menu = self.scene().addParentContextMenus(self, self.getMenu(), ev)
## Make sure it is still ok to remove this handle ## 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) self.removeAction.setEnabled(removeAllowed)
pos = ev.screenPos() pos = ev.screenPos()
menu.popup(QtCore.QPoint(pos.x(), pos.y())) menu.popup(QtCore.QPoint(pos.x(), pos.y()))
@ -1518,13 +1519,13 @@ class Handle(UIGraphicsItem):
return None return None
v = dt.map(QtCore.QPointF(1, 0)) - dt.map(QtCore.QPointF(0, 0)) 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) dti = fn.invertQTransform(dt)
devPos = dt.map(QtCore.QPointF(0,0)) devPos = dt.map(QtCore.QPointF(0,0))
tr = QtGui.QTransform() tr = QtGui.QTransform()
tr.translate(devPos.x(), devPos.y()) tr.translate(devPos.x(), devPos.y())
tr.rotate(va * 180. / 3.1415926) tr.rotateRadians(va)
return dti.map(tr.map(self.path)) return dti.map(tr.map(self.path))
@ -1663,7 +1664,7 @@ class LineROI(ROI):
d = pos2-pos1 d = pos2-pos1
l = d.length() l = d.length()
ang = Point(1, 0).angle(d) 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)) c = Point(-width/2. * sin(ra), -width/2. * cos(ra))
pos1 = pos1 + c pos1 = pos1 + c
@ -1673,9 +1674,6 @@ class LineROI(ROI):
self.addScaleHandle([0.5, 1], [0.5, 0.5]) self.addScaleHandle([0.5, 1], [0.5, 0.5])
class MultiRectROI(QtGui.QGraphicsObject): class MultiRectROI(QtGui.QGraphicsObject):
r""" r"""
Chain of rectangular ROIs connected by handles. Chain of rectangular ROIs connected by handles.
@ -1887,7 +1885,7 @@ class EllipseROI(ROI):
h = arr.shape[axes[1]] h = arr.shape[axes[1]]
## generate an ellipsoidal mask ## 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 # reshape to match array axes
if axes[0] > axes[1]: if axes[0] > axes[1]:
@ -1944,8 +1942,7 @@ class CircleROI(EllipseROI):
if radius is None: if radius is None:
raise TypeError("Must provide either size or radius.") raise TypeError("Must provide either size or radius.")
size = (radius*2, radius*2) size = (radius*2, radius*2)
EllipseROI.__init__(self, pos, size, **args) EllipseROI.__init__(self, pos, size, aspectLocked=True, **args)
self.aspectLocked = True
def _addHandles(self): def _addHandles(self):
self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5]) 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.""" """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): def __init__(self, pos=None, size=None, **kargs):
if size == None: if size is None:
size=[1,1] size=[1,1]
if pos == None: if pos is None:
pos = [0,0] pos = [0,0]
self._shape = None self._shape = None
ROI.__init__(self, pos, size, **kargs) ROI.__init__(self, pos, size, aspectLocked=True, **kargs)
self.sigRegionChanged.connect(self.invalidate) self.sigRegionChanged.connect(self.invalidate)
self.addScaleRotateHandle(Point(1, 0), Point(0, 0)) self.addScaleRotateHandle(Point(1, 0), Point(0, 0))
self.aspectLocked = True
def invalidate(self): def invalidate(self):
self._shape = None self._shape = None
@ -2394,8 +2390,7 @@ class TriangleROI(ROI):
""" """
def __init__(self, pos, size, **args): def __init__(self, pos, size, **args):
ROI.__init__(self, pos, [size, size], **args) ROI.__init__(self, pos, [size, size], aspectLocked=True, **args)
self.aspectLocked = True
angles = np.linspace(0, np.pi * 4 / 3, 3) angles = np.linspace(0, np.pi * 4 / 3, 3)
verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0 verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0
self.poly = QtGui.QPolygonF() self.poly = QtGui.QPolygonF()

View File

@ -6,6 +6,7 @@ try:
except ImportError: except ImportError:
imap = map imap = map
import itertools import itertools
import math
import numpy as np import numpy as np
import weakref import weakref
from ..Qt import QtGui, QtCore, QT_LIB from ..Qt import QtGui, QtCore, QT_LIB
@ -116,7 +117,7 @@ def renderSymbol(symbol, size, pen, brush, device=None):
for more information). for more information).
""" """
## Render a spot with the given parameters to a pixmap ## 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: if device is None:
device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32) device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32)
device.fill(0) device.fill(0)
@ -950,6 +951,8 @@ class ScatterPlotItem(GraphicsObject):
elif ax == 1: elif ax == 1:
d = self.data['y'] d = self.data['y']
d2 = self.data['x'] d2 = self.data['x']
else:
raise ValueError("Invalid axis value")
if orthoRange is not None: if orthoRange is not None:
mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1]) 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 # 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 = np.vstack([self.data['x'], self.data['y']])
pts = fn.transformCoordinates(p.transform(), pts) 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() p.resetTransform()
if self.opts['useCache'] and self._exportOpts is False: if self.opts['useCache'] and self._exportOpts is False:

View File

@ -1,5 +1,4 @@
from math import atan2, pi from math import atan2
from ..Qt import QtGui, QtCore from ..Qt import QtGui, QtCore
from ..Point import Point from ..Point import Point
from .. import functions as fn from .. import functions as fn
@ -243,7 +242,7 @@ class TargetItem(UIGraphicsItem):
tr = QtGui.QTransform() tr = QtGui.QTransform()
tr.translate(devPos.x(), devPos.y()) tr.translate(devPos.x(), devPos.y())
va = atan2(v.y(), v.x()) va = atan2(v.y(), v.x())
tr.rotate(va * 180.0 / pi) tr.rotateRadians(va)
tr.scale(self.scale, self.scale) tr.scale(self.scale, self.scale)
return dti.map(tr.map(self._path)) return dti.map(tr.map(self._path))

View File

@ -1,4 +1,4 @@
import numpy as np from math import atan2, degrees
from ..Qt import QtCore, QtGui from ..Qt import QtCore, QtGui
from ..Point import Point from ..Point import Point
from .. import functions as fn from .. import functions as fn
@ -208,7 +208,7 @@ class TextItem(GraphicsObject):
angle = -self.angle angle = -self.angle
if self.rotateAxis is not None: if self.rotateAxis is not None:
d = pt.map(self.rotateAxis) - pt.map(Point(0, 0)) 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 angle += a
t.rotate(angle) t.rotate(angle)
self.setTransform(t) self.setTransform(t)

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import weakref import weakref
import sys import sys
import math
from copy import deepcopy from copy import deepcopy
import numpy as np import numpy as np
from ...Qt import QtGui, QtCore from ...Qt import QtGui, QtCore
@ -549,10 +550,9 @@ class ViewBox(GraphicsWidget):
dy = 1 dy = 1
mn -= dy*0.5 mn -= dy*0.5
mx += dy*0.5 mx += dy*0.5
xpad = 0.0
# Make sure no nan/inf get through # 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))) raise Exception("Cannot set range [%s, %s]" % (str(mn), str(mx)))
# Apply padding # Apply padding
@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget):
def suggestPadding(self, axis): def suggestPadding(self, axis):
l = self.width() if axis==0 else self.height() l = self.width() if axis==0 else self.height()
if l > 0: 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: else:
padding = 0.02 padding = 0.02
return padding return padding
@ -906,8 +906,8 @@ class ViewBox(GraphicsWidget):
# check for and ignore bad ranges # check for and ignore bad ranges
for k in ['xRange', 'yRange']: for k in ['xRange', 'yRange']:
if k in args: if k in args:
if not np.all(np.isfinite(args[k])): if not math.isfinite(args[k][0]) or not math.isfinite(args[k][1]):
r = args.pop(k) _ = args.pop(k)
#print("Warning: %s is invalid: %s" % (k, str(r)) #print("Warning: %s is invalid: %s" % (k, str(r))
if len(args) == 0: if len(args) == 0:
@ -1369,10 +1369,20 @@ class ViewBox(GraphicsWidget):
xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0]) xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0])
yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1]) yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1])
pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding() 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 useX = False
xr = (0,0) 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 useY = False
yr = (0,0) yr = (0,0)

View File

@ -12,10 +12,12 @@ Widget used for displaying 2D or 3D data. Features:
- ROI plotting - ROI plotting
- Image normalization through a variety of methods - Image normalization through a variety of methods
""" """
import os, sys import os
from math import log10
import numpy as np import numpy as np
from ..Qt import QtCore, QtGui, QT_LIB from ..Qt import QtCore, QtGui, QT_LIB
from .. import functions as fn
import importlib import importlib
ui_template = importlib.import_module( ui_template = importlib.import_module(
f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__) f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__)
@ -272,7 +274,7 @@ class ImageView(QtGui.QWidget):
if not isinstance(img, np.ndarray): if not isinstance(img, np.ndarray):
required = ['dtype', 'max', 'min', 'ndim', 'shape', 'size'] 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 " raise TypeError("Image must be NumPy array or any object "
"that provides compatible attributes/methods:\n" "that provides compatible attributes/methods:\n"
" %s" % str(required)) " %s" % str(required))
@ -512,7 +514,7 @@ class ImageView(QtGui.QWidget):
def setCurrentIndex(self, ind): def setCurrentIndex(self, ind):
"""Set the currently displayed frame index.""" """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 self.ignorePlaying = True
# Implicitly call timeLineChanged # Implicitly call timeLineChanged
self.timeLine.setValue(self.tVals[index]) self.timeLine.setValue(self.tVals[index])
@ -807,7 +809,7 @@ class ImageView(QtGui.QWidget):
img = self.getProcessedImage() img = self.getProcessedImage()
if self.hasTimeAxis(): if self.hasTimeAxis():
base, ext = os.path.splitext(fileName) 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]): for i in range(img.shape[0]):
self.imageItem.setImage(img[i], autoLevels=False) self.imageItem.setImage(img[i], autoLevels=False)
self.imageItem.save(fmt % (base, i, ext)) self.imageItem.save(fmt % (base, i, ext))

View File

@ -124,7 +124,7 @@ class MetaArray(object):
nameTypes = [basestring, tuple] nameTypes = [basestring, tuple]
@staticmethod @staticmethod
def isNameType(var): 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 ## methods to wrap from embedded ndarray / HDF5

View File

@ -5,6 +5,7 @@ import numpy as np
from .. import Vector from .. import Vector
from .. import functions as fn from .. import functions as fn
import warnings import warnings
from math import cos, sin, tan, radians
##Vector = QtGui.QVector3D ##Vector = QtGui.QVector3D
ShareWidget = None ShareWidget = None
@ -174,7 +175,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
nearClip = dist * 0.001 nearClip = dist * 0.001
farClip = dist * 1000. farClip = dist * 1000.
r = nearClip * np.tan(fov * 0.5 * np.pi / 180.) r = nearClip * tan(0.5 * radians(fov))
t = r * h / w t = r * h / w
## Note that X0 and width in these equations must be the values used in viewport ## 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) ) pos = center - self.opts['rotation'].rotatedVector( Vector(0,0,dist) )
else: else:
# using 'euler' rotation method # using 'euler' rotation method
elev = self.opts['elevation'] * np.pi / 180 elev = radians(self.opts['elevation'])
azim = self.opts['azimuth'] * np.pi / 180 azim = radians(self.opts['azimuth'])
pos = Vector( pos = Vector(
center.x() + dist * np.cos(elev) * np.cos(azim), center.x() + dist * cos(elev) * cos(azim),
center.y() + dist * np.cos(elev) * np.sin(azim), center.y() + dist * cos(elev) * sin(azim),
center.z() + dist * np.sin(elev) center.z() + dist * sin(elev)
) )
return pos return pos
@ -335,7 +336,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
self.opts['rotation'] = q self.opts['rotation'] = q
else: # default euler rotation method else: # default euler rotation method
self.opts['azimuth'] += azim 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() self.update()
def pan(self, dx, dy, dz, relative='global'): def pan(self, dx, dy, dz, relative='global'):
@ -378,7 +379,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
cPos = self.cameraPosition() cPos = self.cameraPosition()
cVec = self.opts['center'] - cPos cVec = self.opts['center'] - cPos
dist = cVec.length() ## distance from camera to center 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() xScale = xDist / self.width()
zVec = QtGui.QVector3D(0,0,1) zVec = QtGui.QVector3D(0,0,1)
xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized() xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized()
@ -399,15 +400,15 @@ class GLViewWidget(QtWidgets.QOpenGLWidget):
# apply translation # apply translation
self.opts['center'] += scale_factor * (xv*-dx + yv*dy + zv*dz) self.opts['center'] += scale_factor * (xv*-dx + yv*dy + zv*dz)
else: # use default euler rotation method else: # use default euler rotation method
elev = np.radians(self.opts['elevation']) elev = radians(self.opts['elevation'])
azim = np.radians(self.opts['azimuth']) azim = radians(self.opts['azimuth'])
fov = np.radians(self.opts['fov']) fov = radians(self.opts['fov'])
dist = (self.opts['center'] - self.cameraPosition()).length() 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() scale_factor = dist * fov_factor / self.width()
z = scale_factor * np.cos(elev) * dy z = scale_factor * cos(elev) * dy
x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy) x = scale_factor * (sin(azim) * dx - sin(elev) * cos(azim) * dy)
y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy) y = scale_factor * (cos(azim) * dx + sin(elev) * sin(azim) * dy)
self.opts['center'] += QtGui.QVector3D(x, -y, z) self.opts['center'] += QtGui.QVector3D(x, -y, z)
else: else:
raise ValueError("relative argument must be global, view, or view-upright") 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 dist = ((pos-cam)**2).sum(axis=-1)**0.5
else: else:
dist = (pos-cam).length() 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() return xDist / self.width()
def mousePressEvent(self, ev): def mousePressEvent(self, ev):

View File

@ -3,7 +3,8 @@ from OpenGL.GL import *
from OpenGL.arrays import vbo from OpenGL.arrays import vbo
from .. GLGraphicsItem import GLGraphicsItem from .. GLGraphicsItem import GLGraphicsItem
from .. import shaders from .. import shaders
from ... import QtGui from ... import functions as fn
from ...Qt import QtGui
import numpy as np import numpy as np
__all__ = ['GLScatterPlotItem'] __all__ = ['GLScatterPlotItem']
@ -61,13 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem):
## Generate texture for rendering points ## Generate texture for rendering points
w = 64 w = 64
def fn(x,y): def genTexture(x,y):
r = ((x-(w-1)/2.)**2 + (y-(w-1)/2.)**2) ** 0.5 r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.))
return 255 * (w/2. - np.clip(r, w/2.-1.0, w/2.)) return 255 * (w / 2 - fn.clip_array(r, w / 2 - 1, w / 2))
pData = np.empty((w, w, 4)) pData = np.empty((w, w, 4))
pData[:] = 255 pData[:] = 255
pData[:,:,3] = np.fromfunction(fn, pData.shape[:2]) pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2])
#print pData.shape, pData.min(), pData.max()
pData = pData.astype(np.ubyte) pData = pData.astype(np.ubyte)
if getattr(self, "pointTexture", None) is None: if getattr(self, "pointTexture", None) is None:

View File

@ -1,6 +1,8 @@
from collections import OrderedDict from collections import OrderedDict
import numpy as np import numpy as np
import copy import copy
from math import log2
from .. import functions as fn
class SystemSolver(object): class SystemSolver(object):
@ -390,15 +392,12 @@ if __name__ == '__main__':
sh = self.shutter # this raises RuntimeError if shutter has not sh = self.shutter # this raises RuntimeError if shutter has not
# been specified # been specified
ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light) 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: except RuntimeError:
# program mode; we can select a suitable shutter # program mode; we can select a suitable shutter
# value at the same time. # value at the same time.
sh = (1./60.) sh = (1./60.)
raise raise
return ap return ap
def _balance(self): def _balance(self):
@ -406,10 +405,8 @@ if __name__ == '__main__':
light = self.lightMeter light = self.lightMeter
sh = self.shutter sh = self.shutter
ap = self.aperture ap = self.aperture
fl = self.flash
bal = (4.0 / ap) * (sh / (1./60.)) * (iso / 100.) * (2 ** light) bal = (4.0 / ap) * (sh / (1./60.)) * (iso / 100.) * (2 ** light)
return np.log2(bal) return log2(bal)
camera = Camera() camera = Camera()

View File

@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter):
elif op == 'Set': elif op == 'Set':
colors[mask] = colors2[mask] colors[mask] = colors2[mask]
colors = np.clip(colors, 0, 1) colors = fn.clip_array(colors, 0., 1.)
if mode == 'byte': if mode == 'byte':
colors = (colors * 255).astype(np.ubyte) colors = (colors * 255).astype(np.ubyte)
@ -210,11 +210,11 @@ class RangeColorMapItem(ptree.types.SimpleParameter):
def map(self, data): def map(self, data):
data = data[self.fieldName] 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() cmap = self.value()
colors = cmap.map(scaled, mode='float') colors = cmap.map(scaled, mode='float')
mask = np.isnan(data) | np.isinf(data) mask = np.invert(np.isfinite(data))
nanColor = self['NaN'] nanColor = self['NaN']
nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.) nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.)
colors[mask] = nanColor colors[mask] = nanColor

View File

@ -7,10 +7,7 @@ Distributed under MIT/X11 license. See license.txt for more information.
from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB
from ..Point import Point from ..Point import Point
import sys, os
from .FileDialog import FileDialog
from ..GraphicsScene import GraphicsScene from ..GraphicsScene import GraphicsScene
import numpy as np
from .. import functions as fn from .. import functions as fn
from .. import debug as debug from .. import debug as debug
from .. import getConfigOption from .. import getConfigOption
@ -378,7 +375,7 @@ class GraphicsView(QtGui.QGraphicsView):
return return
if ev.buttons() == QtCore.Qt.RightButton: 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 scale = 1.01 ** delta
self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos)) self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos))
self.sigDeviceRangeChanged.emit(self, self.range) self.sigDeviceRangeChanged.emit(self, self.range)

View File

@ -1,4 +1,5 @@
from ..Qt import QtGui, QtCore from math import hypot
from ..Qt import QtGui, QtCore, mkQApp
__all__ = ['JoystickButton'] __all__ = ['JoystickButton']
@ -41,7 +42,7 @@ class JoystickButton(QtGui.QPushButton):
def setState(self, *xy): def setState(self, *xy):
xy = list(xy) xy = list(xy)
d = (xy[0]**2 + xy[1]**2)**0.5 d = hypot(xy[0], xy[1]) # length
nxy = [0, 0] nxy = [0, 0]
for i in [0,1]: for i in [0,1]:
if xy[i] == 0: if xy[i] == 0:
@ -84,7 +85,7 @@ class JoystickButton(QtGui.QPushButton):
if __name__ == '__main__': if __name__ == '__main__':
app = pg.mkQApp() app = mkQApp()
w = QtGui.QMainWindow() w = QtGui.QMainWindow()
b = JoystickButton() b = JoystickButton()
w.setCentralWidget(b) w.setCentralWidget(b)