Илья
Илья14 марта 2017 г. 20:15

Проблема при перерисовке сцены - "скакание" элементов сцены по вертикали

QGraphicsView, QGraphicsScene, QWidget

Здравствуйте. Использую Qt 5.6.1.
В общем, имеется клиентское приложение, которое связывается с сервером, получает от него данные - координаты узлов сети системы управления и данные с них, и отрисовывает местоположение узлов на QGraphicsView. На сцене рисую координатную сетку, которое можно "зуммировать" колесиком мыши, "скролить" правой кнопкой мыши. Возникает интересная вещь - при скроллинге по горизонтали все отлично, при скроллинге по вертикали начинают скакать горизонтальные линии координатной сетки, подписи к оси Х. Такое ощущение, что выключена какая нибудь "вертикальная привязка" или еще что-то в этом роде. Ничего похожего в интернете не нашел.
Выкладываю 2 класса - главного окна (myclient.h, myclient.cpp) и виджета, на котором происходит рисование (mygraphicview.cpp, mygraphicview.h).
Также для лицезрения сего чуда прикрепляю папку с проектом - сначала сделайте Zoom колесиком мыши и попробуйте правой кнопкой мыши поскроллить. Увидите, что горизонтальная линия зеленого цвета начнем прыгать то в одну сторону, то в другую (в зависимости от направления скролла).
p.s. При компиляции укажите путь к БД (database.cpp).

myclient.h

#ifndef MYCLIENT_H
#define MYCLIENT_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QGraphicsScene>
#include <QLabel>
#include <QStatusBar>
#include <database.h>
#include <stdlib.h>
#include <QSqlTableModel>
#include <QMessageBox>
#include <mypoint.h>
#include "mygraphicview.h"
#include <QKeyEvent>
#include <QProgressBar>

namespace Ui {
class MyClient;
}
class MyClient : public QMainWindow
{
    Q_OBJECT

public:
    explicit MyClient(QWidget *parent = 0);
    ~MyClient();
    Ui::MyClient *ui;

private:
    QProgressBar *pBar;
    MyPoint *point;
    QTcpSocket* pTcpSocket;             // Объявляем сокет
    bool Connect = false;               // Объявляем переменную, отвечающую за наличие соединения
    QGraphicsScene  *scene;             // Объявляем графическую сцену
    MyGraphicView   *myPicture;         // Наш кастомный виджет
    DataBase        *db;                // Объявляем переменную для доступа к БД
    QSqlTableModel  *model;             // Объект для взаимодействия с моделью представления таблицы БД
    // Объявяем метод для парсинга входящего сообщения
    void parsingRecMsg(const QString &RecMsg);
    // Метод для формирования модели представления таблицы БД
    void setupModel(const QString &tableName, const QStringList &headers);
    // Метод для отрисовки медели представления таблицы БД
    void createModel();
    //QLabel *mouseTracker;

protected:
    //void mouseMoveEvent(QMouseEvent* pe);
    QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;

signals:
    void signalRepaint();

private slots:
    void slotReadyRead();
    void slotError(QAbstractSocket::SocketError);
    void slotConnected();
    void slotDisconnected();
    void on_Button_Connect_clicked();
    void on_Button_Send_clicked();


    void on_Button_AddNode_clicked();

public slots:
    void slotSelectRow(int idRow);
};

#endif // MYCLIENT_H

myclient.cpp
#include "myclient.h"
#include "ui_myclient.h"

// Конструктор:
MyClient::MyClient(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MyClient)
{
    qDebug()<<"startMakeUI";
    ui->setupUi(this);
    this->resize(732,694);                                                  // Задаем размеры окна
    this->setFixedSize(732,694);                                            // Фиксируем размеры окна
    ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);   // Отключаем скроллбар по вертикали
    ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Отключаем скроллбар по горизонтали
    this->setWindowTitle("Monitoring control system");
    // Подключаемся к БД:
    db = new DataBase();
    db->connectToDataBase();
    //this->setMouseTracking(true);
    // Инициализируем виджет с графикой
    myPicture = new MyGraphicView();
    // и добавляем его на слой
    scene = new QGraphicsScene();                                           // Инициализируем графическую сцену
    ui->graphicsView->setScene(scene);                                      // Устанавливаем графическую сцену в graphicsView
    scene->addWidget(myPicture);
    /*/ Для отображения координат указателя мыши в StatusBar
    mouseTracker = new QLabel(this);
    statusBar()->addWidget(mouseTracker);*/
    connect(this,&MyClient::signalRepaint, myPicture, &MyGraphicView::slotAlarmTimer);
    connect(myPicture,&MyGraphicView::signalSelectRow,this,&MyClient::slotSelectRow);
    // Инициализируем модель для представления данных с заданием названий колонок
    this->setupModel(TABLE, QStringList() << trUtf8("NodeID")
                                          << trUtf8("Х")
                                          << trUtf8("Y")
                                          << trUtf8("Z")
                                          << trUtf8("Data 1")
                                          << trUtf8("Data 2")
                                          << trUtf8("Data 3")
                                          << trUtf8("State"));
    this->createModel();
}

// Диструктор:
MyClient::~MyClient()
{
    qDebug()<<"startDeleteUI";
    delete ui;
}

// Обработчик нажатия кнопки Connect:
void MyClient::on_Button_Connect_clicked()
{
    qDebug()<<"on_Button_Connect_clicked()";
    if (Connect==false){ // если соединения нет
        qDebug()<<"on_Button_Connect_clicked(): connecting...";
        bool PortNumber_ok;
        const QString IP = this->ui->LEdit_IPAdress->text();
        const ushort port = this->ui->LEdit_PortNumber->text().toUShort(&PortNumber_ok);
        if (!PortNumber_ok)
        {
            QMessageBox msgBox;
            msgBox.setText("The port is not valid.");
            msgBox.exec();
            return;
        }
        pTcpSocket = new QTcpSocket(this);
        pTcpSocket->connectToHost(IP, port);
        connect(pTcpSocket, SIGNAL(connected()), SLOT(slotConnected()));
        connect(pTcpSocket, SIGNAL(disconnected()), SLOT(slotDisconnected()));
        connect(pTcpSocket, SIGNAL(readyRead()), SLOT(slotReadyRead()));
        connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
                this,         SLOT(slotError(QAbstractSocket::SocketError))
               );
    }
    else { // если соединения есть
        qDebug()<<"on_Button_Connect_clicked(): disconnecting...";
        pTcpSocket->disconnectFromHost();
    }
}

// Слот успешного соединения клиента с сервером:
void MyClient::slotConnected()
{
    qDebug()<<"slotConnected(): connected";
    Connect=true;
    ui->TEdit_RMsg->append("State: Connected");
    ui->LEdit_IPAdress->setEnabled(false);
    ui->LEdit_PortNumber->setEnabled(false);
    ui->Button_Connect->setText("Disconnect");
    ui->Button_Send->setEnabled(true);
}

// Слот отсоединения клиента с сервером:
void MyClient::slotDisconnected()
{
    qDebug()<<"slotDisconnected(): disconnected";
    Connect = false;
    ui->TEdit_RMsg->append("State: Disconnected");
    ui->LEdit_IPAdress->setEnabled(true);
    ui->LEdit_PortNumber->setEnabled(true);
    ui->Button_Connect->setText("Connect");
    ui->Button_Send->setEnabled(false);
}

// Слот приема данных от сервера:
void MyClient::slotReadyRead()
{
    qDebug()<<"slotReadyRead(): StartReceiveData";
    QString line = "";
    // Определяем размер входящего пакета:
    int bytesAvail = pTcpSocket->bytesAvailable();
    qDebug()<<"waitForInput(): Receive " << bytesAvail << "bytes";
    int cnt = 0;
    bool endOfStream = false;
    while (cnt < bytesAvail && (!endOfStream)) {
        char ch;
        int bytesRead = pTcpSocket->read(&ch, sizeof(ch));
        if (bytesRead == sizeof(ch)) {
            cnt++;
            line.append( ch );
        }
        else {
            endOfStream = true;
        }
    }
    ui->TEdit_RMsg->append("Receive: "+line);
    parsingRecMsg(line);

}

// Слот обработки ошибок в соединении клиента с сервером:
void MyClient::slotError(QAbstractSocket::SocketError err)
{
    qDebug()<<"slotError";
    QString strError =
        "Error: " + (err == QAbstractSocket::HostNotFoundError ?
                     "The host was not found." :
                     err == QAbstractSocket::RemoteHostClosedError ?
                     "The remote host is closed." :
                     err == QAbstractSocket::ConnectionRefusedError ?
                     "The connection was refused." :
                     QString(pTcpSocket->errorString())
                    );
    ui->TEdit_RMsg->append(strError);
}

// Обработчик нажатия кнопки Send:
void MyClient::on_Button_Send_clicked()
{
    qDebug()<<"on_Button_Send_clicked(): send...";
    QByteArray  arrBlock;
    arrBlock.append(QString(ui->LEdit_TMsg->text()));
    pTcpSocket->write(arrBlock);
    ui->TEdit_RMsg->append("Send: "+QString(arrBlock));
}

/*void MyClient::mouseMoveEvent(QMouseEvent* pe)
{
    QPoint bsc = myPicture->pos();
    int x = pe->x() - bsc.x();
    int y = pe->y() - bsc.y();
    if((x < 0)||(x > scene->width())||(y < 0)||(y > scene->height()))
    {
        mouseTracker->setText("out of scene");
        return;
    }
    mouseTracker->setText(QString::number(x) + "  " + QString::number(y));
}*/

// Метод для парсинга входящего сообщения:
void MyClient::parsingRecMsg(const QString &RecMsg){
    qDebug()<<"parsingRecMsg()";
    if (RecMsg.length()==22){
        int id = strtol(RecMsg.mid(0,2).toUtf8().data(),NULL,16);
        int x = strtol(RecMsg.mid(2,4).toUtf8().data(),NULL,16);
        int y = strtol(RecMsg.mid(6,4).toUtf8().data(),NULL,16);
        int z = strtol(RecMsg.mid(10,4).toUtf8().data(),NULL,16);
        int data1 = strtol(RecMsg.mid(14,2).toUtf8().data(),NULL,16);
        int data2 = strtol(RecMsg.mid(16,2).toUtf8().data(),NULL,16);
        int data3 = strtol(RecMsg.mid(18,2).toUtf8().data(),NULL,16);
        int state = strtol(RecMsg.mid(20,2).toUtf8().data(),NULL,16);
        QString state_s;
        if(state == 1) state_s = "work_ON";
        else state_s = "work_OFF";
        QVariantList data;
        data.append(id);
        data.append(x);
        data.append(y);
        data.append(z);
        data.append(data1);
        data.append(data2);
        data.append(data3);
        data.append(state_s);
        QSqlQuery query;
        query.prepare("SELECT * FROM TableSystNode WHERE NodeID=:NodeID");
        query.bindValue(":NodeID", id);
        query.exec();
        if(query.next()==false){
            // Вставляем данные в БД
            db->inserIntoTable(data);
            model->select();
            emit signalRepaint();
        }
        else{
            // Изменяем данные в БД
            db->updateTable(data);
            model->select();
            emit signalRepaint();
        }
    }
    else{
        qDebug()<<"parsingRecMsg(): bad RecMsg: "<<RecMsg;
    }

}

// Метод для инициализации модели представления таблицы БД:
void MyClient::setupModel(const QString &tableName, const QStringList &headers)
{
    /* Производим инициализацию модели представления данных
     * с установкой имени таблицы в базе данных, по которому
     * будет производится обращение в таблице
     * */
    model = new QSqlTableModel(this);
    model->setTable(tableName);
    // Устанавливаем названия колонок в таблице с сортировкой данных
    for(int i = 0, j = 0; i < model->columnCount(); i++, j++){
        model->setHeaderData(i,Qt::Horizontal,headers[j]);

    }
    // Устанавливаем сортировку по возрастанию данных по нулевой колонке
    model->setSort(0,Qt::AscendingOrder);

}

// Метод для отрисовки модели представления таблицы БД:
void MyClient::createModel()
{
    ui->tableView->setModel(model);             // Устанавливаем модель на TableView
    //ui->tableView->setColumnHidden(0, true);    // Скрываем колонку с id записей
    ui->tableView->verticalHeader()->hide();

    // Разрешаем выделение строк
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    // Устанавливаем режим выделения лишь одно строки в таблице
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    // Устанавливаем размер колонок по содержимому
    ui->tableView->resizeColumnsToContents();
    ui->tableView->setColumnWidth(1,50);
    ui->tableView->setColumnWidth(2,50);
    ui->tableView->setColumnWidth(3,50);
    ui->tableView->setColumnWidth(4,50);
    ui->tableView->setColumnWidth(5,50);
    ui->tableView->setColumnWidth(6,50);
    ui->tableView->setUpdatesEnabled(true);
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);

    ui->tableView->horizontalHeader()->setStretchLastSection(true);
    model->select(); // Делаем выборку данных из таблицы
}

// Слот для выделения строки в таблице БД:
void MyClient::slotSelectRow(int idRow)
{
    // Выделяем нужную строку
    ui->tableView->selectRow(idRow);
    // Имитируем нажатие кнопки Tab, чтобы выделить строку
    QKeyEvent* pe = new QKeyEvent(QEvent::KeyPress,
            Qt::Key_Tab,Qt::NoModifier, "Tab");
    QApplication::sendEvent(this, pe);
}


void MyClient::on_Button_AddNode_clicked()
{
    pBar = new QProgressBar();
    pBar->setRange(0,0);
    pBar->setTextVisible(true);
    pBar->setFormat("Loading...");
    pBar->setAlignment(Qt::AlignCenter);
    pBar->show();
}


mygraphicview.h
#ifndef MYGRAPHICVIEW_H
#define MYGRAPHICVIEW_H

#include <QWidget>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QWheelEvent>
#include <qmath.h>
#include <math.h>
#include <QDebug>
#include <QRect>
#include <QScrollBar>
#include <QApplication>
#include <mypoint.h>
#include <QMessageBox>
#include <QtSql>
#include <database.h>
#include <QObject>

class MyGraphicView : public QGraphicsView
{
    Q_OBJECT
public:
    explicit MyGraphicView(QWidget *parent = 0);
       ~MyGraphicView();
    double CurZoom=1.0;
    bool FirstStart = true;

private:
    QGraphicsScene      *scene;     // Объявляем сцену для отрисовки
    QGraphicsItemGroup  *group_1;   // Объявляем первую группу элементов
    QGraphicsItemGroup  *group_2;   // Объявляем вторую группу элементов
    MyPoint *point;                 // Объявляем переменную для создания объектов на сцене
    DataBase *db;                   // Объявляем переменную для доступа к БД
    double minX = -250.0;              // Минимальное значение по оси Х
    double maxX = 250.0;            // Максимальное значение по оси Х
    int numXTicks = 10;             // Количество рисок по оси Х
    double minY = -250.0;              // Минимальное значение по оси Y
    double maxY = 250;              // Максимальное значение по оси Y
    int numYTicks = 10;             // Количество рисок по оси Y
    double spanX() const { return fabs(maxX - minX); }  // Разница по между макс и мин X
    double spanY() const { return fabs(maxY - minY); }  // Разница по между макс и мин Y
    double currentX;                // Текущее значение Х
    double currentY;                // Текущее значение Y
    bool StartDraw = false;         // Переменная для разрешения перерисовки сцены
    double pXmin;                   // Временная переменная для хранение мин значения оси Х
    double pYmax;                   // Временная переменная для хранение мин значения оси Y
    void clearScene();              // Метод для удаления всех элементов со сцены
    void adjustAxis(double &min, double &max, int &numTicks);// Метод для выбора оптимальных значений осей и рисок сцены

protected:
    void wheelEvent(QWheelEvent* event);
    void mouseMoveEvent(QMouseEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

private slots:
    void slotFromPoint(int numberPoint, int pX, int pY);       // Слот для обработки сигнала из точки

public slots:
    void slotAlarmTimer();      // Cлот для обработчика переполнения таймера (в нем происходит рисование сцены)
    //void slotSelectedItem();

signals:
    void signalSelectRow(int numRow);
};

#endif // MYGRAPHICVIEW_H

mygraphicview.cpp
#include "mygraphicview.h"

MyGraphicView::MyGraphicView(QWidget *parent)
    : QGraphicsView(parent)
{
    qDebug()<<"MyGraphicView(): start";
    // Настраиваем отображение виджета и его содержимого:
    this->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);             // Отключим скроллбар по горизонтали
    this->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);               // Отключим скроллбар по вертикали
    this->setAlignment(Qt::AlignCenter);                                    // Делаем привязку содержимого к центру
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);    // Растягиваем содержимое по виджету
    this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);         // В качестве якоря используется точка под курсором мышки.
    this->setRenderHint(QPainter::Antialiasing);                            // Устанавливаем сглаживание
    //this->setFocusPolicy(Qt::StrongFocus);
    scene = new QGraphicsScene();                                           // Инициализируем сцену для отрисовки
    scene->setItemIndexMethod(QGraphicsScene::NoIndex);                     // Линейный поиск элементов
    this->setScene(scene);                                                  // Устанавливаем сцену в виджет
    slotAlarmTimer();


    //timer = new QTimer();                   // Инициализируем Таймер
    // Подключаем СЛОТ для отрисовки к таймеру
    //connect(timer, SIGNAL(timeout()), this, SLOT(slotAlarmTimer()));
    //timer->start(50);                   // Стартуем таймер на 50 миллисекунд
    FirstStart = false;
}

MyGraphicView::~MyGraphicView()
{

}

// Метод отвечающий за перерисовку сцены
void MyGraphicView::slotAlarmTimer()
{
    // Запрещаем перерисование сцены
    StartDraw = true;
    qDebug()<<"slotAlarmTimer(): start";
    /* Удаляем все элементы со сцены,
     * если они есть перед новой отрисовкой
     * */
    clearScene();
    /* Устанавливаем размер сцены по размеру виджета
     * Первая координата - это левый верхний угол,
     * а Вторая - это правый нижний угол
     * */
    scene->setSceneRect(-250,-250,500,500);
    // Создаем кисти для рисования на сцене
    QPen penGreen(Qt::green);       // Задаём чёрную кисть
    QPen penRed(Qt::red);           // Задаём красную кисть
    QPen penGray(Qt::lightGray);         // Задаем серую кисть
    QPen penDarkGray(Qt::green); // Задаем темно-серую кисть
    penGray.setCosmetic(true);
    penDarkGray.setCosmetic(true);
    // Получаем текущее значение минимальных значений осей координат
    QPolygon pView = mapFromScene(QRect(0, 0, this->viewport()->width(), this->viewport()->height()));
    if (CurZoom < 2.0) {
        pXmin = 0;
        pYmax = 0;
    }
    else
    {
        pXmin = -pView.at(0).x()/CurZoom;
        pYmax = 500 / CurZoom + minY;
    }

    // Рисуем координатную ось Х:
    QRect rect(minX,minY,spanX(),spanY());
    for (int i = 0; i <= numXTicks; ++i) {
        int x = rect.left() + (i * (rect.width() - 1)/numXTicks);
        double label = minX + (i * spanX()/numXTicks);
        if (label == 0) scene->addLine(x+1,rect.top(),x+1,rect.bottom(),penDarkGray);
        else scene->addLine(x+1,rect.top(),x+1,rect.bottom(),penGray);
        //qDebug()<<"add line X:"<<x<<" "<<rect.top()<<" "<<x<< " "<< rect.bottom();
        QGraphicsSimpleTextItem *name = NULL;
        name = scene->addSimpleText(QString::number(label));
        name->setBrush(Qt::gray);
        name->setScale(1/CurZoom);
        int kname = spanX()/numXTicks;
        name->setPos(minX+i*kname+3/CurZoom, pYmax-14/CurZoom);
    }
    // Рисуем координатную ось Y:
    for (int j = 0; j <= numYTicks; ++j) {
        int y = rect.bottom() - (j * (rect.height() - 1)/numYTicks);
        double label = -(minY + (j * spanY()/numYTicks));
        if ((unsigned int)label == 0)
        {
            scene->addLine(rect.left(), y, rect.right(), y,penDarkGray);
            //qDebug()<<"LABEL:"<<label;
        } else scene->addLine(rect.left(), y, rect.right(), y,penGray);
        //qDebug()<<"add line Y:"<<rect.left()<<" "<<y<<" "<<rect.right()<< " "<< y;
        QGraphicsSimpleTextItem *name = NULL;
        name = scene->addSimpleText(QString::number(label));
        name->setBrush(Qt::gray);
        int kname = spanY()/numYTicks;
        name->setPos(pXmin+3/CurZoom, minY+j*kname-0.7*CurZoom);
        name->setScale(1/CurZoom);
    }
    scene->addRect(rect,penGray);

    // Рисуем местоположение абонентов системы:
    QSqlQuery query;
    db->selectTable(query);
    while (query.next()) {
         int numberP = query.value(0).toInt();
         int pX = query.value(1).toInt();
         int pY = query.value(2).toInt();
         qDebug()<<"slotAlarmTimer(): new point with cord X = "<<pX<<" Y = "<<pY;
         point = new MyPoint(pX,-pY,5.0,5.0,Qt::green,numberP);
         scene->addItem(point);
         point->setPos(pX,-pY);
         // Подключаем сигнал из точки к СЛОТу в главном классе
         connect(point,&MyPoint::signal1, this, &MyGraphicView::slotFromPoint);

    }
    // Разрешаем перерисование сцены
    StartDraw = false; 
}

/* // Методом перехватываем событие изменения размера виджет
void MyGraphicView::resizeEvent(QResizeEvent *event)
{
    timer->start(50);   // Как только событие произошло стартуем таймер для отрисовки
    QGraphicsView::resizeEvent(event);  // Запускаем событие родителького класса
}
*/

// Метод для удаления всех элементов со сцены:
void MyGraphicView::clearScene()
{
    // Перебираем все элементы сцены и удаляем их
    QList<QGraphicsItem*> all = items();
    for (int i = 0; i < all.size(); i++)
    {
        QGraphicsItem *gi = all[i];
        if(gi->parentItem()==NULL) {
            delete gi;
        }
    }
}

// Метод вращения колесика мыши:
void MyGraphicView::wheelEvent(QWheelEvent *event)
{
    // Scale the view / do the zoom
    const double scaleFactor = 1.15;
    if(event->delta() > 0)
    {
        // Zoom in
        if(CurZoom < 6.0){
            CurZoom = CurZoom*scaleFactor;
            qDebug()<<"wheelEvent(): CurZoom = " << CurZoom;
            scale(scaleFactor, scaleFactor);
            centerOn(mapToScene(event->pos()));
            QPolygon pView = mapFromScene(QRect(0, 0, this->viewport()->width(), this->viewport()->height()));
            qDebug()<<pView;
            minX = -pView.at(0).x()/CurZoom;
            maxX = 500 / CurZoom + minX;
            minY = -pView.at(0).y()/CurZoom;
            maxY = 500 / CurZoom + minY;
            adjustAxis(minX, maxX, numXTicks);
            adjustAxis(minY, maxY, numYTicks);
            qDebug()<<"numXTicks = "<<numXTicks;
            qDebug()<<"numYTicks = "<<numYTicks;
            qDebug()<<"minX = "<< minX << "; minY = " << minY;
            qDebug()<<"maxX = "<< maxX << "; maxY = " << maxY;
            slotAlarmTimer();
        } else qDebug()<<"wheelEvent(): posX = "<<event->pos().x()/CurZoom<< "posY = "<<event->pos().y()/CurZoom;
    }
    else
    {
        // Zooming out
        if (CurZoom > 1.10) {
            CurZoom = CurZoom/scaleFactor;
            qDebug()<<"wheelEvent(): CurZoom = " << CurZoom;
            scale(1.0 / scaleFactor, 1.0 / scaleFactor);
            centerOn(mapToScene(event->pos()));
            QPolygon pView = mapFromScene(QRect(0, 0, this->viewport()->width(), this->viewport()->height()));
            qDebug()<<pView;
            minX = -pView.at(0).x()/CurZoom;
            maxX = 500 / CurZoom + minX;
            minY = -pView.at(0).y()/CurZoom;
            maxY = 500 / CurZoom + minY;
            adjustAxis(minX, maxX, numXTicks);
            adjustAxis(minY, maxY, numYTicks);
            qDebug()<<"numXTicks = "<<numXTicks;
            qDebug()<<"numYTicks = "<<numYTicks;
            qDebug()<<"minX = "<< minX << "; minY = " << minY;
            qDebug()<<"maxX = "<< maxX << "; maxY = " << maxY;
            slotAlarmTimer();
        } else {
            minX = -250;
            maxX = 250;
            minY = -250;
            maxY = 250;
            numXTicks = 10;
            numYTicks = 10;
            qDebug()<<"wheelEvent(): posX = "<<event->pos().x()/CurZoom<< "posY = "<<event->pos().y()/CurZoom;
            slotAlarmTimer();
        }
    }
}

// Метод для определения оптимыльных значений рисок координатных осей:
void MyGraphicView::adjustAxis(double &min, double &max, int &numTicks)
{
    const int MinTicks = 5;
    double grossStep = (max - min) / MinTicks;
    double step = pow(10, floor(log10(grossStep)));
    qDebug()<<"Step = "<<step;
    if (5 * step < grossStep) step *= 5;
    else if (4 * step < grossStep) step *= 4;
    else if (3 * step < grossStep) step *= 3;
    else if (2 * step < grossStep) step *= 2;
    numTicks = (int)(ceil(max / step) - floor(min / step));
    if(CurZoom<1.10) numTicks = 10;
    if (CurZoom > 6.0) numTicks = 10;
    min = floor(min / step) * step;
    max = ceil(max / step) * step;
}

// Метод нажатия кнопки мыши:
void MyGraphicView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton){
        QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
        //this->viewport()->setCursor(Qt::ClosedHandCursor);
        // Store original position.
        currentX = event->x();
        currentY = event->y();
        //event->accept();
        qDebug()<<"mousePressEvent(): originX = "<<currentX<<"; originY = "<<currentY;
        return;
    }
    if (event->button() == Qt::LeftButton){
        slotAlarmTimer();
    }
    //event->ignore();
}

// Метод перемещения указателя мыши:
void MyGraphicView::mouseMoveEvent(QMouseEvent *event)
{
    if (event->buttons() & Qt::RightButton) {
        this->horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - currentX));
        this->verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - currentY));
        currentX = event->x();
        currentY = event->y();
        qDebug()<<"mouseMoveEvent(): originX = "<<currentX<<"; originY = "<<currentY;
        event->accept();
        if(StartDraw == false && CurZoom != 1.0){
            QPolygon pView = mapFromScene(QRect(0, 0, this->viewport()->width(), this->viewport()->height()));
            qDebug()<<pView;
            minX = -pView.at(0).x()/CurZoom;
            maxX = 500 / CurZoom + minX;
            minY = -pView.at(0).y()/CurZoom;
            maxY = 500 / CurZoom + minY;
            qDebug()<<"minX = "<< minX << "; minY = " << minY;
            qDebug()<<"maxX = "<< maxX << "; maxY = " << maxY;
            adjustAxis(minX, maxX, numXTicks);
            adjustAxis(minY, maxY, numYTicks);
            qDebug()<<"numXTicks = "<<numXTicks;
            qDebug()<<"numYTicks = "<<numYTicks;
            qDebug()<<"minX = "<< minX << "; minY = " << minY;
            qDebug()<<"maxX = "<< maxX << "; maxY = " << maxY;
            slotAlarmTimer();
        }
        return;
    }
    event->ignore();
}

// Метод отпускания кнопки мыши:
void MyGraphicView::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button()==Qt::RightButton)
    {
        qDebug()<<"mouseReleaseEvent(): RightButton was released";
        QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
        event->accept();
        if(StartDraw == false && CurZoom != 1.0){
            QPolygon pView = mapFromScene(QRect(0, 0, this->viewport()->width(), this->viewport()->height()));
            qDebug()<<pView;
            minX = -pView.at(0).x()/CurZoom;
            maxX = 500 / CurZoom + minX;
            minY = -pView.at(0).y()/CurZoom;
            maxY = 500 / CurZoom + minY;
            adjustAxis(minX, maxX, numXTicks);
            adjustAxis(minY, maxY, numYTicks);
            qDebug()<<"numXTicks = "<<numXTicks;
            qDebug()<<"numYTicks = "<<numYTicks;
            qDebug()<<"minX = "<< minX << "; minY = " << minY;
            qDebug()<<"maxX = "<< maxX << "; maxY = " << maxY;
            slotAlarmTimer();
        }
    }
    event->ignore();
}

void MyGraphicView::slotFromPoint(int numberPoint, int pX, int pY)
{
    qDebug()<<"slotFromPoint(): click "<<numberPoint<<" node";
    QList<QGraphicsItem *> itemsSelected = scene->items(QPolygonF()
                                                        << QPointF(pX, pY)
                                                        << QPointF(pX, pY)
                                                        << QPointF(pX, pY)
                                                        << QPointF(pX, pY));
    foreach (QGraphicsItem *item, itemsSelected) {
        if(item->pos().x()!=0 && item->pos().y()!=0)
        {
            qDebug()<<"Item = "<<item;
        }
    }

    emit signalSelectRow(numberPoint);
}

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

6
Илья
  • 14 марта 2017 г. 20:18

Извините, забыл прикрепить архив с проектом:

    Evgenii Legotckoi
    • 15 марта 2017 г. 0:46
    • Ответ был помечен как решение.

    Добрый день.
    В Файле mygraphicsview.cpp замените строку 83:

    int y = rect.bottom() - (j * (rect.height() - 1)/numYTicks);
    на следующую строку:
    int y = rect.top() + (j * (rect.height() - 1)/numYTicks);

    Что касается кода со строки 66 по строку 98 в файле mygraphicsview.cpp . Каждый раз перерисовывать сетку не самое хорошее решение. Тем более, что перерисовывать сетку нет острой необходимости, при скролле линии будут всё равно на своём месте. А вообще почитайте про QGraphicsItemGroup . С помощью этого класса можно будет группировать графические объекты, а также удалять и перерисовывать часть объектов.

      Илья
      • 15 марта 2017 г. 6:33

      Спасибо большое, Евгений!!! Наконец-то теперь все работает как надо. Я так понимаю такой эффект получался из неправильно выбранной точки позиционирования объектов по вертикали. Позиционировать нужно было относительно верхней границы сцены - так как оттуда начинается счет координат. По горизонтали так все было и сделано, поэтому там все нормально работало.

      По поводу оптимизации и применения QGraphicsItemGroup - именно об этом я и думал, но чуть позже. Сейчас остро стоит вопрос о первой сырой и рабочей версии проекта. Спасибо за совет.

      Хотелось бы выяснить еще одну странность. При зуммировании колесиком мыши сцена должна переходить в место, где находиться курсор мыши, для этого я включил в конструктор виджета:

      this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);

      и в метод wheelEvent(QWheelEvent *event) делаю центрирование в точке указателя мыши:
      void MyGraphicView::wheelEvent(QWheelEvent *event)
      {
          // Scale the view / do the zoom
          const double scaleFactor = 1.15;
          if(event->delta() > 0)
          {
              // Zoom in
              if(CurZoom < 6.0){
                  CurZoom = CurZoom*scaleFactor;
                  qDebug()<<"wheelEvent(): CurZoom = " << CurZoom;
                  scale(scaleFactor, scaleFactor);
                  centerOn(mapToScene(event->pos()));
                  .....
          }
          else
          {
              // Zooming out
              if (CurZoom > 1.10) {
                  CurZoom = CurZoom/scaleFactor;
                  qDebug()<<"wheelEvent(): CurZoom = " << CurZoom;
                  scale(1.0 / scaleFactor, 1.0 / scaleFactor);
                  centerOn(mapToScene(event->pos()));
                  .....
          }
      }


      Если убрать вызовы centerOn() , то центрирование идет в место, противоположное курсору мыши.
        Илья
        • 15 марта 2017 г. 6:37

        Проблема с центрированием сцены при зуммировании решилась следующим образом. Оказывается

        this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);

        в конструкторе и вызов centerOn() конфликтовали друг с другом.
        В общем закомментил this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); и все заработало как надо!
          Evgenii Legotckoi
          • 15 марта 2017 г. 6:42

          Да. Проблема была именно в отсчёте от нужной границы.

          Что касается центрирования, то проблема скорее всего в том, что постоянно вызывается метод slotAlarmTimer(); . Если говорить в общих чертах, то при зуме и скроллинге не требуется перерисовывать графическую сцену так, как это делаете Вы, с использованием данного метода slotAlarmTimer(); . И так всё нормально должно работать. Здесь требуется переписать логику работу, чтобы отрисовка сетки производилась только тогда, когда это действительно требуется, а не при каждом движении мыши. Тоже самое касается и других объектов на графической сцене. Сцена может скакать, когда изменяются её размеры, но если область ограничена определённым квадратом и новый графические объекты не выходят за границы этого квадрата, то таких проблем со скачками должно быть меньше. Стоит немного пересмотреть логику методов по обработке событий от мыши и избавиться от лишних перерисовок ( В первую очередь от перерисовки координатной сетки )

            Evgenii Legotckoi
            • 15 марта 2017 г. 6:43

            Ну что ж. Если причина в этом, то хорошо.

              Комментарии

              Только авторизованные пользователи могут публиковать комментарии.
              Пожалуйста, авторизуйтесь или зарегистрируйтесь
              e
              • ehot
              • 1 апреля 2024 г. 0:29

              C++ - Тест 003. Условия и циклы

              • Результат:78баллов,
              • Очки рейтинга2
              B

              C++ - Тест 002. Константы

              • Результат:16баллов,
              • Очки рейтинга-10
              B

              C++ - Тест 001. Первая программа и типы данных

              • Результат:46баллов,
              • Очки рейтинга-6
              Последние комментарии
              k
              kmssr9 февраля 2024 г. 5:43
              Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
              АК
              Анатолий Кононенко5 февраля 2024 г. 12:50
              Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
              EVA
              EVA25 декабря 2023 г. 21:30
              Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
              J
              JonnyJo25 декабря 2023 г. 19:38
              Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
              G
              Gvozdik19 декабря 2023 г. 8:01
              Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
              Сейчас обсуждают на форуме
              a
              a_vlasov14 апреля 2024 г. 16:41
              Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
              Павел Дорофеев
              Павел Дорофеев14 апреля 2024 г. 12:35
              QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
              f
              fastrex4 апреля 2024 г. 14:47
              Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…
              P
              Pisych27 февраля 2023 г. 15:04
              Как получить в массив значения из связанной модели? Спасибо, разобрался:))
              AC
              Alexandru Codreanu19 января 2024 г. 22:57
              QML Обнулить значения SpinBox Доброго времени суток, не могу разобраться с обнулением значение SpinBox находящего в делегате. import QtQuickimport QtQuick.ControlsWindow { width: 640 height: 480 visible: tr…

              Следите за нами в социальных сетях