sync changes from acq4:
- numerous fixes in close() functions - added Transform class - ROI widgets now operate in degrees instead of radians for easier Qt compatibility
This commit is contained in:
parent
349561e133
commit
6783f4fa26
@ -9,8 +9,8 @@ class ColorButton(QtGui.QPushButton):
|
|||||||
sigColorChanging = QtCore.Signal(object) ## emitted whenever a new color is picked in the color dialog
|
sigColorChanging = QtCore.Signal(object) ## emitted whenever a new color is picked in the color dialog
|
||||||
sigColorChanged = QtCore.Signal(object) ## emitted when the selected color is accepted (user clicks OK)
|
sigColorChanged = QtCore.Signal(object) ## emitted when the selected color is accepted (user clicks OK)
|
||||||
|
|
||||||
def __init__(self, color=(128,128,128)):
|
def __init__(self, parent=None, color=(128,128,128)):
|
||||||
QtGui.QPushButton.__init__(self)
|
QtGui.QPushButton.__init__(self, parent)
|
||||||
self.setColor(color)
|
self.setColor(color)
|
||||||
self.colorDialog = QtGui.QColorDialog()
|
self.colorDialog = QtGui.QColorDialog()
|
||||||
self.colorDialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True)
|
self.colorDialog.setOption(QtGui.QColorDialog.ShowAlphaChannel, True)
|
||||||
|
@ -91,6 +91,11 @@ class GraphicsView(QtGui.QGraphicsView):
|
|||||||
#prof.finish()
|
#prof.finish()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
self.centralWidget = None
|
||||||
|
self.scene().clear()
|
||||||
|
#print " ", self.scene().itemCount()
|
||||||
|
self.currentItem = None
|
||||||
|
self.sceneObj = None
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
def useOpenGL(self, b=True):
|
def useOpenGL(self, b=True):
|
||||||
|
@ -152,6 +152,7 @@ class ImageView(QtGui.QWidget):
|
|||||||
#QtGui.QWidget.__dtor__(self)
|
#QtGui.QWidget.__dtor__(self)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
self.ui.roiPlot.close()
|
||||||
self.ui.graphicsView.close()
|
self.ui.graphicsView.close()
|
||||||
self.ui.gradientWidget.sigGradientChanged.disconnect(self.updateImage)
|
self.ui.gradientWidget.sigGradientChanged.disconnect(self.updateImage)
|
||||||
self.scene.clear()
|
self.scene.clear()
|
||||||
@ -159,7 +160,6 @@ class ImageView(QtGui.QWidget):
|
|||||||
del self.imageDisp
|
del self.imageDisp
|
||||||
#self.image = None
|
#self.image = None
|
||||||
#self.imageDisp = None
|
#self.imageDisp = None
|
||||||
self.ui.roiPlot.close()
|
|
||||||
self.setParent(None)
|
self.setParent(None)
|
||||||
|
|
||||||
def keyPressEvent(self, ev):
|
def keyPressEvent(self, ev):
|
||||||
|
@ -66,4 +66,6 @@ class MultiPlotItem(QtGui.QGraphicsWidget):
|
|||||||
def close(self):
|
def close(self):
|
||||||
for p in self.plots:
|
for p in self.plots:
|
||||||
p[0].close()
|
p[0].close()
|
||||||
|
self.plots = None
|
||||||
|
for i in range(self.layout.count()):
|
||||||
|
self.layout.removeAt(i)
|
@ -40,4 +40,6 @@ class MultiPlotWidget(GraphicsView):
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.mPlotItem.close()
|
self.mPlotItem.close()
|
||||||
|
self.mPlotItem = None
|
||||||
self.setParent(None)
|
self.setParent(None)
|
||||||
|
GraphicsView.close(self)
|
0
PIL_Fix/Image.py-1.6
Executable file → Normal file
0
PIL_Fix/Image.py-1.6
Executable file → Normal file
50
PlotItem.py
50
PlotItem.py
@ -60,12 +60,13 @@ class PlotItem(QtGui.QGraphicsWidget):
|
|||||||
self.autoBtn = QtGui.QToolButton()
|
self.autoBtn = QtGui.QToolButton()
|
||||||
self.autoBtn.setText('A')
|
self.autoBtn.setText('A')
|
||||||
self.autoBtn.hide()
|
self.autoBtn.hide()
|
||||||
|
self.proxies = []
|
||||||
for b in [self.ctrlBtn, self.autoBtn]:
|
for b in [self.ctrlBtn, self.autoBtn]:
|
||||||
proxy = QtGui.QGraphicsProxyWidget(self)
|
proxy = QtGui.QGraphicsProxyWidget(self)
|
||||||
proxy.setWidget(b)
|
proxy.setWidget(b)
|
||||||
proxy.setAcceptHoverEvents(False)
|
proxy.setAcceptHoverEvents(False)
|
||||||
b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
|
b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
|
||||||
|
self.proxies.append(proxy)
|
||||||
#QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
|
#QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
|
||||||
self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
|
self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
|
||||||
#QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
|
#QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
|
||||||
@ -155,9 +156,9 @@ class PlotItem(QtGui.QGraphicsWidget):
|
|||||||
c.setupUi(w)
|
c.setupUi(w)
|
||||||
dv = QtGui.QDoubleValidator(self)
|
dv = QtGui.QDoubleValidator(self)
|
||||||
self.ctrlMenu = QtGui.QMenu()
|
self.ctrlMenu = QtGui.QMenu()
|
||||||
ac = QtGui.QWidgetAction(self)
|
self.menuAction = QtGui.QWidgetAction(self)
|
||||||
ac.setDefaultWidget(w)
|
self.menuAction.setDefaultWidget(w)
|
||||||
self.ctrlMenu.addAction(ac)
|
self.ctrlMenu.addAction(self.menuAction)
|
||||||
|
|
||||||
if HAVE_WIDGETGROUP:
|
if HAVE_WIDGETGROUP:
|
||||||
self.stateGroup = WidgetGroup(self.ctrlMenu)
|
self.stateGroup = WidgetGroup(self.ctrlMenu)
|
||||||
@ -284,9 +285,46 @@ class PlotItem(QtGui.QGraphicsWidget):
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
#print "delete", self
|
#print "delete", self
|
||||||
|
## All this crap is needed to avoid PySide trouble.
|
||||||
|
## The problem seems to be whenever scene.clear() leads to deletion of widgets (either through proxies or qgraphicswidgets)
|
||||||
|
## the solution is to manually remove all widgets before scene.clear() is called
|
||||||
|
if self.ctrlMenu is None: ## already shut down
|
||||||
|
return
|
||||||
|
self.ctrlMenu.setParent(None)
|
||||||
|
self.ctrlMenu = None
|
||||||
|
|
||||||
|
self.ctrlBtn.setParent(None)
|
||||||
|
self.ctrlBtn = None
|
||||||
|
self.autoBtn.setParent(None)
|
||||||
|
self.autoBtn = None
|
||||||
|
|
||||||
|
for k in self.scales:
|
||||||
|
i = self.scales[k]['item']
|
||||||
|
i.close()
|
||||||
|
|
||||||
|
self.scales = None
|
||||||
|
self.scene().removeItem(self.vb)
|
||||||
|
self.vb = None
|
||||||
|
for i in range(self.layout.count()):
|
||||||
|
self.layout.removeAt(i)
|
||||||
|
|
||||||
|
for p in self.proxies:
|
||||||
|
try:
|
||||||
|
p.setWidget(None)
|
||||||
|
except RuntimeError:
|
||||||
|
break
|
||||||
|
self.scene().removeItem(p)
|
||||||
|
self.proxies = []
|
||||||
|
|
||||||
|
self.menuAction.releaseWidget(self.menuAction.defaultWidget())
|
||||||
|
self.menuAction.setParent(None)
|
||||||
|
self.menuAction = None
|
||||||
|
|
||||||
if self.manager is not None:
|
if self.manager is not None:
|
||||||
self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
|
self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
|
||||||
self.manager.removeWidget(self.name)
|
self.manager.removeWidget(self.name)
|
||||||
|
#else:
|
||||||
|
#print "no manager"
|
||||||
|
|
||||||
def registerPlot(self, name):
|
def registerPlot(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -1042,6 +1080,8 @@ class PlotItem(QtGui.QGraphicsWidget):
|
|||||||
#ev.accept()
|
#ev.accept()
|
||||||
|
|
||||||
def resizeEvent(self, ev):
|
def resizeEvent(self, ev):
|
||||||
|
if self.ctrlBtn is None: ## already closed down
|
||||||
|
return
|
||||||
self.ctrlBtn.move(0, self.size().height() - self.ctrlBtn.size().height())
|
self.ctrlBtn.move(0, self.size().height() - self.ctrlBtn.size().height())
|
||||||
self.autoBtn.move(self.ctrlBtn.width(), self.size().height() - self.autoBtn.size().height())
|
self.autoBtn.move(self.ctrlBtn.width(), self.size().height() - self.autoBtn.size().height())
|
||||||
|
|
||||||
@ -1190,6 +1230,8 @@ class PlotWidgetManager(QtCore.QObject):
|
|||||||
del self.widgets[name]
|
del self.widgets[name]
|
||||||
#self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
#self.emit(QtCore.SIGNAL('widgetListChanged'), self.widgets.keys())
|
||||||
self.sigWidgetListChanged.emit(self.widgets.keys())
|
self.sigWidgetListChanged.emit(self.widgets.keys())
|
||||||
|
else:
|
||||||
|
print "plot %s not managed" % name
|
||||||
|
|
||||||
|
|
||||||
def listWidgets(self):
|
def listWidgets(self):
|
||||||
|
@ -39,6 +39,7 @@ class PlotWidget(GraphicsView):
|
|||||||
#self.scene().clear()
|
#self.scene().clear()
|
||||||
#self.mPlotItem.close()
|
#self.mPlotItem.close()
|
||||||
self.setParent(None)
|
self.setParent(None)
|
||||||
|
GraphicsView.close(self)
|
||||||
|
|
||||||
def __getattr__(self, attr): ## implicitly wrap methods from plotItem
|
def __getattr__(self, attr): ## implicitly wrap methods from plotItem
|
||||||
if hasattr(self.plotItem, attr):
|
if hasattr(self.plotItem, attr):
|
||||||
|
16
Point.py
16
Point.py
@ -6,7 +6,7 @@ Distributed under MIT/X11 license. See license.txt for more infomation.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
from math import acos
|
import numpy as np
|
||||||
|
|
||||||
def clip(x, mn, mx):
|
def clip(x, mn, mx):
|
||||||
if x > mx:
|
if x > mx:
|
||||||
@ -99,17 +99,17 @@ class Point(QtCore.QPointF):
|
|||||||
return (self[0]**2 + self[1]**2) ** 0.5
|
return (self[0]**2 + self[1]**2) ** 0.5
|
||||||
|
|
||||||
def angle(self, a):
|
def angle(self, a):
|
||||||
"""Returns the angle between this vector and the vector a."""
|
"""Returns the angle in degrees between this vector and the vector a."""
|
||||||
n1 = self.length()
|
n1 = self.length()
|
||||||
n2 = a.length()
|
n2 = a.length()
|
||||||
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 = acos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
ang = np.arccos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians
|
||||||
c = self.cross(a)
|
c = self.cross(a)
|
||||||
if c > 0:
|
if c > 0:
|
||||||
ang *= -1.
|
ang *= -1.
|
||||||
return ang
|
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."""
|
||||||
@ -120,6 +120,11 @@ class Point(QtCore.QPointF):
|
|||||||
a = Point(a)
|
a = Point(a)
|
||||||
return self[0]*a[1] - self[1]*a[0]
|
return self[0]*a[1] - self[1]*a[0]
|
||||||
|
|
||||||
|
def proj(self, b):
|
||||||
|
"""Return the projection of this vector onto the vector b"""
|
||||||
|
b1 = b / b.length()
|
||||||
|
return self.dot(b1) * b1
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Point(%f, %f)" % (self[0], self[1])
|
return "Point(%f, %f)" % (self[0], self[1])
|
||||||
|
|
||||||
@ -129,3 +134,6 @@ class Point(QtCore.QPointF):
|
|||||||
|
|
||||||
def max(self):
|
def max(self):
|
||||||
return max(self[0], self[1])
|
return max(self[0], self[1])
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return Point(self)
|
190
Transform.py
Normal file
190
Transform.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
from Point import Point
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Transform(QtGui.QTransform):
|
||||||
|
"""Transform that can always be represented as a combination of 3 matrices: scale * rotate * translate
|
||||||
|
|
||||||
|
This transform always has 0 shear."""
|
||||||
|
def __init__(self, init=None):
|
||||||
|
QtGui.QTransform.__init__(self)
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
if isinstance(init, dict):
|
||||||
|
self.restoreState(init)
|
||||||
|
elif isinstance(init, Transform):
|
||||||
|
self._state = {
|
||||||
|
'pos': Point(init._state['pos']),
|
||||||
|
'scale': Point(init._state['scale']),
|
||||||
|
'angle': init._state['angle']
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
elif isinstance(init, QtGui.QTransform):
|
||||||
|
self.setFromQTransform(init)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._state = {
|
||||||
|
'pos': Point(0,0),
|
||||||
|
'scale': Point(1,1),
|
||||||
|
'angle': 0.0 ## in degrees
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def setFromQTransform(self, tr):
|
||||||
|
p1 = Point(tr.map(0., 0.))
|
||||||
|
p2 = Point(tr.map(1., 0.))
|
||||||
|
p3 = Point(tr.map(0., 1.))
|
||||||
|
|
||||||
|
dp2 = Point(p2-p1)
|
||||||
|
dp3 = Point(p3-p1)
|
||||||
|
|
||||||
|
## detect flipped axes
|
||||||
|
if dp2.angle(dp3) > 0:
|
||||||
|
da = 180
|
||||||
|
sy = -1.0
|
||||||
|
else:
|
||||||
|
da = 0
|
||||||
|
sy = 1.0
|
||||||
|
|
||||||
|
self._state = {
|
||||||
|
'pos': Point(p1),
|
||||||
|
'scale': Point(dp2.length(), dp3.length() * sy),
|
||||||
|
'angle': (np.arctan2(dp2[1], dp2[0]) * 180. / np.pi) + da
|
||||||
|
}
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def translate(self, *args):
|
||||||
|
"""Acceptable arguments are:
|
||||||
|
x, y
|
||||||
|
[x, y]
|
||||||
|
Point(x,y)"""
|
||||||
|
t = Point(*args)
|
||||||
|
self.setTranslate(self._state['pos']+t)
|
||||||
|
|
||||||
|
def setTranslate(self, *args):
|
||||||
|
"""Acceptable arguments are:
|
||||||
|
x, y
|
||||||
|
[x, y]
|
||||||
|
Point(x,y)"""
|
||||||
|
self._state['pos'] = Point(*args)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def scale(self, *args):
|
||||||
|
"""Acceptable arguments are:
|
||||||
|
x, y
|
||||||
|
[x, y]
|
||||||
|
Point(x,y)"""
|
||||||
|
s = Point(*args)
|
||||||
|
self.setScale(self._state['scale'] * s)
|
||||||
|
|
||||||
|
def setScale(self, *args):
|
||||||
|
"""Acceptable arguments are:
|
||||||
|
x, y
|
||||||
|
[x, y]
|
||||||
|
Point(x,y)"""
|
||||||
|
self._state['scale'] = Point(*args)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def rotate(self, angle):
|
||||||
|
"""Rotate the transformation by angle (in degrees)"""
|
||||||
|
self.setRotate(self._state['angle'] + angle)
|
||||||
|
|
||||||
|
def setRotate(self, angle):
|
||||||
|
"""Set the transformation rotation to angle (in degrees)"""
|
||||||
|
self._state['angle'] = angle
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def __div__(self, t):
|
||||||
|
"""A / B == B^-1 * A"""
|
||||||
|
dt = t.inverted()[0] * self
|
||||||
|
return Transform(dt)
|
||||||
|
|
||||||
|
def __mul__(self, t):
|
||||||
|
return Transform(QtGui.QTransform.__mul__(self, t))
|
||||||
|
|
||||||
|
def saveState(self):
|
||||||
|
p = self._state['pos']
|
||||||
|
s = self._state['scale']
|
||||||
|
if s[0] == 0:
|
||||||
|
raise Exception('Invalid scale')
|
||||||
|
return {'pos': (p[0], p[1]), 'scale': (s[0], s[1]), 'angle': self._state['angle']}
|
||||||
|
|
||||||
|
def restoreState(self, state):
|
||||||
|
self._state['pos'] = Point(state.get('pos', (0,0)))
|
||||||
|
self._state['scale'] = Point(state.get('scale', (1.,1.)))
|
||||||
|
self._state['angle'] = state.get('angle', 0)
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
QtGui.QTransform.reset(self)
|
||||||
|
## modifications to the transform are multiplied on the right, so we need to reverse order here.
|
||||||
|
QtGui.QTransform.translate(self, *self._state['pos'])
|
||||||
|
QtGui.QTransform.rotate(self, self._state['angle'])
|
||||||
|
QtGui.QTransform.scale(self, *self._state['scale'])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.saveState())
|
||||||
|
|
||||||
|
def matrix(self):
|
||||||
|
return np.array([[self.m11(), self.m12(), self.m13()],[self.m21(), self.m22(), self.m23()],[self.m31(), self.m32(), self.m33()]])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import widgets
|
||||||
|
import GraphicsView
|
||||||
|
from functions import *
|
||||||
|
app = QtGui.QApplication([])
|
||||||
|
win = QtGui.QMainWindow()
|
||||||
|
win.show()
|
||||||
|
cw = GraphicsView.GraphicsView()
|
||||||
|
#cw.enableMouse()
|
||||||
|
win.setCentralWidget(cw)
|
||||||
|
s = QtGui.QGraphicsScene()
|
||||||
|
cw.setScene(s)
|
||||||
|
|
||||||
|
b = QtGui.QGraphicsRectItem(-5, -5, 10, 10)
|
||||||
|
b.setPen(QtGui.QPen(mkPen('y')))
|
||||||
|
t1 = QtGui.QGraphicsTextItem()
|
||||||
|
t1.setHtml('<span style="color: #F00">R</span>')
|
||||||
|
s.addItem(b)
|
||||||
|
s.addItem(t1)
|
||||||
|
|
||||||
|
tr1 = Transform()
|
||||||
|
tr2 = Transform()
|
||||||
|
tr3 = QtGui.QTransform()
|
||||||
|
tr3.translate(20, 0)
|
||||||
|
tr3.rotate(45)
|
||||||
|
print "QTransform -> Transform:", Transform(tr3)
|
||||||
|
|
||||||
|
print "tr1:", tr1
|
||||||
|
|
||||||
|
tr2.translate(20, 0)
|
||||||
|
tr2.rotate(45)
|
||||||
|
print "tr2:", tr2
|
||||||
|
|
||||||
|
dt = tr2/tr1
|
||||||
|
print "tr2 / tr1 = ", dt
|
||||||
|
|
||||||
|
print "tr2 * tr1 = ", tr2*tr1
|
||||||
|
|
||||||
|
tr4 = Transform()
|
||||||
|
tr4.scale(-1, 1)
|
||||||
|
tr4.rotate(30)
|
||||||
|
print "tr1 * tr4 = ", tr1*tr4
|
||||||
|
|
||||||
|
w1 = widgets.TestROI((0,0), (50, 50))
|
||||||
|
w2 = widgets.TestROI((0,0), (150, 150))
|
||||||
|
s.addItem(w1)
|
||||||
|
s.addItem(w2)
|
||||||
|
w1Base = w1.getState()
|
||||||
|
w2Base = w2.getState()
|
||||||
|
def update():
|
||||||
|
tr1 = w1.getGlobalTransform(w1Base)
|
||||||
|
tr2 = w2.getGlobalTransform(w2Base)
|
||||||
|
t1.setTransform(tr1 * tr2)
|
||||||
|
w1.setState(w1Base)
|
||||||
|
w1.applyGlobalTransform(tr2)
|
||||||
|
w1.sigRegionChanged.connect(update)
|
||||||
|
w2.sigRegionChanged.connect(update)
|
||||||
|
|
||||||
|
|
@ -7,6 +7,8 @@ from graphicsWindows import *
|
|||||||
#import PlotWidget
|
#import PlotWidget
|
||||||
#import ImageView
|
#import ImageView
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
from Point import Point
|
||||||
|
from Transform import Transform
|
||||||
|
|
||||||
plots = []
|
plots = []
|
||||||
images = []
|
images = []
|
||||||
|
0
examples/test_Arrow.py
Normal file → Executable file
0
examples/test_Arrow.py
Normal file → Executable file
0
examples/test_ImageItem.py
Normal file → Executable file
0
examples/test_ImageItem.py
Normal file → Executable file
0
examples/test_ImageView.py
Normal file → Executable file
0
examples/test_ImageView.py
Normal file → Executable file
0
examples/test_PlotWidget.py
Normal file → Executable file
0
examples/test_PlotWidget.py
Normal file → Executable file
0
examples/test_draw.py
Normal file → Executable file
0
examples/test_draw.py
Normal file → Executable file
0
examples/test_scatterPlot.py
Normal file → Executable file
0
examples/test_scatterPlot.py
Normal file → Executable file
@ -256,8 +256,11 @@ class ImageItem(QtGui.QGraphicsObject):
|
|||||||
def getLevels(self):
|
def getLevels(self):
|
||||||
return self.whiteLevel, self.blackLevel
|
return self.whiteLevel, self.blackLevel
|
||||||
|
|
||||||
def updateImage(self, image=None, copy=True, autoRange=False, clipMask=None, white=None, black=None):
|
def updateImage(self, image=None, copy=True, autoRange=False, clipMask=None, white=None, black=None, axes=None):
|
||||||
|
if axes is None:
|
||||||
axh = {'x': 0, 'y': 1, 'c': 2}
|
axh = {'x': 0, 'y': 1, 'c': 2}
|
||||||
|
else:
|
||||||
|
axh = axes
|
||||||
#print "Update image", black, white
|
#print "Update image", black, white
|
||||||
if white is not None:
|
if white is not None:
|
||||||
self.whiteLevel = white
|
self.whiteLevel = white
|
||||||
@ -280,6 +283,10 @@ class ImageItem(QtGui.QGraphicsObject):
|
|||||||
|
|
||||||
# Determine scale factors
|
# Determine scale factors
|
||||||
if autoRange or self.blackLevel is None:
|
if autoRange or self.blackLevel is None:
|
||||||
|
if self.image.dtype is np.ubyte:
|
||||||
|
self.blackLevel = 0
|
||||||
|
self.whiteLevel = 255
|
||||||
|
else:
|
||||||
self.blackLevel = self.image.min()
|
self.blackLevel = self.image.min()
|
||||||
self.whiteLevel = self.image.max()
|
self.whiteLevel = self.image.max()
|
||||||
#print "Image item using", self.blackLevel, self.whiteLevel
|
#print "Image item using", self.blackLevel, self.whiteLevel
|
||||||
@ -325,7 +332,6 @@ class ImageItem(QtGui.QGraphicsObject):
|
|||||||
self.image.shape = shape
|
self.image.shape = shape
|
||||||
im = ((self.image - black) * scale).clip(0.,255.).astype(np.ubyte)
|
im = ((self.image - black) * scale).clip(0.,255.).astype(np.ubyte)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
im1 = np.empty((im.shape[axh['y']], im.shape[axh['x']], 4), dtype=np.ubyte)
|
im1 = np.empty((im.shape[axh['y']], im.shape[axh['x']], 4), dtype=np.ubyte)
|
||||||
except:
|
except:
|
||||||
@ -341,10 +347,13 @@ class ImageItem(QtGui.QGraphicsObject):
|
|||||||
im1[..., 3] = alpha
|
im1[..., 3] = alpha
|
||||||
elif im.ndim == 3: #color image
|
elif im.ndim == 3: #color image
|
||||||
im2 = im.transpose(axh['y'], axh['x'], axh['c'])
|
im2 = im.transpose(axh['y'], axh['x'], axh['c'])
|
||||||
|
## [B G R A] Reorder colors
|
||||||
|
order = [2,1,0,3] ## for some reason, the colors line up as BGR in the final image.
|
||||||
|
|
||||||
for i in range(0, im.shape[axh['c']]):
|
for i in range(0, im.shape[axh['c']]):
|
||||||
im1[..., 2-i] = im2[..., i] ## for some reason, the colors line up as BGR in the final image.
|
im1[..., order[i]] = im2[..., i]
|
||||||
|
|
||||||
|
## fill in unused channels with 0 or alpha
|
||||||
for i in range(im.shape[axh['c']], 3):
|
for i in range(im.shape[axh['c']], 3):
|
||||||
im1[..., i] = 0
|
im1[..., i] = 0
|
||||||
if im.shape[axh['c']] < 4:
|
if im.shape[axh['c']] < 4:
|
||||||
@ -781,7 +790,7 @@ class PlotCurveItem(GraphicsObject):
|
|||||||
def mouseMoveEvent(self, ev):
|
def mouseMoveEvent(self, ev):
|
||||||
#GraphicsObject.mouseMoveEvent(self, ev)
|
#GraphicsObject.mouseMoveEvent(self, ev)
|
||||||
self.mouseMoved = True
|
self.mouseMoved = True
|
||||||
print "move"
|
#print "move"
|
||||||
|
|
||||||
def mouseReleaseEvent(self, ev):
|
def mouseReleaseEvent(self, ev):
|
||||||
#GraphicsObject.mouseReleaseEvent(self, ev)
|
#GraphicsObject.mouseReleaseEvent(self, ev)
|
||||||
@ -848,9 +857,9 @@ class CurvePoint(QtGui.QGraphicsObject):
|
|||||||
|
|
||||||
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())
|
ang = np.arctan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians
|
||||||
self.resetTransform()
|
self.resetTransform()
|
||||||
self.rotate(180+ ang * 180 / np.pi)
|
self.rotate(180+ ang * 180 / np.pi) ## takes degrees
|
||||||
QtGui.QGraphicsItem.setPos(self, *newPos)
|
QtGui.QGraphicsItem.setPos(self, *newPos)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -940,7 +949,7 @@ class CurveArrow(CurvePoint):
|
|||||||
|
|
||||||
class ScatterPlotItem(QtGui.QGraphicsWidget):
|
class ScatterPlotItem(QtGui.QGraphicsWidget):
|
||||||
|
|
||||||
sigPointClicked = QtCore.Signal(object)
|
sigPointClicked = QtCore.Signal(object, object)
|
||||||
|
|
||||||
def __init__(self, spots=None, pxMode=True, pen=None, brush=None, size=5):
|
def __init__(self, spots=None, pxMode=True, pen=None, brush=None, size=5):
|
||||||
QtGui.QGraphicsWidget.__init__(self)
|
QtGui.QGraphicsWidget.__init__(self)
|
||||||
@ -1027,7 +1036,7 @@ class ScatterPlotItem(QtGui.QGraphicsWidget):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def pointClicked(self, point):
|
def pointClicked(self, point):
|
||||||
self.sigPointClicked.emit(point)
|
self.sigPointClicked.emit(self, point)
|
||||||
|
|
||||||
def points(self):
|
def points(self):
|
||||||
return self.spots[:]
|
return self.spots[:]
|
||||||
@ -1044,6 +1053,7 @@ class SpotItem(QtGui.QGraphicsWidget):
|
|||||||
self.pen = pen
|
self.pen = pen
|
||||||
self.brush = brush
|
self.brush = brush
|
||||||
self.path = QtGui.QPainterPath()
|
self.path = QtGui.QPainterPath()
|
||||||
|
self.size = size
|
||||||
#s2 = size/2.
|
#s2 = size/2.
|
||||||
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1))
|
||||||
self.scale(size, size)
|
self.scale(size, size)
|
||||||
@ -1236,7 +1246,7 @@ class LabelItem(QtGui.QGraphicsWidget):
|
|||||||
|
|
||||||
def setAngle(self, angle):
|
def setAngle(self, angle):
|
||||||
self.angle = angle
|
self.angle = angle
|
||||||
self.item.resetMatrix()
|
self.item.resetTransform()
|
||||||
self.item.rotate(angle)
|
self.item.rotate(angle)
|
||||||
self.updateMin()
|
self.updateMin()
|
||||||
|
|
||||||
@ -1311,6 +1321,11 @@ class ScaleItem(QtGui.QGraphicsWidget):
|
|||||||
self.grid = False
|
self.grid = False
|
||||||
|
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.scene().removeItem(self.label)
|
||||||
|
self.label = None
|
||||||
|
self.scene().removeItem(self)
|
||||||
|
|
||||||
def setGrid(self, grid):
|
def setGrid(self, grid):
|
||||||
"""Set the alpha value for the grid, or False to disable."""
|
"""Set the alpha value for the grid, or False to disable."""
|
||||||
self.grid = grid
|
self.grid = grid
|
||||||
@ -1722,7 +1737,7 @@ class ViewBox(QtGui.QGraphicsWidget):
|
|||||||
m = QtGui.QTransform()
|
m = QtGui.QTransform()
|
||||||
|
|
||||||
## First center the viewport at 0
|
## First center the viewport at 0
|
||||||
self.childGroup.resetMatrix()
|
self.childGroup.resetTransform()
|
||||||
center = self.transform().inverted()[0].map(bounds.center())
|
center = self.transform().inverted()[0].map(bounds.center())
|
||||||
#print " transform to center:", center
|
#print " transform to center:", center
|
||||||
if self.yInverted:
|
if self.yInverted:
|
||||||
@ -2009,6 +2024,7 @@ class InfiniteLine(GraphicsObject):
|
|||||||
self.currentPen = self.pen
|
self.currentPen = self.pen
|
||||||
|
|
||||||
def setAngle(self, angle):
|
def setAngle(self, angle):
|
||||||
|
"""Takes angle argument in degrees."""
|
||||||
self.angle = ((angle+45) % 180) - 45 ## -45 <= angle < 135
|
self.angle = ((angle+45) % 180) - 45 ## -45 <= angle < 135
|
||||||
self.updateLine()
|
self.updateLine()
|
||||||
|
|
||||||
|
142
widgets.py
142
widgets.py
@ -9,12 +9,15 @@ for use as region-of-interest markers. ROI class automatically handles extractio
|
|||||||
of array data from ImageItems.
|
of array data from ImageItems.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui, QtOpenGL, QtSvg
|
from PyQt4 import QtCore, QtGui
|
||||||
|
if not hasattr(QtCore, 'Signal'):
|
||||||
|
QtCore.Signal = QtCore.pyqtSignal
|
||||||
#from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
|
#from numpy import array, arccos, dot, pi, zeros, vstack, ubyte, fromfunction, ceil, floor, arctan2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.linalg import norm
|
from numpy.linalg import norm
|
||||||
import scipy.ndimage as ndimage
|
import scipy.ndimage as ndimage
|
||||||
from Point import *
|
from Point import *
|
||||||
|
from Transform import Transform
|
||||||
from math import cos, sin
|
from math import cos, sin
|
||||||
import functions as fn
|
import functions as fn
|
||||||
#from ObjectWorkaround import *
|
#from ObjectWorkaround import *
|
||||||
@ -35,6 +38,8 @@ def rectStr(r):
|
|||||||
|
|
||||||
|
|
||||||
class ROI(QtGui.QGraphicsObject):
|
class ROI(QtGui.QGraphicsObject):
|
||||||
|
"""Generic region-of-interest widget.
|
||||||
|
Can be used for implementing many types of selection box with rotate/translate/scale handles."""
|
||||||
|
|
||||||
sigRegionChangeFinished = QtCore.Signal(object)
|
sigRegionChangeFinished = QtCore.Signal(object)
|
||||||
sigRegionChangeStarted = QtCore.Signal(object)
|
sigRegionChangeStarted = QtCore.Signal(object)
|
||||||
@ -54,10 +59,11 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
self.pen = fn.mkPen(pen)
|
self.pen = fn.mkPen(pen)
|
||||||
self.handlePen = QtGui.QPen(QtGui.QColor(150, 255, 255))
|
self.handlePen = QtGui.QPen(QtGui.QColor(150, 255, 255))
|
||||||
self.handles = []
|
self.handles = []
|
||||||
self.state = {'pos': pos, 'size': size, 'angle': angle}
|
self.state = {'pos': pos, 'size': size, 'angle': angle} ## angle is in degrees for ease of Qt integration
|
||||||
self.lastState = None
|
self.lastState = None
|
||||||
self.setPos(pos)
|
self.setPos(pos)
|
||||||
self.rotate(-angle * 180. / np.pi)
|
#self.rotate(-angle * 180. / np.pi)
|
||||||
|
self.rotate(-angle)
|
||||||
self.setZValue(10)
|
self.setZValue(10)
|
||||||
self.isMoving = False
|
self.isMoving = False
|
||||||
|
|
||||||
@ -114,7 +120,8 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
def setAngle(self, angle, update=True):
|
def setAngle(self, angle, update=True):
|
||||||
self.state['angle'] = angle
|
self.state['angle'] = angle
|
||||||
tr = QtGui.QTransform()
|
tr = QtGui.QTransform()
|
||||||
tr.rotate(-angle * 180 / np.pi)
|
#tr.rotate(-angle * 180 / np.pi)
|
||||||
|
tr.rotate(angle)
|
||||||
self.setTransform(tr)
|
self.setTransform(tr)
|
||||||
if update:
|
if update:
|
||||||
self.updateHandles()
|
self.updateHandles()
|
||||||
@ -129,10 +136,10 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
return self.addHandle({'name': name, 'type': 'f', 'pos': pos, 'item': item})
|
return self.addHandle({'name': name, 'type': 'f', 'pos': pos, 'item': item})
|
||||||
|
|
||||||
def addScaleHandle(self, pos, center, axes=None, item=None, name=None):
|
def addScaleHandle(self, pos, center, axes=None, item=None, name=None, lockAspect=False):
|
||||||
pos = Point(pos)
|
pos = Point(pos)
|
||||||
center = Point(center)
|
center = Point(center)
|
||||||
info = {'name': name, 'type': 's', 'center': center, 'pos': pos, 'item': item}
|
info = {'name': name, 'type': 's', 'center': center, 'pos': pos, 'item': item, 'lockAspect': lockAspect}
|
||||||
if pos.x() == center.x():
|
if pos.x() == center.x():
|
||||||
info['xoff'] = True
|
info['xoff'] = True
|
||||||
if pos.y() == center.y():
|
if pos.y() == center.y():
|
||||||
@ -212,6 +219,11 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
h['item'].hide()
|
h['item'].hide()
|
||||||
|
|
||||||
def mousePressEvent(self, ev):
|
def mousePressEvent(self, ev):
|
||||||
|
## Bug: sometimes we get events we shouldn't.
|
||||||
|
p = ev.pos()
|
||||||
|
if not self.isMoving and not self.shape().contains(p):
|
||||||
|
ev.ignore()
|
||||||
|
return
|
||||||
if ev.button() == QtCore.Qt.LeftButton:
|
if ev.button() == QtCore.Qt.LeftButton:
|
||||||
self.setSelected(True)
|
self.setSelected(True)
|
||||||
if self.translatable:
|
if self.translatable:
|
||||||
@ -345,6 +357,11 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
lp1[0] = round(lp1[0] / self.snapSize) * self.snapSize
|
lp1[0] = round(lp1[0] / self.snapSize) * self.snapSize
|
||||||
lp1[1] = round(lp1[1] / self.snapSize) * self.snapSize
|
lp1[1] = round(lp1[1] / self.snapSize) * self.snapSize
|
||||||
|
|
||||||
|
## preserve aspect ratio (this can override snapping)
|
||||||
|
if h['lockAspect'] or (modifiers & QtCore.Qt.AltModifier):
|
||||||
|
#arv = Point(self.preMoveState['size']) -
|
||||||
|
lp1 = lp1.proj(lp0)
|
||||||
|
|
||||||
## determine scale factors and new size of ROI
|
## determine scale factors and new size of ROI
|
||||||
hs = h['pos'] - c
|
hs = h['pos'] - c
|
||||||
if hs[0] == 0:
|
if hs[0] == 0:
|
||||||
@ -392,15 +409,16 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
## determine new rotation angle, constrained if necessary
|
## determine new rotation angle, constrained if necessary
|
||||||
ang = newState['angle'] + lp0.angle(lp1)
|
ang = newState['angle'] - lp0.angle(lp1)
|
||||||
if ang is None: ## this should never happen..
|
if ang is None: ## this should never happen..
|
||||||
return
|
return
|
||||||
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
||||||
ang = round(ang / (np.pi/12.)) * (np.pi/12.)
|
ang = round(ang / 15.) * 15. ## 180/12 = 15
|
||||||
|
|
||||||
## create rotation transform
|
## create rotation transform
|
||||||
tr = QtGui.QTransform()
|
tr = QtGui.QTransform()
|
||||||
tr.rotate(-ang * 180. / np.pi)
|
#tr.rotate(-ang * 180. / np.pi)
|
||||||
|
tr.rotate(ang)
|
||||||
|
|
||||||
## mvoe ROI so that center point remains stationary after rotate
|
## mvoe ROI so that center point remains stationary after rotate
|
||||||
cc = self.mapToParent(cs) - (tr.map(cs) + self.state['pos'])
|
cc = self.mapToParent(cs) - (tr.map(cs) + self.state['pos'])
|
||||||
@ -473,7 +491,8 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
if ang is None:
|
if ang is None:
|
||||||
return
|
return
|
||||||
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier):
|
||||||
ang = round(ang / (np.pi/12.)) * (np.pi/12.)
|
#ang = round(ang / (np.pi/12.)) * (np.pi/12.)
|
||||||
|
ang = round(ang / 15.) * 15.
|
||||||
|
|
||||||
hs = abs(h['pos'][scaleAxis] - c[scaleAxis])
|
hs = abs(h['pos'][scaleAxis] - c[scaleAxis])
|
||||||
newState['size'][scaleAxis] = lp1.length() / hs
|
newState['size'][scaleAxis] = lp1.length() / hs
|
||||||
@ -484,7 +503,8 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
|
|
||||||
c1 = c * newState['size']
|
c1 = c * newState['size']
|
||||||
tr = QtGui.QTransform()
|
tr = QtGui.QTransform()
|
||||||
tr.rotate(-ang * 180. / np.pi)
|
#tr.rotate(-ang * 180. / np.pi)
|
||||||
|
tr.rotate(-ang)
|
||||||
|
|
||||||
cc = self.mapToParent(cs) - (tr.map(c1) + self.state['pos'])
|
cc = self.mapToParent(cs) - (tr.map(c1) + self.state['pos'])
|
||||||
newState['angle'] = ang
|
newState['angle'] = ang
|
||||||
@ -576,7 +596,8 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
def stateRect(self, state):
|
def stateRect(self, state):
|
||||||
r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1])
|
r = QtCore.QRectF(0, 0, state['size'][0], state['size'][1])
|
||||||
tr = QtGui.QTransform()
|
tr = QtGui.QTransform()
|
||||||
tr.rotate(-state['angle'] * 180 / np.pi)
|
#tr.rotate(-state['angle'] * 180 / np.pi)
|
||||||
|
tr.rotate(-state['angle'])
|
||||||
r = tr.mapRect(r)
|
r = tr.mapRect(r)
|
||||||
return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1])
|
return r.adjusted(state['pos'][0], state['pos'][1], state['pos'][0], state['pos'][1])
|
||||||
|
|
||||||
@ -751,11 +772,60 @@ class ROI(QtGui.QGraphicsObject):
|
|||||||
### Untranspose array before returning
|
### Untranspose array before returning
|
||||||
#return arr5.transpose(tr2)
|
#return arr5.transpose(tr2)
|
||||||
|
|
||||||
|
def getGlobalTransform(self, relativeTo=None):
|
||||||
|
"""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:
|
||||||
|
relativeTo = self.preMoveState
|
||||||
|
st = self.getState()
|
||||||
|
|
||||||
|
## this is only allowed because we will be comparing the two
|
||||||
|
relativeTo['scale'] = relativeTo['size']
|
||||||
|
st['scale'] = st['size']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
t1 = Transform(relativeTo)
|
||||||
|
t2 = Transform(st)
|
||||||
|
return t2/t1
|
||||||
|
|
||||||
|
|
||||||
|
#st = self.getState()
|
||||||
|
|
||||||
|
### rotation
|
||||||
|
#ang = (st['angle']-relativeTo['angle']) * 180. / 3.14159265358
|
||||||
|
#rot = QtGui.QTransform()
|
||||||
|
#rot.rotate(-ang)
|
||||||
|
|
||||||
|
### We need to come up with a universal transformation--one that can be applied to other objects
|
||||||
|
### such that all maintain alignment.
|
||||||
|
### More specifically, we need to turn the ROI's position and angle into
|
||||||
|
### a rotation _around the origin_ and a translation.
|
||||||
|
|
||||||
|
#p0 = Point(relativeTo['pos'])
|
||||||
|
|
||||||
|
### base position, rotated
|
||||||
|
#p1 = rot.map(p0)
|
||||||
|
|
||||||
|
#trans = Point(st['pos']) - p1
|
||||||
|
#return trans, ang
|
||||||
|
|
||||||
|
def applyGlobalTransform(self, tr):
|
||||||
|
st = self.getState()
|
||||||
|
st['scale'] = st['size']
|
||||||
|
st = Transform(st)
|
||||||
|
#trans = QtGui.QTransform()
|
||||||
|
#trans.translate(*translate)
|
||||||
|
#trans.rotate(-rotate)
|
||||||
|
|
||||||
|
#x2, y2 = trans.map(*st['pos'])
|
||||||
|
|
||||||
|
#self.setAngle(st['angle']+rotate*np.pi/180.)
|
||||||
|
#self.setPos([x2, y2])
|
||||||
|
st = (st * tr).saveState()
|
||||||
|
st['size'] = st['scale']
|
||||||
|
self.setState(st)
|
||||||
|
|
||||||
|
|
||||||
class Handle(QtGui.QGraphicsItem):
|
class Handle(QtGui.QGraphicsItem):
|
||||||
|
|
||||||
@ -783,6 +853,7 @@ class Handle(QtGui.QGraphicsItem):
|
|||||||
self.pen.setCosmetic(True)
|
self.pen.setCosmetic(True)
|
||||||
self.isMoving = False
|
self.isMoving = False
|
||||||
self.sides, self.startAng = self.types[typ]
|
self.sides, self.startAng = self.types[typ]
|
||||||
|
self.buildPath()
|
||||||
|
|
||||||
def connectROI(self, roi, i):
|
def connectROI(self, roi, i):
|
||||||
self.roi.append((roi, i))
|
self.roi.append((roi, i))
|
||||||
@ -791,6 +862,12 @@ class Handle(QtGui.QGraphicsItem):
|
|||||||
return self.bounds
|
return self.bounds
|
||||||
|
|
||||||
def mousePressEvent(self, ev):
|
def mousePressEvent(self, ev):
|
||||||
|
# Bug: sometimes we get events not meant for us!
|
||||||
|
p = ev.pos()
|
||||||
|
if not self.isMoving and not self.path.contains(p):
|
||||||
|
ev.ignore()
|
||||||
|
return
|
||||||
|
|
||||||
#print "handle press"
|
#print "handle press"
|
||||||
if ev.button() == QtCore.Qt.LeftButton:
|
if ev.button() == QtCore.Qt.LeftButton:
|
||||||
self.isMoving = True
|
self.isMoving = True
|
||||||
@ -833,6 +910,20 @@ class Handle(QtGui.QGraphicsItem):
|
|||||||
for r in self.roi:
|
for r in self.roi:
|
||||||
r[0].movePoint(r[1], pos, modifiers)
|
r[0].movePoint(r[1], pos, modifiers)
|
||||||
|
|
||||||
|
def buildPath(self):
|
||||||
|
size = self.radius
|
||||||
|
self.path = QtGui.QPainterPath()
|
||||||
|
ang = self.startAng
|
||||||
|
dt = 2*np.pi / self.sides
|
||||||
|
for i in range(0, self.sides+1):
|
||||||
|
x = size * cos(ang)
|
||||||
|
y = size * sin(ang)
|
||||||
|
ang += dt
|
||||||
|
if i == 0:
|
||||||
|
self.path.moveTo(x, y)
|
||||||
|
else:
|
||||||
|
self.path.lineTo(x, y)
|
||||||
|
|
||||||
def paint(self, p, opt, widget):
|
def paint(self, p, opt, widget):
|
||||||
## determine rotation of transform
|
## determine rotation of transform
|
||||||
m = self.sceneTransform()
|
m = self.sceneTransform()
|
||||||
@ -851,15 +942,19 @@ class Handle(QtGui.QGraphicsItem):
|
|||||||
self.prepareGeometryChange()
|
self.prepareGeometryChange()
|
||||||
p.setRenderHints(p.Antialiasing, True)
|
p.setRenderHints(p.Antialiasing, True)
|
||||||
p.setPen(self.pen)
|
p.setPen(self.pen)
|
||||||
ang = self.startAng + va
|
|
||||||
dt = 2*np.pi / self.sides
|
p.rotate(va * 180. / 3.1415926)
|
||||||
for i in range(0, self.sides):
|
p.drawPath(self.path)
|
||||||
x1 = size * cos(ang)
|
|
||||||
y1 = size * sin(ang)
|
#ang = self.startAng + va
|
||||||
x2 = size * cos(ang+dt)
|
#dt = 2*np.pi / self.sides
|
||||||
y2 = size * sin(ang+dt)
|
#for i in range(0, self.sides):
|
||||||
ang += dt
|
#x1 = size * cos(ang)
|
||||||
p.drawLine(Point(x1, y1), Point(x2, y2))
|
#y1 = size * sin(ang)
|
||||||
|
#x2 = size * cos(ang+dt)
|
||||||
|
#y2 = size * sin(ang+dt)
|
||||||
|
#ang += dt
|
||||||
|
#p.drawLine(Point(x1, y1), Point(x2, y2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -902,10 +997,11 @@ 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)
|
||||||
c = Point(-width/2. * sin(ang), -width/2. * cos(ang))
|
ra = ang * np.pi / 180.
|
||||||
|
c = Point(-width/2. * sin(ra), -width/2. * cos(ra))
|
||||||
pos1 = pos1 + c
|
pos1 = pos1 + c
|
||||||
|
|
||||||
ROI.__init__(self, pos1, size=Point(l, width), angle=ang*180/np.pi, **args)
|
ROI.__init__(self, pos1, size=Point(l, width), angle=ang, **args)
|
||||||
self.addScaleRotateHandle([0, 0.5], [1, 0.5])
|
self.addScaleRotateHandle([0, 0.5], [1, 0.5])
|
||||||
self.addScaleRotateHandle([1, 0.5], [0, 0.5])
|
self.addScaleRotateHandle([1, 0.5], [0, 0.5])
|
||||||
self.addScaleHandle([0.5, 1], [0.5, 0.5])
|
self.addScaleHandle([0.5, 1], [0.5, 0.5])
|
||||||
|
Loading…
Reference in New Issue
Block a user