Source code for pylorenzmie.lmtool.ImageWidget
import pyqtgraph as pg
from pyqtgraph.Qt.QtCore import (pyqtSignal, pyqtSlot, pyqtProperty, QRectF)
import numpy as np
from numpy.typing import NDArray
[docs]
class ImageWidget(pg.GraphicsLayoutWidget):
'''Image display with a circular ROI for selecting a particle region.'''
#: Emitted with ``(x, y)`` centre coordinates when the ROI moves.
roiChanged = pyqtSignal(float, float)
#: Emitted with the new radius in pixels when the ROI is resized.
radiusChanged = pyqtSignal(int)
def __init__(self,
*args,
data: NDArray[float] | None = None,
**kwargs) -> None:
super().__init__(*args, **kwargs)
self._radius = 100
self._configurePlot()
self._connectSignals()
self.data = np.ones((480, 640)) if data is None else data
def _configurePlot(self) -> None:
self.setBackground('w')
pen = pg.mkPen('k', width=2)
self.image = pg.ImageItem(border=pen)
self.image.axisOrder = 'row-major'
plot = self.addPlot(row=0, col=0)
plot.addItem(self.image)
plot.getAxis('bottom').setPen(pen)
plot.getAxis('left').setPen(pen)
plot.setAspectLocked()
pen = pg.mkPen('w', width=3)
hpen = pg.mkPen('y', width=3)
self.roi = pg.CircleROI([0, 0],
pen=pen, handlePen=pen,
hoverPen=hpen, handleHoverPen=hpen,
radius=self._radius,
parent=self.image)
def _connectSignals(self) -> None:
self.roi.sigRegionChangeFinished.connect(self._handleChange)
@pyqtSlot(object)
def _handleChange(self, roi) -> None:
radius = int(self.roi.size()[0]) // 2
x0, y0 = roi.pos()
if radius != self._radius:
self._radius = radius
self.radiusChanged.emit(radius)
self.roiChanged.emit(x0 + radius, y0 + radius)
@pyqtProperty(np.ndarray)
def data(self) -> NDArray[float]:
return self._data
@data.setter
def data(self, data: NDArray[float]) -> None:
prev_data = getattr(self, '_data', None)
self._data = data
self.image.setImage(data)
h, w = data.shape
self.image.setRect(QRectF(0, 0, w, h))
if prev_data is None or prev_data.shape != data.shape:
self.roi.setPos([0, 0])
self.roi.setSize([200, 200])
@pyqtProperty(float)
def x_p(self) -> float:
return self.roi.pos()[0] + self._radius
@x_p.setter
def x_p(self, x_p: float) -> None:
pos = self.roi.pos()
pos[0] = x_p - self._radius
self.roi.setPos(pos)
@pyqtProperty(float)
def y_p(self) -> float:
return self.roi.pos()[1] + self._radius
@y_p.setter
def y_p(self, y_p: float) -> None:
pos = self.roi.pos()
pos[1] = y_p - self._radius
self.roi.setPos(pos)
@pyqtProperty(int)
def radius(self) -> int:
return self._radius
[docs]
def rect(self) -> QRectF:
pos = self.roi.pos()
size = self.roi.size()
return QRectF(*pos, *size)
[docs]
@classmethod
def example(cls) -> None:
def radius(r):
print(f'radius = {r}', end='\r')
def position(x, y):
print(f'position = ({x:.1f}, {y:.1f})', end='\r')
app = pg.mkQApp()
widget = cls()
widget.show()
widget.radiusChanged.connect(radius)
widget.roiChanged.connect(position)
app.exec()
if __name__ == '__main__': # pragma: no cover
ImageWidget.example()