From 4f7f5bb36d0dc2f8da9e75e1d5a85d6f55f274d5 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Sat, 1 Nov 2025 10:57:39 +0100 Subject: [PATCH] jfjoch_viewer: Change mouse clicks to draw ROI (shift + ctrl with left click) --- viewer/JFJochViewerImage.cpp | 116 +++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 54 deletions(-) diff --git a/viewer/JFJochViewerImage.cpp b/viewer/JFJochViewerImage.cpp index b1915708..f410f413 100644 --- a/viewer/JFJochViewerImage.cpp +++ b/viewer/JFJochViewerImage.cpp @@ -88,24 +88,25 @@ void JFJochViewerImage::mousePressEvent(QMouseEvent *event) { } if (event->button() == Qt::LeftButton) { - setCursor(Qt::ClosedHandCursor); // Change cursor to indicate panning - lastMousePos = event->pos(); + if (event->modifiers() & Qt::Modifier::SHIFT) { + setCursor(Qt::CursorShape::CrossCursor); - if (mouse_inside_roi) - mouse_event_type = MouseEventType::MovingROI; - else - mouse_event_type = MouseEventType::Panning; + if (mouse_inside_roi) { + roiEndPos = RoundPoint(mapToScene(event->pos())); + } else { + roiStartPos = RoundPoint(mapToScene(event->pos())); + roi_type = (event->modifiers() & Qt::Modifier::CTRL) ? RoiType::RoiCircle : RoiType::RoiBox; + } + mouse_event_type = MouseEventType::DrawingROI; + } else { + setCursor(Qt::ClosedHandCursor); // Change cursor to indicate panning + lastMousePos = event->pos(); - } else if (event->buttons() & Qt::RightButton) { - setCursor(Qt::CursorShape::CrossCursor); - if (mouse_inside_roi) - roiEndPos = RoundPoint(mapToScene(event->pos())); - else { - roiStartPos = RoundPoint(mapToScene(event->pos())); - roi_type = (event->modifiers() == Qt::Modifier::SHIFT) ? RoiType::RoiCircle : RoiType::RoiBox; + if (mouse_inside_roi) + mouse_event_type = MouseEventType::MovingROI; + else + mouse_event_type = MouseEventType::Panning; } - mouse_event_type = MouseEventType::DrawingROI; - } QGraphicsView::mousePressEvent(event); // Call base implementation } @@ -120,7 +121,6 @@ void JFJochViewerImage::mouseMoveEvent(QMouseEvent *event) { && (coord.x() < image->Dataset().experiment.GetXPixelsNum()) && (coord.y() >= 0) && (coord.y() < image->Dataset().experiment.GetYPixelsNum())) { - float res = image->Dataset().experiment.GetDiffractionGeometry().PxlToRes(coord.x(), coord.y()); float q = 2 * M_PI / res; if (res <= 0) @@ -139,12 +139,12 @@ void JFJochViewerImage::mouseMoveEvent(QMouseEvent *event) { intensity_str = " Bad pxl "; emit writeStatusBar(QString("x=%1 y=%2 %3 d=%4 Å") - .arg(coord.x(), 0, 'f', 1) - .arg(coord.y(), 0, 'f', 1) - .arg(intensity_str) - .arg(res, 0, 'f', 2)); + .arg(coord.x(), 0, 'f', 1) + .arg(coord.y(), 0, 'f', 1) + .arg(intensity_str) + .arg(res, 0, 'f', 2)); } else - emit writeStatusBar(""); + emit writeStatusBar(""); QPointF delta; switch (mouse_event_type) { @@ -193,14 +193,15 @@ void JFJochViewerImage::LoadImageInternal() { return; // Create a QImage with RGB format - QImage qimage(image->Dataset().experiment.GetXPixelsNum(), image->Dataset().experiment.GetYPixelsNum(), QImage::Format_RGB888); + QImage qimage(image->Dataset().experiment.GetXPixelsNum(), image->Dataset().experiment.GetYPixelsNum(), + QImage::Format_RGB888); image_rgb.resize(image->Dataset().experiment.GetXPixelsNum() * image->Dataset().experiment.GetYPixelsNum()); rgb sat_color{}; - int r,g,b,a; + int r, g, b, a; feature_color.getRgb(&r, &g, &b, &a); - auto bad_color = rgb{.r = static_cast(r), .g = static_cast(g), .b= static_cast(b)}; + auto bad_color = rgb{.r = static_cast(r), .g = static_cast(g), .b = static_cast(b)}; if (show_saturation) { sat_color = bad_color; @@ -236,16 +237,16 @@ void JFJochViewerImage::LoadImageInternal() { pixmap = QPixmap::fromImage(qimage); - QGraphicsScene* currentScene = scene(); + QGraphicsScene *currentScene = scene(); if (!currentScene) { // First time - create a new scene currentScene = new QGraphicsScene(this); setScene(currentScene); } - } -void JFJochViewerImage::DrawSpots() {// Compute current visible area in scene coordinates +void JFJochViewerImage::DrawSpots() { + // Compute current visible area in scene coordinates const QRectF visibleRect = mapToScene(viewport()->geometry()).boundingRect(); for (const auto &s: image->ImageData().spots) { @@ -274,7 +275,6 @@ void JFJochViewerImage::DrawSpots() {// Compute current visible area in scene co } void JFJochViewerImage::DrawPredictions() { - QFont font("Arial", 2); // Font for pixel value text font.setPixelSize(2); // This will render very small text (1-pixel high). @@ -295,10 +295,10 @@ void JFJochViewerImage::DrawPredictions() { continue; auto rect = scene()->addEllipse(s.predicted_x - spot_size + 0.5f, - s.predicted_y - spot_size + 0.5f, - 2.0f * spot_size, - 2.0f * spot_size, - pen); + s.predicted_y - spot_size + 0.5f, + 2.0f * spot_size, + 2.0f * spot_size, + pen); // When zoomed in enough, draw "h k l" above the box if (scale_factor >= 10.0) { @@ -315,7 +315,6 @@ void JFJochViewerImage::DrawPredictions() { textItem->setPos(text_x, text_y); // Position the text over the pixel // textItem->setScale(1.0); // Scale down to 10% of the original size } - } } @@ -324,9 +323,11 @@ void JFJochViewerImage::DrawResolutionRings() { QRectF visibleRect = mapToScene(viewport()->geometry()).boundingRect(); int startX = std::max(0, static_cast(std::floor(visibleRect.left()))); - int endX = std::min(static_cast(image->Dataset().experiment.GetXPixelsNum()), static_cast(std::ceil(visibleRect.right()))); + int endX = std::min(static_cast(image->Dataset().experiment.GetXPixelsNum()), + static_cast(std::ceil(visibleRect.right()))); int startY = std::max(0, static_cast(std::floor(visibleRect.top()))); - int endY = std::min(static_cast(image->Dataset().experiment.GetYPixelsNum()), static_cast(std::ceil(visibleRect.bottom()))); + int endY = std::min(static_cast(image->Dataset().experiment.GetYPixelsNum()), + static_cast(std::ceil(visibleRect.bottom()))); auto geom = image->Dataset().experiment.GetDiffractionGeometry(); geom.PoniRot3_rad(0.0); @@ -349,8 +350,10 @@ void JFJochViewerImage::DrawResolutionRings() { radius = radius_x; if (radius > 0) - res_ring = {geom.PxlToRes(radius / 2.0f), - geom.PxlToRes(radius / 1.02f)}; + res_ring = { + geom.PxlToRes(radius / 2.0f), + geom.PxlToRes(radius / 1.02f) + }; else res_ring = {}; } @@ -369,12 +372,12 @@ void JFJochViewerImage::DrawResolutionRings() { auto [x3,y3] = geom.ResPhiToPxl(d, M_PI); auto [x4,y4] = geom.ResPhiToPxl(d, 3.0 * M_PI_2); - auto x_min = std::min({x1,x2,x3,x4}); - auto x_max = std::max({x1,x2,x3,x4}); - auto y_min = std::min({y1,y2,y3,y4}); - auto y_max = std::max({y1,y2,y3,y4}); + auto x_min = std::min({x1, x2, x3, x4}); + auto x_max = std::max({x1, x2, x3, x4}); + auto y_min = std::min({y1, y2, y3, y4}); + auto y_max = std::max({y1, y2, y3, y4}); - QRectF boundingRect(x_min, y_min, x_max - x_min, y_max-y_min); + QRectF boundingRect(x_min, y_min, x_max - x_min, y_max - y_min); scene()->addEllipse(boundingRect, pen); auto [x5,y5] = geom.ResPhiToPxl(d, phi_offset + 0); @@ -383,7 +386,7 @@ void JFJochViewerImage::DrawResolutionRings() { auto [x8,y8] = geom.ResPhiToPxl(d, phi_offset + 3.0 * M_PI_2); QPointF point_1(x5, y5); - QPointF point_2(x6,y6); + QPointF point_2(x6, y6); QPointF point_3(x7, y7); QPointF point_4(x8, y8); std::optional point; @@ -404,7 +407,7 @@ void JFJochViewerImage::DrawResolutionRings() { font.setPointSizeF(16.0 / sqrt(f)); // base 12pt around scale_factor ~10 QGraphicsTextItem *textItem = scene()->addText( - QString("%1 Å").arg(QString::number(d, 'f', 2)), font); + QString("%1 Å").arg(QString::number(d, 'f', 2)), font); textItem->setDefaultTextColor(feature_color); textItem->setPos(point.value()); } @@ -424,7 +427,8 @@ void JFJochViewerImage::DrawTopPixels() { for (auto iter = image->ValidPixels().crbegin(); iter != image->ValidPixels().rend() && i < show_highest_pixels; iter++, i++) - DrawCross(iter->second % image->Dataset().experiment.GetXPixelsNum() + 0.5, iter->second / image->Dataset().experiment.GetXPixelsNum() + 0.5, 15, 3); + DrawCross(iter->second % image->Dataset().experiment.GetXPixelsNum() + 0.5, + iter->second / image->Dataset().experiment.GetXPixelsNum() + 0.5, 15, 3); } void JFJochViewerImage::updateROI() { @@ -436,7 +440,7 @@ void JFJochViewerImage::updateROI() { QPointF delta = roiStartPos - roiEndPos; double radius = std::sqrt(delta.x() * delta.x() + delta.y() * delta.y()); roi_box = QRectF(roiStartPos.x() - radius, roiStartPos.y() - radius, - 2 * radius, 2 * radius).normalized(); + 2 * radius, 2 * radius).normalized(); emit roiCircleUpdated(roiStartPos.x(), roiStartPos.y(), radius); } updateOverlay(); @@ -456,9 +460,11 @@ void JFJochViewerImage::updateOverlay() { // Calculate the range of pixels to process int startX = std::max(0, static_cast(std::floor(visibleRect.left()))); - int endX = std::min(static_cast(image->Dataset().experiment.GetXPixelsNum()), static_cast(std::ceil(visibleRect.right()))); + int endX = std::min(static_cast(image->Dataset().experiment.GetXPixelsNum()), + static_cast(std::ceil(visibleRect.right()))); int startY = std::max(0, static_cast(std::floor(visibleRect.top()))); - int endY = std::min(static_cast(image->Dataset().experiment.GetYPixelsNum()), static_cast(std::ceil(visibleRect.bottom()))); + int endY = std::min(static_cast(image->Dataset().experiment.GetYPixelsNum()), + static_cast(std::ceil(visibleRect.bottom()))); if (scale_factor > 20.0 && (endX - startX + 1) * (endY - startY + 1) < 500) { // Iterate through the pixels within the visible range @@ -482,7 +488,7 @@ void JFJochViewerImage::updateOverlay() { textItem->setDefaultTextColor(Qt::black); // Text color else textItem->setDefaultTextColor(Qt::white); // Text color - textItem->setPos(x-0.7, y-0.8); // Position the text over the pixel + textItem->setPos(x - 0.7, y - 0.8); // Position the text over the pixel textItem->setScale(0.2); // Scale down to 10% of the original size } } @@ -509,7 +515,7 @@ void JFJochViewerImage::updateOverlay() { scene()->addEllipse(roi_box, pen); double pointRadius = 0.5; - QRectF pointBoundingBox = QRectF(roiStartPos.x() - pointRadius,roiStartPos.y() - pointRadius, + QRectF pointBoundingBox = QRectF(roiStartPos.x() - pointRadius, roiStartPos.y() - pointRadius, 2 * pointRadius, 2 * pointRadius); scene()->addEllipse(pointBoundingBox, pen); } @@ -565,7 +571,8 @@ void JFJochViewerImage::setColorMap(int color_map) { try { color_scale.Select(static_cast(color_map)); Redraw(); - } catch (...) {} + } catch (...) { + } } void JFJochViewerImage::setResolutionRing(QVector v) { @@ -611,7 +618,8 @@ void JFJochViewerImage::showHighestPixels(int32_t v) { void JFJochViewerImage::DrawSaturation() { for (const auto &iter: image->SaturatedPixels()) - DrawCross(iter % image->Dataset().experiment.GetXPixelsNum() + 0.5, iter / image->Dataset().experiment.GetXPixelsNum() + 0.5, 20, 4); + DrawCross(iter % image->Dataset().experiment.GetXPixelsNum() + 0.5, + iter / image->Dataset().experiment.GetXPixelsNum() + 0.5, 20, 4); } void JFJochViewerImage::DrawCross(float x, float y, float size, float width, float z) { @@ -620,11 +628,11 @@ void JFJochViewerImage::DrawCross(float x, float y, float size, float width, flo QPen pen(feature_color, width); pen.setCosmetic(true); - QGraphicsLineItem *horizontalLine = scene()->addLine(x - sc_size,y, x + sc_size, y, pen); + QGraphicsLineItem *horizontalLine = scene()->addLine(x - sc_size, y, x + sc_size, y, pen); QGraphicsLineItem *verticalLine = scene()->addLine(x, y - sc_size, x, y + sc_size, pen); horizontalLine->setZValue(z); // Ensure it appears above other items - verticalLine->setZValue(z); // Ensure it appears above other items + verticalLine->setZValue(z); // Ensure it appears above other items } void JFJochViewerImage::showSaturation(bool input) {