如何获取图像像素位置在QGraphicsView中加载-奇怪的MapToScene()行为

时间:2018-12-26 14:00:35

标签: c++ qt qgraphicsview qgraphicsscene

我最初是在QGraphicsView中加载图像,并使用this method进行基本的缩小和放大功能。 但是,我无法使用mapToScene类的eventFilter函数中的Graphics_view_zoom函数来检索实际图像像素位置。以下代码产生的行为与Windows Photo Viewer仅缩放选定区域时完全相同。

MapToScene()返回与鼠标事件位置相同的Point

这里是处理缩放的课程。

#include "Graphics_view_zoom.h"
#include <QMouseEvent>
#include <QApplication>
#include <QScrollBar>
#include <qmath.h>

Graphics_view_zoom::Graphics_view_zoom(QGraphicsView* view)
  : QObject(view), _view(view)
{
  _view->viewport()->installEventFilter(this);
  _view->setMouseTracking(true);
  _modifiers = Qt::ControlModifier;
  _zoom_factor_base = 1.0015;
}

void Graphics_view_zoom::gentle_zoom(double factor) {
  _view->scale(factor, factor);
  _view->centerOn(target_scene_pos);
  QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
                                                             _view->viewport()->height() / 2.0);
  QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
  _view->centerOn(_view->mapToScene(viewport_center.toPoint()));
  emit zoomed();
}

void Graphics_view_zoom::set_modifiers(Qt::KeyboardModifiers modifiers) {
  _modifiers = modifiers;

}

void Graphics_view_zoom::set_zoom_factor_base(double value) {
  _zoom_factor_base = value;
}

bool Graphics_view_zoom::eventFilter(QObject *object, QEvent *event) {
  if (event->type() == QEvent::MouseMove) {
    QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
    QPointF delta = target_viewport_pos - mouse_event->pos();
    // Here I want to get absolute image coordinates
    if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5) {
      target_viewport_pos = mouse_event->pos();
      target_scene_pos = _view->mapToScene(mouse_event->pos());
    }
  } else if (event->type() == QEvent::Wheel) {
    QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event);
    if (QApplication::keyboardModifiers() == _modifiers) {
      if (wheel_event->orientation() == Qt::Vertical) {
        double angle = wheel_event->angleDelta().y();
        double factor = qPow(_zoom_factor_base, angle);
        gentle_zoom(factor);
        return true;
      }
    }
  }
  Q_UNUSED(object)
  return false;

mainwindow.cpp 中, 我正在创建此类的对象并加载如下图像:

   m_GraphicsScene = new QGraphicsScene();
   pixmapItem = new QGraphicsPixmapItem();
   m_GraphicsScene->addItem(multiview[i].pixmapItem);
   view_wrapper = new Graphics_view_zoom(ui->GraphicsView);
   ui->GraphicsView->setScene(multiview[i].m_GraphicsScene);

   pixmapItem->setPixmap(QPixmap::fromImage("img.jpg"));
   multiview[view].m_GraphicsView->fitInView(QRectF(0,0,640,320),Qt::KeepAspectRatio);

任何人都可以帮助我实现这一目标吗?

2 个答案:

答案 0 :(得分:0)

使用从QGraphicsScene子类化的自定义图形场景可能会更好,因为这使得提取必要的坐标变得更加容易。唯一的麻烦是您必须在自定义QGraphicsPixmapItem::pos类中使用QGraphicsScene-我提供了一个完整的工作示例,该示例使用{{3}中的Graphics_view_zoom.hGraphics_view_zoom.cpp }}。 QGraphicsPixmapItem的位置将传递给QGraphicsScene子类Frame的成员,以便进行必要的更正。

#include <QPixmap>
#include <QGraphicsPixmapItem>
#include <QGraphicsTextItem>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <qfont.h>
#include "Graphics_view_zoom.h"

class Frame : public QGraphicsScene {
    Q_OBJECT
public:
    QGraphicsTextItem * coords;
    QPointF pic_tl;
    Frame::Frame(QWidget* parent)
        : QGraphicsScene(parent) {
        coords = new QGraphicsTextItem();
        coords->setZValue(1);
        coords->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
        addItem(coords);
    }

    void Frame::tl(QPointF p) {
        pic_tl = p;
    }

protected:
    void Frame::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
        QPointF pos = event->scenePos();
        coords->setPlainText("(" + QString("%1").arg(int(pos.x() - pic_tl.x())) + ", " 
                       + QString("%1").arg(int(pos.y() - pic_tl.y())) + ")");
        coords->setPos(pos);
        coords->adjustSize();
    }
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QMainWindow* main = new QMainWindow();

    QGraphicsView* GraphicsView = new QGraphicsView(main);
    Graphics_view_zoom* view_wrapper = new Graphics_view_zoom(GraphicsView);
    Frame* frame = new Frame(main);
    QGraphicsPixmapItem* pixmapItem = new QGraphicsPixmapItem();
    frame->addItem(pixmapItem);
    GraphicsView->setScene(frame);

    // Loads a 497x326 pixel test image
    pixmapItem->setPixmap(QPixmap(":/StackOverflow/test"));
    // small offset to ensure it works for pictures which are not at 
    // (0,0). Larger offsets produce the same result but require manual
    // adjustments of the view, I have neglected those for brevity as
    // they are not in the scope of the question.
    pixmapItem->setPos(-20, 20);
    frame->tl(pixmapItem->pos());
    GraphicsView->fitInView(QRectF(0, 0, 640, 320), Qt::KeepAspectRatio);
    GraphicsView->centerOn(pixmapItem->pos());

    main->resize(1920, 1080);
    main->show();
    GraphicsView->resize(main->width(), main->height());

    return a.exec();
}

这将相对于左上角的(0,0)显示鼠标下方像素的图像坐标。

在这些屏幕截图中鼠标不可见,但在第一个位置恰好在左上角,第二个位置恰好在右下角。如果在Graphics_view_zoom对象中需要这些坐标,则只需要适当地调整Frame实例的范围,或根据需要传递值。

注意-在此示例中,显示的确切坐标可能无法精确表示鼠标的位置,因为它们被强制转换为int s进行演示,但是浮点值很容易自QGraphicsSceneMoveEvent::scenePos()被访问以来返回QPointF。另外,请注意,在运行此演示时,鼠标相对于其“实际”位置的位置可能会有一些(希望很小)变化-我建议使用Qt::CrossCursor来缓解这种情况。例如,在我的系统上,较小的显示器上某些区域的默认光标关闭了大约一个像素,这也受缩放级别的影响-较高的缩放将产生更准确的结果,较小的缩放将导致较不准确。

答案 1 :(得分:0)

请记住,您使用的缩放比例只会缩放场景,而不会缩放项目。鉴于此,可以获得像素的位置,因此算法为:

  • 获取相对于QGraphicsView的鼠标位置
  • 使用mapToScene相对于场景转换该位置
  • 使用QGraphicsItem的mapFromScene相对于项目相对于场景的坐标。

考虑到上述情况,我实现了以下示例:

#include <QtWidgets>
#include <random>

static QPixmap create_image(const QSize & size){
    QImage image(size, QImage::Format_ARGB32);
    image.fill(Qt::blue);
    std::random_device rd;
    std::mt19937_64 rng(rd());
    std::uniform_int_distribution<int> uni(0, 255);
    for(int i=0; i< image.width(); ++i)
        for(int j=0; j < image.height(); ++j)
            image.setPixelColor(QPoint(i, j), QColor(uni(rng), uni(rng), uni(rng)));
    return QPixmap::fromImage(image);
}

class GraphicsView : public QGraphicsView
{
    Q_OBJECT
    Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers WRITE setModifiers)
public:
    GraphicsView(QWidget *parent=nullptr): QGraphicsView(parent){
        setScene(new QGraphicsScene);

        setModifiers(Qt::ControlModifier);
        auto item = scene()->addPixmap(create_image(QSize(100, 100)));
        item->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
        item->setPos(40, 40);
        fitInView(QRectF(0, 0, 640, 320),Qt::KeepAspectRatio);
        resize(640, 480);
    }
    void setModifiers(const Qt::KeyboardModifiers &modifiers){
        m_modifiers = modifiers;
    }
    Qt::KeyboardModifiers modifiers() const{
        return  m_modifiers;
    }
signals:
    void pixelChanged(const QPoint &);
protected:
    void mousePressEvent(QMouseEvent *event) override{
        if(QGraphicsPixmapItem *item = qgraphicsitem_cast<QGraphicsPixmapItem *>(itemAt(event->pos()))){
            QPointF p = item->mapFromScene(mapToScene(event->pos()));
            QPoint pixel_pos = p.toPoint();
            emit pixelChanged(pixel_pos);
        }
        QGraphicsView::mousePressEvent(event);
    }
    void wheelEvent(QWheelEvent *event) override{
        if(event->modifiers() == m_modifiers){
            double angle = event->orientation() == Qt::Vertical ? event->angleDelta().y(): event->angleDelta().x();
            double factor = qPow(base, angle);
            applyZoom(factor, event->pos());
        }
    }
private:
    void applyZoom(double factor, const QPoint & fixedViewPos)
    {
        QPointF fixedScenePos = mapToScene(fixedViewPos);
        centerOn(fixedScenePos);
        scale(factor, factor);
        QPointF delta = mapToScene(fixedViewPos) - mapToScene(viewport()->rect().center());
        centerOn(fixedScenePos - delta);
    }
    Qt::KeyboardModifiers m_modifiers;
    const double base = 1.0015;
};


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    GraphicsView *view = new GraphicsView;
    QLabel *label = new QLabel;
    QObject::connect(view, &GraphicsView::pixelChanged, label, [label](const QPoint & p){
        label->setText(QString("(%1, %2)").arg(p.x()).arg(p.y()));
    });
    label->setAlignment(Qt::AlignCenter);
    QWidget w;
    QVBoxLayout *lay = new QVBoxLayout(&w);
    lay->addWidget(view);
    lay->addWidget(label);
    w.show();
    return a.exec();
}

#include "main.moc"