Qt/C++ Передвижение мышкой по сцене

Рейтинг: 1Ответов: 1Опубликовано: 09.05.2023

Я делаю свой виджет с графиком. На данный момент мне нужно сделать четкое масштабирование на колесико и перемещение мышкой по сцене.

Ниже я скину код, там будет куча комментариев в обработке события перемещения мыши со всеми моими идеями. Когда использую setSceneRect, то все работает отлично, но когда сильно приближаешься, то поведение становится странным, что-то похожее на то, что я не могу ухватиться мышкой за сцену, чтобы по ней переместиться. Матрицы преобразования почему-то работают при масштабировании, но ничего не делают при перемещении, ну как ничего, сцена просто колеблется как-то непонятно и всё.

Раньше у меня все работало, но я циклом проходился по всем объектам на сцене и менял им координаты, но подумал, что это как-то не очень, поэтому начал работать со сценой.

Вот заголовочный:

#ifndef CUSTOMPLOT_H
#define CUSTOMPLOT_H

#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItemGroup>
#include <QTimer>
#include <QMouseEvent>
#include <QScrollEvent>
#include <QTransform>
#include <QVector>
#include <QGraphicsSimpleTextItem>

class customPlot : public QGraphicsView
{
    Q_OBJECT

public:
    customPlot(QWidget *parent = 0);
    ~customPlot();
    //Метод возвращения в начало координат
    void returnToCenter();
    //Метод добавления графика
    void addPlot(QVector<double> x, QVector<double> y);
    //Метод добавления точек
    void addPoints(QVector<double> x, QVector<double> y);
public slots:

signals:

private slots:

private:
    QVector<QGraphicsItemGroup*> verticalGrid;
    QVector<QGraphicsItemGroup*> gorizontalGrid;
    //скорости увеличения и уменьшения шасштаба
    const qreal SCALE_INCREMENT = 0.2;
    const qreal SCALE_DECREMENT = 0.2;
    //скорости передвижения по плоскости по осям
    qreal MOVE_SPEED_X = 100.0;
    qreal MOVE_SPEED_Y = 100.0;
    //Константы минимума и максимума скоростей переджижения
    const qreal MAX_MOVE_SPEED_X = 200;
    const qreal MAX_MOVE_SPEED_Y = 200;
    const qreal MIN_MOVE_SPEED_X = 100;
    const qreal MIN_MOVE_SPEED_Y = 100;
    //Переопределение методов для перемещения по плоскости
    void mouseMoveEvent(QMouseEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;
    //Переопределение методa прокрутки колеса мыши для масштабирования
    void wheelEvent(QWheelEvent *event) override;
    //Координаты мыши в момент удержания графика
    QPoint curruntMousePos;
    //Переменная для проверки нажатия мыши
    bool isMousePressed;
    //сцена для отрисовки координатной плоскости
    QGraphicsScene *scene;
    //Сама сетка
    QGraphicsItemGroup *grid;
    //Шаг сетки графикa
    int grid_step;
    //Ручка для отрисовки сетки
    QPen grid_pen;
    //график
    QGraphicsItemGroup *plot;
    //точки
    QGraphicsItemGroup *dots;
    //цифры
    QVector<QGraphicsSimpleTextItem*> numbers;
};

#endif // CUSTOMPLOT_H

И cpp:

#include "customplot.h"

#include <QDebug>

customPlot::customPlot(QWidget *parent)
{
    Q_UNUSED(parent);
    MOVE_SPEED_X = 100.0;
    MOVE_SPEED_Y = 100.0;
    plot = nullptr;
    scene = nullptr;
    grid = nullptr;
    dots = nullptr;
    //Настройка отображения виджета
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    //Создание сцены
    scene = new QGraphicsScene();
    this->setScene(scene);
    scene->setSceneRect(0 , 0, this->width(), this->height());
    scene->addRect(0, 0, this->width(), this->height(), QPen(QColor(250, 1, 1), 10));

    //Отрисовка сетки графика
    grid = new QGraphicsItemGroup();
    //Отрисовка сетки и чисел
    grid_step = 50;
    grid_pen.setWidthF(0.3);
    grid_pen.setColor(Qt::lightGray);


    //Вертикальные линии
    for(int i = 0, j = 0; i > -(int)this->width() - grid_step; i -= grid_step, j++)
    {
        verticalGrid.push_back(new QGraphicsItemGroup());
        verticalGrid[j]->addToGroup(scene->addLine(i, this->height() + grid_step, i, -this->height() - grid_step, grid_pen));
    }
    for(int i = 0, j = 0; i < (int)this->width() + grid_step; i += grid_step, j++)
    {
        verticalGrid.push_back(new QGraphicsItemGroup());
        verticalGrid[j]->addToGroup(scene->addLine(i, this->height() + grid_step, i, -this->height() - grid_step, grid_pen));
    }

    //Горизонтальные линии
    for(int i = 0, j = 0; i > -(int)this->height() - grid_step; i -= grid_step, j++)
    {
        gorizontalGrid.push_back(new QGraphicsItemGroup());
        gorizontalGrid[j]->addToGroup(scene->addLine(this->width() + grid_step, i, -this->width() - grid_step, i, grid_pen));
    }
    for(int i = 0, j = 0; i < (int)this->height() + grid_step; i += grid_step, j++)
    {
        gorizontalGrid.push_back(new QGraphicsItemGroup());
        gorizontalGrid[j]->addToGroup(scene->addLine(this->width() + grid_step, i, -this->width() - grid_step, i, grid_pen));
    }
    for(int i = 0, j = 0; i < (int)this->height() + grid_step; i += grid_step, j -= grid_step)
    {

    }
    //Отрисовка осей
    grid_pen.setColor(Qt::black);
    grid_pen.setWidthF(1.2);
    grid->addToGroup(scene->addLine(-16777215, 0, 16777215, 0, grid_pen));
    grid->addToGroup(scene->addLine(0, 16777215, 0, -16777215, grid_pen));

    for(auto i : verticalGrid)
    {
        scene->addItem(i);
    }
    for(auto i : gorizontalGrid)
    {
        scene->addItem(i);
    }

    scene->addItem(grid);

    //ставим поле зрения в центр
    returnToCenter();
}

customPlot::~customPlot()
{
    if(dots != nullptr)
        delete dots;
    if(plot != nullptr)
        delete plot;
    if(grid != nullptr)
        delete grid;
    if(scene != nullptr)
        delete scene;
}

void customPlot::returnToCenter()
{
    QTransform center;
    center.translate(this->width() / 4, this->height() / 4);
    center.scale(1, 1);

    foreach(QGraphicsItem *item, scene->items())
    {
        item->setTransform(center);
    }
}


void customPlot::addPlot(QVector<double> x, QVector<double> y)
{
    if(plot != nullptr)
        scene->removeItem(plot);
    plot = new QGraphicsItemGroup();
    for(int i = 0; i < x.size() ; i++)
    {
        plot->addToGroup(scene->addEllipse(x[i] * grid_step, -y[i] * grid_step, 2, 2, QPen(QColor(250, 1, 1), 2)));
    }
    scene->addItem(plot);
    returnToCenter();
}

void customPlot::addPoints(QVector<double> x, QVector<double> y)
{
    if(dots != nullptr)
        scene->removeItem(dots);
    dots = new QGraphicsItemGroup();
    for(int i = 0; i < x.size(); i++)
    {
        dots->addToGroup(scene->addEllipse(x[i] * grid_step, -y[i] * grid_step, 5, 5, QPen(QColor(1,1,250)), QBrush(Qt::blue)));
    }
    scene->addItem(dots);
    returnToCenter();
}

void customPlot::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        //Сохраняем точку, где нажали мышку
        curruntMousePos = event->pos();
        isMousePressed = true;
        setCursor(Qt::ClosedHandCursor);

    }
}



void customPlot::mouseMoveEvent(QMouseEvent *event)
{

    if(isMousePressed)
    {

        //ПРИ ЭТОМ КОДЕ ВСЕ ОКЕЙ, НО ПРИ БОЛЬШОМ ПРИБЛИЖЕНИИ ВСЕ ПЛОХО

//        QPointF delta = mapToScene(event->pos()) - mapToScene(curruntMousePos);
//        curruntMousePos = event->pos();
//        curruntMousePos = event->pos();
//        this->setSceneRect(this->sceneRect().translated(-delta)); // сместить сцену на вектор

        //ПРИ ЭТОМ КОДЕ СЦЕНА ЛИШЬ КОЛЕБЛЕТСЯ ПРИ ПЕРЕМЕЩЕНИИ МЫШКОЙ
//        QPointF delta = event->pos() - curruntMousePos;
//               curruntMousePos = event->pos();

//               // Получаем текущую матрицу преобразования
//               QTransform transform = this->transform();

//               // Сдвигаем матрицу на вектор delta
//               transform.translate(delta.x(), delta.y());

//               // Устанавливаем новую матрицу преобразования
//               this->setTransform(transform);

        //ПРИ ЭТОМ КОДЕ СЦЕНА ЛИШЬ КОЛЕБЛЕТСЯ ПРИ ПЕРЕМЕЩЕНИИ МЫШКОЙ

//        QPointF delta = mapToScene(event->pos()) - mapToScene(curruntMousePos);
//       QTransform translate = this->transform();
//       translate.setMatrix(translate.m11(), translate.m12(), translate.m13(),
//                           translate.m21(), translate.m22(), translate.m23(),
//                           translate.m31() + delta.x(), translate.m32() + delta.y(), translate.m33());

//       scene->setTransform(translate);

        //ПРИ ЭТОМ КОДЕ СЦЕНА ЛИШЬ КОЛЕБЛЕТСЯ ПРИ ПЕРЕМЕЩЕНИИ МЫШКОЙ

//                QPointF delta = mapToScene(event->pos()) - mapToScene(curruntMousePos);
//                QTransform translate;
//                translate.setMatrix(1, 0, 0,
//                                    0, 1, 0,
//                                    delta.x(), delta.y(), 1);
//        this->setTransform(this->transform() * translate);


         //(этот от чатгпт)ПРИ ЭТОМ КОДЕ ВСЁ ОКЕЙ, НО Я МОГУ ПЕРЕМЕЩАТЬСЯ ЗА ПРЕДЕЛЫ СЦЕНЫ

//        QPoint delta = event->pos() - curruntMousePos;
//            QPointF currentCenter = this->mapToScene(this->viewport()->rect().center());

//            // Move the center point by the mouse delta
//            QPointF newCenter(currentCenter.x() - delta.x(), currentCenter.y() - delta.y());

//            // Set the new center point of the scene
//            this->centerOn(newCenter);

//            // Update the last mouse position
//            curruntMousePos = event->pos();

//            // Update the view
//            this->update();
    }
}



void customPlot::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        isMousePressed = false;
        setCursor(Qt::ArrowCursor);
    }
}


void customPlot::wheelEvent(QWheelEvent *event)
{
    QTransform scale = this->transform();
    if(event->angleDelta().y() > 0)
    {
        scale.setMatrix(scale.m11()+ SCALE_INCREMENT, scale.m12(), scale.m13(),
                        scale.m21(), scale.m22() + SCALE_INCREMENT, scale.m23(),
                        scale.m31(), scale.m32(), scale.m33() );
        this->setTransform(scale);
    }
    else
    {
        scale.setMatrix(scale.m11()- SCALE_DECREMENT, scale.m12(), scale.m13(),
                        scale.m21(), scale.m22() - SCALE_DECREMENT, scale.m23(),
                        scale.m31(), scale.m32(), scale.m33() );
        this->setTransform(scale);
    }
}



Ответы

▲ 0Принят

Если вы хотите сделать масштабирование колесом и перемещение по нажатию какой-то кнопки мыши, то у меня есть код, который сварганил из нескольких других кодов:

Правда, тут используются не матрицы а просто scale()

Заголовочный:

#ifndef M_GRAPHICSVIEW_H
#define M_GRAPHICSVIEW_H

#include <QGraphicsView>
#include <QMouseEvent>
#include <QWheelEvent>

class m_graphicsView : public QGraphicsView
{
public:
    m_graphicsView(QWidget *parent = nullptr);

    void gentle_zoom(double factor);

    void set_modifiers(Qt::KeyboardModifiers modifiers);

    void set_zoom_factor_base(double value);

private:
    Qt::KeyboardModifiers _modifiers;
    double _zoom_factor_base;
    QPointF target_scene_pos, target_viewport_pos;
    double scaleFactor = 1;

    void mouseMoveEvent(QMouseEvent *event);
    void wheelEvent(QWheelEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
};
#endif // M_GRAPHICSVIEW_H

Исходный:

#include "m_graphicsview.h"

m_graphicsView::m_graphicsView(QWidget *parent) // конструктор
{
    _modifiers = Qt::ControlModifier;
    _zoom_factor_base = 1.0015;
    scaleFactor = 1;
    setParent(parent);
    setDragMode(QGraphicsView::NoDrag);

    setMouseTracking(true);
}

void m_graphicsView::gentle_zoom(double factor) // функция для зума
{
    if ((scaleFactor*factor) < 2 && (scaleFactor*factor) >= 0.5)
    {
        scaleFactor = scaleFactor*factor;

        scale(factor, factor);
        centerOn(target_scene_pos);

        QPointF delta_viewport_pos = target_viewport_pos - QPointF(viewport()->width() / 2.0,viewport()->height() / 2.0);

        QPointF viewport_center = mapFromScene(target_scene_pos) - delta_viewport_pos;
        centerOn(mapToScene(viewport_center.toPoint()));
    }

}

void m_graphicsView::set_modifiers(Qt::KeyboardModifiers modifiers) // задать кнопки, на которых будет происходить масштабирование
{
    _modifiers = modifiers;
}

void m_graphicsView::set_zoom_factor_base(double value) // задать коэффициент увеличения
{
    _zoom_factor_base = value;
}



void m_graphicsView::mouseMoveEvent(QMouseEvent *event) // слежка за событием передвижения мыши
{
    QPointF delta = target_viewport_pos - event->pos();

    if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5)
    {
        target_viewport_pos = event->pos();
        target_scene_pos = mapToScene(event->pos());
    }

    QGraphicsView::mouseMoveEvent(event);
}

void m_graphicsView::wheelEvent(QWheelEvent *event) // слежка за событием вращения колеса мыши
{
    if (QApplication::keyboardModifiers() == _modifiers)
    {
        if (event->orientation() == Qt::Vertical)
        {
            double angle = event->angleDelta().y();
            double factor = qPow(_zoom_factor_base, angle); // рассчет коэффициента увеличения
            gentle_zoom(factor);
        }
    }
    else
    {
        QGraphicsView::wheelEvent(event);
    }
}

void m_graphicsView::mousePressEvent(QMouseEvent *event) // слежка за событием 
нажатия кнопки мыши
{
    if (event->buttons() & Qt::MiddleButton)
    {
        // по нажатию средней кнопки мыши создаем событие ее отпускания выставляем моду перетаскивания и создаем событие зажатой левой кнопки мыши
       QMouseEvent releaseEvent(QEvent::MouseButtonRelease,
                            event->localPos(),
                            event->screenPos(),
                            event->windowPos(),
                             Qt::LeftButton, nullptr,
                            event->modifiers());

        QGraphicsView::mouseReleaseEvent(&releaseEvent);

        setDragMode(QGraphicsView::ScrollHandDrag);

        QMouseEvent fakeEvent(event->type(), event->localPos(), event->screenPos(),event->windowPos(),Qt::LeftButton, event->buttons() | Qt::LeftButton, event->modifiers());

        QGraphicsView::mousePressEvent(&fakeEvent);
    }

    QGraphicsView::mousePressEvent(event);
}

void m_graphicsView::mouseReleaseEvent(QMouseEvent *event) // слежка за событием отжатия кнопки мыши
{
    if (event->button() == Qt::MiddleButton)
    {
       //отпускаем левую кнопку мыши которую виртуально зажали в mousePressEvent
       QMouseEvent fakeEvent(event->type(), event->localPos(), event->screenPos(), 
                         event->windowPos(),
                         Qt::LeftButton,
                         event->buttons() & ~Qt::LeftButton,
                         event->modifiers());

        QGraphicsView::mouseReleaseEvent(&fakeEvent);

        setDragMode(QGraphicsView::NoDrag);
    }

    QGraphicsView::mouseReleaseEvent(event);
}

Попробуйте переложить на ваш код Буду рад, если поможет