На цьому уроці освоїмо малювання мишею в Qt з урахуванням примітивного аналога Paint з використанням QGraphicsScene . Жодних регулювань, ні розмірів кисті, ні палітри, ні спецефектів, а просто червона лінія, яку ми малюватимемо мишею.
Завдання поставлене – вперед виконувати!
Структура проекта
Структура проекту До структури проекту входять такі файли:
- paint.h - заголовний файл віджету, в якому розташовуватиметься графічна сцена для малювання;
- paint.cpp - відповідно файл вихідних кодів для цього віджету;
- paintscene.h - заголовний файл кастомізованої графічної сцени, з якої ми працюватимемо;
- paintscene.cpp – файл вихідних кодів кастомізованої графічної сцени.
paint.ui
Форма головного вікна програми складається з самого віджету та вміщеного в нього об'єкта QGraphicsView.
фарба.ч
У цьому файлі оголошується кастомізована графічна сцена, а також таймер зі слотом для цього таймера, який слугує для коректної обробки зміни розмірів вікна програми.
- #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
Евгений, исправил: благодарю! Всё работает!