Evgenii Legotckoi
Evgenii Legotckoi24 вересня 2015 р. 10:56

Qt/C++ - Урок 021. Миша для малювання в Qt

На цьому уроці освоїмо малювання мишею в 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

Відеоурок про малювання в Qt

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Р
  • 25 серпня 2017 р. 04:00

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

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

Evgenii Legotckoi
  • 25 серпня 2017 р. 04:20

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

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

#include <QGraphicsScene>
Р
  • 25 серпня 2017 р. 06:04

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

Evgenii Legotckoi
  • 25 серпня 2017 р. 06:25

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

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

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

По сигнатуре функции я предположил, что метод должен вызываться сценой при простом движением мышки по сцене.
Evgenii Legotckoi
  • 11 травня 2018 р. 03: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 р. 03:56

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

Михаиллл
  • 10 червня 2019 р. 02:43
  • (відредаговано)

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

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

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

class paintScene : public QLabel
Evgenii Legotckoi
  • 10 червня 2019 р. 03:52

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

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

ИВ
  • 28 квітня 2020 р. 17:12

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

Evgenii Legotckoi
  • 28 квітня 2020 р. 18:31

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

f
  • 09 жовтня 2022 р. 18:40
  • (відредаговано)

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

f
  • 09 жовтня 2022 р. 21:18

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

J
  • 30 березня 2023 р. 11:57

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

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

Evgenii Legotckoi
  • 16 квітня 2023 р. 04:03

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

J
  • 19 квітня 2023 р. 05:18
  • (відредаговано)

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 01:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 01:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 11:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 14:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 06:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 03:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 09:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Слідкуйте за нами в соціальних мережах