viewer: click to select an ROI, with live move and a hand cursor

Clicking a box/circle on the image now selects it (syncing the side-panel
combobox) and grabs it for moving, with a closed-hand cursor during the
drag. The move recomputes ROI statistics live rather than only on release,
throttled to at most one in-flight recompute so the worker is not flooded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 13:56:09 +02:00
co-authored by Claude Opus 4.8
parent 308265c7b1
commit d9dfd36413
7 changed files with 54 additions and 20 deletions
+37 -20
View File
@@ -466,35 +466,39 @@ void JFJochDiffractionImage::setSelectedROI(QString name) {
}
bool JFJochDiffractionImage::roiEditPress(const QPointF &scenePos) {
if (!image || selected_roi_.isEmpty())
if (!image)
return false;
const auto &rois = image->Dataset().experiment.ROI().GetROIDefinition();
const std::string sel = selected_roi_.toStdString();
for (const auto &b : rois.boxes)
if (b.GetName() == sel) {
const QRectF r(QPointF(b.GetXMin(), b.GetYMin()), QPointF(b.GetXMax(), b.GetYMax()));
if (!r.contains(scenePos))
return false;
// Pick the first box/circle under the cursor, so an ROI can be grabbed by clicking it.
for (const auto &b : rois.boxes) {
const QRectF r(QPointF(b.GetXMin(), b.GetYMin()), QPointF(b.GetXMax(), b.GetYMax()));
if (r.contains(scenePos)) {
roi_edit_ = RoiEdit::MoveBox;
edit_name_ = selected_roi_;
edit_name_ = QString::fromStdString(b.GetName());
edit_box_ = r;
move_last_ = scenePos;
selected_roi_ = edit_name_;
emit roiSelected(edit_name_);
setCursor(Qt::ClosedHandCursor);
return true;
}
for (const auto &c : rois.circles)
if (c.GetName() == sel) {
const QPointF center(c.GetX(), c.GetY());
if (QLineF(center, scenePos).length() > c.GetRadius_pxl())
return false;
}
for (const auto &c : rois.circles) {
const QPointF center(c.GetX(), c.GetY());
if (QLineF(center, scenePos).length() <= c.GetRadius_pxl()) {
roi_edit_ = RoiEdit::MoveCircle;
edit_name_ = selected_roi_;
edit_name_ = QString::fromStdString(c.GetName());
edit_center_ = center;
edit_radius_ = c.GetRadius_pxl();
move_last_ = scenePos;
selected_roi_ = edit_name_;
emit roiSelected(edit_name_);
setCursor(Qt::ClosedHandCursor);
return true;
}
}
return false; // azimuthal editing is not yet supported
}
@@ -506,15 +510,28 @@ void JFJochDiffractionImage::roiEditMove(const QPointF &scenePos) {
else if (roi_edit_ == RoiEdit::MoveCircle)
edit_center_ += delta;
updateOverlay();
// Live recompute, but keep at most one in flight (cleared in loadImage) so the
// worker is not flooded with edits faster than it can recompute them.
if (!live_pending_) {
live_pending_ = true;
emit roiGeometryEdited(BuildEditedROIDefinition());
}
}
void JFJochDiffractionImage::roiEditRelease() {
if (roi_edit_ == RoiEdit::None || !image) {
roi_edit_ = RoiEdit::None;
if (roi_edit_ == RoiEdit::None)
return;
}
const ROIDefinition rois = BuildEditedROIDefinition();
roi_edit_ = RoiEdit::None;
setCursor(Qt::ArrowCursor);
emit roiGeometryEdited(rois); // final, exact position
}
ROIDefinition rois = image->Dataset().experiment.ROI().GetROIDefinition();
ROIDefinition JFJochDiffractionImage::BuildEditedROIDefinition() const {
ROIDefinition rois;
if (image)
rois = image->Dataset().experiment.ROI().GetROIDefinition();
const std::string sel = edit_name_.toStdString();
if (roi_edit_ == RoiEdit::MoveBox) {
for (auto &b : rois.boxes)
@@ -530,8 +547,7 @@ void JFJochDiffractionImage::roiEditRelease() {
break;
}
}
roi_edit_ = RoiEdit::None;
emit roiGeometryEdited(rois);
return rois;
}
@@ -556,6 +572,7 @@ void JFJochDiffractionImage::setHDRMode(bool input) {
}
void JFJochDiffractionImage::loadImage(std::shared_ptr<const JFJochReaderImage> in_image) {
live_pending_ = false; // a live ROI edit (if any) has now been recomputed
if (in_image) {
image = in_image;
UpdateForeground();