Evgenii Legotckoi
24 сентября 2015 г. 20:56

Qt/C++ - Урок 021. Рисование мышью в Qt

А в этом уроке освоим рисование мышью в Qt на основе примитивнейшего аналога Paint с использованием QGraphicsScene . Никаких регулировок, ни размеров кисти, ни палитры, ни спецэффектов, а просто красная линия, которую мы будем рисовать мышью.

Задача поставлена - вперёд исполнять!

Структура проекта

Структура проекта В структуру проекта входят следующие файлы:

  • paint.h - заголовочный файл виджета, в котором будет располагаться графическая сцена для рисования;
  • paint.cpp - соответственно файл исходных кодов для этого виджета;
  • paintscene.h - заголовочный файл кастомизированной графической сцены, с которой мы будем работать;
  • paintscene.cpp - файл исходных кодов кастомизированной графической сцены.

paint.ui

Форма главного окна приложения состоит из самого виджета и помещенного в него объекта QGraphicsView.

paint.h

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

  1. #ifndef PAINT_H
  2. #define PAINT_H
  3.  
  4. #include <QWidget>
  5. #include <QTimer>
  6. #include <QResizeEvent>
  7.  
  8. #include <paintscene.h>
  9.  
  10. namespace Ui {
  11. class Paint;
  12. }
  13.  
  14. class Paint : public QWidget
  15. {
  16. Q_OBJECT
  17.  
  18. public:
  19. explicit Paint(QWidget *parent = 0);
  20. ~Paint();
  21.  
  22. private:
  23. Ui::Paint *ui;
  24. QTimer *timer; /* Определяем таймер для подготовки актуальных размеров
  25. * графической сцены
  26. * */
  27. paintScene *scene; // Объявляем кастомную графическую сцену
  28.  
  29. private:
  30. /* Переопределяем событие изменения размера окна
  31. * для пересчёта размеров графической сцены
  32. * */
  33. void resizeEvent(QResizeEvent * event);
  34.  
  35. private slots:
  36. void slotTimer();
  37. };
  38.  
  39. #endif // PAINT_H

paint.cpp

В данном классе происходит добавление кастомизированной графической сцены в объект класса QGraphicsView , фактически в целях обучения программный код из данной части примера не имеет особого отношения к самому процессу рисования, но отработка изменения размеров окна включена в данный пример для полноты картины. Само рисование происходит исключительно в кастомизированной графической сцене.

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

paintscene.h

А вот и заголовочный файл виновника данного примера. Рисование происходит с помощью линий, посредством обработки события mouseMoveEvent. Для этого переопределяется функция mouseMoveEvent в которой по двум координатам, первая от прошлого события и вторая от текущего, строятся красные линии, которые в итоге образуют общую кривую. Но чтобы при отпускании кнопки мыши и повторном нажатии мы могли рисовать новую линию, а не продолжать старую, переопределяем функцию mousePressEvent.

В mousePressEvent отрисовывается эллипс, который служит стартовой точкой рисования кривой мышью. Этот метод обновляет значения первой координаты для первой линии, таким образом разрывая и отделяя старую кривую от новой. В видеоуроке это продемонстрировано. Точка первой координаты для линии сохраняется в объект previousPoint.

  1. #ifndef PAINTSCENE_H
  2. #define PAINTSCENE_H
  3.  
  4. #include <QGraphicsScene>
  5. #include <QGraphicsSceneMouseEvent>
  6. #include <QTimer>
  7. #include <QDebug>
  8.  
  9. class paintScene : public QGraphicsScene
  10. {
  11.  
  12. Q_OBJECT
  13.  
  14. public:
  15. explicit paintScene(QObject *parent = 0);
  16. ~paintScene();
  17.  
  18. private:
  19. QPointF previousPoint; // Координаты предыдущей точки
  20.  
  21. private:
  22. // Для рисования используем события мыши
  23. void mousePressEvent(QGraphicsSceneMouseEvent * event);
  24. void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
  25.  
  26. };
  27.  
  28. #endif // PAINTSCENE_H

paintscene.cpp

В данном файле вся работа с рисованием происходит в методах mouseMoveEvent и mousePressEvent. Тогда как в конструкторе класса вообще не происходит какой-либо инициализации.

  1. #include "paintscene.h"
  2.  
  3. paintScene::paintScene(QObject *parent) : QGraphicsScene(parent)
  4. {
  5.  
  6. }
  7.  
  8. paintScene::~paintScene()
  9. {
  10.  
  11. }
  12.  
  13. void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
  14. {
  15. // При нажатии кнопки мыши отрисовываем эллипс
  16. addEllipse(event->scenePos().x() - 5,
  17. event->scenePos().y() - 5,
  18. 10,
  19. 10,
  20. QPen(Qt::NoPen),
  21. QBrush(Qt::red));
  22. // Сохраняем координаты точки нажатия
  23. previousPoint = event->scenePos();
  24. }
  25.  
  26. void paintScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
  27. {
  28. // Отрисовываем линии с использованием предыдущей координаты
  29. addLine(previousPoint.x(),
  30. previousPoint.y(),
  31. event->scenePos().x(),
  32. event->scenePos().y(),
  33. QPen(Qt::red,10,Qt::SolidLine,Qt::RoundCap));
  34. // Обновляем данные о предыдущей координате
  35. previousPoint = event->scenePos();
  36. }

Итог

В результате Вы сможете рисовать на графической сцене красные линии, а как дальше уже развить эти возможности - это уже зависит от Вас.

Также вы можете ознакомиться с комментариями и демонстрацией проекта в видеоуроке по данной статье.

Рисование в Qt Архив с исходным кодом проекта: Qt paint

Видеоурок про рисование в Qt

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

Р
  • 25 августа 2017 г. 14:00

Здравствуйте! Спасибо за подробное описание данного примера, а вы бы не могли показать, что все это время происходило в main.cpp?
Потому что при использовании данного примера я получила ошибку:

321: ошибка: 'addLine' was not declared in this scope
QPen(Qt::red,10,Qt::SolidLine));
^
И я просто не могу понять, что же не так?

Evgenii Legotckoi
  • 25 августа 2017 г. 14:20

Добрый день!
main.cpp остаётся созданным по умолчанию, там ничего интересного для Вас нет.
А ошибка говорит о том, что у Вас отсутствует необходимый заголовочный файл, вернее его подключение.

Думаю, что Вы пропустили следующий include

#include <QGraphicsScene>
Р
  • 25 августа 2017 г. 16:04

Нет, не пропустила, есть инклюд
Полагаю, ошибка в вызове в главном окне этой сцены.
Или в чем-то еще, ибо он не заходит в эту библиотеку и соотвественно не видит addline

Evgenii Legotckoi
  • 25 августа 2017 г. 16:25

то есть вызываете этот метод так?

void mainWindow::someMethod()
{
    graphicsScene->addLine();
}
Вообще такая ошибка говорит о том, что метод или функция не объявлены, а это значит зачастую, что пропущен необходимый инклюд.
a
  • 11 мая 2018 г. 12:28

А почему mouseMoveEvent() выполняется лишь при на нажатой кнопки мыши?

По сигнатуре функции я предположил, что метод должен вызываться сценой при простом движением мышки по сцене.
Evgenii Legotckoi
  • 11 мая 2018 г. 13:10

Чтобы включить такое поведение, необходимо вызвать метод setMouseTracking(true) у QGraphicsView, в котором расположена графическая сцена, тогда события движения мыши должны передаваться при движении курсора как с нажатыми клавишами мыши, так и без.


Paint::Paint(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Paint)
{
    ui->setupUi(this);
 
    scene = new paintScene();       
    ui->graphicsView->setScene(scene);  
    ui->graphicsView->setMouseTracking(true);
 
}
a
  • 11 мая 2018 г. 13:56

Спасибо, попробовал. Работает )

Михаиллл
  • 10 июня 2019 г. 12:43
  • (ред.)

Я задал в конструкторе Paint картинку на лэйбел. Скажите пожалуйста, как рисовать на mapImage.

  1. QImage mapImage("C:\\Users\\New Owner\\Downloads\\mapMain.png");
  2. ui->label->setPixmap(QPixmap::fromImage(mapImage,Qt::AutoColor));

и я унаследовал клас рисовальщик так, но в нем нет таких сигналов, какие используется в .срр:

  1. class paintScene : public QLabel
Evgenii Legotckoi
  • 10 июня 2019 г. 13:52

Вообще не понимаю, зачем вы используете QLabel для этого. Используйте графическую сцену и рисуйте на ней. QLabel был написан для отображения текста и картинок, но никак не для рисования.

И естественно, что там не будет тех сигналов и методов, потому что PaintScene нужно наследовать от QGraphicsScene. Рисовать нужно на графической сцене.

ИВ
  • 29 апреля 2020 г. 3:12

Добрый день. Извините за нюбский вопрос.
Данный проект у меня работает, что вызвало удивление. Так как я не нашел слотов, принимающих события от void mousePressEvent(QGraphicsSceneMouseEvent * event); и void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
Я пока разбираюсь со слотами, обьясните пожалуйста - выходит если сигнал (в данном случае событие) описан внутри класса, то для обьектов класса слот не нужен ?

Evgenii Legotckoi
  • 29 апреля 2020 г. 4:31

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

f
  • 10 октября 2022 г. 4:40
  • (ред.)

Здравствуйте! Не понимаю в чем проблема, ui(new Ui::Paint) обозначается как недопустимый неполный тип и программа не запускается. Уже скопировал тупо только ваш код без собственных фрагментов, но все та же проблема. Помогите пожалуйста, срочно! Программирую в среде Visual Studio 2019.

f
  • 10 октября 2022 г. 7:18

upd: все исправил

J
  • 30 марта 2023 г. 21:57

Евгений, здравствуйте! Только начал изучение Qt и возник вопрос по 21ому уроку. После написания кода, выдаёт следующие ошибки

В чём может быть проблема?

Evgenii Legotckoi
  • 16 апреля 2023 г. 14:03

В UI файле не был добавлен QGraphicsView объект с object name graphicsView

J
  • 19 апреля 2023 г. 15:18
  • (ред.)

Евгений, исправил: благодарю! Всё работает!

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь