P
Peknic30 июня 2016 г. 4:17

Присвоение ID объекту и обращение к нему

id, item, qlist, Qt

Имеются объекты на сцене. Необходимо определять расстояние между объектами. Решил объекты пронумеровать.А как сказать qt что мне нужна координата n-го объекта, а потом другого объекта.
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

11
Evgenii Legotckoi
  • 30 июня 2016 г. 5:22

Рассмотрим такой пример. Имеются прямоугольники, при создании которых происходит инкрементирование счётчика ID и каждый раз на сцену добавляется объект с уникальным ID. Для этого нужно будет создать отдельный класс, который реализует данную логику. Далее, добавим на графическую сцену 20 таких объектов и проведём между двумя из них прямую линию. Допустим между 5-ым и 10-ым. То есть ID у них будут равны 5 и 10, соответственно.

Заголовочный файл прямоугольника

#ifndef RECTANGLE_H
#define RECTANGLE_H
 
#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
 
class Rectangle : public QGraphicsItem
{
 
public:
    explicit Rectangle();
    static int countId;         // Статическая переменная, счетчик номеров прямоугольников
    int getId() const;          // Функция для возврата локального номера прямоугольника
 
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
    QRectF boundingRect() const override;
 
private:
    int m_id;   // ID прямоугольника
};
 
#endif // RECTANGLE_H

Исходный код прямоугольника

#include "rectangle.h"
 
Rectangle::Rectangle() : QGraphicsItem()
{
    countId++;
    m_id = countId;
}
 
int Rectangle::getId() const
{
    return m_id;
}
 
void Rectangle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::green);
    painter->drawRect(-10, -10, 20, 20);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}
 
QRectF Rectangle::boundingRect() const
{
    return QRectF (-10, -10, 20, 20);
}
 
int Rectangle::countId = 0;  // Тот самый статический счётчик

Проработаем всё это дело в главном окне приложения, в которому у нас будет создана графическая сцена.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QGraphicsScene>
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
private:
    Ui::MainWindow *ui;
    QGraphicsScene *scene;
};
 
#endif // MAINWINDOW_H

Ну и проработаем всю логику, которая была написана в начале

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include "rectangle.h"
 
/* Функция для получения рандомного числа
 * в диапазоне от минимального до максимального
 * */
static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->resize(600, 600);                 // Изменим размер главного окна приложения
    scene = new QGraphicsScene(this);       // Инициализируем графическую сцену
    ui->graphicsView->setScene(scene);      // Установим сцену в Graphics View
    scene->setSceneRect(0, 0, 500, 500);    // Установим размеры сцены
 
    // Создадим 20 прямоугольников со случайными координатами
    for (auto i = 0; i < 20; i++) {
        Rectangle *rect = new Rectangle();
        rect->setX(randomBetween(15, 485));
        rect->setY(randomBetween(15, 485));
        scene->addItem(rect);
    }
 
    // Временные указатели на прямоугольники, координаты которых нас будут интересовать
    Rectangle *item_1;
    Rectangle *item_2;
 
    // Пройдёмся по всем объектам на грфической сцене, чтобы найти нужные
    foreach (QGraphicsItem *item, scene->items()) {
        // Необходимо скастовать QGraphicsItem в объект класса прямоугольника,
        // чтобы получить доступ к его ID
        Rectangle *rect = static_cast<Rectangle *>(item);
        // Если ID равен 5 или 10, то сохраним указатель на него в соответсвующие указатели item_1 или item_2
        switch (rect->getId()) {
        case 5:
            item_1 = rect;
            break;
        case 10:
            item_2 = rect;
            break;
        default:
            break;
        }
    }
 
    if (item_1 != nullptr && item_2 != nullptr) {
        // создадим линию между этими двумя объектами
        QLineF lineBetweenItems;
        lineBetweenItems.setP1(item_1->scenePos()); // используем координаты прямоугольников на сцене,
        lineBetweenItems.setP2(item_2->scenePos()); // чтобы задать положение концов линии
 
        // добавляем линию на сцену
        scene->addLine(lineBetweenItems);
        qDebug() << lineBetweenItems.length(); // Выводим расстояние между объектами
    }
}
 
MainWindow::~MainWindow()
{
    delete ui;
}

    P
    • 6 июля 2016 г. 5:12

    Спасибо. Все работает. У меня еще вопросик. А как сделать так чтоб объект одного класса взаимодействовал только с определенным объектом другого класса ( связать объекты между собой, как на картинке) классы унаследованны от одного родителя. И как можно присвоить и отобразить имена объектов? Пробовал через Qlabel,не получилось.

      Evgenii Legotckoi
      • 6 июля 2016 г. 10:31
      • Ответ был помечен как решение.

      связать объекты между собой, как на картинке

      Ну если я правильно понял, то проблем в этом никаких быть не должно. В том примере, который я привел, будем считать, что класс Rectangle – это у нас базовый класс, от которого будут наследованы остальные классы. Таким образом у нас остаётся глобальная система id и для этого базового класса и для всех его наследников. Поэтому можно сделать допустим два класса наследника, и переопределить только вид их отрисовки. Например, у нас это будут красные и синие прямоугольники.
      Синий прямоугольник

      #ifndef RECTANGLEBLUE_H
      #define RECTANGLEBLUE_H
       
      #include "rectangle.h"
       
      class RectangleBlue : public Rectangle
      {
      public:
          RectangleBlue();
       
      protected:
          void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
          QRectF boundingRect() const override;
      };
       
      #endif // RECTANGLEBLUE_H
      #include "rectangleblue.h"
       
      RectangleBlue::RectangleBlue() : Rectangle()
      {
       
      }
       
      void RectangleBlue::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
      {
          painter->setPen(Qt::black);
          painter->setBrush(Qt::blue);
          painter->drawRect(-10, -10, 20, 20);
          painter->drawText(-10, 20, QString::number(getId()));
          Q_UNUSED(option);
          Q_UNUSED(widget);
      }
       
      QRectF RectangleBlue::boundingRect() const
      {
          return QRectF (-10, -10, 20, 30);
      }

      Красный прямоугольник

      #ifndef RECTANGLERED_H
      #define RECTANGLERED_H
       
      #include "rectangle.h"
       
      class RectangleRed : public Rectangle
      {
      public:
          RectangleRed();
       
      protected:
          void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
          QRectF boundingRect() const override;
      };
       
      #endif // RECTANGLERED_H
      #include "rectanglered.h"
       
      RectangleRed::RectangleRed() : Rectangle()
      {
       
      }
       
      void RectangleRed::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
      {
          painter->setPen(Qt::black);
          painter->setBrush(Qt::red);
          painter->drawRect(-10, -10, 20, 20);
          painter->drawText(-10, 20, QString::number(getId()));
          Q_UNUSED(option);
          Q_UNUSED(widget);
      }
       
      QRectF RectangleRed::boundingRect() const
      {
          return QRectF (-10, -10, 20, 30);
      }

      Ну а далее я приведу немного модифицированный код MainWindow, где вместо зеленных прямоугольников класса Rectangle, будут создавать его наследники, классов RectangleBlue и RectangleRed.
      Обращаю внимание на то, что во время перебора всех элементов, благодаря полиморфизму, их можно кастовать к их общему базовому классу Rectangle, у которого, также как и его наследников доступен метод getId().

      #include "mainwindow.h"
      #include "ui_mainwindow.h"
      #include <QDebug>
      #include "rectangle.h"
      #include "rectanglered.h"
      #include "rectangleblue.h"
       
      /* Функция для получения рандомного числа
       * в диапазоне от минимального до максимального
       * */
      static int randomBetween(int low, int high)
      {
          return (qrand() % ((high + 1) - low) + low);
      }
       
      MainWindow::MainWindow(QWidget *parent) :
          QMainWindow(parent),
          ui(new Ui::MainWindow)
      {
          ui->setupUi(this);
          this->resize(600, 600);                 // Изменим размер главного окна приложения
          scene = new QGraphicsScene(this);       // Инициализируем графическую сцену
          ui->graphicsView->setScene(scene);      // Установим сцену в Graphics View
          scene->setSceneRect(0, 0, 500, 500);    // Установим размеры сцены
       
          // Создадим 10 красных прямоугольников со случайными координатами
          for (auto i = 0; i < 10; i++) {
              RectangleRed *rect = new RectangleRed();
              rect->setX(randomBetween(15, 485));
              rect->setY(randomBetween(15, 485));
              scene->addItem(rect);
          }
       
          // Создадим 20 синих прямоугольников со случайными координатами
          for (auto i = 0; i < 20; i++) {
              RectangleBlue *rect = new RectangleBlue();
              rect->setX(randomBetween(15, 485));
              rect->setY(randomBetween(15, 485));
              scene->addItem(rect);
          }
       
          // Временные указатели на прямоугольники, координаты которых нас будут интересовать
          Rectangle *item_1;
          Rectangle *item_2;
       
          // Пройдёмся по всем объектам на грфической сцене, чтобы найти нужные
          foreach (QGraphicsItem *item, scene->items()) {
              // Необходимо скастовать QGraphicsItem в объект класса прямоугольника,
              // чтобы получить доступ к его ID
              Rectangle *rect = static_cast<Rectangle *>(item);
              // Если ID равен 5 или 10, то сохраним указатель на него в соответсвующие указатели item_1 или item_2
              switch (rect->getId()) {
              case 5:
                  item_1 = rect;
                  break;
              case 15:
                  item_2 = rect;
                  break;
              default:
                  break;
              }
          }
       
          if (item_1 != nullptr && item_2 != nullptr) {
              // создадим линию между этими двумя объектами
              QLineF lineBetweenItems;
              lineBetweenItems.setP1(item_1->scenePos()); // используем координаты прямоугольников на сцене,
              lineBetweenItems.setP2(item_2->scenePos()); // чтобы задать положение концов линии
       
              // добавляем линию на сцену
              scene->addLine(lineBetweenItems);
              qDebug() << lineBetweenItems.length(); // Выводим расстояние между объектами
          }
      }
       
      MainWindow::~MainWindow()
      {
          delete ui;
      }

      Ну и там же в исходных кодах наследованных классов я добавил подпись под прямоугольниками.

        P
        • 8 июля 2016 г. 8:44

        Немного не верно объяснил. Сделать так, чтоб синие только с синими были связаны, а красные, например, как с красными, так и , допустим, с черными.

        Еще возникла такая проблема. При включении слота двойного клика мыши, перестаем отображаться на сцене planeAEW, унаследованный от plane,причем другие объекты классов, унаследованные от plane отображаются.

        plane.h

        planeaew.h

          P
          • 8 июля 2016 г. 8:47
          #include "plane.h"
          #include "mainwindow.h"
           
          int q=0;
           
          Plane::Plane(QObject *parent) : QObject(parent), QGraphicsItem()
          {
              ResID++;
              IDplane = ResID;
              n = ResID;
          }
           
          Plane::~Plane()
          {
           
          }
           
          QRectF Plane::boundingRect() const
          {
              return QRectF(-30,-20,30,40);   //ограничиваем область в которой лежит треугольник
          }
           
          void Plane::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *MainWindow)
          {
              //рисуем самолет
              painter->setPen(QPen(Qt::black,2));
              painter->drawLine(0,0,-20,0);    //тело
              painter->drawLine(0,0,-4,3);     //нос
              painter->drawLine(0,0,-4,-3);    //нос
              painter->drawLine(-8,10,-8,-10); //крыло
              painter->drawLine(-11,10,-11,-10);//крыло
           
              //рисуем хвост
              QPolygon polygon;
              polygon << QPoint(-20,0) << QPoint(-24,3) << QPoint(-24,-3);
              painter->setBrush(Qt::black);
              painter->drawPolygon(polygon);
           
              //установим имя
              painter->drawText(-12, 20, QString::number(getID()));
           
              Q_UNUSED(option);
              Q_UNUSED(MainWindow);
           
          }
           
          //движение объекта
          void Plane::slotGameTimer()
          {
              if(q<=20)
              {
              setPos(mapToParent(2,-1));
              }
              else
              {
              setPos(mapToParent(2,0));
              }
              q++;
           
          //проверка выхода за границы поля
          //если объект выходит за заданные границы, то возвращаем его назад
              if(this->x() + 2 < 0)
              {
                  this->setX(0);//слева
              }
              if(this->x() - 2 > 600)
              {
                  emit signalStopTimer();
                  //this->deleteLater();
              }
              if(this->y() + 2 < -600)
              {
                  this->setY(-600);//сверху
              }
              if(this->y() - 2 > 0)
              {
                  this->setY(0);  //снизу
              }
              emit signalNumber();
          }
           
          void Plane::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
          {
              //устанавливаем объект в новую позицию
              this->setPos(mapToScene(event->pos()));
          }
           
          void Plane::mousePressEvent(QGraphicsSceneMouseEvent *event)
          {
              //при нажатии на элемент меняем курсор
              this->setCursor(QCursor(Qt::ClosedHandCursor));
              //emit signalNumber();
              Q_UNUSED(event);
          }
           
          void Plane::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
          {
              //после отпускания меняем курсор на стрелку
              this->setCursor(QCursor(Qt::ArrowCursor));
              Q_UNUSED(event);
          }
           
          //void Plane::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
          //{
          //    this->N = this->IDplane;
          //    Q_UNUSED(event);
          //}
           
          int Plane::getID() const
          {
              return IDplane;
          }
           
          int Plane::ResID = 0;
          int Plane::n=0;
          //int Plane::N;

           

            Evgenii Legotckoi
            • 8 июля 2016 г. 11:56

            Немного не верно объяснил. Сделать так, чтоб синие только с синими были связаны, а красные, например, как с красными, так и , допустим, с черными.

            Я Вам показал всю механику определения по ID способа “связывания” объектов. Всё остальное делается по аналогии. Для определения, к какому типу (то бишь классу) относится тот или иной объект перечитайте урок связанный с полиморфизмом
            Там имеется enum с перечислениями типов объектов. Такое перечисление можно сделать в базовом классе. А во всех наследованных от него объектах можно переопределить виртуальный метод type(), который относится к классу QGraphicsItem, от которого должен быть наследован уже ваш базовый класс, от которого наследуются все остальные графические объекты.

            То есть в заголовочном файле базового класса

            enum Types {
                RedBlock = UserType + 1,
                BlueBlock,
                GreenBlock,
                BlackBlock
            }

            В заголовочном файле классов наследников
            int type() const override

            и в файле исходных кодов классов наследников (Допустим в синем прямоугольнике)

            int type()
            {
             return BlueBlock;
            }

            И уже от возвращаемых типов можете плясать и связывать все объекты так, как вам будет угодно.

              Evgenii Legotckoi
              • 8 июля 2016 г. 12:10

              При включении слота двойного клика мыши, перестаем отображаться на сцене planeAEW

              А вот тут я понятия не имею, на первый взгляд, весь код в полном порядке. Ну в том плане, что не заметно чего-то криминального из-за чего бы объект должен был быть нерабочим.

                f
                • 1 мая 2018 г. 7:27

                А как нарисовать дугу, между объектами?

                painter.drawArc
                видимо не подходит, тк QPainter, не связан с  QGraphicsObject / QGraphicsItem.
                https://upload.wikimedia.org/wikipedia/commons/thumb/9/9d/DFAexample.svg/250px-DFAexample.svg.png
                хотелось бы уметь рисовать что нибудь похожее на это
                  f
                  • 1 мая 2018 г. 7:46
                  QGraphicsEllipseItem *arc = scene->addEllipse(QRectF(_source->scenePos(),
                              QSize(_source->scenePos().rx() - _destination->scenePos().rx(),
                                    _source->scenePos().ry() - _destination->scenePos().ry()) ));
                          arc->setSpanAngle(90 * 16);

                  Нашел setSpanAngle, но он вместо дуги рисует "кусок пирога"
                    f
                    • 1 мая 2018 г. 10:01
                    • (ред.)

                    И сразу вопрос как нарисовать стрелку с помощью линий?


                    void Widget::printArrow(QLineF lineF)
                    {
                        QGraphicsLineItem *line = new GraphicsLineItem();
                        QLineF ll1; // /\ острая часть стрелы
                        QLineF ll2; // /\ острая часть стрелы
                    
                        qreal x, y, angl, len = 20;
                        x = lineF.p2().x();
                        y = lineF.p2().y();
                        angl = lineF.angle();
                    
                        ll1.setLine(x, y,
                                    x + len * cos((angl - 30) * 3.14159265 / 180),
                                    y + len * cos((angl - 30) * 3.14159265 / 180));
                        ll2.setLine(x, y,
                                    x + len * cos((angl + 30) * 3.14159265 / 180),
                                    y + len * cos((angl + 30) * 3.14159265 / 180));
                    
                        QGraphicsLineItem *l1 = new QGraphicsLineItem(ll1);
                        QGraphicsLineItem *l2 = new QGraphicsLineItem(ll2);
                        line->setLine(lineF);
                        scene->addItem(line);
                        scene->addItem(l1);
                        scene->addItem(l2);
                    }

                    Почему то не работает. У двух линий козырька почему то всегда угол 315 или 135 градусов
                      f
                      • 1 мая 2018 г. 10:16

                      косяк со стрелкой у себя нашел. Надо было синус у игрека ставить а не косинус.

                      void Widget::printArrow(QLineF lineF)
                      {
                          GraphicsLineItem *line = new GraphicsLineItem();
                          QLineF ll1; // /\ острая часть стрелы
                          QLineF ll2; // /\ острая часть стрелы
                      
                          qreal x, y, angl, len = 20;
                          x = lineF.p2().x();
                          y = lineF.p2().y();
                          angl = lineF.angle();
                          ll1.setLine(x, y,
                                      x - len * cos((angl - 10) * 3.14159265 / 180),
                                      y + len * sin((angl - 10) * 3.14159265 / 180));
                          ll2.setLine(x, y,
                                      x - len * cos((angl + 10) * 3.14159265 / 180),
                                      y + len * sin((angl + 10) * 3.14159265 / 180));
                          QGraphicsLineItem *l1 = new QGraphicsLineItem(ll1);
                          QGraphicsLineItem *l2 = new QGraphicsLineItem(ll2);
                          line->setLine(lineF);
                          scene->addItem(line);
                          scene->addItem(l1);
                          scene->addItem(l2);
                      }

                        Комментарии

                        Только авторизованные пользователи могут публиковать комментарии.
                        Пожалуйста, авторизуйтесь или зарегистрируйтесь
                        d
                        • dsfs
                        • 26 апреля 2024 г. 14:56

                        C++ - Тест 004. Указатели, Массивы и Циклы

                        • Результат:80баллов,
                        • Очки рейтинга4
                        d
                        • dsfs
                        • 26 апреля 2024 г. 14:45

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

                        • Результат:50баллов,
                        • Очки рейтинга-4
                        d
                        • dsfs
                        • 26 апреля 2024 г. 14:35

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

                        • Результат:73баллов,
                        • Очки рейтинга1
                        Последние комментарии
                        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" она решит проблему , лично мне помогло.
                        Сейчас обсуждают на форуме
                        G
                        Gar22 апреля 2024 г. 15:46
                        Clipboard Как скопировать окно целиком в clipb?
                        DA
                        Dr Gangil Academics20 апреля 2024 г. 17:45
                        Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
                        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…

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