Qt/C++ Передвижение мышкой по сцене
Я делаю свой виджет с графиком. На данный момент мне нужно сделать четкое масштабирование на колесико и перемещение мышкой по сцене.
Ниже я скину код, там будет куча комментариев в обработке события перемещения мыши со всеми моими идеями. Когда использую 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);
}
}