Проблема при перерисовке сцены - "скакание" элементов сцены по вертикали
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); }
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.Do you like it? Share on social networks!
- Akiv Doros
- Nov. 12, 2024, 1:58 a.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:50points,
- Rating points-4
- molni99
- Oct. 26, 2024, 11:37 a.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:80points,
- Rating points4
- molni99
- Oct. 26, 2024, 11:29 a.m.
C ++ - Test 004. Pointers, Arrays and Loops
- Result:20points,
- Rating points-10
Извините, забыл прикрепить архив с проектом:
Добрый день.
на следующую строку:В Файле mygraphicsview.cpp замените строку 83:
Что касается кода со строки 66 по строку 98 в файле mygraphicsview.cpp . Каждый раз перерисовывать сетку не самое хорошее решение. Тем более, что перерисовывать сетку нет острой необходимости, при скролле линии будут всё равно на своём месте. А вообще почитайте про QGraphicsItemGroup . С помощью этого класса можно будет группировать графические объекты, а также удалять и перерисовывать часть объектов.
Спасибо большое, Евгений!!! Наконец-то теперь все работает как надо. Я так понимаю такой эффект получался из неправильно выбранной точки позиционирования объектов по вертикали. Позиционировать нужно было относительно верхней границы сцены - так как оттуда начинается счет координат. По горизонтали так все было и сделано, поэтому там все нормально работало.
По поводу оптимизации и применения QGraphicsItemGroup - именно об этом я и думал, но чуть позже. Сейчас остро стоит вопрос о первой сырой и рабочей версии проекта. Спасибо за совет.
Хотелось бы выяснить еще одну странность. При зуммировании колесиком мыши сцена должна переходить в место, где находиться курсор мыши, для этого я включил в конструктор виджета:
и в метод wheelEvent(QWheelEvent *event) делаю центрирование в точке указателя мыши:
Если убрать вызовы centerOn() , то центрирование идет в место, противоположное курсору мыши.
Проблема с центрированием сцены при зуммировании решилась следующим образом. Оказывается
в конструкторе и вызов centerOn() конфликтовали друг с другом.
В общем закомментил this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); и все заработало как надо!
Да. Проблема была именно в отсчёте от нужной границы.
Что касается центрирования, то проблема скорее всего в том, что постоянно вызывается метод slotAlarmTimer(); . Если говорить в общих чертах, то при зуме и скроллинге не требуется перерисовывать графическую сцену так, как это делаете Вы, с использованием данного метода slotAlarmTimer(); . И так всё нормально должно работать. Здесь требуется переписать логику работу, чтобы отрисовка сетки производилась только тогда, когда это действительно требуется, а не при каждом движении мыши. Тоже самое касается и других объектов на графической сцене. Сцена может скакать, когда изменяются её размеры, но если область ограничена определённым квадратом и новый графические объекты не выходят за границы этого квадрата, то таких проблем со скачками должно быть меньше. Стоит немного пересмотреть логику методов по обработке событий от мыши и избавиться от лишних перерисовок ( В первую очередь от перерисовки координатной сетки )
Ну что ж. Если причина в этом, то хорошо.