Features:

- GLScatterPlotItem can work with arbitrarily-shaped vertex arrays
- added colorToAlpha function

Bugfixes:
- GraphicsScene emits sigMouseClicked for all clicks
- CanvasItem emits transformation change signal when mirrored
- GLViewWidget.pixelSize correctly handles position specified as array
- SRTTransform3D assumes Zscale=1 when converting from 2D transform
This commit is contained in:
Luke Campagnola 2012-12-22 17:04:07 -05:00
parent b25e34f564
commit 4ceae9f1a1
7 changed files with 74 additions and 14 deletions

View File

@ -73,7 +73,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
sigMouseHover = QtCore.Signal(object) ## emits a list of objects hovered over
sigMouseMoved = QtCore.Signal(object) ## emits position of mouse on every move
sigMouseClicked = QtCore.Signal(object) ## emitted when MouseClickEvent is not accepted by any items under the click.
sigMouseClicked = QtCore.Signal(object) ## emitted when mouse is clicked. Check for event.isAccepted() to see whether the event has already been acted on.
_addressCache = weakref.WeakValueDictionary()
@ -343,10 +343,11 @@ class GraphicsScene(QtGui.QGraphicsScene):
if int(item.flags() & item.ItemIsFocusable) > 0:
item.setFocus(QtCore.Qt.MouseFocusReason)
break
if not ev.isAccepted() and ev.button() is QtCore.Qt.RightButton:
#if not ev.isAccepted() and ev.button() is QtCore.Qt.RightButton:
#print "GraphicsScene emitting sigSceneContextMenu"
self.sigMouseClicked.emit(ev)
ev.accept()
#self.sigMouseClicked.emit(ev)
#ev.accept()
self.sigMouseClicked.emit(ev)
return ev.isAccepted()
#def claimEvent(self, item, button, eventType):

View File

@ -35,6 +35,7 @@ class SRTTransform3D(pg.Transform3D):
'angle': init._state['angle'],
'axis': Vector(0, 0, 1),
}
self._state['scale'][2] = 1.0
self.update()
elif isinstance(init, QtGui.QMatrix4x4):
self.setFromMatrix(init)

View File

@ -205,6 +205,7 @@ class CanvasItem(QtCore.QObject):
self.userTransform = self.userTransform * inv
self.updateTransform()
self.selectBoxFromUser()
self.sigTransformChangeFinished.emit(self)
#if flip:
#if tr['scale'][0] < 0 xor tr['scale'][1] < 0:
#return

View File

@ -991,7 +991,53 @@ def imageToArray(img, copy=False, transpose=True):
else:
return arr
def colorToAlpha(data, color):
"""
Given an RGBA image in *data*, convert *color* to be transparent.
*data* must be an array (w, h, 3 or 4) of ubyte values and *color* must be
an array (3) of ubyte values.
This is particularly useful for use with images that have a black or white background.
Algorithm is taken from Gimp's color-to-alpha function in plug-ins/common/colortoalpha.c
Credit:
/*
* Color To Alpha plug-in v1.0 by Seth Burgess, sjburges@gimp.org 1999/05/14
* with algorithm by clahey
*/
"""
data = data.astype(float)
if data.shape[-1] == 3: ## add alpha channel if needed
d2 = np.empty(data.shape[:2]+(4,), dtype=data.dtype)
d2[...,:3] = data
d2[...,3] = 255
data = d2
color = color.astype(float)
alpha = np.zeros(data.shape[:2]+(3,), dtype=float)
output = data.copy()
for i in [0,1,2]:
d = data[...,i]
c = color[i]
mask = d > c
alpha[...,i][mask] = (d[mask] - c) / (255. - c)
imask = d < c
alpha[...,i][imask] = (c - d[imask]) / c
output[...,3] = alpha.max(axis=2) * 255.
mask = output[...,3] >= 1.0 ## avoid zero division while processing alpha channel
correction = 255. / output[...,3][mask] ## increase value to compensate for decreased alpha
for i in [0,1,2]:
output[...,i][mask] = ((output[...,i][mask]-color[i]) * correction) + color[i]
output[...,3][mask] *= data[...,3][mask] / 255. ## combine computed and previous alpha values
#raise Exception()
return np.clip(output, 0, 255).astype(np.ubyte)
#def isosurface(data, level):
#"""
#Generate isosurface from volumetric data using marching tetrahedra algorithm.

View File

@ -1269,7 +1269,15 @@ class ViewBox(GraphicsWidget):
for item in g.childItems():
item.setPen(fn.mkPen(color='y', width=3))
item.setZValue(1000000)
g.setZValue(1000000)
if children:
g.path = QtGui.QGraphicsPathItem(g.childrenShape())
else:
g.path = QtGui.QGraphicsPathItem(g.shape())
g.path.setParentItem(g)
g.path.setPen(fn.mkPen('g'))
g.path.setZValue(100)
QtCore.QTimer.singleShot(timeout*1000, self.clearLocate)

View File

@ -202,9 +202,9 @@ class GLViewWidget(QtOpenGL.QGLWidget):
Pos may be a Vector or an (N,3) array of locations
"""
cam = self.cameraPosition()
if isinstance(pos, np.ndarray) and pos.ndim == 2:
cam = np.array(cam).reshape(1,3)
dist = ((pos-cam)**2).sum(axis=1)**0.5
if isinstance(pos, np.ndarray):
cam = np.array(cam).reshape((1,)*(pos.ndim-1)+(3,))
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.)

View File

@ -118,7 +118,10 @@ class GLScatterPlotItem(GLGraphicsItem):
#glUniform1i(self.shader.uniform('texture'), 0) ## inform the shader which texture to use
glEnableClientState(GL_VERTEX_ARRAY)
try:
glVertexPointerf(self.pos)
pos = self.pos
#if pos.ndim > 2:
#pos = pos.reshape((reduce(lambda a,b: a*b, pos.shape[:-1]), pos.shape[-1]))
glVertexPointerf(pos)
if isinstance(self.color, np.ndarray):
glEnableClientState(GL_COLOR_ARRAY)
@ -131,19 +134,19 @@ class GLScatterPlotItem(GLGraphicsItem):
if not self.pxMode or isinstance(self.size, np.ndarray):
glEnableClientState(GL_NORMAL_ARRAY)
norm = np.empty(self.pos.shape)
norm = np.empty(pos.shape)
if self.pxMode:
norm[:,0] = self.size
norm[...,0] = self.size
else:
gpos = self.mapToView(self.pos.transpose()).transpose()
gpos = self.mapToView(pos.transpose()).transpose()
pxSize = self.view().pixelSize(gpos)
norm[:,0] = self.size / pxSize
norm[...,0] = self.size / pxSize
glNormalPointerf(norm)
else:
glNormal3f(self.size, 0, 0) ## vertex shader uses norm.x to determine point size
#glPointSize(self.size)
glDrawArrays(GL_POINTS, 0, len(self.pos))
glDrawArrays(GL_POINTS, 0, pos.size / pos.shape[-1])
finally:
glDisableClientState(GL_NORMAL_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)