Evgenii Legotckoi
Evgenii LegotckoiҚаз. 7, 2015, 11:26 Т.Ж.

Qt/C++ - 023-сабақ. QGraphicsItem элементін тінтуірмен QGraphicsScene бетіне апару

Сіз [графикалық көріністе] желілік инфрақұрылым элементтерін көрсететін қолданба жасап жатырсыз делік (https://evileg.com/ru/post/80/). Мәселе бұл элементтердің қозғалысын тінтуірдің көмегімен қалай жасауға болады, яғни элементтерді сүйреуді жүзеге асыруда. Басқаша айтқанда, тінтуірдің көмегімен элементті алыңыз және оны графикалық көріністің басқа орнына жылжытыңыз.

Мұны қалай жасауға болатынын көрейік.

Жоба құрылымы

Мысалды көрсету үшін біз жаңа жоба жасаймыз және QGraphicsItem ішінен мұраланған жаңа класс қосамыз .

  • MoveGraphicsItem.pro - профильдік жоба;
  • main.cpp - іске қосу файлы;
  • widget.h - негізгі терезенің тақырып файлы;
  • widget.cpp - терезенің негізгі бастапқы код файлы;
  • moveitem.h - графикалық элемент тақырыбының файлы;
  • moveitem.cpp - графикалық элементтің бастапқы код файлы.
  • widget.ui - негізгі терезе пішіні.

widget.ui

Біз екі нысанды негізгі терезе түрінде орналастырамыз:

  • QGraphicsView - графикалық көріністі қамтиды;
  • QPushButton - жаңа графикалық объектілерді жасайды.

Негізгі терезе

виджет.h

Негізгі терезенің тақырып файлында біз тек графикалық көріністі және түймеге арналған ұяшықты жариялаймыз.

#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

Бұл файлда біз қолданбаны косметикалық реттеуді жүзеге асырамыз - бұл класс конструкторына қатысты. Ал біз графикалық көріністе сүйрейтін графикалық объектілерді жасаймыз. Сонымен бірге объектілер құру кезінде ерікті ретпен орналастырылады.

#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);   // Добавляем элемент на графическую сцену
}

жылжытылатын элемент.сағ

Графикалық нысандарды әдемі сүйреуді жүзеге асыру үшін mouseMoveEvent , mousePressEvent және mouseReleaseEvent функцияларын пайдалануымыз керек. mouseMoveEvent функциясында графикалық нысан тікелей апарылады, ал қалған екеуінде тінтуір курсорының көрінісі өзгереді, бұл графикалық нысанды алып, шығаратынымызды білдіреді.

#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

moveitem.cpp

Графикалық объект кәдімгі жасыл шаршы болады.

#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);
}

Төменгі жол - сүйреп апарыңыз

Нәтижесінде сіз түймені басқан кезде графикалық көріністе кездейсоқ жерде жасыл шаршы динамикалық түрде жасалған қолданбаны алуыңыз керек. Және оны тінтуірмен сүйреп апаруға болады.

Қолданбаның демонстрациясын бейне оқулықтан көре аласыз.

Жобаны zip мұрағатында жүктеп алу сілтемесі: MoveGraphicsItem

Бейне оқулық

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

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

d
  • Маусым 15, 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, 2017, 2: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, 2017, 5:44 Т.Ж.

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

d
  • Маусым 29, 2017, 4:39 Т.Ж.

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

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

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

Evgenii Legotckoi
  • Маусым 30, 2017, 12:13 Т.Қ.

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

F
  • Шілде 3, 2017, 2:32 Т.Қ.

Спасибо

d
  • Шілде 5, 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, 2017, 1:07 Т.Қ.

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

F
  • Шілде 12, 2017, 3:26 Т.Қ.

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

Evgenii Legotckoi
  • Шілде 12, 2017, 4:12 Т.Қ.

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

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

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

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

m
  • Қаз. 8, 2017, 6:24 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 8, 2017, 6:58 Т.Ж.

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

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

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

Evgenii Legotckoi
  • Қаз. 8, 2017, 8:48 Т.Ж.

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

m
  • Қаз. 8, 2017, 9:28 Т.Ж.

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

Evgenii Legotckoi
  • Қаз. 8, 2017, 9:47 Т.Ж.

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

m
  • Қаз. 9, 2017, 12:10 Т.Қ.

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

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

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

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

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

Evgenii Legotckoi
  • Қаз. 10, 2017, 11:19 Т.Ж.

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

a
  • Мамыр 11, 2018, 6:10 Т.Ж.

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

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

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

a
  • Мамыр 11, 2018, 8:07 Т.Ж.

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

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

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

МТ
  • Там. 30, 2018, 1:36 Т.Ж.

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

d
  • Там. 30, 2018, 7: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, 2018, 10: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, 2018, 7: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, 2018, 5: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, 2018, 10:03 Т.Қ.

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

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

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

EF
  • Маусым 13, 2019, 3:24 Т.Ж.

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

d
  • Маусым 13, 2019, 10:47 Т.Ж.

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

EF
  • Маусым 14, 2019, 9:56 Т.Ж.

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

QGraphicsItem::setPos()

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

Михаиллл
  • Шілде 20, 2019, 8:49 Т.Ж.

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

Михаиллл
  • Шілде 20, 2019, 9:31 Т.Ж.

Вот так

qDebug()<<"position:"<<event->scenePos();
ЛП
  • Жел. 5, 2019, 8:09 Т.Ж.

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

Михаиллл
  • Жел. 5, 2019, 8:11 Т.Ж.
    //qDebug()<<"position:"<<event->pos();
    //qDebug()<<"position:"<<event->screenPos();
    qDebug()<<"position:"<<event->scenePos();
ЛП
  • Жел. 5, 2019, 8:30 Т.Ж.

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

Evgenii Legotckoi
  • Жел. 5, 2019, 4:15 Т.Қ.

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

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

ЛП
  • Жел. 8, 2019, 4:12 Т.Ж.

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

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

Evgenii Legotckoi
  • Жел. 8, 2019, 7:23 Т.Ж.
  • (өңделген)

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

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

K
  • Сәуір 7, 2020, 8:55 Т.Ж.
  • (өңделген)

А вот уже и на 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, 2020, 6:40 Т.Ж.
  • (өңделген)

Добрый день.
Разрешите вопрос, вожусь с перетаскиванием мышью. Сделал свой класс 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, 2020, 6:54 Т.Ж.
  • (өңделген)

Добрый день.

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

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

ИВ
  • Сәуір 29, 2020, 7:05 Т.Ж.
  • (өңделген)

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

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

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

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

Evgenii Legotckoi
  • Сәуір 29, 2020, 7:17 Т.Ж.
  • (өңделген)

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

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

ИВ
  • Сәуір 29, 2020, 8:20 Т.Ж.

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

Evgenii Legotckoi
  • Сәуір 29, 2020, 8:22 Т.Ж.

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

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, 2020, 9:08 Т.Ж.

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

CF
  • Мамыр 28, 2020, 9:18 Т.Ж.
  • (өңделген)

Добрый день, очень помог ваш код и видео. Я пытаюсь сделать так ,чтобы при нажати разных кнопок рисовались разные фигуру, и столкнулась с тем , что не могу сделать проверку нажата ли кнопка в 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, 2020, 9:31 Т.Ж.

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

CF
  • Мамыр 28, 2020, 9:50 Т.Ж.
  • (өңделген)

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

R
  • Шілде 13, 2020, 9:16 Т.Ж.

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

Evgenii Legotckoi
  • Шілде 13, 2020, 9:49 Т.Ж.

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

КС
  • Там. 26, 2021, 6:29 Т.Ж.

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

AO
  • Жел. 25, 2021, 10:09 Т.Ж.

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

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

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

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

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз