Форум программистов, компьютерный форум, киберфорум
C++ Qt
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/7: Рейтинг темы: голосов - 7, средняя оценка - 4.86
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 735

Построение объектов на кастомной графической сцене

17.05.2022, 13:00. Показов 1441. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем доброго времени суток!
Не так давно я начал активно заниматься графикой (если таким громким словом можно назвать мои потуги) в Qt, поэтому проше не судить строго.
Прошу помощи в осознании логики создания и управления новыми объектами в окне GraphicsView, помещённом в окно главного приложения (UI), созданном с помощью дизайнера форм.
Что я делаю: создаю форму и добавляю туда GraphicsView. В MainWindow.cpp создаю сцену, определяющуюся не стандартным классом QGraphicsScene, а кастомным классом paintScene, наследующимся от стандартного. Далее я создаю класс paint_polygon, который должен помогать выводить на сцену полигональные фигуры по моему усмотрению.
Что я хотел бы получить: по одиночному нажатию левой кнопкой мыши по граф. сцене в месте нажатия должен появляться маркер, а за курсором мыши должен начать ездить (двигаться и менять размер, если движение мыши того требует) сектор (сколько-то-градусный "раструб"), начинающийся в маркере и центром дуги касающийся курсора мыши (приложу изображение ниже). "Ездить" сектор должен до тех пор, пока я не нажму на ЛКМ ещё раз - по второму нажатию по ЛКМ сектор должен закрепиться на сцене в текущем виде и сохраниться в какое-то хранилище таких объектов, откуда мы в дальнейшем сможем получать информацию о каждом из них.
Что есть сейчас: кастомная сцена, на которой удаётся создать квадрат - объект класса paint_polygon. Далее я хотел научиться двигать его по сцене мышкой (как в куче примеров), но с кастомной сценой что-то пошло не так - Qt не понимает, что я жмакаю по квадрату, а не по сцене, и создаёт ещё один квадрат. Вероятно, я что-то упускаю в наследовании, хотя делаю один-в-один с примерами (отдельный пример для перетаскивания, и отдельный - для кастомизации сцены) и ошибок при сборке не возникает. Код приложу (а если нужно, и весь проект, но на Qt 5.15.2 всё работает).
По сути, я хотел бы разобраться с тем, почему не работает то, что есть сейчас (идеи кончились - не заработало), но если кто-нибудь подскажет, как реализовать изначально задуманное - буду невероятно благодарен!
mainwindow.h
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
 
#include <windows.h>
 
#include <paintscene.h>
#include <paint_polygon.h>
 
 
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
 
private slots:
 
private:
    Ui::MainWindow *ui;
    QTimer *timer;      /* Определяем таймер для подготовки актуальных размеров
                         * графической сцены
                         * */
    paintScene *scene;  // Объявляем кастомную графическую сцену
 
private:
    /* Переопределяем событие изменения размера окна
     * для пересчёта размеров графической сцены
     * */
    void resizeEvent(QResizeEvent * event);
 
private slots:
    void slotTimer();
 
};
#endif // MAINWINDOW_H

mainwindow.cpp
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    QProgressBar *PB = ui->progressBar;
    scene = new paintScene(PB);       // Инициализируем графическую сцену
    scene->setItemIndexMethod(QGraphicsScene::NoIndex); // настраиваем индексацию элементов
    ui->graphicsView->setScene(scene);  // Устанавливаем графическую сцену
 
    timer = new QTimer();       // Инициализируем таймер
    connect(timer, &QTimer::timeout, this, &MainWindow::slotTimer);
    timer->start(100);          // Запускаем таймер
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::slotTimer()
{
    /* Переопределяем размеры графической сцены в зависимости
     * от размеров окна
     * */
    timer->stop();
    scene->setSceneRect(0,0, ui->graphicsView->width() - 20, ui->graphicsView->height() - 20);
}
 
void MainWindow::resizeEvent(QResizeEvent *event)
{
    timer->start(100);
    QWidget::resizeEvent(event);
}

main.cpp
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
#include "mainwindow.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.setWindowTitle("CeRa");
 
    w.show();
    return a.exec();
}

paintScene.h
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef PAINTSCENE_H
#define PAINTSCENE_H
 
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QTimer>
#include <QDebug>
 
#include <QGraphicsTextItem>
#include <QPainter>
#include <QCursor>
#include <QProgressBar>
#include <QGraphicsItem>
 
class paintScene : public QGraphicsScene
{
 
    Q_OBJECT
 
 
public:
    explicit paintScene(QProgressBar *PB, QObject *parent = 0);
    ~paintScene();
 
private:
    QPointF     previousPoint;      // Координаты предыдущей точки
    QProgressBar *PrBar;
 
public:
    int tek_sost_rassch;
    QVector<QGraphicsEllipseItem> *tochki;
    QVector<QGraphicsTextItem> tochki_text;
    QVector<QGraphicsTextItem> tochki_nom;
 
private:
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
 
private slots:
};
 
#endif // PAINTSCENE_H

paintScene.cpp
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include "paintscene.h"
#include <mainwindow.h>
#include <paint_polygon.h>
 
paintScene::paintScene(QProgressBar *PB, QObject *parent) : QGraphicsScene(parent), PrBar(PB)
{
    tek_sost_rassch = 1;
}
 
paintScene::~paintScene()
{
 
}
 
void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
int klavisha = event->button(); // 1 - левая, 2 - правая, 4 - колесо, 8 - нижняя боковая (слева), 16 - верхняя боковая (слева)
 
switch (klavisha) {
case 1: {
    paint_polygon *poligon = new paint_polygon(this);
    poligon->setPos(event->scenePos());
    this->addItem(poligon);
    break;
}
case 2: { // ПРАВАЯ кнопка мыши
    break;
}
}
}
 
void paintScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
 
}
 
QRectF paintScene::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}
 
void paintScene::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 paintScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
 
}

paint_polygon.h
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifndef PAINT_POLYGON_H
#define PAINT_POLYGON_H
 
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <paintscene.h>
#include <QGraphicsSceneMouseEvent>
 
class paint_polygon : public QObject, public QGraphicsItem {
 
    Q_OBJECT
 
public:
    explicit  paint_polygon(QObject *parent = 0);
    ~paint_polygon();
 
private:
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
 
    void mousePressEvent(QGraphicsSceneMouseEvent * event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
    void otrisovka_poligona(QPointF center, double Xmouse, double Ymouse, double ugol_rastr);
 
    int condition;
    QPointF center;
};
 
#endif // PAINT_POLYGON_H

paint_polygon.cpp
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "paint_polygon.h"
#include <mainwindow.h>
#include <QMouseEvent>
 
paint_polygon::paint_polygon(QObject *parent) : QObject(parent), QGraphicsItem()
{
    condition = 0;
}
 
paint_polygon::~paint_polygon()
{
 
}
 
void paint_polygon::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    switch (event->button()) {
    case Qt::LeftButton: {
        switch (condition) {
        case 0: {
            center = event->scenePos();
            condition = 1;
            break;
        }
 
        }
    }
    case Qt::RightButton: {
        delete this;
        break;
    }
 
    }
}
 
void paint_polygon::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    /* Устанавливаем позицию графического элемента
         * в графической сцене, транслировав координаты
         * курсора внутри графического элемента
         * в координатную систему графической сцены
         * */
        this->setPos(mapToScene(event->pos()));
}
 
void paint_polygon::otrisovka_poligona(QPointF center, double Xmouse, double Ymouse, double ugol_rastr) {
    // Не знаю, нужно ли здесь и как должно быть в целом
    //QGraphicsLineItem *L = scene->addLine(QLineF(center.x(), center.y(), Xmouse, Ymouse), QPen(Qt::green, 3, Qt::DashDotLine, Qt::RoundCap, Qt::RoundJoin));
    //QGraphicsTextItem *A = this->addText(QString::number(1), QFont("Arial", 30, QFont::Bold));
 
}
 
void paint_polygon::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
 
}
 
 
QRectF paint_polygon::boundingRect() const
{
    return QRectF (-30,-30,60,60);
}
 
void paint_polygon::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);
}


P.S. Этот код, почему-то, удаляет новосозданный кубик, если кликнуть ЛКМ после создания кубика, не перемещая указатель мыши (или если попасть мышью в его центр). Такого функционала я в коде не вижу в принципе, но предполагаю, что это может так отработать искомое мной перемещение кубика; тогда два вопроса - куда он переезжает и почему реагирует только на клик ровно по центру объекта?
Миниатюры
Построение объектов на кастомной графической сцене  
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
17.05.2022, 13:00
Ответы с готовыми решениями:

Дерганая анимация на графической сцене
Для изучения графики и анимации в Qt решил написать простенькую анимацию кривошипно-шатунного механизма на графической сцене. Шатун...

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

Обращение к графической сцене из объекта, входящего в нее
есть QStackedWidget, в котором на определенной странице есть QGraphicsView, в котором QGraphicsScene, на которой много QGraphicsItem. так...

5
Покинул чат.
1132 / 727 / 195
Регистрация: 30.03.2021
Сообщений: 2,379
19.05.2022, 01:40
Лучший ответ Сообщение было отмечено Ромуальд_7 как решение

Решение

не разбирался в Вашем коде, нафигачил свой вариант-не думал что он выйдет несколько более обьемным чем хотелось, разобраться будет сложно, наверное... (выкладываю "как есть", хотя наверное тут многое можно упростить)
Поэтому вот краткое описание проекта:
Кликните здесь для просмотра всего текста

Проект без использования дизайнера(снимите галочку "добавить форму" при создании)
-класс CustomScene: кастомная сцена, перехватывает события мыши и создает/двигает фигуры из класса Figure.
-класс Figure: рассчет и отрисовка фигуры на графической сцене. Наследован от QGraphicsItem.
-класс FigureButton: это кнопка на форме, при нажатии на которую сцене передается тип фигуры, которую следует создать.
Наследован от QWidget, включает в себя графический вид и сцену для отображения на кнопке рисунка фигуры.
-класс MainWindow управляет компоновкой формы, связи ее элементов сигналами/слотами
файл main.cpp -дефолтный.


Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
что-то пошло не так - Qt не понимает, что я жмакаю по квадрату, а не по сцене, и создаёт ещё один квадрат.
это надо разделять на два разных режима работы со сценой-режим добавления фигур, и манипуляций с ними.


customscene.h
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef CUSTOMSCENE_H
#define CUSTOMSCENE_H
 
#include <QGraphicsScene>
#include "figure.h"
#include <QGraphicsSceneMouseEvent>
#include <QCursor>
#include <QGraphicsView>
 
class CustomScene :  public QGraphicsScene
{
    Q_OBJECT
public:
    explicit CustomScene(QObject *parent=nullptr);
 
private:
    Figure* figure=nullptr ,*pressedItem=nullptr;
    QPointF m_shiftMouseCoords{};
    Figure::FigureType currentType{Figure::NONE};
    QColor currentColor{Qt::white};
    bool mouseMode=false;
    static int zvalue;
 
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)override;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)override;
 
public slots:
    void currentFigureType(const Figure::FigureType &);
    void currentFigureColor(const QColor &color);
    void setMouseMode(const bool &st);
 
};
 
#endif // CUSTOMSCENE_H
-------------------------------------------------------------------------------------------------------------------


customscene.cpp
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include "customscene.h"
 
int CustomScene::zvalue=9;
 
CustomScene::CustomScene(QObject *parent):QGraphicsScene(parent)
{
    setItemIndexMethod(QGraphicsScene::NoIndex);
}
 
void CustomScene::setMouseMode(const bool &st)
{
    mouseMode=st;
}
 
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
 
    if(!mouseMode){
        pressedItem = static_cast<Figure*>(itemAt(mouseEvent->scenePos(),QTransform()));
 
        if(pressedItem){
            m_shiftMouseCoords = pressedItem->scenePos() - mouseEvent->scenePos();
            if(views().at(0)) views().at(0)->setCursor(QCursor(Qt::ClosedHandCursor));
            pressedItem->setZValue(++zvalue);
        }
    }
 
    if(currentType != Figure::NONE && mouseMode){
        figure=new Figure(currentType);
        figure->setFigureColor(Qt::black, currentColor);
        figure->setStartPoint(mouseEvent->scenePos());
        addItem(figure);
        figure->setZValue(++zvalue);
        if(views().at(0)) views().at(0)->setCursor(QCursor(Qt::SizeAllCursor));
        figure->hide();
    }
    QGraphicsScene::mousePressEvent(mouseEvent);
}
 
void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{    
    if(!mouseMode && pressedItem){
        pressedItem->setPos(mouseEvent->scenePos() + m_shiftMouseCoords);
 
    }
 
    if(figure && mouseMode) {
        if(!figure->isVisible())figure->show();
        figure->setEndPoint(mouseEvent->scenePos());
        update();
    }
 
    QGraphicsScene::mouseMoveEvent(mouseEvent);
}
 
void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if(views().at(0)) views().at(0)->setCursor(QCursor(Qt::ArrowCursor));
    figure=nullptr;
    pressedItem=nullptr;
    m_shiftMouseCoords=QPointF{};
 
    QGraphicsScene::mouseReleaseEvent(mouseEvent);
}
 
void CustomScene::currentFigureType(const Figure::FigureType &type)
{
    currentType=type;
}
 
void CustomScene::currentFigureColor(const QColor &color)
{
    currentColor=color;
}
---------------------------------------------------------------------------------------------------


figure.h
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef FIGURE_H
#define FIGURE_H
 
#include <QGraphicsItem>
#include <QPainter>
 
class Figure : public QObject, public QGraphicsItem
{
    Q_OBJECT
public:
    enum FigureType{
        NONE,
        RECT,
        SEGMENT,
        SEGMENT2
    }figureType;
 
    explicit Figure(const FigureType &type, QObject *parent=nullptr);
 
    void setStartPoint(const QPointF &point);
    void setEndPoint(const QPointF &point);
    qreal getFigureWidth()const;
    qreal getFigureHeight()const;
    QPointF getFigureCenter()const;
    void setFigureColor(const QColor &pen=Qt::black,
                        const QColor &brush=Qt::white);
 
 
private:
    QPointF startPoint{}, endPoint{};
    QPainterPath figurePath;
    void calculateFigure();
    QColor penColor{}, brushColor{};
 
protected:
    QRectF boundingRect()const override;
    QPainterPath shape()const override;
    void paint(QPainter *painter,
               const QStyleOptionGraphicsItem *,
               QWidget *)override ;
};
 
#endif // FIGURE_H
--------------------------------------------------------------------------------------------


figure.cpp
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "figure.h"
 
Figure::Figure(const FigureType &type, QObject *parent)
    :QObject(parent), QGraphicsItem(), figureType(type)
{
    setFigureColor();
}
 
 
void Figure::setStartPoint(const QPointF &point)
{
    startPoint=point;
}
 
 
void Figure::setEndPoint(const QPointF &point)
{
    endPoint=point;
    calculateFigure();
}
 
 
qreal Figure::getFigureWidth() const
{
    return boundingRect().width();
}
 
 
qreal Figure::getFigureHeight() const
{
    return boundingRect().height();
}
 
QPointF Figure::getFigureCenter() const
{
    return boundingRect().center();
}
 
void Figure::setFigureColor(const QColor &pen, const QColor &brush)
{
    penColor=pen;
    brushColor=brush;
}
 
 
void Figure::calculateFigure()
{
    bool seg2=false;
 
    switch (figureType) {
    case NONE:{
        QPainterPath rect;
        rect.addRect(QRectF(0,0, getFigureWidth(), getFigureHeight()));
        figurePath=rect;
        break;
    }
    case RECT:{
        QPainterPath rect;
        rect.addRect(QRectF(startPoint, endPoint).normalized());
        figurePath=rect;
        break;
    }
    case SEGMENT2:{seg2=true;}
    case SEGMENT:{
 
        QPainterPath circle, triangle, segment;
        QPolygon trianglePoly;
        qreal angle, length;
        int f=5;
 
        QLineF dirLine=QLineF(startPoint, endPoint);
        QLineF line1=dirLine,
               line2=dirLine;
 
        length=dirLine.length();
        angle=length/f;
        angle>=80 ? angle=80 : angle;
 
        line1.setAngle(dirLine.angle() + angle);
        line2.setAngle(dirLine.angle() - angle);
        line1.setLength(length * 10);
        line2.setLength(length * 10);
 
        trianglePoly<<startPoint.toPoint()
                    <<line1.p2().toPoint()
                    <<line2.p2().toPoint();
 
        triangle.addPolygon(trianglePoly);
        circle.addEllipse(startPoint, length, length);
 
        if(seg2){
            segment=circle.subtracted(triangle);
        }else        {
            segment=circle.intersected(triangle);
        }
 
        figurePath=segment;
        break;
    }
    default:break;
    }
}
 
 
QRectF Figure::boundingRect() const
{
    return figurePath.boundingRect().normalized();
}
 
QPainterPath Figure::shape() const
{
    return figurePath;
}
 
 
void Figure::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->setBrush(QBrush(brushColor));
    QPen pen;
    pen.setColor(penColor);
    painter->setPen(pen);
 
    painter->drawPath(figurePath);
}
--------------------------------------------------------------------------------


figurebutton.h
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef FIGUREBUTTON_H
#define FIGUREBUTTON_H
 
#include <QWidget>
#include <QMouseEvent>
#include "figure.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QVBoxLayout>
 
 
class FigureButton : public QWidget
{
    Q_OBJECT
public:
    FigureButton(const Figure::FigureType &type,
                 const int &buttonSize, QWidget* parent=nullptr);
 
    void clearButtonColor(const QColor &color);
    void setButtonChecked(const bool &st);
    bool getButtonChecked()const;
 
private:
    Figure* figure=nullptr;
    Figure::FigureType figureType;
    void figureImport();
    void figureUpdate();
    bool isChecked=false;
    QGraphicsScene *scene;
    QGraphicsView *view;
 
    QColor checkedButtonFrame{Qt::yellow},
           uncheckedButtonFrame{Qt::black},
           checkedButtonBack{Qt::white},
           uncheckedButtonBack{Qt::white},
 
           checkedFigureFrame{Qt::yellow},
           uncheckedFigureFrame{Qt::black},
           checkedFigureBack{Qt::yellow},
           uncheckedFigureBack{Qt::black};
 
signals:
    void pressed(const Figure::FigureType &);
 
protected:
    void paintEvent(QPaintEvent *) override;
    void mousePressEvent(QMouseEvent *event)override;
    //void mouseReleaseEvent(QMouseEvent *event)override;
};
 
#endif // FIGUREBUTTON_H
----------------------------------------------------------------------------------------------


figurebutton.cpp
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "figurebutton.h"
 
FigureButton::FigureButton(const Figure::FigureType &type,
                           const int &buttonSize, QWidget *parent)
    :QWidget(parent), figureType(type)
{
    setFixedSize(buttonSize,buttonSize);
 
    view=new QGraphicsView(this);
    view->setFrameShape(QFrame::NoFrame);
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setRenderHint(QPainter::Antialiasing);
 
 
    scene=new QGraphicsScene(this);
    view->setScene(scene);
    int border=15;
    view->setFixedSize(buttonSize-border,buttonSize-border);
 
    QVBoxLayout* layout=new QVBoxLayout(this);
    layout->addWidget(view);
    setLayout(layout);
 
    figureImport();
    figureUpdate();
}
 
 
void FigureButton::clearButtonColor(const QColor &color)
{
    uncheckedButtonBack=color;
    uncheckedFigureBack=color;
    update();
    figureUpdate();
}
 
 
void FigureButton::setButtonChecked(const bool &st)
{
    isChecked=st;
    figureUpdate();
    update();
}
 
bool FigureButton::getButtonChecked() const
{
    return isChecked;
}
 
 
void FigureButton::figureImport()
{
    figure=new Figure(figureType, this);
 
    qreal scale{};
    switch (figureType) {
    case Figure::NONE:return;
    case Figure::RECT:
    case Figure::SEGMENT:{scale=0.4;break;}
    case Figure::SEGMENT2:{scale=0.2;break;}
    default:break;
    }
 
    figure->setStartPoint(QPointF(0,0));
    figure->setEndPoint(QPointF(geometry().width(), geometry().height()));
 
    scene->addItem(figure);
 
    QTransform transform;
    transform.translate(figure->getFigureCenter().x(),
                        figure->getFigureCenter().y());
    transform.scale(scale,scale);
    transform.translate(-figure->getFigureCenter().x(),
                        -figure->getFigureCenter().y());
    figure->setTransform(transform);
 
    view->centerOn(figure);
}
 
void FigureButton::figureUpdate()
{
    if(figure && view){
        if(!isChecked){
            figure->setFigureColor(uncheckedFigureFrame,
                                   uncheckedFigureBack);
            view->setBackgroundBrush(QBrush(uncheckedButtonBack));
 
        }else {
            figure->setFigureColor(checkedFigureFrame,
                                   checkedFigureBack);
            view->setBackgroundBrush(QBrush(checkedButtonBack));
        }
        figure->update();
    }
}
 
 
void FigureButton::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
 
    if(!isChecked){
        painter.setPen(QPen(uncheckedButtonFrame));
        painter.setBrush(QBrush(uncheckedButtonBack));
 
    }else{
        painter.setPen(QPen(checkedButtonFrame));
        painter.setBrush(QBrush(checkedButtonBack));
    }
    int radius=width()/7;
    painter.drawRoundedRect(1,1,width()-2,height()-2,radius,radius);
}
 
 
void FigureButton::mousePressEvent(QMouseEvent *event)
{
    if(event->button()==Qt::LeftButton && !isChecked){
        setButtonChecked(true);
        figureUpdate();
        emit pressed(figureType);
    }else{
        setButtonChecked(false);
        figureUpdate();
        emit pressed(Figure::NONE);
    }
    QWidget::mousePressEvent(event);
}
 
//void FigureButton::mouseReleaseEvent(QMouseEvent *event)
//{
 
//    QWidget::mouseReleaseEvent(event);
//}
---------------------------------------------------------------------------------------


mainwindow.h
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include "figurebutton.h"
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
 
private:
    QColor currentColor{Qt::white};
    bool figureCreated=false;
    QList<FigureButton*> buttons;
 
public slots:
    void uncheckButtons();
 
signals:
    void setSceneState(const bool &);
 
 
};
#endif // MAINWINDOW_H
--------------------------------------------------------------------------------


mainwindow.cpp
Кликните здесь для просмотра всего текста
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include "mainwindow.h"
#include "customscene.h"
#include <QGraphicsView>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QColorDialog>
#include <QLabel>
 
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    resize(400,300);
 
    QGraphicsView *view=new QGraphicsView(this);
    CustomScene *scene=new CustomScene(this);
 
    view->setRenderHint(QPainter::Antialiasing);
 
    connect(this, &MainWindow::setSceneState, scene, &CustomScene::setMouseMode);
    view->setScene(scene);
 
    view->setBackgroundBrush(QBrush(Qt::lightGray));
 
    auto form=new QWidget(this);
    auto formLayout=new QHBoxLayout(form);
 
    //buttons group
    auto btnGroup=new QWidget(form);
    btnGroup->setStyleSheet("background: gray;");
    auto btnLayout=new QVBoxLayout(btnGroup);
    btnLayout->setAlignment(form, Qt::AlignLeft);
    btnGroup->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Expanding);
 
    //color dialog
    auto btnColorDialog=new FigureButton(Figure::NONE, 50 ,btnGroup);
    btnColorDialog->clearButtonColor(currentColor);
    buttons<<btnColorDialog;
 
    connect(btnColorDialog, &FigureButton::pressed,this,[this, btnColorDialog, scene]{
 
        QColor color=QColorDialog::getColor(Qt::white,this);
        color.isValid() ? currentColor=color : currentColor;
 
        btnColorDialog->clearButtonColor(currentColor);
        btnColorDialog->setButtonChecked(false);
 
        scene->currentFigureColor(currentColor);
    });
 
 
    //buttons group labels
    auto colorLabel=new QLabel(btnGroup);
    colorLabel->setText("Color:");
    QFont labelFont;
    labelFont.setBold(true);
    colorLabel->setFont(labelFont);
    colorLabel->setAlignment(Qt::AlignCenter);
    btnLayout->addWidget(colorLabel);
    btnLayout->addWidget(btnColorDialog);
 
    QLabel* btnLabel=new QLabel(btnGroup);
    btnLabel->setAlignment(Qt::AlignCenter);
    btnLabel->setFont(colorLabel->font());
    btnLabel->setText("Figures:");
    btnLayout->addWidget(btnLabel);
 
    //figure buttons
    auto btnRECT=new FigureButton(Figure::RECT, 50,btnGroup);
    connect(btnRECT, &FigureButton::pressed, scene, &CustomScene::currentFigureType);
    connect(btnRECT, &FigureButton::pressed, this, &MainWindow::uncheckButtons);
    btnLayout->addWidget(btnRECT);
    buttons<<btnRECT;
 
    auto btnSEGMENT=new FigureButton(Figure::SEGMENT, 50,btnGroup);
    btnLayout->addWidget(btnSEGMENT);
    connect(btnSEGMENT, &FigureButton::pressed, scene, &CustomScene::currentFigureType);
    connect(btnSEGMENT, &FigureButton::pressed, this, &MainWindow::uncheckButtons);
    buttons<<btnSEGMENT;
 
    auto btnSEGMENT2=new FigureButton(Figure::SEGMENT2, 50,btnGroup);
    connect(btnSEGMENT2, &FigureButton::pressed, scene, &CustomScene::currentFigureType);
    connect(btnSEGMENT2, &FigureButton::pressed, this, &MainWindow::uncheckButtons);
    btnLayout->addWidget(btnSEGMENT2);
    buttons<<btnSEGMENT2;
 
    btnLayout->addStretch();
    btnGroup->setLayout(btnLayout);
 
    //view group
    auto viewGroup=new QWidget(form);
    auto viewLayout=new QVBoxLayout(viewGroup);
    viewLayout->setAlignment(form, Qt::AlignJustify);
    viewLayout->addWidget(view);
    viewGroup->setLayout(btnLayout);
 
    //form compoze
    formLayout->addWidget(btnGroup);
    formLayout->addWidget(viewGroup);
    form->setLayout(formLayout);
 
    setCentralWidget(form);
}
 
 
void MainWindow::uncheckButtons()
{
    bool st=false;
    foreach (auto button , buttons) {
        if(button != sender()){
            button->setButtonChecked(false);
        }
        if(button->getButtonChecked()){
            st=true;
        }
    }
    emit setSceneState(st);
}
 
 
MainWindow::~MainWindow()
{
}


результат:
2
Покинул чат.
1132 / 727 / 195
Регистрация: 30.03.2021
Сообщений: 2,379
19.05.2022, 02:00
зы: код особо не тестил, так что если где чего упустил/недопилил/набыдлокодил-на критику не обижаюсь.
1
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 735
20.05.2022, 16:42  [ТС]
sdf45, гениально! Огромное Вам спасибо!

Цитата Сообщение от sdf45 Посмотреть сообщение
на критику не обижаюсь
Как от матлабиста критики с моей стороны быть не может однако, если Вы не возражаете, существует пара вопросов, поскольку я не вполне осознаю некоторые моменты.
Скажите, пожалуйста, правильно ли я понимаю, что именно НЕОБХОДИМО создавать кастомную граф. сцену во всех подобных ситуациях, поскольку стандартным событием QMouseEvent, обрабатывающимся непосредственно внутри mainwindow.cpp, не получится корректно (и/или без костылей) обрабатывать события мыши на граф. сцене?
Также я правда не понимаю - оно (Qt) правда не умеет само определять нажатие на конкретный объект на сцене? То есть, matlab, например, работает иначе - при помощи одного и того же обработчика событий нажатия на клавиши мыши верхнего уровня (уровня всего GUI) делается абсолютно всё - только код успевай писать; так, например, нажатие на граф. поле там провоцирует отклик граф. элемента, по которому попали (и если их в этой точке несколько, выбирается лежащий на верхнем слое). Я вижу в Вашем файле кучку коннектов, с механизмом работы которых я ещё не разобрался, но мне уже страшно Правильно ли я понимаю, что эти connect'ы как раз и заставляют нажатие мышью на объект обрабатываться исключительным образом, минуя главное окно, а за тем и сцену?
И не могли бы Вы рассказать, что именно делает класс figurebutton? Это тот ряд кнопок на панели слева?

Добавлено через 11 минут
P.S. Про figurebutton - я тупой - вопрос снимаю

Добавлено через 49 минут
P.P.S. А, и для чего происходит рекурсивный вызов функций, вроде QGraphicsScene::mousePressEvent(mouseEve nt); ? Де-факто, рекурсивной эта конструкция не оказывается, однако выглядит именно так, и предназначение её не вполне ясно, ведь работает и без этого вызова.
0
Покинул чат.
1132 / 727 / 195
Регистрация: 30.03.2021
Сообщений: 2,379
20.05.2022, 21:08
Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
правильно ли я понимаю, что именно НЕОБХОДИМО создавать кастомную граф. сцену во всех подобных ситуациях, поскольку стандартным событием QMouseEvent, обрабатывающимся непосредственно внутри mainwindow.cpp, не получится корректно (и/или без костылей) обрабатывать события мыши на граф. сцене?
Кликните здесь для просмотра всего текста

C++ (Qt)
1
2
void QWidget           ::mousePressEvent(QMouseEvent *event);
void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent);
Обратите внимание на класс указателя на событие мыши.
В первом случае ловим событие виджетом (который для QGraphicsScene ,будет QGraphicsView, на котором расположена сцена)
Во втором случае ловим непосредственно сценой, виджет событий не получает. Можно конечно ловить события виджетом окна, переводить координаты из глобальных в коорд. систему сцены, но зачем, если можно проще.

Что касается кастомной сцены-совсем не обязательно, если нет необходимости в дополнительном функционале. Вы можете обрабатывать события в кастомном графическом элементе, порожденным от QGraphicsItem, либо установить для него флаги itemIsMovable | itemIsSelextable | itemIsFocusable - и Qt все сделает сам - итем можно будет выделять, перемещать, а так же обрабатывать события Hover


Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
для чего происходит рекурсивный вызов функций, вроде QGraphicsScene::mousePressEvent(mouseEve nt); ?
это не рекурсивный вызов, а вызов метода базового класса, ведь CustomScene порожден от QGraphicsScene, и мы переопределяем виртуальный метод mousePressEvent() - более подробно есть в доках по классам.

Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
гениально!
с кнопками я набыдлокодил, надо было на базе QCheckBox`e делать, и использовать QToolBox, QButtonGroup, но переделывать уже было лень

Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
Я вижу в Вашем файле кучку коннектов, с механизмом работы которых я ещё не разобрался
Кликните здесь для просмотра всего текста

Когда кликаем (ловим это событие) в графической сцене, мы должны знать, нажата ли кнопка создания фигуры на форме.
Если кнопка нажата, то активируем флаг "создаем фигуру", первый клик мышкой на сцене определяет первую точку фигуры, двигая мышью и нажав лкм второй раз-определяем вторую точку фигуры, создаем фигуру и добавляем на сцену.
C++ (Qt)
1
connect(btnRECT, &FigureButton::pressed, scene, &CustomScene::currentFigureType);
Сигнал pressed при этом передает сцене тип фигуры, которую следует создать. При первом нажатии делаем вид кнопки Checable и отсылаем тип фигуры в сцену. Если кнопка была нажата, и нажали еще раз-кнопка принимает вид UnChecable, а сигнал передает в сцену тип фигуры NONE.
Так же если мы нажимаем на кнопку, остальные кнопки надо отжать, поэтому связываем
C++ (Qt)
1
     connect(btnSEGMENT2, &FigureButton::pressed, this, &MainWindow::uncheckButtons);
uncheckButtons перебирает кнопки в контейнере buttons (куда добавляем все кнопки, когда создаем их), и если там есть нажатые кнопки, и они не являются нажатой кнопкой в данный момент-отжимаем их.
Всю эту канитель надо было делать на QGroupButton как я выше написал.

Если ниодна из кнопок не нажата, в сцене устанавливается флаг "выбираем и двигаем фигуры", при котором при нажатии лкм определяется, есть ли фигура под курсором (itemAt) и если есть-сохраняем в поле класса. Если двигаем мышкой-двигаем и эту сохраненную фигуру (setPos). Там еще я несколько криво сделал манипуляции с zValue фигур, но переделывать тоже лень.


Цитата Сообщение от Ромуальд_7 Посмотреть сообщение
Огромное Вам спасибо!
Всегда рад помочь.
1
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 735
21.05.2022, 22:28  [ТС]
Цитата Сообщение от sdf45 Посмотреть сообщение
это не рекурсивный вызов, а вызов метода базового класса
Да, да, да - я слепой, спасибо
Также благодарю и за прочие разъяснения - действительно стало понятнее.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
21.05.2022, 22:28
Помогаю со студенческими работами здесь

Qt. Как задать точное расположение на графической сцене объекту класса, наследованного от QWidget
Как задать точное расположение на графической сцене объекту класса, наследованного от QWidget. Дело в том, что для QGrpahicsItem есть метод...

Поиск объектов на сцене
Как проверить наличие на сцене 2х объектов с одинаковыми названиями?Мне нужно чтобы за каждый одинаковый объект из переменной вычиталось...

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

Перемещение объектов по сцене
я написал код на Яве, но в FX он не работает, как исправить? точнее от ругается на oldX и хочет переопределить в ...

Проверка на наличие объектов на сцене
Доброго времени суток у меня есть данный код int DipDrop = Random.Range(0, 101); if (DipDrop &lt;= ChansDropa) ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru