Evgenii Legotckoi
Evgenii Legotckoi7. Oktober 2015 11:26

Qt/C++ - Lektion 023. Verschieben von QGraphicsItem in QGraphicsScene mit Maushilfe

Angenommen, Sie erstellen eine Anwendung, die Elemente der Netzwerkinfrastruktur in der Grafikszene anzeigt. Die Frage ist, wie Sie diese Elemente mit der Maus verschieben, dh die Elemente ziehen. Mit anderen Worten, nehmen Sie ein Element mit der Maus und verschieben Sie es an eine andere Stelle in der Grafikszene.

Lassen Sie uns herausfinden, wie das geht.

Projektstruktur

Um ein Beispiel zu demonstrieren, erstellen Sie ein neues Projekt und fügen Sie eine neue Klasse hinzu, die von QGraphicsItem geerbt wurde .

  • MoveGraphicsItem.pro - Projektprofil;
  • main.cpp - Startdatei;
  • widget.h - Header-Datei des Hauptfensters;
  • widget.cpp - Quellcodedatei für das Hauptfenster;
  • moveitem.h - Header-Datei des grafischen Elements;
  • moveitem.cpp - Datei mit Quellcodes eines Grafikelements.
  • widget.ui - die Form des Hauptfensters.

widget.ui

Platzieren Sie zwei Objekte im Hauptfensterformular:

  • QGraphicsView - enthält die Grafikszene;
  • QPushButton - erstellt neue grafische Objekte.

Hauptfenster

widget.h

In der Header-Datei des Hauptfensters deklarieren wir nur die Grafikszene und den Slot für die Schaltfläche.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QGraphicsScene>

#include <moveitem.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QGraphicsScene *scene;
};

#endif // WIDGET_H

widget.cpp

In dieser Datei nehmen wir die kosmetische Einstellung der Anwendung vor - dies betrifft den Klassenkonstruktor. Und wir erstellen grafische Objekte, die wir auf die grafische Szene ziehen. In diesem Fall werden die Objekte bei der Erstellung in zufälliger Reihenfolge platziert.

#include "widget.h"
#include "ui_widget.h"

/* Функция для получения рандомного числа
 * в диапазоне от минимального до максимального
 * */
static int randomBetween(int low, int high)
{
    return (qrand() % ((high + 1) - low) + low);
}

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // Косметическая подготовка приложения
    this->resize(640,640);          // Устанавливаем размеры окна приложения
    this->setFixedSize(640,640);

    scene = new QGraphicsScene(this);   // Инициализируем графическую сцену
    scene->setItemIndexMethod(QGraphicsScene::NoIndex); // настраиваем индексацию элементов

    ui->graphicsView->resize(600,600);  // Устанавливаем размер graphicsView
    ui->graphicsView->setScene(scene);  // Устанавливаем графическую сцену в graphicsView
    ui->graphicsView->setRenderHint(QPainter::Antialiasing);    // Настраиваем рендер
    ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground); // Кэш фона
    ui->graphicsView->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);

    scene->setSceneRect(0,0,500,500); // Устанавливаем размер сцены
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();        // Создаём графический элемента
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30,470));
    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

movitem.h

Um ein schönes Ziehen von Grafikobjekten durchzuführen, müssen wir die Funktionen mouseMoveEvent , mousePressEvent und mouseReleaseEvent verwenden. Die Funktion mouseMoveEvent zieht das Grafikobjekt direkt, während die anderen beiden das Aussehen des Mauszeigers ändern, was signalisiert, dass wir nehmen ein grafisches Objekt und geben es frei.

#ifndef MOVEITEM_H
#define MOVEITEM_H

#include <QObject>
#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QDebug>
#include <QCursor>

class MoveItem : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit MoveItem(QObject *parent = 0);
    ~MoveItem();

signals:

private:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

public slots:
};

#endif // MOVEITEM_H

movietem.cpp

Das Grafikobjekt ist ein regelmäßiges grünes Quadrat.

#include "moveitem.h"

MoveItem::MoveItem(QObject *parent) :
    QObject(parent), QGraphicsItem()
{

}

MoveItem::~MoveItem()
{

}

QRectF MoveItem::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}

void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->setPen(Qt::black);
    painter->setBrush(Qt::green);
    painter->drawRect(-30,-30,60,60);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    /* Устанавливаем позицию графического элемента
     * в графической сцене, транслировав координаты
     * курсора внутри графического элемента
     * в координатную систему графической сцены
     * */
    this->setPos(mapToScene(event->pos()));
}

void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    /* При нажатии мышью на графический элемент
     * заменяем курсор на руку, которая держит этот элемента
     * */
    this->setCursor(QCursor(Qt::ClosedHandCursor));
    Q_UNUSED(event);
}

void MoveItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    /* При отпускании мышью элемента
     * заменяем на обычный курсор стрелку
     * */
    this->setCursor(QCursor(Qt::ArrowCursor));
    Q_UNUSED(event);
}

Fazit - Drag & Drop

Als Ergebnis sollten Sie eine Anwendung haben, bei der auf Knopfdruck an einer zufälligen Stelle in der Grafikszene dynamisch ein grünes Quadrat erstellt wird. Und Sie können es mit der Maus ziehen.

Eine Demonstration der Anwendung können Sie im Video-Tutorial sehen.

Link zum Download des Projekts in einem Zip-Archiv: MoveGraphicsItem

Videoanleitung

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

d
  • 15. Juni 2017 11:39

Лучше где-то в классе MoveItem объявить

QPointF mouseCoords;
А потом
void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    mouseCoords = event->pos();
}

void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
  QPointF coords = event->pos();
  setPos(mapToScene(coords) - mouseCoords);
}
Иначе движение получается неестественное. Дергается при клике.
Evgenii Legotckoi
  • 16. Juni 2017 02:15

Не, не совсем так. Здесь нужно будет учитывать сдвиг позиции курсора относительно координаты (0, 0) в координатной системе MoveItem.

Получается тогда так:

Координаты сдвига объявляем в private секции MoveItem

QPointF m_shiftMouseCoords;
А в методах учитываем этот сдвиг следующим образом:
void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    m_shiftMouseCoords = this->pos() - mapToScene(event->pos());
    this->setCursor(QCursor(Qt::ClosedHandCursor));
    Q_UNUSED(event);
}

void MoveItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    this->setPos(mapToScene(event->pos() + m_shiftMouseCoords));
}
F
  • 28. Juni 2017 05:44

Подскажите пожалуйста как я могу обратиться к одному из сформированных элементов (в данном случае квадратов). Нужно ли сначала присвоить ему какой либо индекс???

d
  • 29. Juni 2017 04:39

Надо просто где-то хранить указатели на графические элементы, в списке, векторе или массиве. В widget.cpp в методе

on_pushButton_clicked()
надо указатель item сохранять. Объявить у класса widget в private-секции например QVector<MoveItem*> v, а потом v.append(item). И обращаться к ним, как к элементам вектора.
F
  • 29. Juni 2017 07:31

Попробую, спасибо))

Evgenii Legotckoi
  • 30. Juni 2017 12:13

Если перед обращением графические объекты выбирались мышью, то можно использовать метод selectedItems() у QGraphicsScene. Этот метод можно использовать в том случае, когда у графических объектов устанавливается флаг ItemIsSelectable. Тогда, если вам необходимо что-то делать с выделенными объектами, то может и не понадобиться хранить графические объекты в отдельном векторе.

F
  • 3. Juli 2017 14:32

Спасибо

d
  • 5. Juli 2017 12:17

Можно вообще

void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();        // Создаём графический элемент
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30, 470));

    item->setFlag(QGraphicsItem::ItemIsMovable); // делаем элемент перемещаемым

    scene->addItem(item);   // Добавляем элемент на графическую сцену
}
Одним методом setFlag решить всю задачу. И тогда события mouseMoveEvent, mousePressEvent и mouseReleaseEvent можно даже не переопределять.
Evgenii Legotckoi
  • 5. Juli 2017 13:07

Да, это стандартный вариант для перемещения элементов в Qt. Но установка флага не всегда помогает для создания перемещаемого элемента. Например, переопределение методов mouseMoveEvent, mousePressEvent и mouseReleaseEvent позволит сделать кастомный интерфейс самого приложения, как например здесь .

F
  • 12. Juli 2017 15:26

Подскажите пожалуйста как в данном проекте по перетаскиванию организовать удаление объекта со scene методом delete item, допустим при щелчке ПКМ по объекту QGraphicsScene. Мои попытки оказались тщетными (маны пока маловато наверное). Заранее спасибо.

Evgenii Legotckoi
  • 12. Juli 2017 16:12

Ну например так можете сделать.

void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (QApplication::mouseButtons() == Qt::RightButton)
    {
        this->deleteLater();
    }
}

Не забудьте только подключить QApplication в файле через #include <QApplication>

И используйте deleteLater(), тогда объект будет удалён тогда, когда он не будет использоваться.

m
  • 8. Oktober 2017 06:24

Добрый день. У меня возник такой вопрос: Каким образом в данном примере можно организовать выделение объекта? Просто setSelected метод QGraphicsScene, а мы переопределяли  методы для работы с мышью в QGraphicsItem.  Так как правильно это реализовать(варианты реализации которые я вижу): 1 вариант -  переопределить методы мышки для QGraphicsScene, 2 вариант -эмитировать сигнал в методе мышки QGraphicsItem, который запустит slotSelected в QGraphicsScene, 3 вариант - передать указатель на QGraphicsScene в QGraphicsItem. Или может можно сделать все проще?

Evgenii Legotckoi
  • 8. Oktober 2017 06:58

Смотрите, какой здесь момент...Эта статья писалась с расчётом на альтернативный вариант того, как можно сделать перемещение объектов на графической сцене. Фактически, если использовать флаги ItemIsMovable , ItemIsSelectable в самом QGraphicsItem, то не понадобится никаких переопределений методов. Просто в некоторых ситуациях, когда не хватает функционала Qt, приходится делать переопределение методов и писать логику всех этих перемещений и выделений.

Так что попробуйте для начала в конструкторе классов, наследованных от QGraphicsItem просто установить необходимые флаги.
setFlags(GraphicsItem::ItemIsMovable | GraphicsItem::ItemIsSelectable)
m
  • 8. Oktober 2017 08:32

Пробую реализовать на вашем примере, установил флаги, даже убрал реализацию методов мышки из класса, все равно ItemIsSelectable не срабатывает, при том что ItemIsMovable работает. Так же попробовал дописать в методы мышки вызов методов базового класса QGraphicsItem::mousePress(Move, Release)Event(event); - все равно не срабатывает. Поэтому возник еще один вопрос, правильно ли я понимаю, что при выделении должен контур штрихом перерисовываться или визуально никаких отображений видно не будет?

Evgenii Legotckoi
  • 8. Oktober 2017 08:48

Вообще, если просто удалить все переопределённые методы мыши из графических объектов, то выделение должно работать. А границы будут перерисовываться пунктирной линией. Так что, Вы на правильном пути.

m
  • 8. Oktober 2017 09:28

Реализовал с помощью setSelected в обработке клика и добавления условия в paint. Но попутно возник вопрос если у меня например на сцене два объекта, один из них выделен, я щелкаю по второму, чтобы выделить его, как снять выделение с 1? 1 вариант - сохранять указатель на пред. элемент и выставлять значение setSelected в false. 2 вариант - получать выделенный при помощи selectedItems() сцены, и все остальные элементы отмечать как false.

Evgenii Legotckoi
  • 8. Oktober 2017 09:47

я как-то писал небольшой векторный редактор в качестве тестового задания, и насколько помню, хранил в векторе указатели на выделяемые объекты (не просто один указатель, а век тор указателей). Так что предложенный вам вариант вполне адекватен на мой взгляд. Плюс имеется возможность контролировать варианты выделения с использованием клавиш CTRL или SHIFT, чтобы например добавлять объекты к выделенным.

m
  • 9. Oktober 2017 12:10

Добрый вечер. Продолжаю переделывать ваш пример. Возникло 2 вопроса:

1. Хочу сделать кнопку триггером (если зажат, то рисуем квадрат по щелку мыши в сцене и кнопка отжимается). Я так понимаю при такой реализации как в примере, мы должны с эмитировать сигнал добавления элемента (который объявим в итеме) из нажатия кнопки мыши и связать со слотом добавления элемента (который объявим в сцене). Либо, как вариант, перенести реализацию методов мыши в сцену. Собственно вопрос, какой из этих вариантов предпочтительней? (Еще заметил, что вы в своих проектах, чаще используете реализацию методов мыши непосредствено в Итемах. А вот в офиц. документации и примерах других авторов, чаще встречаю вариант с реализацией методов мыши в Сцене. Возникает вопрос, это различие зависит от подхода программиста или есть какие либо правила, когда использовать лучше то, а не другое).
2. Хочу давать имена квадратам (при щелчке мышью для от рисовки квадрата будет появляться диалог для ввода имени, после успешного выполнения диалога имя должно появиться слева от квадрата, а при перемещении имя должно автоматом перемещаться за квадратом). Как это лучше реализовать?  Сделать с помощью shape() произвольную форму для отрисовки и вместе с рисованием квадарата добавлять текст или сделать отдельным итемом текст и неким образом связать с квадратом, сам больше склоняюсь ко второму способу, а что вы можете посоветовать?
Evgenii Legotckoi
  • 9. Oktober 2017 16:47

Добрый вечер!

1. Состояние зажатой кнопки можно инициировать через метод setDown(true), тогда сигналы clicked() и pressed() не будут испускаться. Да, нужно будет испускать сигнал либо от Item`a, либо от графической сцены. По поводу переопределения методов мыши в Item, либо в графической сцене я так скажу, это всё зависит от архитектуры приложения. Если очень много графических объектов будет, например делаете графический редактор типа Paint, то если всё реализовывать в графической сцене, то получите огромную неясную портянку кода, которая станет быстро неудобной в управлении. Поэтому лучше реализовывать индивидуальную логику внутри графическиъ объектов, а в графической сцене реализовывать лишь необходимый минимальный функционал, который будет лишь помогать работе графических объектов.
2. shape() лишь указывает форму объекта. Имя отрисовывать можно в методе paint в графическом объекте. Что касается имени, то можно вызывать диалоговое окно через сигнал от графического объекта. Для поддержки сигналов и слотов нужно наследоваться от QGraphicsObject . В принципе можно добавлять текст и отдельным item`ом, смотрите по удобству реализации.
 
P/S/ По возможности, задавайте вопросы на форуме сайта , чтобы были отдельные темы. Это будет полезнее для развития ресурса. Спасибо.
m
  • 10. Oktober 2017 11:10

Еще не большой вопрос к предыдущему. (Про форум ваше замечание видел, и следующий вопрос, если такой найдется, задам там, а этот чтобы не терять нить разговора напишу здесь)
Переопределять методы мыши одновременно для сцены и итема допускается?

Evgenii Legotckoi
  • 10. Oktober 2017 11:19

Да. Допускается.

a
  • 11. Mai 2018 06:10

Подскажите, пожалуйста, как сделать чтобы при нажатие на квадрат и переносе квадрата курсор фиксировался в месте нажатия квадрата? Точнее - центр квадрата не смещался в точку нажатия.

d
  • 11. Mai 2018 07:20
Вот так. Сам столкнулся с этой проблемой, когда впервые попробовал запустить коды из этого примера. Но это как улучшить этот способ.
А вообще есть намного проще способ, как сделать то же самое. Просто воспользоваться методом setFlag , например, в конструкторе и переопределить boundingRect и paint. Если нужны просто движущиеся объекты, например, в 2D игре или анимации какой-то, то скорее всего, это то что Вам нужно.
a
  • 11. Mai 2018 08:00

спасибо за наводку, изучаю.

a
  • 11. Mai 2018 08:07

Заработал первый вариант.

Второй нет. Правда, во втором я не удалял переопределенные функции.
d
  • 11. Mai 2018 08:37

У меня оба работали. Но я во втором способе удалил (закомментировал) переопределенные функции и походу в этом дело.

Добрый день. Подскажите, пожалуйста, как быть, если вместо зеленых квадратов мне нужно перемещать изображение QPixmap? Где его определить и как добавить на сцену? если делаю просто scene->addPixmap(myPixmap); то ничего не работает - фото не перемещается. Заранее спасибо!

d
  • 30. August 2018 07:35
#include "moveitem.h"

MoveItem::MoveItem(QObject *parent) :
    QObject(parent), QGraphicsItem()
{

}

MoveItem::~MoveItem()
{

}

QRectF MoveItem::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}

void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPointF point(-30, -30);
    QRectF source(0, 0, 60, 60);
    QPixmap myPixmap("file.png");
    painter->drawPixmap(point, myPixmap, source);
}

Вот так поправить moveitem.cpp. Перемещать можно только что-то отнаследованное от QGraphicsItem. Рекомендую прочитать про все методы drawPixmap, там им разные аргументы можно передавать. Ну и код добавления на сцену поправить так:
void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();        // Создаём графический элемент
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30, 470));

    item->setFlag(QGraphicsItem::ItemIsMovable); // делаем элемент перемещаемым

    scene->addItem(item);   // Добавляем элемент на графическую сцену
}
МТ
  • 30. August 2018 22:02

Сделала вчера немного иначе уже. Собственно пока этого достаточно, но большое спасибо - попробую потом так, как вы сказали.

        scene = new QGraphicsScene(this);
        scene->setItemIndexMethod(QGraphicsScene::NoIndex);
        ui->graphicsView->setScene(scene);
        ui->graphicsView->setRenderHint(QPainter::Antialiasing);
        ui->progressBar_control->setValue(0);
        scene->setSceneRect(0,0,640,480);
        QGraphicsPixmapItem *item = new QGraphicsPixmapItem();
        item->setFlag(QGraphicsItem::ItemIsMovable, true);
        QPixmap myPixmap("D:/1.jpg");
        item->setPixmap(myPixmap);
        scene->addItem(item);

Пробовала как вы показали, только без флага, с переопределением методов, ничего не вышло, почему-то в области graphicsView не срабатывали события
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

Срабатывают только простые
void mouseMoveEvent(QMouseEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event);
d
  • 31. August 2018 07:48
Ну да, так еще проще. Класс QGraphicsPixmapItem я и пытался создать, но его уже создали, он тоже отнаследован от QGraphicsItem . Для вашей задачи вообще не нужно создавать свой класс. Только в строчке
item->setFlag(QGraphicsItem::ItemIsMovable, true);
лишнее, оно там и так по умолчанию, и pixmap можно передать в конструктор:
void Widget::on_pushButton_clicked()
{
    QPixmap myPixmap("file.png"); // создаем картинку
    QGraphicsPixmapItem *item = new QGraphicsPixmapItem(myPixmap); // Создаём графический элемент
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30, 470));

    item->setFlag(QGraphicsItem::ItemIsMovable); // делаем элемент перемещаемым

    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

d
  • 1. September 2018 05:04
А если есть желание все же изобрести свой велосипед, то с переопределением методов работает, это уже я багов наделал. Если скачать оригинальный проект из этой статьи и заменить один метод:
void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPointF point(-30, -30);
    QRectF source(0, 0, 60, 60);
    QPixmap myPixmap("file.png");
    painter->drawPixmap(point, myPixmap, source);

    Q_UNUSED(option);
    Q_UNUSED(widget);
}
то все работает.
МТ
  • 3. September 2018 22:03

Вот как раз так я и делала - меняла только этот метод, у меня не сработало. Видимо где-то еще я набедокурила. Спасибо Вам!)

d
  • 4. September 2018 10:56
Быть не может, чтобы не работало, если скачать архив с этим же проектом. До замены метода зеленые квадратики двигаются, а после - картинки появляются, но не двигаются?
А что за задача? Игру пишете?
МТ
  • 11. September 2018 01:32

нет, пишу мини фото-редактор, чтобы фото можно было  зуммировать и перемещать в окне, когда оно все не влазит (большой масштаб)

EF
  • 13. Juni 2019 03:24

Добрый день. Передо мной стоит схожая с данным примером задача - мне надо реализовать возможность добавлять сплайн-аппроксимацию на загруженном изображении и я хочу, чтобы пользователь мог корректировать полученную кривую вручную. Кривая формируется из точек и соединяющих эти точки кривых(QGraphicsPathItsm в моем случае). Для описания точек у меня есть отдельный класс, который так же, как и в примере, унаследован от QGraphicsItem и QObject. Меня устраивает поведение этих точек, если добавить их на сцену. Второй класс содержит QList этих точек, там все точки хранятся. В этом классе так же находится QList кривых, соединяющих эти точки. Моя задача - изменять положение криввых когда я передвигаю точки (чтобы кривая оставалась связной). И здесь у меня возникли сложности. Сложность в том, что я не знаю, как обратиться к элементу, который я передвигаю. Например, было бы здорово знать индекс этого элемента в списке. Как я могу это сделать? Если для ответа необходимы фрагменты моего кода - я добавлю его в вопрос. Заранее благодарю за любую помощь!

d
  • 13. Juni 2019 10:47

Можно классу, который описывает точку, добавить сигнал, который подавать (emit), когда точка перемещается (переопределить mouseMoveEvent или mouseReleaseEvent). Так вот эти сигналы у каждой из точек (например, при добавлении в QList) привязать к слоту какого-нибудь другого объекта. В этом слоте вызывать методы, которые перерисовывают нужные кривые. Статический метод QObject::sender() вернет указатель на точку, которая послала сигнал, т.е. которую переместили. Только нужно привести тип этого указателя.

EF
  • 14. Juni 2019 09:56

Спасибо за ваш ответ, у меня получилось реализовать это. Тем не менее появилась другая проблема, поэтому опять надеюсь на вашу помощь. Скажем, я уже выставил точки и они соеденены. Когда я начинаю перемещать точку, первым делом она оказывается в левом верхнем углу QGraphicsView и только после этого начинает двигаться, при чем с отличной от скорости движения курсора скоростью. Я более подробно описал проблему вот тут . Я читал о схожей проблеме на том же ресурсе и решение состояло в том, чтобы испольозвать в конструкторе метод

QGraphicsItem::setPos()

Тогда предмет будет выставлен в ту точку, в которую указано. Проблема в том, что у меня предметы уже выставляются туда, куда нужно, и если использовать тот метод в конструкотре, то предмет будет выставлен совершенно в другое место, хотя метод я вызываю с теми же координатами, что и передаются в конструктор. То есть можно сделать вывод, что я получаю какие-то не те координаты, когда кликаю мышкой. Мне было посоветованно использовать функции mapToScene и mapFromScene, но они не помогли - координаты так же отличаются от координат клика мыши.
Если мой вопрос нужно дополнить кодом для большей понятности - скажите, я дополню. Заранее благодарю за ваше время

Михаиллл
  • 20. Juli 2019 08:49

Добрый день.
Как можно узнать координату на графической сцене при отпускании клавиши мыши?

Михаиллл
  • 20. Juli 2019 09:31

Вот так

qDebug()<<"position:"<<event->scenePos();

Подскажите пожалуйста,
К graphicsView я подключил обработку контекстного меню:
сonnect(ui->graphicsView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotCustomMenuRequested(QPoint)));
А теперь хочу получать координаты сцены, как мне это лучше сделать?

Михаиллл
  • 5. Dezember 2019 08:11
    //qDebug()<<"position:"<<event->pos();
    //qDebug()<<"position:"<<event->screenPos();
    qDebug()<<"position:"<<event->scenePos();

А без переопределения qgraphicsScene это сделать возможно?
Есть же коорината нажатия кнопки мыши slotCustomMenuRequested(QPoint)

В этом слоте ваам нужно будет правильно смаппить координату. У QGraphicsView есть методы mapToScene, mapFromScene. Попробуйте использовать их.

Большое вам спасибо!

Вот выбор/радактирование/изменение положения одного объекта item- это понятно.
А как реализовать выбор нескольких item'ов?
Например мышкой выделяю область- те, объекты которые туда попали- выделяются.

У меня пока только такой вариант:
-Создать подкласс QGraphicsScene
-Переопределить mousePress/Move/Release event-ы

Evgenii Legotckoi
  • 8. Dezember 2019 07:23
  • (bearbeitet)

У меня здесь есть одна старая статья с примером векторного редактора. Там есть ответы на ваши вопросы. Поизучайте

Qt/C++ - Урок 072. Пример векторного редактора на Qt QGraphicsItem, QGraphicsScene, QGraphicsView, Vector Editor, QGraphicsObject

K
  • 7. April 2020 08:55
  • (bearbeitet)

А вот уже и на python...

#!/usr/bin/env python
# -'''- coding: utf-8 -'''-

import sys
from PySide2.QtWidgets import *
from PySide2.QtQuick import *
from PySide2.QtCore import *
from PySide2.QtGui import *

class MoveItem(QGraphicsItem):
    def __init__(self):
        super().__init__()   

    def boundingRect(self):
        return QRectF(-30,-30,60,60)

    def paint(self, painter, option, widget):
        painter.setPen(Qt.black);
        painter.setBrush(Qt.green);
        painter.drawRect(-30,-30,60,60);

    def mouseMoveEvent(self, event):
        self.setPos(self.mapToScene(event.pos()))

    def mousePressEvent(self, event):
        self.setCursor(QCursor(Qt.ClosedHandCursor))

    def mouseReleaseEvent(self, event):
        self.setCursor(QCursor(Qt.ArrowCursor))

def randomBetween(low, high):
    return (qrand() % ((high + 1) - low) + low)

class Widget(QWidget):
    def __init__(self):
        super().__init__()

        layout = QVBoxLayout()
        self.setLayout(layout)

        pb = QPushButton('Добавить квадрат')
        layout.addWidget(pb)
        pb.clicked.connect(self.button_click)

        self.resize(640,640);         
        self.setFixedSize(640,640)

        scene = QGraphicsScene()
        self.scene = scene
        scene.setItemIndexMethod(QGraphicsScene.NoIndex); 

        view = QGraphicsView()
        layout.addWidget(view)

        view.resize(600,600)
        view.setScene(scene)
        view.setRenderHint(QPainter.Antialiasing)  
        view.setCacheMode(QGraphicsView.CacheBackground)
        view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)

        scene.setSceneRect(0,0,500,500); 

    def button_click(self):
        item = MoveItem()
        item.setPos(randomBetween(30, 470), randomBetween(30,470))        
        self.scene.addItem(item);   

if __name__ == '__main__':
    # Create the Qt Application
    app = QApplication(sys.argv)

    w = Widget()
    w.show()

    # Run the main Qt loop
    sys.exit(app.exec_())
ИВ
  • 29. April 2020 06:40
  • (bearbeitet)

Добрый день.
Разрешите вопрос, вожусь с перетаскиванием мышью. Сделал свой класс Plugs, в котором отрисовывается объект на сцене, и есть private: void mouseMoveEvent(QGraphicsSceneMouseEvent *event); Но я его не наследую.
Если я пытаюсь описать его в cpp файле , то в строчке this->setPos(mapToScene(event->pos())); то компилятор ругается - ошибка: no member named 'setCursor' in 'Plugs'. Вроде логично, так как setCursor я не реализовывал (вначале подумал, что этот метод работает везде).
Переписал класс, с наследованием(начало заголовка):

class Plugs : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    explicit Plugs(QObject *parent = 0);
    ~Plugs();

public:
    //Plugs();
    int x,y;

Реализация:

Plugs::Plugs(QObject *parent) :
    QObject(parent), QGraphicsItem()
{
}

Plugs::~Plugs()
{
}

Так ошибки понятно исчезают, но появляется другая.
Когда я в mainwindow.cpp создаю экземпляр Plugs plug01 = new Plugs();, компилятор мне пишет, что это абстрактный класс и создание его объектов невозможно. Но вот у вас же работает это:

void Widget::on_pushButton_clicked()
{
    MoveItem *item = new MoveItem();        // Создаём графический элемента

Хотя вы тоже наследуете MoveItem , подскажите почему это у вас сработало ?

Evgenii Legotckoi
  • 29. April 2020 06:54
  • (bearbeitet)

Добрый день.

  1. отредактируйте, пожалуйста, комментарий, чтобы была разметка кода, в тулбаре редактора комментариев есть кнопка для вставки кода, это позволит разметить код так, чтобы он правильно рендерился.
  2. У вас перетаскивание работать и не будет, если не будет сделана правильная реализация метода mouseMoveEvent
  3. setCursor должен работать, перезапустите qmake. Этот метод есть в QGraphicsItem
  4. это void Widget::on_pushButton_clicked(), который был добавлен через графический дизайнер. Qt при сборке генерирует ui файлы, которые разруливают подключение сигналов и слотов в таком формате. У вас очевидно это не сделано.

setCursor должен работать, этот метод есть в QGraphicsItem

ИВ
  • 29. April 2020 07:05
  • (bearbeitet)

Да, прошу прощения отредактировал, не заметил что там есть такая кнопка.
Перетаскивание я реализовал, через флаг

pic->setPixmap(QPixmap(":/new/prefix1/Images/iconfinder_Monster.png"));
    pic->setFlags(QGraphicsItem::ItemIsMovable);

это работает, а вот отслеживание отпускания кнопки мыши сделать и не получается.
Кнопки (QPushButton) у меня вообще нет, все добавляется кодом (вот тут и появляется проблема) в mouseMoveEvent.cpp.
А вот ваш код работает без проблем.

Курсор работает (компилятор не ругается) в том случа если я делаю наследование класса от public QObject, public QGraphicsItem, но в этом случае невозможно создать обьект класса.

Evgenii Legotckoi
  • 29. April 2020 07:17
  • (bearbeitet)

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

Вообще... я думаю, что проблема в том, что вам нужно переопределить метод paint, поскольку он является чисто виртуальным. Но если вам нужно таскать картинку, вместо кастомной отрисовки, то замените QGraphicsItem на QGraphicsPixmapItem. Тогда вам не придётся переопределять метод paint

ИВ
  • 29. April 2020 08:20

Спасибо, вы оказались правы. Замена наследования на QGraphicsPixmapItem, сделало код рабочим.
Хотя теперь, хоть все и компилируется, но по какой-то причине не срабатывают события - курсор не меняется. Так что буду копать дальше.
Еще раз , презнателен.

думаю, что всё-таки нужно переопределить эти методы

void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

и переопределить их так

void MoveItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsPixmapItem::mousePressEvent(event);
    this->setCursor(QCursor(Qt::ClosedHandCursor));
}

void MoveItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    QGraphicsPixmapItem::mouseReleaseEvent(event);
    this->setCursor(QCursor(Qt::ArrowCursor));
}
ИВ
  • 29. April 2020 09:08

Извитине, не сработало. Да не страшно, наверно какую-то мелочь упустил. Сейчас еще раз код проверю.

CF
  • 28. Mai 2020 09:18
  • (bearbeitet)

Добрый день, очень помог ваш код и видео. Я пытаюсь сделать так ,чтобы при нажати разных кнопок рисовались разные фигуру, и столкнулась с тем , что не могу сделать проверку нажата ли кнопка в moveitem.cpp
Подскажите, пожалуйста, как осуществить можно?
делала вот так

//mainwindow.cpp
void MainWindow::on_pushButton_clicked()
{   ui->pushButton->setCheckable(true);
    ui->pushButton->setChecked(true);
    MoveItem *item = new MoveItem();        // Создаём графический элемента
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30,470));
    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

void MainWindow::on_pushButton_2_clicked()
{ui->pushButton_2->setCheckable(true);
    MoveItem *item = new MoveItem();        // Создаём графический элемента
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30,470));
    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

void MainWindow::on_pushButton_3_clicked()
{ui->pushButton_3->setCheckable(true);
    MoveItem *item = new MoveItem();        // Создаём графический элемента
    item->setPos(randomBetween(30, 470),    // Устанавливаем случайную позицию элемента
                 randomBetween(30,470));
    scene->addItem(item);   // Добавляем элемент на графическую сцену
}

//moveitem.cpp
void MoveItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{ MainWindow *m =new MainWindow();

    painter->setPen(Qt::black);
    painter->setBrush(Qt::green);

 if (m->ui->pushButton->isChecked())
            painter->drawEllipse(-30,-30,90,60);
     if (m->ui->pushButton_2->isChecked())
            painter->drawLine(-30,-30,90,60);
     if (m->ui->pushButton_3->isChecked())
            painter->drawLine(-50,-50,90,60);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

Михаиллл
  • 28. Mai 2020 09:31

Не совсем понятен вопрос. Если проверить на то, нажата ли конкретная кнопа, то в дизайнере нажмите правой кнопкой мыши на кнопу ->перейти к слото -> и там помойму clicked(bool).И в классе MainWindow создаете объект moveitem и с ним играете.

CF
  • 28. Mai 2020 09:50
  • (bearbeitet)

решила проблему разбив на файлы, спасибо,за попытку помочь

R
  • 13. Juli 2020 09:16

Подскажите, пожалуйста, почему функция рандома определена только в спп файле и объявлена при этом статической?

Evgenii Legotckoi
  • 13. Juli 2020 09:49

Пережиток plain C, ограничение видимости. По идее можно и .c, .cpp файлы подключать через директиву include. Для компилятора разницы особой нет, какое расширение будет.

здравствуйте! Подскажите, пожалуйста, как можно на Вашем примере реализовать вывод контекстного меню при нажатии на квадрат правой кнопкой мыши с целью решения задач: изменить объект и удалить? Как я поняла, мне может помочь QGraphicsSceneContextMenuEvent.

AO
  • 25. Dezember 2021 10:09

А есть возможность передавать координаты классу, что-бы он рисвал, допустим линию?

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
A
ALO1ZE19. Oktober 2024 08:19
Fb3-Dateileser auf Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken