Реклама

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

QGraphicsScene, Qt, Qt урок, Qt5, рисование, рисование QGraphicsScene, Рисование Qt, рисование мышью

А в этом уроке освоим рисование мышью в 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

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

Реклама

Комментарии

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

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

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

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

#include <QGraphicsScene>

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

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

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

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
  • falcon
  • 16 января 2018 г. 17:25

Qt - Тест 001. Сигналы и слоты

  • Результат 100 баллов
  • Очки рейтинга 10
  • falcon
  • 16 января 2018 г. 17:22

Qt - Тест 001. Сигналы и слоты

  • Результат 68 баллов
  • Очки рейтинга -1
  • falcon
  • 16 января 2018 г. 17:18

C++ - Тест 001. Первая программа и типы данных

  • Результат 73 баллов
  • Очки рейтинга 1
Последние комментарии

QML - Урок 021. Переключение между окнами в QML

Спасибо всем. Все получилось. Прикручиваю логику.

  • BlinCT
  • 14 января 2018 г. 19:28

Разработка на Qt под iOS

Вот честно, на сколько же муторно под огрызок что то делать. Куча проблем) А вод линь или под Андроид все просто и тривиально))

  • folax
  • 12 января 2018 г. 9:16

QML - Урок 021. Переключение между окнами в QML

Ничего сложного, делаете по тех заданию 3 файла qml, называете их как указанно в тех задании, потом из первого окна через Loader их переключаете, в окне 2 и 3 делаете сигналы которые при закры...

QML - Урок 021. Переключение между окнами в QML

Все верно, я и не говорил что этот кусок кода лично мое произведение. Это тоже верно: Это задание для прохождения на собеседование в одну из крупных украинских IT компаний. Логику ...

  • folax
  • 12 января 2018 г. 8:13

QML - Урок 021. Переключение между окнами в QML

int main(int argc, char *argv[]){ QApplication app(argc, argv); Logic logic; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("logic", &logic)...

Сейчас обсуждают на форуме

ChartView. Отображение метки данных точки серии при наведении курсора

Спасибо большущее за советы! Все получилось через ScatterSeries. Методы remove() как-то сходу не дались, удаляет в первый раз, а потом программа падает... Не стал тратить время и воспользовалс...

  • EVILEG
  • 16 января 2018 г. 14:23

Как проверить доступность сервера

Добрый день! Теоретически можно использовать QTcpSocket, у него есть метод connectToHost. Возможно, что проверка доступности через этот класс будет осуществляться несколько быстрее,...

QGraphicsScene

спасибо, за подробное объяснение строчки, а с зумом я разобрался, все работает

  • EVILEG
  • 15 января 2018 г. 17:21

Qt webgl

Насчёт проверки подключения клиента я не в курсе. Что касается экземпляров приложения, то из того, что я читал получается, что нет необходимости в нескольких экземплярах для нескольких кл...

  • EVILEG
  • 15 января 2018 г. 11:39

Проблема добавления #DEFINE при сборке CMak'ом

А Вы не пробовали сделать предкомпилированные библиотеки boost под свою систему, а потом уже подключать собранные библиотеки Boost`а? Просто один только boost может собираться на пару гиг...