На цьому уроці освоїмо малювання мишею в 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
Евгений, исправил: благодарю! Всё работает!