Как получить положение пикселя изображения, загруженное в QGraphicsView - странное поведение MapToScene () - PullRequest
0 голосов
/ 26 декабря 2018

Я изначально загружаю изображение в QGraphicsView и использую этот метод для базового уменьшения и увеличения функциональности.Однако я не могу получить фактическую позицию пикселя изображения, используя функцию 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 голосов
/ 03 января 2019

Имейте в виду, что масштабирование, которое вы используете, масштабирует только сцену, а не элементы.Учитывая это, положение пикселя может быть получено, поэтому алгоритм:

  • Получить положение мыши относительно QGraphicsView
  • Преобразовать эту позицию относительно сцены, используяmapToScene
  • Преобразование координаты относительно сцены относительно элемента с использованием mapFromScene объекта QGraphicsItem.

Учитывая вышеизложенное, я реализовал следующий пример:

#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"
0 голосов
/ 03 января 2019

Может быть лучше использовать пользовательскую графическую сцену с подклассами из QGraphicsScene, поскольку это значительно упрощает извлечение необходимых координат.Единственная загвоздка в том, что у вас должен быть QGraphicsPixmapItem::pos доступный в пользовательском классе QGraphicsScene - я включил полный рабочий пример, который использует Graphics_view_zoom.h и Graphics_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) в верхнем левом углу.

image image

Мышь не видна на этих снимках экрана, но в первом она находится точно в левом верхнем углу, а во втором она находится точно в нижнем правом углу.Если эти координаты необходимы внутри объекта Graphics_view_zoom, тогда вам просто нужно соответствующим образом охватить экземпляр Frame или передать значение по мере необходимости.

Примечание - точные отображаемые координаты могут не точно отражать положение мыши в этом примере, поскольку они приведены к int с для демонстрации, но значения с плавающей запятой могут быть легкодоступ с QGraphicsSceneMoveEvent::scenePos() возвращает QPointF.Кроме того, обратите внимание, что при запуске этой демонстрации могут быть некоторые (мы надеемся, очень маленькие) вариации того, где мышь выглядит относительно ее «фактического» положения - я рекомендую использовать Qt::CrossCursor, чтобы смягчить это.Например, в моей системе курсор по умолчанию отключен примерно на пиксель для определенных областей на моем меньшем дисплее, это также зависит от уровня масштабирования - чем выше масштаб, тем более точные результаты, меньше масштаб будет менее точным.

...