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()