А в этом уроке освоим рисование мышью в Qt на основе примитивнейшего аналога Paint с использованием QGraphicsScene . Никаких регулировок, ни размеров кисти, ни палитры, ни спецэффектов, а просто красная линия, которую мы будем рисовать мышью.
Задача поставлена - вперёд исполнять!
Структура проекта
Структура проекта В структуру проекта входят следующие файлы:
- paint.h - заголовочный файл виджета, в котором будет располагаться графическая сцена для рисования;
- paint.cpp - соответственно файл исходных кодов для этого виджета;
- paintscene.h - заголовочный файл кастомизированной графической сцены, с которой мы будем работать;
- paintscene.cpp - файл исходных кодов кастомизированной графической сцены.
paint.ui
Форма главного окна приложения состоит из самого виджета и помещенного в него объекта QGraphicsView.
paint.h
В данном файле объявляется кастомизированная графическая сцена, а также таймер со слотом для этого таймера, который служит для корректной обработки изменения размеров окна приложения.
- #ifndef PAINT_H
- #define PAINT_H
- #include <QWidget>
- #include <QTimer>
- #include <QResizeEvent>
- #include <paintscene.h>
- namespace Ui {
- class Paint;
- }
- class Paint : public QWidget
- {
- Q_OBJECT
- public:
- explicit Paint(QWidget *parent = 0);
- ~Paint();
- private:
- Ui::Paint *ui;
- QTimer *timer; /* Определяем таймер для подготовки актуальных размеров
- * графической сцены
- * */
- paintScene *scene; // Объявляем кастомную графическую сцену
- private:
- /* Переопределяем событие изменения размера окна
- * для пересчёта размеров графической сцены
- * */
- void resizeEvent(QResizeEvent * event);
- private slots:
- void slotTimer();
- };
- #endif // PAINT_H
paint.cpp
В данном классе происходит добавление кастомизированной графической сцены в объект класса QGraphicsView , фактически в целях обучения программный код из данной части примера не имеет особого отношения к самому процессу рисования, но отработка изменения размеров окна включена в данный пример для полноты картины. Само рисование происходит исключительно в кастомизированной графической сцене.
- #include "paint.h"
- #include "ui_paint.h"
- Paint::Paint(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::Paint)
- {
- ui->setupUi(this);
- scene = new paintScene(); // Инициализируем графическую сцену
- ui->graphicsView->setScene(scene); // Устанавливаем графическую сцену
- timer = new QTimer(); // Инициализируем таймер
- connect(timer, &QTimer::timeout, this, &Paint::slotTimer);
- timer->start(100); // Запускаем таймер
- }
- Paint::~Paint()
- {
- delete ui;
- }
- void Paint::slotTimer()
- {
- /* Переопределяем размеры графической сцены в зависимости
- * от размеров окна
- * */
- timer->stop();
- scene->setSceneRect(0,0, ui->graphicsView->width() - 20, ui->graphicsView->height() - 20);
- }
- void Paint::resizeEvent(QResizeEvent *event)
- {
- timer->start(100);
- QWidget::resizeEvent(event);
- }
paintscene.h
А вот и заголовочный файл виновника данного примера. Рисование происходит с помощью линий, посредством обработки события mouseMoveEvent. Для этого переопределяется функция mouseMoveEvent в которой по двум координатам, первая от прошлого события и вторая от текущего, строятся красные линии, которые в итоге образуют общую кривую. Но чтобы при отпускании кнопки мыши и повторном нажатии мы могли рисовать новую линию, а не продолжать старую, переопределяем функцию mousePressEvent.
В mousePressEvent отрисовывается эллипс, который служит стартовой точкой рисования кривой мышью. Этот метод обновляет значения первой координаты для первой линии, таким образом разрывая и отделяя старую кривую от новой. В видеоуроке это продемонстрировано. Точка первой координаты для линии сохраняется в объект previousPoint.
- #ifndef PAINTSCENE_H
- #define PAINTSCENE_H
- #include <QGraphicsScene>
- #include <QGraphicsSceneMouseEvent>
- #include <QTimer>
- #include <QDebug>
- class paintScene : public QGraphicsScene
- {
- Q_OBJECT
- public:
- explicit paintScene(QObject *parent = 0);
- ~paintScene();
- private:
- QPointF previousPoint; // Координаты предыдущей точки
- private:
- // Для рисования используем события мыши
- void mousePressEvent(QGraphicsSceneMouseEvent * event);
- void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
- };
- #endif // PAINTSCENE_H
paintscene.cpp
В данном файле вся работа с рисованием происходит в методах mouseMoveEvent и mousePressEvent. Тогда как в конструкторе класса вообще не происходит какой-либо инициализации.
- #include "paintscene.h"
- paintScene::paintScene(QObject *parent) : QGraphicsScene(parent)
- {
- }
- paintScene::~paintScene()
- {
- }
- void paintScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
- {
- // При нажатии кнопки мыши отрисовываем эллипс
- addEllipse(event->scenePos().x() - 5,
- event->scenePos().y() - 5,
- 10,
- 10,
- QPen(Qt::NoPen),
- QBrush(Qt::red));
- // Сохраняем координаты точки нажатия
- previousPoint = event->scenePos();
- }
- void paintScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
- {
- // Отрисовываем линии с использованием предыдущей координаты
- addLine(previousPoint.x(),
- previousPoint.y(),
- event->scenePos().x(),
- event->scenePos().y(),
- QPen(Qt::red,10,Qt::SolidLine,Qt::RoundCap));
- // Обновляем данные о предыдущей координате
- previousPoint = event->scenePos();
- }
Итог
В результате Вы сможете рисовать на графической сцене красные линии, а как дальше уже развить эти возможности - это уже зависит от Вас.
Также вы можете ознакомиться с комментариями и демонстрацией проекта в видеоуроке по данной статье.
Рисование в Qt Архив с исходным кодом проекта: Qt paint
Здравствуйте! Спасибо за подробное описание данного примера, а вы бы не могли показать, что все это время происходило в main.cpp?
Потому что при использовании данного примера я получила ошибку:
321: ошибка: 'addLine' was not declared in this scope
QPen(Qt::red,10,Qt::SolidLine));
^
И я просто не могу понять, что же не так?
Добрый день!
main.cpp остаётся созданным по умолчанию, там ничего интересного для Вас нет.
А ошибка говорит о том, что у Вас отсутствует необходимый заголовочный файл, вернее его подключение.
Думаю, что Вы пропустили следующий include
Нет, не пропустила, есть инклюд
Полагаю, ошибка в вызове в главном окне этой сцены.
Или в чем-то еще, ибо он не заходит в эту библиотеку и соотвественно не видит addline
то есть вызываете этот метод так?
Вообще такая ошибка говорит о том, что метод или функция не объявлены, а это значит зачастую, что пропущен необходимый инклюд.А почему mouseMoveEvent() выполняется лишь при на нажатой кнопки мыши?
Чтобы включить такое поведение, необходимо вызвать метод setMouseTracking(true) у QGraphicsView, в котором расположена графическая сцена, тогда события движения мыши должны передаваться при движении курсора как с нажатыми клавишами мыши, так и без.
Спасибо, попробовал. Работает )
Я задал в конструкторе Paint картинку на лэйбел. Скажите пожалуйста, как рисовать на mapImage.
и я унаследовал клас рисовальщик так, но в нем нет таких сигналов, какие используется в .срр:
Вообще не понимаю, зачем вы используете QLabel для этого. Используйте графическую сцену и рисуйте на ней. QLabel был написан для отображения текста и картинок, но никак не для рисования.
И естественно, что там не будет тех сигналов и методов, потому что PaintScene нужно наследовать от QGraphicsScene. Рисовать нужно на графической сцене.
Добрый день. Извините за нюбский вопрос.
Данный проект у меня работает, что вызвало удивление. Так как я не нашел слотов, принимающих события от void mousePressEvent(QGraphicsSceneMouseEvent * event); и void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
Я пока разбираюсь со слотами, обьясните пожалуйста - выходит если сигнал (в данном случае событие) описан внутри класса, то для обьектов класса слот не нужен ?
Добрый день. Это переопределённые методы из базового класса, они не являются слотами и вызываются в очереди событий внутри ядра Qt фреймворка. В обычном программировании на Qt никто не ищет откуда они конкретно вызываются. Для этого нужно копаться в исходниках Qt. То есть сигнала нет внутри класса и сигналы нужны для слотов, но в данном конкретном случае к вызову этих методов обобщённо говоря сигналы отношения не имеют.
Здравствуйте! Не понимаю в чем проблема, ui(new Ui::Paint) обозначается как недопустимый неполный тип и программа не запускается. Уже скопировал тупо только ваш код без собственных фрагментов, но все та же проблема. Помогите пожалуйста, срочно! Программирую в среде Visual Studio 2019.
upd: все исправил
Евгений, здравствуйте! Только начал изучение Qt и возник вопрос по 21ому уроку. После написания кода, выдаёт следующие ошибки

В чём может быть проблема?
В UI файле не был добавлен QGraphicsView объект с object name graphicsView
Евгений, исправил: благодарю! Всё работает!